1 {pkgs, lib, config, ...}:
 
   2 let inherit (builtins) baseNameOf readFile;
 
   4     inherit (pkgs.lib) unlinesAttrs;
 
   5     inherit (config.services) openldap;
 
   6     inherit (config.users) ldap;
 
   7     copyFile = file: pkgs.writeText (baseNameOf file) (readFile file);
 
   8     configLDIF = pkgs.writeText "cn=config.ldif" (''
 
  10       objectClass: olcGlobal
 
  11       #olcPidFile: /run/slapd/slapd.pid
 
  12       # List of arguments that were passed to the server
 
  13       #olcArgsFile: /run/slapd/slapd.args
 
  14       # Read slapd-config(5) for possible values
 
  16       # The tool-threads parameter sets the actual amount of CPU's
 
  17       # that is used for indexing.
 
  20       dn: olcDatabase={-1}frontend,cn=config
 
  21       objectClass: olcDatabaseConfig
 
  22       objectClass: olcFrontendConfig
 
  23       # The maximum number of entries that is returned for a search operation
 
  25       # Allow unlimited access to local connection from the local root user
 
  27         by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
 
  29       # Allow unauthenticated read access for schema and base DN autodiscovery
 
  30       olcAccess: to dn.exact=""
 
  32       olcAccess: to dn.base="cn=Subschema"
 
  35       dn: olcDatabase=config,cn=config
 
  36       objectClass: olcDatabaseConfig
 
  37       olcRootDN: cn=admin,cn=config
 
  38       # Access to cn=config, system root can be manager
 
  39       # with SASL mechanism (-Y EXTERNAL) over unix socket (-H ldapi://)
 
  41         by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
 
  44       dn: cn=schema,cn=config
 
  45       objectClass: olcSchemaConfig
 
  47       include: file://${pkgs.openldap}/etc/schema/core.ldif
 
  48       include: file://${pkgs.openldap}/etc/schema/cosine.ldif
 
  49       include: file://${pkgs.openldap}/etc/schema/nis.ldif
 
  50       include: file://${pkgs.openldap}/etc/schema/inetorgperson.ldif
 
  51       include: file://${copyFile openldap/schema/postfix-book.ldif}
 
  53       dn: cn=module{0},cn=config
 
  54       objectClass: olcModuleList
 
  55       # Where the dynamically loaded modules are stored
 
  56       #olcModulePath: /usr/lib/ldap
 
  57       olcModuleLoad: back_mdb
 
  59     '' + unlinesAttrs (olcSuffix: {conf, olcDbDirectory, ...}:
 
  60       "include: file://" + pkgs.writeText "config.ldif" (conf + ''
 
  61         olcSuffix: ${olcSuffix}
 
  62         olcDbDirectory: ${olcDbDirectory}
 
  64     ) openldap.databases);
 
  68     openldap/commonsoft.nix
 
  71     services.openldap.domainSuffix = lib.mkOption {
 
  73       default = "dc=${lib.concatStringsSep ",dc=" (lib.splitString "." config.networking.domain)}";
 
  75         LDAP suffix for config.networking.domain.
 
  78     services.openldap.databases = lib.mkOption {
 
  80       type = types.attrsOf (types.submodule ({name, options, config, ...}: {
 
  84             description = "The database's config in LDIF.";
 
  88             description = "The database's data in LDIF.";
 
  90           olcDbDirectory = lib.mkOption {
 
  92             description = "The directory where the database is stored.";
 
  93             default = "${openldap.dataDir}/${name}";
 
  95           resetData = lib.mkOption {
 
  97             description = "Whether to reset the data at each start of the slapd service.";
 
 107       server = "ldapi:///";
 
 108       base = "ou=posix,${openldap.domainSuffix}";
 
 110         #distinguishedName = "cn=admin,${openldap.domainSuffix}";
 
 116           # NOTE: nslcd cannot use SASL to bind to rootpwmoddn
 
 117           # which is the DN used by nslcd when passwd is run by root
 
 118           # to change the userPassword of an LDAP user.
 
 119           # SEE: https://www.reddit.com/r/linuxadmin/comments/53sxpl/how_do_i_configure_nslcd_to_use_a_sasl_external/d7w9awd/
 
 120           # Thus, use: ldappasswd -H ldapi:// -Y EXTERNAL uid=$SomeUID,ou=accounts,ou=posix,dc=commonsoft,dc=org
 
 124     services.openldap = {
 
 126       dataDir   = "/var/db/ldap";
 
 127       configDir = "/var/db/slapd";
 
 128       urlList   = [ "ldapi:///" ]; # UNIX socket
 
 130     systemd.services.openldap = {
 
 133           # NOTE: slapd's config is always re-initialized.
 
 134           rm -rf "${openldap.configDir}"/cn=config \
 
 135                  "${openldap.configDir}"/cn=config.ldif
 
 136           install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${openldap.configDir}"
 
 137           # NOTE: olcDbDirectory must be created before adding the config.
 
 139           unlinesAttrs (olcSuffix: {data, olcDbDirectory, resetData, ...}:
 
 140             lib.optionalString resetData ''
 
 141               rm -rf "${olcDbDirectory}"
 
 143             install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${olcDbDirectory}"
 
 144             '') openldap.databases
 
 146           # NOTE: slapd is supposed to have been stopped by systemd
 
 147           # before entering this preStart,
 
 148           # hence slap* commands can safely be used.
 
 151           # To populate the config database slapd-config(5),
 
 152           # use -n 0 as it is always the first database.
 
 153           # It must physically exist on the filesystem prior to this, however.
 
 155           ${pkgs.openldap}/bin/slapadd -n 0 \
 
 156            -F "${openldap.configDir}" \
 
 158           chown -R "${openldap.user}:${openldap.group}" "${openldap.configDir}"
 
 160         unlinesAttrs (olcSuffix: {data, olcDbDirectory, resetData, ...}:
 
 161           lib.optionalString resetData ''
 
 162             ${pkgs.openldap}/bin/slapadd \
 
 163              -F "${openldap.configDir}" \
 
 164              -l ${pkgs.writeText "data.ldif" data}
 
 166           test ! -e "${olcDbDirectory}" ||
 
 167           chown -R "${openldap.user}:${openldap.group}" "${olcDbDirectory}"
 
 168         '') openldap.databases;