diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix
index 5b714c384de3..11ea169fe269 100644
--- a/nixos/modules/services/mail/mailman.nix
+++ b/nixos/modules/services/mail/mailman.nix
@@ -6,7 +6,7 @@ let
cfg = config.services.mailman;
- inherit (pkgs.mailmanPackages.buildEnvs { withHyperkitty = cfg.hyperkitty.enable; })
+ inherit (pkgs.mailmanPackages.buildEnvs { withHyperkitty = cfg.hyperkitty.enable; withLDAP = cfg.ldap.enable; })
mailmanEnv webEnv;
withPostgresql = config.services.postgresql.enable;
@@ -87,6 +87,114 @@ in {
description = "Enable Mailman on this host. Requires an active MTA on the host (e.g. Postfix).";
};
+ ldap = {
+ enable = mkEnableOption "LDAP auth";
+ serverUri = mkOption {
+ type = types.str;
+ example = "ldaps://ldap.host";
+ description = ''
+ LDAP host to connect against.
+ '';
+ };
+ bindDn = mkOption {
+ type = types.str;
+ example = "cn=root,dc=nixos,dc=org";
+ description = ''
+ Service account to bind against.
+ '';
+ };
+ bindPasswordFile = mkOption {
+ type = types.str;
+ example = "/run/secrets/ldap-bind";
+ description = ''
+ Path to the file containing the bind password of the servie account
+ defined by .
+ '';
+ };
+ superUserGroup = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ example = "cn=admin,ou=groups,dc=nixos,dc=org";
+ description = ''
+ Group where a user must be a member of to gain superuser rights.
+ '';
+ };
+ userSearch = {
+ query = mkOption {
+ type = types.str;
+ example = "(&(objectClass=inetOrgPerson)(|(uid=%(user)s)(mail=%(user)s)))";
+ description = ''
+ Query to find a user in the LDAP database.
+ '';
+ };
+ ou = mkOption {
+ type = types.str;
+ example = "ou=users,dc=nixos,dc=org";
+ description = ''
+ Organizational unit to look up a user.
+ '';
+ };
+ };
+ groupSearch = {
+ type = mkOption {
+ type = types.enum [
+ "posixGroup" "groupOfNames" "memberDNGroup" "nestedMemberDNGroup" "nestedGroupOfNames"
+ "groupOfUniqueNames" "nestedGroupOfUniqueNames" "activeDirectoryGroup" "nestedActiveDirectoryGroup"
+ "organizationalRoleGroup" "nestedOrganizationalRoleGroup"
+ ];
+ default = "posixGroup";
+ apply = v: "${toUpper (substring 0 1 v)}${substring 1 (stringLength v) v}Type";
+ description = ''
+ Type of group to perform a group search against.
+ '';
+ };
+ query = mkOption {
+ type = types.str;
+ example = "(objectClass=groupOfNames)";
+ description = ''
+ Query to find a group associated to a user in the LDAP database.
+ '';
+ };
+ ou = mkOption {
+ type = types.str;
+ example = "ou=groups,dc=nixos,dc=org";
+ description = ''
+ Organizational unit to look up a group.
+ '';
+ };
+ };
+ attrMap = {
+ username = mkOption {
+ default = "uid";
+ type = types.str;
+ description = ''
+ LDAP-attribute that corresponds to the username-attribute in mailman.
+ '';
+ };
+ firstName = mkOption {
+ default = "givenName";
+ type = types.str;
+ description = ''
+ LDAP-attribute that corresponds to the firstName-attribute in mailman.
+ '';
+ };
+ lastName = mkOption {
+ default = "sn";
+ type = types.str;
+ description = ''
+ LDAP-attribute that corresponds to the lastName-attribute in mailman.
+ '';
+ };
+ email = mkOption {
+ default = "mail";
+ type = types.str;
+ description = ''
+ LDAP-attribute that corresponds to the email-attribute in mailman.
+ '';
+ };
+ };
+ };
+
enablePostfix = mkOption {
type = types.bool;
default = true;
@@ -274,6 +382,34 @@ in {
with open('/var/lib/mailman-web/settings_local.json') as f:
globals().update(json.load(f))
+
+ ${optionalString (cfg.ldap.enable) ''
+ import ldap
+ from django_auth_ldap.config import LDAPSearch, ${cfg.ldap.groupSearch.type}
+ AUTH_LDAP_SERVER_URI = "${cfg.ldap.serverUri}"
+ AUTH_LDAP_BIND_DN = "${cfg.ldap.bindDn}"
+ with open("${cfg.ldap.bindPasswordFile}") as f:
+ AUTH_LDAP_BIND_PASSWORD = f.read().rstrip('\n')
+ AUTH_LDAP_USER_SEARCH = LDAPSearch("${cfg.ldap.userSearch.ou}",
+ ldap.SCOPE_SUBTREE, "${cfg.ldap.userSearch.query}")
+ AUTH_LDAP_GROUP_TYPE = ${cfg.ldap.groupSearch.type}()
+ AUTH_LDAP_GROUP_SEARCH = LDAPSearch("${cfg.ldap.groupSearch.ou}",
+ ldap.SCOPE_SUBTREE, "${cfg.ldap.groupSearch.query}")
+ AUTH_LDAP_USER_ATTR_MAP = {
+ ${concatStrings (flip mapAttrsToList cfg.ldap.attrMap (key: value: ''
+ "${key}": "${value}",
+ ''))}
+ }
+ ${optionalString (cfg.ldap.superUserGroup != null) ''
+ AUTH_LDAP_USER_FLAGS_BY_GROUP = {
+ "is_superuser": "${cfg.ldap.superUserGroup}"
+ }
+ ''}
+ AUTHENTICATION_BACKENDS = (
+ "django_auth_ldap.backend.LDAPBackend",
+ "django.contrib.auth.backends.ModelBackend"
+ )
+ ''}
'';
services.nginx = mkIf (cfg.serve.enable && cfg.webHosts != []) {
diff --git a/pkgs/development/python-modules/privacyidea-ldap-proxy/default.nix b/pkgs/development/python-modules/privacyidea-ldap-proxy/default.nix
index 455f4a6e2294..417113537b9c 100644
--- a/pkgs/development/python-modules/privacyidea-ldap-proxy/default.nix
+++ b/pkgs/development/python-modules/privacyidea-ldap-proxy/default.nix
@@ -1,4 +1,4 @@
-{ lib, buildPythonPackage, fetchFromGitHub, twisted, ldaptor, configobj }:
+{ lib, buildPythonPackage, fetchFromGitHub, twisted, ldaptor, configobj, fetchpatch }:
buildPythonPackage rec {
pname = "privacyidea-ldap-proxy";
@@ -11,6 +11,14 @@ buildPythonPackage rec {
sha256 = "1i2kgxqd38xvb42qj0a4a35w4vk0fyp3n7w48kqmvrxc77p6r6i8";
};
+ patches = [
+ # support for LDAPCompareRequest.
+ (fetchpatch {
+ url = "https://github.com/mayflower/privacyidea-ldap-proxy/commit/a13356717379b174f1a6abf767faa0dbd459f5dd.patch";
+ sha256 = "sha256-SBTj9ayQ8JFD8BoYIl77nxWVV3PXnHZ8JMlJnxd/nEk=";
+ })
+ ];
+
propagatedBuildInputs = [ twisted ldaptor configobj ];
pythonImportsCheck = [ "pi_ldapproxy" ];
diff --git a/pkgs/servers/mail/mailman/default.nix b/pkgs/servers/mail/mailman/default.nix
index 50742c0abc0e..77dbef745c8d 100644
--- a/pkgs/servers/mail/mailman/default.nix
+++ b/pkgs/servers/mail/mailman/default.nix
@@ -1,9 +1,9 @@
{ newScope, lib, python3 }:
let
- callPackage = newScope self;
+ self = lib.makeExtensible (self: let inherit (self) callPackage; in {
+ callPackage = newScope self;
- self = lib.makeExtensible (self: {
python3 = callPackage ./python.nix { inherit python3; };
hyperkitty = callPackage ./hyperkitty.nix { };
@@ -20,13 +20,15 @@ let
, mailman ? self.mailman
, mailman-hyperkitty ? self.mailman-hyperkitty
, withHyperkitty ? false
+ , withLDAP ? false
}:
{
mailmanEnv = self.python3.withPackages
(ps: [ mailman ps.psycopg2 ]
- ++ lib.optional withHyperkitty mailman-hyperkitty);
+ ++ lib.optional withHyperkitty mailman-hyperkitty
+ ++ lib.optionals withLDAP [ ps.ldap ps.django-auth-ldap ]);
webEnv = self.python3.withPackages
- (ps: [ web ps.psycopg2 ]);
+ (ps: [ web ps.psycopg2 ] ++ lib.optionals withLDAP [ ps.ldap ps.django-auth-ldap ]);
};
});