{pkgs, lib, config, ...}: let inherit (builtins) baseNameOf readFile; inherit (config.services) openldap; inherit (config.users) ldap; copyFile = file: pkgs.writeText (baseNameOf file) (readFile file); configLDIF = pkgs.writeText "cn=config.ldif" '' dn: cn=config objectClass: olcGlobal #olcPidFile: /run/slapd/slapd.pid # List of arguments that were passed to the server #olcArgsFile: /run/slapd/slapd.args # Read slapd-config(5) for possible values olcLogLevel: none # The tool-threads parameter sets the actual amount of cpu's # that is used for indexing. olcToolThreads: 1 dn: olcDatabase={-1}frontend,cn=config objectClass: olcDatabaseConfig objectClass: olcFrontendConfig # The maximum number of entries that is returned for a search operation olcSizeLimit: 500 # Allow unlimited access to local connection from the local root user olcAccess: to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break # Allow unauthenticated read access for schema and base DN autodiscovery olcAccess: to dn.exact="" by * read olcAccess: to dn.base="cn=Subschema" by * read dn: olcDatabase=config,cn=config objectClass: olcDatabaseConfig olcRootDN: cn=admin,cn=config # Access to cn=config, system root can be manager # with SASL mechanism (-Y EXTERNAL) over unix socket (-H ldapi://) olcAccess: to * by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage by * break dn: cn=schema,cn=config objectClass: olcSchemaConfig include: file://${pkgs.openldap}/etc/schema/core.ldif include: file://${pkgs.openldap}/etc/schema/cosine.ldif include: file://${pkgs.openldap}/etc/schema/nis.ldif include: file://${pkgs.openldap}/etc/schema/inetorgperson.ldif include: file://${copyFile openldap/postfix-book.ldif} include: file://${copyFile openldap/postfix2.ldif} dn: cn=module{0},cn=config objectClass: olcModuleList # Where the dynamically loaded modules are stored #olcModulePath: /usr/lib/ldap olcModuleLoad: back_mdb dn: olcBackend={1}mdb,cn=config objectClass: olcBackendConfig include: file://${domainConfigLDIF openldap.domainSuffix} ''; domainConfigLDIF = dbSuffix: pkgs.writeText "config.ldif" '' # sudo ldapsearch -LLL -H ldapi:// -D cn=admin,cn=config -Y EXTERNAL -b 'olcDatabase={1}mdb,cn=config' -s sub dn: olcDatabase={1}mdb,cn=config objectClass: olcDatabaseConfig objectClass: olcMdbConfig # NOTE: checkpoint the database periodically in case of system failure # and to speed slapd shutdown. olcDbCheckpoint: 512 30 # Database max size is 1G olcDbMaxSize: 1073741824 olcLastMod: TRUE olcSuffix: ${dbSuffix} olcDbDirectory: ${openldap.dataDir} # NOTE: database superuser. Needed for syncrepl. olcRootDN: cn=admin,${dbSuffix} # NOTE: superuser password, generated with slappasswd -s SECRET # FIXME: remove when dovecot2 compiled with SASL olcRootPW: {SSHA}NONVwwKnKsCBmFxkMqTCFekdu3SJQHc9 olcDbIndex: objectClass eq olcDbIndex: cn,uid eq olcDbIndex: uidNumber,gidNumber eq olcDbIndex: member,memberUid eq olcDbIndex: mail eq olcDbIndex: mailEnabled eq olcAccess: to attrs=userPassword by self write by anonymous auth by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by * none olcAccess: to attrs=shadowLastChange by self write by * none olcAccess: to dn.sub="ou=posix,${dbSuffix}" by dn="gidNumber=${toString config.users.groups.nslcd.gid}+uidNumber=${toString config.users.users.nslcd.uid},cn=peercred,cn=external,cn=auth" read by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read olcAccess: to * by self read by * none ''; domainDataLDIF = dbSuffix: pkgs.writeText "data.ldif" '' dn: ${dbSuffix} objectClass: top objectClass: dcObject objectClass: organization o: ${config.networking.baseName} dn: cn=admin,${dbSuffix} objectClass: simpleSecurityObject objectClass: organizationalRole description: ${config.networking.baseName} LDAP administrator roleOccupant: ${dbSuffix} userPassword: #userPassword: {SSHA}NONVwwKnKsCBmFxkMqTCFekdu3SJQHc9 dn: ou=posix,${dbSuffix} objectClass: top objectClass: organizationalUnit dn: ou=accounts,ou=posix,${dbSuffix} objectClass: top objectClass: organizationalUnit dn: ou=groups,ou=posix,${dbSuffix} objectClass: top objectClass: organizationalUnit dn: cn=users,ou=groups,ou=posix,${dbSuffix} objectclass: top objectclass: posixGroup gidnumber: 10000 memberuid: ju memberuid: sevy #dn: cn=dovemail,ou=groups,ou=posix,${dbSuffix} #objectclass: top #objectclass: posixGroup #gidnumber: 497 # # FIXME: do not hardcode this gid #memberuid: ju #memberuid: sevy dn: uid=ju,ou=accounts,ou=posix,${dbSuffix} #objectClass: account objectclass: person objectClass: posixAccount objectclass: postfixUser objectclass: PostfixBookMailAccount objectclass: PostfixBookMailForward cn: Julien M. sn: julm mail: ju@commonsoft.coop mailAlias: julien.moutinho@commonsoft.coop uidNumber: 10000 gidNumber: 497 homeDirectory: /home/ju loginShell: /run/current-system/sw/bin/bash userPassword: {SSHA}144Rfau9KJ14U0U4KdLNB7OrtpiEc3E3 dn: uid=sevy,ou=accounts,ou=posix,${dbSuffix} #objectClass: account objectclass: person objectClass: posixAccount objectclass: postfixUser objectclass: PostfixBookMailAccount objectclass: PostfixBookMailForward cn: Séverine P. sn: sévy mail: sevy@commonsoft.coop mailAlias: severine.popek@commonsoft.coop uidNumber: 10001 gidNumber: 10000 homeDirectory: /home/sevy loginShell: /run/current-system/sw/bin/bash userPassword: {SSHA}dwqaKo5nmId8Bym5PghloK+UEndwrVTN ''; in { options.services.openldap.domainSuffix = lib.mkOption { type = lib.types.str; default = "dc=${lib.concatStringsSep ",dc=" (lib.splitString "." config.networking.domain)}"; description = '' LDAP suffix for the first database. ''; }; config = { users.ldap = { enable = true; # FIXME: even with the correct LD_LIBRARY_PATH to libnss_ldap.so, # passwd still does not work on LDAP accounts. daemon = { enable = true; extraConfig = '' sasl_mech EXTERNAL ''; }; server = "ldapi:///"; base = "ou=posix,${openldap.domainSuffix}"; bind = { #distinguishedName = "cn=admin,${openldap.domainSuffix}"; }; }; services.openldap = { enable = true; dataDir = "/var/db/ldap"; configDir = "/var/db/slapd"; urlList = [ "ldapi:///" ]; # UNIX socket }; systemd.services.openldap = { preStart = '' # NOTE: slapd's config is always re-initialized. rm -rf "${openldap.configDir}"/cn=config \ "${openldap.configDir}"/cn=config.ldif umask 0077 install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${openldap.configDir}" # NOTE: slapd is supposed to be stopped while in preStart, # hence slap* commands can safely be used. ${pkgs.openldap}/bin/slapadd -n 0 -F "${openldap.configDir}" -l ${configLDIF} # NOTE: slapadd(8): To populate the config database slapd-config(5), # use -n 0 as it is always the first database. # It must physically exist on the filesystem prior to this, however. # NOTE: the data are only initialized, never re-initialized. if test ! -e "${openldap.dataDir}"/data.mdb then install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${openldap.dataDir}" ${pkgs.openldap}/bin/slapadd -F "${openldap.configDir}" -l ${domainDataLDIF openldap.domainSuffix} fi chown -R "${openldap.user}:${openldap.group}" \ "${openldap.dataDir}" \ "${openldap.configDir}" ''; }; }; }