{ pkgs, lib, config, ... }: let inherit (builtins) hasAttr; inherit (builtins.extraBuiltins) pass-chomp; inherit (config) networking; inherit (config.services) openldap; inherit (config.users) users groups; inherit (pkgs.lib) unlines; domainSuffix = openldap.domainSuffix; posixAccount = { uid , uidNumber , gidNumber ? uidNumber , cn ? "" , sn ? "" , userPassword ? null # NOTE: doveadm pw -s SSHA -u $user -p $pass , mailAlias ? [] , homeDirectory ? "" , mailStorageDirectory ? null , loginShell ? "/run/current-system/sw/bin/bash" , mailEnabled ? true , mailForwardingAddress ? [] , domain ? networking.domain }: "\n" + lib.concatStringsSep "\n\n" [ (unlines ([ '' dn: uid=${uid},ou=accounts,ou=posix,${domainSuffix} objectClass: person objectClass: posixAccount objectClass: shadowAccount objectClass: PostfixBookMailAccount objectClass: PostfixBookMailForward cn: ${cn} sn: ${sn} mail: ${uid}${lib.optionalString (networking.domain != "") "@${networking.domain}"} mailEnabled: ${if mailEnabled then "TRUE" else "FALSE"} #mailGroupMember: ${networking.domainBase}''] ++ [ "uidNumber: ${toString uidNumber}" ] ++ [ "gidNumber: ${toString gidNumber}" ] ++ [ "homeDirectory: ${homeDirectory}" ] ++ lib.optional (loginShell != null) "loginShell: ${loginShell}" ++ lib.optional (userPassword != null) "userPassword: ${userPassword}" ++ lib.optional (mailStorageDirectory != null) "mailStorageDirectory: ${mailStorageDirectory}" ++ map (forward: "mailForwardingAddress: ${forward}") mailForwardingAddress ++ map (alias: "mailAlias: ${alias}@${networking.domain}") mailAlias ++ lib.optional (mailAlias == []) "mailAlias:" # NOTE: required by PostfixBookMailForward )) '' dn: cn=${uid},ou=groups,ou=posix,${domainSuffix} objectClass: top objectClass: posixGroup gidNumber: ${toString gidNumber} memberUid: ${uid} '' ]; in { config = lib.mkIf config.users.ldap.enable { services.openldap = { databases = { # DEBUG: echo "$(nixops show-option mermet -d production services.openldap.databases."dc=sourcephile,dc=fr".data)" "${domainSuffix}" = { # # # WARNING: this deletes data. # # resetData = true; # DEBUG: sudo ldapsearch -LLL -H ldapi:// -D cn=admin,cn=config -Y EXTERNAL -b 'olcDatabase={1}mdb,cn=config' -s sub # WARNING: newlines matter conf = '' dn: olcBackend={1}mdb,cn=config objectClass: olcBackendConfig 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 # NOTE: database superuser. Needed for syncrepl. olcRootDN: cn=admin,${domainSuffix} # NOTE: superuser password, generated with slappasswd -h "{SSHA}" -s "$password" #olcRootPW: {SSHA}COkATGNe7rs/g8vWcYP5rqt4u5sWdMgP # olcDbIndex: objectClass eq olcDbIndex: cn,uid eq olcDbIndex: uidNumber,gidNumber eq olcDbIndex: member,memberUid eq olcDbIndex: mail eq olcDbIndex: mailAlias 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" write by * none olcAccess: to attrs=shadowLastChange by self write by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write by * none olcAccess: to dn.sub="ou=posix,${domainSuffix}" by self read by dn="gidNumber=${toString groups.nslcd.gid}+uidNumber=${toString users.nslcd.uid},cn=peercred,cn=external,cn=auth" read ${lib.optionalString (hasAttr "postfix" users) ''by dn="gidNumber=${toString groups.postfix.gid}+uidNumber=${toString users.postfix.uid},cn=peercred,cn=external,cn=auth" read''} by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read # NOTE: dovecot/auth runs as root, hence the gidNumber=0+uidNumber=0 olcAccess: to * by self read by * none ''; data = '' dn: ${domainSuffix} objectClass: top objectClass: dcObject objectClass: organization o: ${networking.domainBase} dn: cn=admin,${domainSuffix} objectClass: simpleSecurityObject objectClass: organizationalRole description: ${networking.domainBase} LDAP administrator roleOccupant: ${domainSuffix} userPassword: #userPassword: {SSHA}NONVwwKnKsCBmFxkMqTCFekdu3SJQHc9 dn: ou=posix,${domainSuffix} objectClass: top objectClass: organizationalUnit dn: ou=accounts,ou=posix,${domainSuffix} objectClass: top objectClass: organizationalUnit dn: ou=groups,ou=posix,${domainSuffix} objectClass: top objectClass: organizationalUnit dn: cn=${networking.domainBase},ou=groups,ou=posix,${domainSuffix} objectClass: top objectClass: posixGroup gidNumber: 20000 memberUid: julm memberUid: sevy '' + lib.concatMapStrings posixAccount [ rec { uid = "julm"; cn = "Julien Moutinho"; sn = uid; uidNumber = users.julm.uid; gidNumber = groups.julm.gid; mailAlias = [ "julien.moutinho" ]; userPassword = pass-chomp "members/julm/mail/hashedPassword"; mailStorageDirectory = let stateDir = "/var/lib/dovecot"; d="sourcephile.fr"; in # I'm personnaly using "maildir:" instead of "sdbox:" to be able to use a local (neo)mutt on it, # bypassing IMAP because (neo)mutt support of IMAP is very bad # (can't even have a decent $folder_format (with %n or %m) working). "maildir:${stateDir}/mail/${d}/${uid}/mail.d:LAYOUT=maildir++:UTF-8:CONTROL=${stateDir}/control/${d}/${uid}:INDEX=${stateDir}/index/${d}/${uid}"; } #{ uid="sevy"; uidNumber=10001; cn="Séverine Popek"; sn="sévy"; # mailAlias = ["severine.popek" "ouais-ouais"]; } #{ uid="nomail"; uidNumber=10002; mailAlias = ["noalias"]; mailEnabled = false; } #{ uid="post"; domain="friot"; mailForwardingAddress = ["ju@${networking.domain}"]; } #{ uid="host"; mailForwardingAddress = ["ju@${networking.domain}"]; } ]; }; }; }; }; }