From 351529231b162f61c00c59a4e913bedb765929a0 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Thu, 30 Jun 2022 19:35:10 +0200 Subject: [PATCH 1/4] python3Packages.privacyidea-ldap-proxy: add patch to support LDAPCompareRequest --- .../python-modules/privacyidea-ldap-proxy/default.nix | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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" ]; From dd4b6b81fa95dbd71d9f2db5dee968dd8bcb5e29 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Thu, 30 Jun 2022 19:35:43 +0200 Subject: [PATCH 2/4] nixos/mailman: implement LDAP support for postorius --- nixos/modules/services/mail/mailman.nix | 138 +++++++++++++++++++++++- pkgs/servers/mail/mailman/default.nix | 6 +- 2 files changed, 141 insertions(+), 3 deletions(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 5b714c384de3..14f4e1460b8c 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() + 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/servers/mail/mailman/default.nix b/pkgs/servers/mail/mailman/default.nix index 50742c0abc0e..d887d91d20f6 100644 --- a/pkgs/servers/mail/mailman/default.nix +++ b/pkgs/servers/mail/mailman/default.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 ]); }; }); From 6c7fbcd21e7e137f4fde9a5c4acaec1d2b594b94 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 4 Jul 2022 16:45:54 +0200 Subject: [PATCH 3/4] mailman: make `mailmanPackages.extend` actually work in overlays --- pkgs/servers/mail/mailman/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/servers/mail/mailman/default.nix b/pkgs/servers/mail/mailman/default.nix index d887d91d20f6..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 { }; From 6a5b1bc0a376b4bb3e9199ed396c7f73b567cd14 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 4 Jul 2022 20:26:26 +0200 Subject: [PATCH 4/4] nixos/mailman: strip trailing `\n` when reading the secret --- nixos/modules/services/mail/mailman.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/mail/mailman.nix b/nixos/modules/services/mail/mailman.nix index 14f4e1460b8c..11ea169fe269 100644 --- a/nixos/modules/services/mail/mailman.nix +++ b/nixos/modules/services/mail/mailman.nix @@ -389,7 +389,7 @@ in { 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() + 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}()