2014-04-14 15:26:48 +01:00
|
|
|
{ config, lib, pkgs, ... }:
|
2011-04-13 18:35:19 +01:00
|
|
|
|
2014-04-14 15:26:48 +01:00
|
|
|
with lib;
|
2011-04-13 18:35:19 +01:00
|
|
|
let
|
|
|
|
cfg = config.services.openldap;
|
2020-07-01 19:11:00 +01:00
|
|
|
openldap = cfg.package;
|
2011-04-13 18:35:19 +01:00
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
configFile = pkgs.writeText "slapd.conf" ((optionalString (cfg.defaultSchemas != null && cfg.defaultSchemas) ''
|
|
|
|
include ${openldap}/etc/schema/core.schema
|
|
|
|
include ${openldap}/etc/schema/cosine.schema
|
|
|
|
include ${openldap}/etc/schema/inetorgperson.schema
|
|
|
|
include ${openldap}/etc/schema/nis.schema
|
2018-12-10 09:54:30 +00:00
|
|
|
'') + ''
|
2020-08-02 23:52:37 +01:00
|
|
|
pidfile /run/slapd/slapd.pid
|
|
|
|
${if cfg.extraConfig != null then cfg.extraConfig else ""}
|
2018-12-10 09:54:30 +00:00
|
|
|
database ${cfg.database}
|
|
|
|
suffix ${cfg.suffix}
|
|
|
|
rootdn ${cfg.rootdn}
|
2019-04-23 15:13:45 +01:00
|
|
|
${if (cfg.rootpw != null) then ''
|
|
|
|
rootpw ${cfg.rootpw}
|
|
|
|
'' else ''
|
|
|
|
include ${cfg.rootpwFile}
|
|
|
|
''}
|
2018-12-10 09:54:30 +00:00
|
|
|
directory ${cfg.dataDir}
|
2020-08-02 23:52:37 +01:00
|
|
|
${if cfg.extraDatabaseConfig != null then cfg.extraDatabaseConfig else ""}
|
2018-12-10 09:54:30 +00:00
|
|
|
'');
|
2011-04-13 18:35:19 +01:00
|
|
|
|
2020-08-24 00:19:35 +01:00
|
|
|
configDir = if cfg.configDir != null then cfg.configDir else "/etc/openldap/slapd.d";
|
2011-04-13 18:35:19 +01:00
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
ldapValueType = let
|
2020-09-13 22:42:14 +01:00
|
|
|
singleLdapValueType = types.oneOf [
|
|
|
|
types.str
|
|
|
|
(types.submodule {
|
|
|
|
options = {
|
|
|
|
path = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
description = ''
|
|
|
|
A path containing the LDAP attribute. This is included at run-time, so
|
|
|
|
is recommended for storing secrets.
|
|
|
|
'';
|
|
|
|
};
|
2020-08-02 23:52:37 +01:00
|
|
|
};
|
2020-09-13 22:42:14 +01:00
|
|
|
})
|
|
|
|
(types.submodule {
|
|
|
|
options = {
|
|
|
|
base64 = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
description = ''
|
|
|
|
A base64-encoded LDAP attribute. Useful for storing values which
|
|
|
|
contain special characters (e.g. newlines) in LDIF files.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})
|
|
|
|
];
|
2020-08-02 23:52:37 +01:00
|
|
|
in types.either singleLdapValueType (types.listOf singleLdapValueType);
|
|
|
|
|
|
|
|
ldapAttrsType =
|
|
|
|
let
|
|
|
|
options = {
|
|
|
|
attrs = mkOption {
|
|
|
|
type = types.attrsOf ldapValueType;
|
|
|
|
default = {};
|
|
|
|
description = "Attributes of the parent entry.";
|
|
|
|
};
|
|
|
|
children = mkOption {
|
|
|
|
# Hide the child attributes, to avoid infinite recursion in e.g. documentation
|
|
|
|
# Actual Nix evaluation is lazy, so this is not an issue there
|
|
|
|
type = let
|
|
|
|
hiddenOptions = lib.mapAttrs (name: attr: attr // { visible = false; }) options;
|
|
|
|
in types.attrsOf (types.submodule { options = hiddenOptions; });
|
|
|
|
default = {};
|
|
|
|
description = "Child entries of the current entry, with recursively the same structure.";
|
|
|
|
example = lib.literalExample ''
|
|
|
|
{
|
|
|
|
"cn=schema" = {
|
|
|
|
# The attribute used in the DN must be defined
|
|
|
|
attrs = { cn = "schema"; };
|
|
|
|
children = {
|
|
|
|
# This entry's DN is expanded to "cn=foo,cn=schema"
|
|
|
|
"cn=foo" = { ... };
|
|
|
|
};
|
|
|
|
# These includes are inserted after "cn=schema", but before "cn=foo,cn=schema"
|
|
|
|
includes = [ ... ];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
includes = mkOption {
|
|
|
|
type = types.listOf types.path;
|
|
|
|
default = [];
|
|
|
|
description = ''
|
|
|
|
LDIF files to include after the parent's attributes but before its children.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
in types.submodule { inherit options; };
|
|
|
|
|
|
|
|
valueToLdif = attr: values: let
|
2020-09-13 22:42:14 +01:00
|
|
|
listValues = if lib.isList values then values else lib.singleton values;
|
|
|
|
in map (value:
|
|
|
|
if lib.isAttrs value then
|
|
|
|
if lib.hasAttr "path" value
|
|
|
|
then "${attr}:< file://${value.path}"
|
|
|
|
else "${attr}:: ${value.base64}"
|
|
|
|
else "${attr}: ${lib.replaceStrings [ "\n" ] [ "\n " ] value}"
|
|
|
|
) listValues;
|
2020-08-02 23:52:37 +01:00
|
|
|
|
|
|
|
attrsToLdif = dn: { attrs, children, includes, ... }: [''
|
|
|
|
dn: ${dn}
|
|
|
|
${lib.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList valueToLdif attrs))}
|
|
|
|
''] ++ (map (path: "include: file://${path}\n") includes) ++ (
|
|
|
|
lib.flatten (lib.mapAttrsToList (name: value: attrsToLdif "${name},${dn}" value) children)
|
|
|
|
);
|
|
|
|
in {
|
2011-04-13 18:35:19 +01:00
|
|
|
options = {
|
|
|
|
services.openldap = {
|
|
|
|
enable = mkOption {
|
2015-01-26 08:35:56 +00:00
|
|
|
type = types.bool;
|
2011-04-13 18:35:19 +01:00
|
|
|
default = false;
|
|
|
|
description = "
|
|
|
|
Whether to enable the ldap server.
|
|
|
|
";
|
|
|
|
};
|
|
|
|
|
2020-07-01 19:11:00 +01:00
|
|
|
package = mkOption {
|
|
|
|
type = types.package;
|
|
|
|
default = pkgs.openldap;
|
|
|
|
description = ''
|
|
|
|
OpenLDAP package to use.
|
|
|
|
|
|
|
|
This can be used to, for example, set an OpenLDAP package
|
|
|
|
with custom overrides to enable modules or other
|
|
|
|
functionality.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2013-11-28 21:21:50 +00:00
|
|
|
user = mkOption {
|
2019-08-08 21:48:27 +01:00
|
|
|
type = types.str;
|
2013-11-28 21:21:50 +00:00
|
|
|
default = "openldap";
|
|
|
|
description = "User account under which slapd runs.";
|
|
|
|
};
|
|
|
|
|
|
|
|
group = mkOption {
|
2019-08-08 21:48:27 +01:00
|
|
|
type = types.str;
|
2013-11-28 21:21:50 +00:00
|
|
|
default = "openldap";
|
|
|
|
description = "Group account under which slapd runs.";
|
|
|
|
};
|
|
|
|
|
2016-05-21 12:49:14 +01:00
|
|
|
urlList = mkOption {
|
2019-08-08 21:48:27 +01:00
|
|
|
type = types.listOf types.str;
|
2016-05-21 12:49:14 +01:00
|
|
|
default = [ "ldap:///" ];
|
|
|
|
description = "URL list slapd should listen on.";
|
|
|
|
example = [ "ldaps:///" ];
|
|
|
|
};
|
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
settings = mkOption {
|
|
|
|
type = ldapAttrsType;
|
|
|
|
description = "Configuration for OpenLDAP, in OLC format";
|
|
|
|
example = lib.literalExample ''
|
|
|
|
{
|
|
|
|
attrs.olcLogLevel = [ "stats" ];
|
|
|
|
children = {
|
|
|
|
"cn=schema".includes = [
|
|
|
|
"\${pkgs.openldap}/etc/schema/core.ldif"
|
|
|
|
"\${pkgs.openldap}/etc/schema/cosine.ldif"
|
|
|
|
"\${pkgs.openldap}/etc/schema/inetorgperson.ldif"
|
|
|
|
];
|
|
|
|
"olcDatabase={-1}frontend" = {
|
|
|
|
attrs = {
|
|
|
|
objectClass = "olcDatabaseConfig";
|
|
|
|
olcDatabase = "{-1}frontend";
|
|
|
|
olcAccess = [ "{0}to * by dn.exact=uidNumber=0+gidNumber=0,cn=peercred,cn=external,cn=auth manage stop by * none stop" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
"olcDatabase={0}config" = {
|
|
|
|
attrs = {
|
|
|
|
objectClass = "olcDatabaseConfig";
|
|
|
|
olcDatabase = "{0}config";
|
|
|
|
olcAccess = [ "{0}to * by * none break" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
"olcDatabase={1}mdb" = {
|
|
|
|
attrs = {
|
|
|
|
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
|
|
|
olcDatabase = "{1}mdb";
|
|
|
|
olcDbDirectory = "/var/db/ldap";
|
|
|
|
olcDbIndex = [
|
|
|
|
"objectClass eq"
|
|
|
|
"cn pres,eq"
|
|
|
|
"uid pres,eq"
|
|
|
|
"sn pres,eq,subany"
|
|
|
|
];
|
|
|
|
olcSuffix = "dc=example,dc=com";
|
|
|
|
olcAccess = [ "{0}to * by * read break" ];
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
# These options are translated into settings
|
2015-01-26 08:35:56 +00:00
|
|
|
dataDir = mkOption {
|
2020-08-02 23:52:37 +01:00
|
|
|
type = types.nullOr types.path;
|
2015-01-26 08:35:56 +00:00
|
|
|
default = "/var/db/openldap";
|
|
|
|
description = "The database directory.";
|
|
|
|
};
|
|
|
|
|
2018-12-10 09:54:30 +00:00
|
|
|
defaultSchemas = mkOption {
|
2020-08-02 23:52:37 +01:00
|
|
|
type = types.nullOr types.bool;
|
2018-12-10 09:54:30 +00:00
|
|
|
default = true;
|
2020-08-02 23:52:37 +01:00
|
|
|
|
2018-12-10 09:54:30 +00:00
|
|
|
description = ''
|
|
|
|
Include the default schemas core, cosine, inetorgperson and nis.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
database = mkOption {
|
2020-08-02 23:52:37 +01:00
|
|
|
type = types.nullOr types.str;
|
2018-12-10 09:54:30 +00:00
|
|
|
default = "mdb";
|
2020-08-02 23:52:37 +01:00
|
|
|
description = "Backend to use for the first database.";
|
2018-12-10 09:54:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
suffix = mkOption {
|
2020-08-02 23:52:37 +01:00
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
2018-12-10 09:54:30 +00:00
|
|
|
example = "dc=example,dc=org";
|
|
|
|
description = ''
|
2020-08-02 23:52:37 +01:00
|
|
|
Specify the DN suffix of queries that will be passed to the first
|
2020-08-24 00:07:24 +01:00
|
|
|
backend database.
|
2018-12-10 09:54:30 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
rootdn = mkOption {
|
2020-08-02 23:52:37 +01:00
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
2018-12-10 09:54:30 +00:00
|
|
|
example = "cn=admin,dc=example,dc=org";
|
|
|
|
description = ''
|
|
|
|
Specify the distinguished name that is not subject to access control
|
|
|
|
or administrative limit restrictions for operations on this database.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
rootpw = mkOption {
|
2019-04-23 15:13:45 +01:00
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
2018-12-10 09:54:30 +00:00
|
|
|
description = ''
|
2020-08-02 23:52:37 +01:00
|
|
|
Password for the root user.Using this option will store the root
|
|
|
|
password in plain text in the world-readable nix store. To avoid this
|
|
|
|
the <literal>rootpwFile</literal> can be used.
|
2019-04-23 15:13:45 +01:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
rootpwFile = mkOption {
|
|
|
|
type = types.nullOr types.str;
|
|
|
|
default = null;
|
|
|
|
description = ''
|
|
|
|
Password file for the root user.
|
2020-08-02 23:52:37 +01:00
|
|
|
|
|
|
|
If the deprecated <literal>extraConfig</literal> or
|
|
|
|
<literal>extraDatabaseConfig</literal> options are set, this should
|
|
|
|
contain <literal>rootpw</literal> followed by the password
|
|
|
|
(e.g. <literal>rootpw thePasswordHere</literal>).
|
|
|
|
|
|
|
|
Otherwise the file should contain only the password (no trailing
|
|
|
|
newline or leading <literal>rootpw</literal>).
|
2018-12-10 09:54:30 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2018-12-06 14:43:50 +00:00
|
|
|
logLevel = mkOption {
|
2020-08-24 00:19:35 +01:00
|
|
|
type = types.nullOr (types.coercedTo types.str (lib.splitString " ") (types.listOf types.str));
|
2020-08-02 23:52:37 +01:00
|
|
|
default = null;
|
|
|
|
example = literalExample "[ \"acl\" \"trace\" ]";
|
|
|
|
description = "The log level.";
|
2018-12-06 14:43:50 +00:00
|
|
|
};
|
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
# This option overrides settings
|
2016-10-06 09:01:38 +01:00
|
|
|
configDir = mkOption {
|
2016-10-23 14:58:37 +01:00
|
|
|
type = types.nullOr types.path;
|
2016-10-23 12:55:23 +01:00
|
|
|
default = null;
|
2020-08-02 23:52:37 +01:00
|
|
|
description = ''
|
|
|
|
Use this optional config directory instead of generating one from the
|
|
|
|
<literal>settings</literal> option.
|
|
|
|
'';
|
2016-10-06 09:01:38 +01:00
|
|
|
example = "/var/db/slapd.d";
|
|
|
|
};
|
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
# These options are deprecated
|
2011-04-13 18:35:19 +01:00
|
|
|
extraConfig = mkOption {
|
2015-01-26 08:35:56 +00:00
|
|
|
type = types.lines;
|
2011-04-13 18:35:19 +01:00
|
|
|
default = "";
|
|
|
|
description = "
|
2016-05-21 12:49:14 +01:00
|
|
|
slapd.conf configuration
|
2011-04-13 18:35:19 +01:00
|
|
|
";
|
2016-06-05 06:40:26 +01:00
|
|
|
example = literalExample ''
|
|
|
|
'''
|
2020-08-02 23:52:37 +01:00
|
|
|
include ${openldap}/etc/schema/core.schema
|
|
|
|
include ${openldap}/etc/schema/cosine.schema
|
|
|
|
include ${openldap}/etc/schema/inetorgperson.schema
|
|
|
|
include ${openldap}/etc/schema/nis.schema
|
2016-01-17 18:34:55 +00:00
|
|
|
|
2019-04-23 15:13:45 +01:00
|
|
|
database bdb
|
|
|
|
suffix dc=example,dc=org
|
|
|
|
rootdn cn=admin,dc=example,dc=org
|
2016-01-17 18:34:55 +00:00
|
|
|
# NOTE: change after first start
|
|
|
|
rootpw secret
|
|
|
|
directory /var/db/openldap
|
2016-06-05 06:40:26 +01:00
|
|
|
'''
|
2016-01-17 18:34:55 +00:00
|
|
|
'';
|
2011-04-13 18:35:19 +01:00
|
|
|
};
|
2018-03-03 14:33:23 +00:00
|
|
|
|
|
|
|
declarativeContents = mkOption {
|
2020-08-24 00:07:24 +01:00
|
|
|
type = with types; either lines (attrsOf lines);
|
|
|
|
default = {};
|
2018-03-03 14:33:23 +00:00
|
|
|
description = ''
|
2020-08-24 00:07:24 +01:00
|
|
|
Declarative contents for the first LDAP database, in LDIF format.
|
2018-03-03 14:33:23 +00:00
|
|
|
|
|
|
|
Note a few facts when using it. First, the database
|
|
|
|
<emphasis>must</emphasis> be stored in the directory defined by
|
|
|
|
<code>dataDir</code>. Second, all <code>dataDir</code> will be erased
|
|
|
|
when starting the LDAP server. Third, modifications to the database
|
|
|
|
are not prevented, they are just dropped on the next reboot of the
|
|
|
|
server. Finally, performance-wise the database and indexes are rebuilt
|
|
|
|
on each server startup, so this will slow down server startup,
|
|
|
|
especially with large databases.
|
|
|
|
'';
|
|
|
|
example = ''
|
|
|
|
dn: dc=example,dc=org
|
|
|
|
objectClass: domain
|
|
|
|
dc: example
|
|
|
|
|
|
|
|
dn: ou=users,dc=example,dc=org
|
|
|
|
objectClass = organizationalUnit
|
|
|
|
ou: users
|
|
|
|
|
|
|
|
# ...
|
|
|
|
'';
|
|
|
|
};
|
2018-12-10 09:54:30 +00:00
|
|
|
|
|
|
|
extraDatabaseConfig = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
|
|
|
description = ''
|
|
|
|
slapd.conf configuration after the database option.
|
|
|
|
This setting will be ignored if configDir is set.
|
|
|
|
'';
|
|
|
|
example = ''
|
|
|
|
# Indices to maintain for this directory
|
|
|
|
# unique id so equality match only
|
|
|
|
index uid eq
|
|
|
|
# allows general searching on commonname, givenname and email
|
|
|
|
index cn,gn,mail eq,sub
|
|
|
|
# allows multiple variants on surname searching
|
|
|
|
index sn eq,sub
|
|
|
|
# sub above includes subintial,subany,subfinal
|
|
|
|
# optimise department searches
|
|
|
|
index ou eq
|
|
|
|
# if searches will include objectClass uncomment following
|
|
|
|
# index objectClass eq
|
|
|
|
# shows use of default index parameter
|
|
|
|
index default eq,sub
|
|
|
|
# indices missing - uses default eq,sub
|
|
|
|
index telephonenumber
|
|
|
|
|
|
|
|
# other database parameters
|
|
|
|
# read more in slapd.conf reference section
|
|
|
|
cachesize 10000
|
|
|
|
checkpoint 128 15
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
2011-04-13 18:35:19 +01:00
|
|
|
};
|
2011-09-14 19:20:50 +01:00
|
|
|
|
2011-04-13 18:35:19 +01:00
|
|
|
};
|
|
|
|
|
2020-05-05 21:58:50 +01:00
|
|
|
meta = {
|
2020-08-02 23:52:37 +01:00
|
|
|
maintainers = with lib.maintainters; [ mic92 kwohlfahrt ];
|
2020-05-05 21:58:50 +01:00
|
|
|
};
|
|
|
|
|
2020-08-24 00:07:24 +01:00
|
|
|
# TODO: Check that dataDir/declarativeContents/configDir all match
|
|
|
|
# - deprecate declarativeContents = ''...'';
|
|
|
|
# - no declarativeContents = ''...'' if dataDir == null;
|
|
|
|
# - no declarativeContents = { ... } if configDir != null
|
2018-03-03 14:33:01 +00:00
|
|
|
config = mkIf cfg.enable {
|
2020-08-02 23:52:37 +01:00
|
|
|
warnings = let
|
|
|
|
deprecations = [
|
|
|
|
{ old = "logLevel"; new = "attrs.olcLogLevel"; }
|
|
|
|
{ old = "defaultSchemas";
|
|
|
|
new = "children.\"cn=schema\".includes";
|
|
|
|
newValue = "[\n ${lib.concatStringsSep "\n " [
|
|
|
|
"\${pkgs.openldap}/etc/schema/core.ldif"
|
|
|
|
"\${pkgs.openldap}/etc/schema/cosine.ldif"
|
|
|
|
"\${pkgs.openldap}/etc/schema/inetorgperson.ldif"
|
|
|
|
"\${pkgs.openldap}/etc/schema/nis.ldif"
|
|
|
|
]}\n ]"; }
|
|
|
|
{ old = "database"; new = "children.\"cn={1}${cfg.database}\""; newValue = "{ }"; }
|
|
|
|
{ old = "suffix"; new = "children.\"cn={1}${cfg.database}\".attrs.olcSuffix"; }
|
|
|
|
{ old = "dataDir"; new = "children.\"cn={1}${cfg.database}\".attrs.olcDbDirectory"; }
|
|
|
|
{ old = "rootdn"; new = "children.\"cn={1}${cfg.database}\".attrs.olcRootDN"; }
|
|
|
|
{ old = "rootpw"; new = "children.\"cn={1}${cfg.database}\".attrs.olcRootPW"; }
|
|
|
|
{ old = "rootpwFile";
|
|
|
|
new = "children.\"cn={1}${cfg.database}\".attrs.olcRootPW";
|
|
|
|
newValue = "{ path = \"${cfg.rootpwFile}\"; }";
|
|
|
|
note = "The file should contain only the password (without \"rootpw \" as before)"; }
|
|
|
|
];
|
|
|
|
in (optional (cfg.extraConfig != "" || cfg.extraDatabaseConfig != "") ''
|
|
|
|
The options `extraConfig` and `extraDatabaseConfig` of `services.openldap`
|
|
|
|
are deprecated. This is due to the deprecation of `slapd.conf`
|
|
|
|
upstream. Please migrate to `services.openldap.settings`.
|
|
|
|
|
|
|
|
After deploying this configuration, you can run:
|
|
|
|
slapcat -F ${configDir} -n0 -H 'ldap:///???(!(objectClass=olcSchemaConfig))'
|
2020-09-13 22:43:11 +01:00
|
|
|
on the same host to print your current configuration in LDIF format, which
|
|
|
|
should be straightforward to convert into Nix settings. This does not show
|
|
|
|
your schema configuration (as this is unnecessarily verbose users of the
|
|
|
|
default schemas), so be sure to migrate that as well.
|
2020-08-02 23:52:37 +01:00
|
|
|
'') ++ (flatten (map (args@{old, new, ...}: lib.optional ((lib.hasAttr old cfg) && (lib.getAttr old cfg) != null) ''
|
|
|
|
The attribute `services.openldap.${old}` is deprecated. Please set it to
|
|
|
|
`null` and use the following option instead:
|
|
|
|
|
|
|
|
services.openldap.settings.${new} = ${args.newValue or (
|
|
|
|
let oldValue = (getAttr old cfg);
|
|
|
|
in if (isList oldValue) then "[ ${concatStringsSep " " oldValue} ]" else oldValue
|
|
|
|
)}
|
2020-09-13 22:42:54 +01:00
|
|
|
'') deprecations));
|
2020-08-02 23:52:37 +01:00
|
|
|
|
|
|
|
assertions = [{
|
|
|
|
assertion = !(cfg.rootpwFile != null && cfg.rootpw != null);
|
|
|
|
message = "services.openldap: at most one of rootpw or rootpwFile must be set";
|
|
|
|
}];
|
2011-04-13 18:35:19 +01:00
|
|
|
|
|
|
|
environment.systemPackages = [ openldap ];
|
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
# Literal attributes must always be set (even if other top-level attributres are deprecated)
|
|
|
|
services.openldap.settings = {
|
|
|
|
attrs = {
|
|
|
|
objectClass = "olcGlobal";
|
|
|
|
cn = "config";
|
|
|
|
olcPidFile = "/run/slapd/slapd.pid";
|
|
|
|
} // (lib.optionalAttrs (cfg.logLevel != null) {
|
|
|
|
olcLogLevel = cfg.logLevel;
|
|
|
|
});
|
|
|
|
children = {
|
|
|
|
"cn=schema" = {
|
|
|
|
attrs = {
|
|
|
|
cn = "schema";
|
|
|
|
objectClass = "olcSchemaConfig";
|
|
|
|
};
|
|
|
|
includes = lib.optionals (cfg.defaultSchemas != null && cfg.defaultSchemas) [
|
|
|
|
"${openldap}/etc/schema/core.ldif"
|
|
|
|
"${openldap}/etc/schema/cosine.ldif"
|
|
|
|
"${openldap}/etc/schema/inetorgperson.ldif"
|
|
|
|
"${openldap}/etc/schema/nis.ldif"
|
|
|
|
];
|
|
|
|
};
|
|
|
|
} // (lib.optionalAttrs (cfg.database != null) {
|
|
|
|
"olcDatabase={1}${cfg.database}".attrs = {
|
|
|
|
# objectClass is case-insensitive, so don't need to capitalize ${database}
|
|
|
|
objectClass = [ "olcdatabaseconfig" "olc${cfg.database}config" ];
|
|
|
|
olcDatabase = "{1}${cfg.database}";
|
|
|
|
} // (lib.optionalAttrs (cfg.suffix != null) {
|
|
|
|
olcSuffix = cfg.suffix;
|
|
|
|
}) // (lib.optionalAttrs (cfg.dataDir != null) {
|
|
|
|
olcDbDirectory = cfg.dataDir;
|
|
|
|
}) // (lib.optionalAttrs (cfg.rootdn != null) {
|
|
|
|
olcRootDN = cfg.rootdn; # TODO: Optional
|
|
|
|
}) // (lib.optionalAttrs (cfg.rootpw != null || cfg.rootpwFile != null) {
|
|
|
|
olcRootPW = (if cfg.rootpwFile != null then { path = cfg.rootpwFile; } else cfg.rootpw); # TODO: Optional
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-04-09 16:50:53 +01:00
|
|
|
systemd.services.openldap = {
|
|
|
|
description = "LDAP server";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "network.target" ];
|
2020-08-02 23:52:37 +01:00
|
|
|
preStart = let
|
|
|
|
dbSettings = lib.filterAttrs (name: value: lib.hasPrefix "olcDatabase=" name) cfg.settings.children;
|
2020-08-24 00:07:24 +01:00
|
|
|
dataDirs = lib.mapAttrs' (name: value: lib.nameValuePair value.attrs.olcSuffix value.attrs.olcDbDirectory)
|
|
|
|
(lib.filterAttrs (_: value: value.attrs ? olcDbDirectory) dbSettings);
|
2020-08-02 23:52:37 +01:00
|
|
|
settingsFile = pkgs.writeText "config.ldif" (lib.concatStringsSep "\n" (attrsToLdif "cn=config" cfg.settings));
|
|
|
|
in ''
|
2018-12-19 21:36:00 +00:00
|
|
|
mkdir -p /run/slapd
|
|
|
|
chown -R "${cfg.user}:${cfg.group}" /run/slapd
|
2020-08-02 23:52:37 +01:00
|
|
|
|
2020-08-24 00:19:35 +01:00
|
|
|
mkdir -p ${lib.escapeShellArg configDir} ${lib.escapeShellArgs (lib.attrValues dataDirs)}
|
|
|
|
chown "${cfg.user}:${cfg.group}" ${lib.escapeShellArg configDir} ${lib.escapeShellArgs (lib.attrValues dataDirs)}
|
2020-08-02 23:52:37 +01:00
|
|
|
|
|
|
|
${lib.optionalString (cfg.configDir == null) (
|
|
|
|
if (cfg.extraConfig != "" || cfg.extraDatabaseConfig != "") then ''
|
2020-08-24 00:07:24 +01:00
|
|
|
rm -Rf ${configDir}/*
|
2020-08-02 23:52:37 +01:00
|
|
|
# -u disables config generation, so just ignore the return code
|
|
|
|
${openldap}/bin/slaptest -f ${configFile} -F ${configDir} || true
|
|
|
|
'' else ''
|
2020-08-24 00:07:24 +01:00
|
|
|
rm -Rf ${configDir}/*
|
2020-08-16 20:17:06 +01:00
|
|
|
${openldap}/bin/slapadd -F ${configDir} -bcn=config -l ${settingsFile}
|
2020-08-02 23:52:37 +01:00
|
|
|
''
|
|
|
|
)}
|
2020-08-24 00:19:35 +01:00
|
|
|
chown -R "${cfg.user}:${cfg.group}" ${lib.escapeShellArg configDir}
|
2020-08-24 00:07:24 +01:00
|
|
|
|
|
|
|
${if types.lines.check cfg.declarativeContents then (let
|
|
|
|
dataFile = pkgs.writeText "ldap-contents.ldif" cfg.declarativeContents;
|
|
|
|
in ''
|
|
|
|
rm -rf ${lib.escapeShellArg cfg.dataDir}/*
|
2020-08-24 00:19:35 +01:00
|
|
|
${openldap}/bin/slapadd -F ${lib.escapeShellArg configDir} -l ${dataFile}
|
2020-08-24 00:07:24 +01:00
|
|
|
chown -R "${cfg.user}:${cfg.group}" ${lib.escapeShellArg cfg.dataDir}
|
|
|
|
'') else (let
|
|
|
|
dataFiles = lib.mapAttrs (dn: contents: pkgs.writeText "${dn}.ldif" contents) cfg.declarativeContents;
|
|
|
|
in ''
|
|
|
|
${lib.concatStrings (lib.mapAttrsToList (dn: file: let
|
|
|
|
dataDir = lib.escapeShellArg (getAttr dn dataDirs);
|
|
|
|
in ''
|
|
|
|
rm -rf ${dataDir}/*
|
2020-08-24 00:19:35 +01:00
|
|
|
${openldap}/bin/slapadd -F ${lib.escapeShellArg configDir} -b ${dn} -l ${file}
|
2020-08-24 00:07:24 +01:00
|
|
|
chown -R "${cfg.user}:${cfg.group}" ${dataDir}
|
|
|
|
'') dataFiles)}
|
|
|
|
'')}
|
2020-01-12 13:40:08 +00:00
|
|
|
|
2020-08-24 00:19:35 +01:00
|
|
|
${openldap}/bin/slaptest -u -F ${lib.escapeShellArg configDir}
|
2013-04-09 16:50:53 +01:00
|
|
|
'';
|
2020-08-02 23:52:37 +01:00
|
|
|
serviceConfig = {
|
2020-08-24 00:19:35 +01:00
|
|
|
ExecStart = lib.escapeShellArgs ([
|
|
|
|
"${openldap}/libexec/slapd" "-u" cfg.user "-g" cfg.group "-F" configDir
|
|
|
|
"-h" (lib.concatStringsSep " " cfg.urlList)
|
|
|
|
]);
|
2020-08-02 23:52:37 +01:00
|
|
|
Type = "forking";
|
|
|
|
PIDFile = cfg.settings.attrs.olcPidFile;
|
2015-01-26 08:35:56 +00:00
|
|
|
};
|
2020-08-02 23:52:37 +01:00
|
|
|
};
|
2013-11-28 21:21:50 +00:00
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
users.users = lib.optionalAttrs (cfg.user == "openldap") {
|
2020-08-24 00:19:35 +01:00
|
|
|
openldap = {
|
|
|
|
group = cfg.group;
|
|
|
|
isSystemUser = true;
|
|
|
|
};
|
2020-08-02 23:52:37 +01:00
|
|
|
};
|
2011-04-13 18:35:19 +01:00
|
|
|
|
2020-08-02 23:52:37 +01:00
|
|
|
users.groups = lib.optionalAttrs (cfg.group == "openldap") {
|
|
|
|
openldap = {};
|
|
|
|
};
|
2013-11-28 21:21:50 +00:00
|
|
|
};
|
2011-04-13 18:35:19 +01:00
|
|
|
}
|