Merge pull request #94610 from kwohlfahrt/openldap
This commit is contained in:
commit
258903e725
@ -185,6 +185,32 @@
|
|||||||
which is the new stable release. OpenAFS 1.6 was removed.
|
which is the new stable release. OpenAFS 1.6 was removed.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The <literal>openldap</literal> module now has support for OLC-style
|
||||||
|
configuration, users of the <literal>configDir</literal> option may wish
|
||||||
|
to migrate. If you continue to use <literal>configDir</literal>, ensure that
|
||||||
|
<literal>olcPidFile</literal> is set to <literal>/run/slapd/slapd.pid</literal>.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
As a result, <literal>extraConfig</literal> and <literal>extraDatabaseConfig</literal>
|
||||||
|
are removed. To help with migration, you can convert your <literal>slapd.conf</literal>
|
||||||
|
file to OLC configuration with the following script (find the location of this
|
||||||
|
configuration file by running <literal>systemctl status openldap</literal>, it is the
|
||||||
|
<literal>-f</literal> option.
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
slaptest -f /path/to/slapd.conf $TMPDIR
|
||||||
|
slapcat -F $TMPDIR -n0 -H 'ldap:///???(!(objectClass=olcSchemaConfig))'
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
This will dump 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 for users of the default schemas
|
||||||
|
and <literal>slaptest</literal> is buggy with schemas directly in the config file.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ in
|
|||||||
#keys = 96; # unused
|
#keys = 96; # unused
|
||||||
#haproxy = 97; # dynamically allocated as of 2020-03-11
|
#haproxy = 97; # dynamically allocated as of 2020-03-11
|
||||||
mongodb = 98;
|
mongodb = 98;
|
||||||
openldap = 99;
|
#openldap = 99; # dynamically allocated as of PR#94610
|
||||||
#users = 100; # unused
|
#users = 100; # unused
|
||||||
cgminer = 101;
|
cgminer = 101;
|
||||||
munin = 102;
|
munin = 102;
|
||||||
@ -451,7 +451,7 @@ in
|
|||||||
keys = 96;
|
keys = 96;
|
||||||
#haproxy = 97; # dynamically allocated as of 2020-03-11
|
#haproxy = 97; # dynamically allocated as of 2020-03-11
|
||||||
#mongodb = 98; # unused
|
#mongodb = 98; # unused
|
||||||
openldap = 99;
|
#openldap = 99; # dynamically allocated as of PR#94610
|
||||||
munin = 102;
|
munin = 102;
|
||||||
#logcheck = 103; # unused
|
#logcheck = 103; # unused
|
||||||
#nix-ssh = 104; # unused
|
#nix-ssh = 104; # unused
|
||||||
|
@ -1,43 +1,121 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.services.openldap;
|
cfg = config.services.openldap;
|
||||||
|
legacyOptions = [ "rootpwFile" "suffix" "dataDir" "rootdn" "rootpw" ];
|
||||||
openldap = cfg.package;
|
openldap = cfg.package;
|
||||||
|
configDir = if cfg.configDir != null then cfg.configDir else "/etc/openldap/slapd.d";
|
||||||
|
|
||||||
dataFile = pkgs.writeText "ldap-contents.ldif" cfg.declarativeContents;
|
ldapValueType = let
|
||||||
configFile = pkgs.writeText "slapd.conf" ((optionalString cfg.defaultSchemas ''
|
# Can't do types.either with multiple non-overlapping submodules, so define our own
|
||||||
include ${openldap.out}/etc/schema/core.schema
|
singleLdapValueType = lib.mkOptionType rec {
|
||||||
include ${openldap.out}/etc/schema/cosine.schema
|
name = "LDAP";
|
||||||
include ${openldap.out}/etc/schema/inetorgperson.schema
|
description = "LDAP value";
|
||||||
include ${openldap.out}/etc/schema/nis.schema
|
check = x: lib.isString x || (lib.isAttrs x && (x ? path || x ? base64));
|
||||||
'') + ''
|
merge = lib.mergeEqualOption;
|
||||||
${cfg.extraConfig}
|
};
|
||||||
database ${cfg.database}
|
# We don't coerce to lists of single values, as some values must be unique
|
||||||
suffix ${cfg.suffix}
|
in types.either singleLdapValueType (types.listOf singleLdapValueType);
|
||||||
rootdn ${cfg.rootdn}
|
|
||||||
${if (cfg.rootpw != null) then ''
|
|
||||||
rootpw ${cfg.rootpw}
|
|
||||||
'' else ''
|
|
||||||
include ${cfg.rootpwFile}
|
|
||||||
''}
|
|
||||||
directory ${cfg.dataDir}
|
|
||||||
${cfg.extraDatabaseConfig}
|
|
||||||
'');
|
|
||||||
configOpts = if cfg.configDir == null then "-f ${configFile}"
|
|
||||||
else "-F ${cfg.configDir}";
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
###### interface
|
|
||||||
|
|
||||||
|
ldapAttrsType =
|
||||||
|
let
|
||||||
options = {
|
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
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
imports = let
|
||||||
|
deprecationNote = "This option is removed due to the deprecation of `slapd.conf` upstream. Please migrate to `services.openldap.settings`, see the release notes for advice with this process.";
|
||||||
|
mkDatabaseOption = old: new:
|
||||||
|
lib.mkChangedOptionModule [ "services" "openldap" old ] [ "services" "openldap" "settings" "children" ]
|
||||||
|
(config: let
|
||||||
|
database = lib.getAttrFromPath [ "services" "openldap" "database" ] config;
|
||||||
|
value = lib.getAttrFromPath [ "services" "openldap" old ] config;
|
||||||
|
in lib.setAttrByPath ([ "olcDatabase={1}${database}" "attrs" ] ++ new) value);
|
||||||
|
in [
|
||||||
|
(lib.mkRemovedOptionModule [ "services" "openldap" "extraConfig" ] deprecationNote)
|
||||||
|
(lib.mkRemovedOptionModule [ "services" "openldap" "extraDatabaseConfig" ] deprecationNote)
|
||||||
|
|
||||||
|
(lib.mkChangedOptionModule [ "services" "openldap" "logLevel" ] [ "services" "openldap" "settings" "attrs" "olcLogLevel" ]
|
||||||
|
(config: lib.splitString " " (lib.getAttrFromPath [ "services" "openldap" "logLevel" ] config)))
|
||||||
|
(lib.mkChangedOptionModule [ "services" "openldap" "defaultSchemas" ] [ "services" "openldap" "settings" "children" "cn=schema" "includes"]
|
||||||
|
(config: lib.optionals (lib.getAttrFromPath [ "services" "openldap" "defaultSchemas" ] config) (
|
||||||
|
map (schema: "${openldap}/etc/schema/${schema}.ldif") [ "core" "cosine" "inetorgperson" "nis" ])))
|
||||||
|
|
||||||
|
(lib.mkChangedOptionModule [ "services" "openldap" "database" ] [ "services" "openldap" "settings" "children" ]
|
||||||
|
(config: let
|
||||||
|
database = lib.getAttrFromPath [ "services" "openldap" "database" ] config;
|
||||||
|
in {
|
||||||
|
"olcDatabase={1}${database}".attrs = {
|
||||||
|
# objectClass is case-insensitive, so don't need to capitalize ${database}
|
||||||
|
objectClass = [ "olcdatabaseconfig" "olc${database}config" ];
|
||||||
|
olcDatabase = "{1}${database}";
|
||||||
|
olcDbDirectory = lib.mkDefault "/var/db/openldap";
|
||||||
|
};
|
||||||
|
"cn=schema".includes = lib.mkDefault (
|
||||||
|
map (schema: "${openldap}/etc/schema/${schema}.ldif") [ "core" "cosine" "inetorgperson" "nis" ]
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
(mkDatabaseOption "rootpwFile" [ "olcRootPW" "path" ])
|
||||||
|
(mkDatabaseOption "suffix" [ "olcSuffix" ])
|
||||||
|
(mkDatabaseOption "dataDir" [ "olcDbDirectory" ])
|
||||||
|
(mkDatabaseOption "rootdn" [ "olcRootDN" ])
|
||||||
|
(mkDatabaseOption "rootpw" [ "olcRootPW" ])
|
||||||
|
];
|
||||||
|
options = {
|
||||||
services.openldap = {
|
services.openldap = {
|
||||||
|
|
||||||
enable = mkOption {
|
enable = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
@ -77,125 +155,80 @@ in
|
|||||||
example = [ "ldaps:///" ];
|
example = [ "ldaps:///" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
dataDir = mkOption {
|
settings = mkOption {
|
||||||
type = types.path;
|
type = ldapAttrsType;
|
||||||
default = "/var/db/openldap";
|
description = "Configuration for OpenLDAP, in OLC format";
|
||||||
description = "The database directory.";
|
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" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
defaultSchemas = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
description = ''
|
|
||||||
Include the default schemas core, cosine, inetorgperson and nis.
|
|
||||||
This setting will be ignored if configDir is set.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
database = mkOption {
|
# This option overrides settings
|
||||||
type = types.str;
|
|
||||||
default = "mdb";
|
|
||||||
description = ''
|
|
||||||
Database type to use for the LDAP.
|
|
||||||
This setting will be ignored if configDir is set.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
suffix = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "dc=example,dc=org";
|
|
||||||
description = ''
|
|
||||||
Specify the DN suffix of queries that will be passed to this backend
|
|
||||||
database.
|
|
||||||
This setting will be ignored if configDir is set.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
rootdn = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
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.
|
|
||||||
This setting will be ignored if configDir is set.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
rootpw = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Password for the root user.
|
|
||||||
This setting will be ignored if configDir is set.
|
|
||||||
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.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
rootpwFile = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Password file for the root user.
|
|
||||||
The file should contain the string <literal>rootpw</literal> followed by the password.
|
|
||||||
e.g.: <literal>rootpw mysecurepassword</literal>
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
logLevel = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "0";
|
|
||||||
example = "acl trace";
|
|
||||||
description = "The log level selector of slapd.";
|
|
||||||
};
|
|
||||||
|
|
||||||
configDir = mkOption {
|
configDir = mkOption {
|
||||||
type = types.nullOr types.path;
|
type = types.nullOr types.path;
|
||||||
default = null;
|
default = null;
|
||||||
description = "Use this optional config directory instead of using slapd.conf";
|
description = ''
|
||||||
|
Use this config directory instead of generating one from the
|
||||||
|
<literal>settings</literal> option. Overrides all NixOS settings. If
|
||||||
|
you use this option,ensure `olcPidFile` is set to `/run/slapd/slapd.conf`.
|
||||||
|
'';
|
||||||
example = "/var/db/slapd.d";
|
example = "/var/db/slapd.d";
|
||||||
};
|
};
|
||||||
|
|
||||||
extraConfig = mkOption {
|
|
||||||
type = types.lines;
|
|
||||||
default = "";
|
|
||||||
description = "
|
|
||||||
slapd.conf configuration
|
|
||||||
";
|
|
||||||
example = literalExample ''
|
|
||||||
'''
|
|
||||||
include ${openldap.out}/etc/schema/core.schema
|
|
||||||
include ${openldap.out}/etc/schema/cosine.schema
|
|
||||||
include ${openldap.out}/etc/schema/inetorgperson.schema
|
|
||||||
include ${openldap.out}/etc/schema/nis.schema
|
|
||||||
|
|
||||||
database bdb
|
|
||||||
suffix dc=example,dc=org
|
|
||||||
rootdn cn=admin,dc=example,dc=org
|
|
||||||
# NOTE: change after first start
|
|
||||||
rootpw secret
|
|
||||||
directory /var/db/openldap
|
|
||||||
'''
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
declarativeContents = mkOption {
|
declarativeContents = mkOption {
|
||||||
type = with types; nullOr lines;
|
type = with types; attrsOf lines;
|
||||||
default = null;
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Declarative contents for the LDAP database, in LDIF format.
|
Declarative contents for the LDAP database, in LDIF format by suffix.
|
||||||
|
|
||||||
Note a few facts when using it. First, the database
|
All data will be erased when starting the LDAP server. Modifications
|
||||||
<emphasis>must</emphasis> be stored in the directory defined by
|
to the database are not prevented, they are just dropped on the next
|
||||||
<code>dataDir</code>. Second, all <code>dataDir</code> will be erased
|
reboot of the server. Performance-wise the database and indexes are
|
||||||
when starting the LDAP server. Third, modifications to the database
|
rebuilt on each server startup, so this will slow down server startup,
|
||||||
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.
|
especially with large databases.
|
||||||
'';
|
'';
|
||||||
example = ''
|
example = lib.literalExample ''
|
||||||
dn: dc=example,dc=org
|
{
|
||||||
|
"dc=example,dc=org" = '''
|
||||||
|
dn= dn: dc=example,dc=org
|
||||||
objectClass: domain
|
objectClass: domain
|
||||||
dc: example
|
dc: example
|
||||||
|
|
||||||
@ -204,97 +237,88 @@ in
|
|||||||
ou: users
|
ou: users
|
||||||
|
|
||||||
# ...
|
# ...
|
||||||
|
''';
|
||||||
|
}
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
};
|
||||||
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
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
meta.maintainers = with lib.maintainters; [ mic92 kwohlfahrt ];
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
maintainers = [ lib.maintainers.mic92 ];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
###### implementation
|
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
assertions = [
|
assertions = map (opt: {
|
||||||
{
|
assertion = ((getAttr opt cfg) != "_mkMergedOptionModule") -> (cfg.database != "_mkMergedOptionModule");
|
||||||
assertion = cfg.configDir != null || cfg.rootpwFile != null || cfg.rootpw != null;
|
message = "Legacy OpenLDAP option `services.openldap.${opt}` requires `services.openldap.database` (use value \"mdb\" if unsure)";
|
||||||
message = "services.openldap: Unless configDir is set, either rootpw or rootpwFile must be set";
|
}) legacyOptions;
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
environment.systemPackages = [ openldap ];
|
environment.systemPackages = [ openldap ];
|
||||||
|
|
||||||
|
# Literal attributes must always be set
|
||||||
|
services.openldap.settings = {
|
||||||
|
attrs = {
|
||||||
|
objectClass = "olcGlobal";
|
||||||
|
cn = "config";
|
||||||
|
olcPidFile = "/run/slapd/slapd.pid";
|
||||||
|
};
|
||||||
|
children."cn=schema".attrs = {
|
||||||
|
cn = "schema";
|
||||||
|
objectClass = "olcSchemaConfig";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services.openldap = {
|
systemd.services.openldap = {
|
||||||
description = "LDAP server";
|
description = "LDAP server";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
preStart = ''
|
preStart = let
|
||||||
|
settingsFile = pkgs.writeText "config.ldif" (lib.concatStringsSep "\n" (attrsToLdif "cn=config" cfg.settings));
|
||||||
|
|
||||||
|
dbSettings = lib.filterAttrs (name: value: lib.hasPrefix "olcDatabase=" name) cfg.settings.children;
|
||||||
|
dataDirs = lib.mapAttrs' (name: value: lib.nameValuePair value.attrs.olcSuffix value.attrs.olcDbDirectory)
|
||||||
|
(lib.filterAttrs (_: value: value.attrs ? olcDbDirectory) dbSettings);
|
||||||
|
dataFiles = lib.mapAttrs (dn: contents: pkgs.writeText "${dn}.ldif" contents) cfg.declarativeContents;
|
||||||
|
mkLoadScript = dn: let
|
||||||
|
dataDir = lib.escapeShellArg (getAttr dn dataDirs);
|
||||||
|
in ''
|
||||||
|
rm -rf ${dataDir}/*
|
||||||
|
${openldap}/bin/slapadd -F ${lib.escapeShellArg configDir} -b ${dn} -l ${getAttr dn dataFiles}
|
||||||
|
chown -R "${cfg.user}:${cfg.group}" ${dataDir}
|
||||||
|
'';
|
||||||
|
in ''
|
||||||
mkdir -p /run/slapd
|
mkdir -p /run/slapd
|
||||||
chown -R "${cfg.user}:${cfg.group}" /run/slapd
|
chown -R "${cfg.user}:${cfg.group}" /run/slapd
|
||||||
${optionalString (cfg.declarativeContents != null) ''
|
|
||||||
rm -Rf "${cfg.dataDir}"
|
|
||||||
''}
|
|
||||||
mkdir -p "${cfg.dataDir}"
|
|
||||||
${optionalString (cfg.declarativeContents != null) ''
|
|
||||||
${openldap.out}/bin/slapadd ${configOpts} -l ${dataFile}
|
|
||||||
''}
|
|
||||||
chown -R "${cfg.user}:${cfg.group}" "${cfg.dataDir}"
|
|
||||||
|
|
||||||
${openldap}/bin/slaptest ${configOpts}
|
mkdir -p ${lib.escapeShellArg configDir} ${lib.escapeShellArgs (lib.attrValues dataDirs)}
|
||||||
|
chown "${cfg.user}:${cfg.group}" ${lib.escapeShellArg configDir} ${lib.escapeShellArgs (lib.attrValues dataDirs)}
|
||||||
|
|
||||||
|
${lib.optionalString (cfg.configDir == null) (''
|
||||||
|
rm -Rf ${configDir}/*
|
||||||
|
${openldap}/bin/slapadd -F ${configDir} -bcn=config -l ${settingsFile}
|
||||||
|
'')}
|
||||||
|
chown -R "${cfg.user}:${cfg.group}" ${lib.escapeShellArg configDir}
|
||||||
|
|
||||||
|
${lib.concatStrings (map mkLoadScript (lib.attrNames cfg.declarativeContents))}
|
||||||
|
${openldap}/bin/slaptest -u -F ${lib.escapeShellArg configDir}
|
||||||
'';
|
'';
|
||||||
serviceConfig.ExecStart =
|
serviceConfig = {
|
||||||
"${openldap.out}/libexec/slapd -d '${cfg.logLevel}' " +
|
ExecStart = lib.escapeShellArgs ([
|
||||||
"-u '${cfg.user}' -g '${cfg.group}' " +
|
"${openldap}/libexec/slapd" "-u" cfg.user "-g" cfg.group "-F" configDir
|
||||||
"-h '${concatStringsSep " " cfg.urlList}' " +
|
"-h" (lib.concatStringsSep " " cfg.urlList)
|
||||||
"${configOpts}";
|
]);
|
||||||
|
Type = "forking";
|
||||||
|
PIDFile = cfg.settings.attrs.olcPidFile;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.openldap =
|
users.users = lib.optionalAttrs (cfg.user == "openldap") {
|
||||||
{ name = cfg.user;
|
openldap = {
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
uid = config.ids.uids.openldap;
|
isSystemUser = true;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.openldap =
|
users.groups = lib.optionalAttrs (cfg.group == "openldap") {
|
||||||
{ name = cfg.group;
|
openldap = {};
|
||||||
gid = config.ids.gids.openldap;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,5 @@
|
|||||||
import ./make-test-python.nix {
|
{ pkgs, system ? builtins.currentSystem, ... }: let
|
||||||
name = "openldap";
|
dbContents = ''
|
||||||
|
|
||||||
machine = { pkgs, ... }: {
|
|
||||||
services.openldap = {
|
|
||||||
enable = true;
|
|
||||||
suffix = "dc=example";
|
|
||||||
rootdn = "cn=root,dc=example";
|
|
||||||
rootpw = "notapassword";
|
|
||||||
database = "bdb";
|
|
||||||
extraDatabaseConfig = ''
|
|
||||||
directory /var/db/openldap
|
|
||||||
'';
|
|
||||||
declarativeContents = ''
|
|
||||||
dn: dc=example
|
dn: dc=example
|
||||||
objectClass: domain
|
objectClass: domain
|
||||||
dc: example
|
dc: example
|
||||||
@ -20,14 +8,118 @@ import ./make-test-python.nix {
|
|||||||
objectClass: organizationalUnit
|
objectClass: organizationalUnit
|
||||||
ou: users
|
ou: users
|
||||||
'';
|
'';
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
machine.wait_for_unit("openldap.service")
|
machine.wait_for_unit("openldap.service")
|
||||||
machine.succeed(
|
machine.succeed(
|
||||||
"systemctl status openldap.service",
|
|
||||||
'ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"',
|
'ldapsearch -LLL -D "cn=root,dc=example" -w notapassword -b "dc=example"',
|
||||||
)
|
)
|
||||||
'';
|
'';
|
||||||
|
in {
|
||||||
|
# New-style configuration
|
||||||
|
current = import ./make-test-python.nix {
|
||||||
|
inherit testScript;
|
||||||
|
name = "openldap";
|
||||||
|
|
||||||
|
machine = { pkgs, ... }: {
|
||||||
|
environment.etc."openldap/root_password".text = "notapassword";
|
||||||
|
services.openldap = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
children = {
|
||||||
|
"cn=schema".includes = [
|
||||||
|
"${pkgs.openldap}/etc/schema/core.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/cosine.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/nis.ldif"
|
||||||
|
];
|
||||||
|
"olcDatabase={1}mdb" = {
|
||||||
|
# This tests string, base64 and path values, as well as lists of string values
|
||||||
|
attrs = {
|
||||||
|
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
||||||
|
olcDatabase = "{1}mdb";
|
||||||
|
olcDbDirectory = "/var/db/openldap";
|
||||||
|
olcSuffix = "dc=example";
|
||||||
|
olcRootDN = {
|
||||||
|
# cn=root,dc=example
|
||||||
|
base64 = "Y249cm9vdCxkYz1leGFtcGxl";
|
||||||
|
};
|
||||||
|
olcRootPW = {
|
||||||
|
path = "/etc/openldap/root_password";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
declarativeContents."dc=example" = dbContents;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Old-style configuration
|
||||||
|
oldOptions = import ./make-test-python.nix {
|
||||||
|
inherit testScript;
|
||||||
|
name = "openldap";
|
||||||
|
|
||||||
|
machine = { pkgs, ... }: {
|
||||||
|
services.openldap = {
|
||||||
|
enable = true;
|
||||||
|
logLevel = "stats acl";
|
||||||
|
defaultSchemas = true;
|
||||||
|
database = "mdb";
|
||||||
|
suffix = "dc=example";
|
||||||
|
rootdn = "cn=root,dc=example";
|
||||||
|
rootpw = "notapassword";
|
||||||
|
declarativeContents."dc=example" = dbContents;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Manually managed configDir, for example if dynamic config is essential
|
||||||
|
manualConfigDir = import ./make-test-python.nix {
|
||||||
|
name = "openldap";
|
||||||
|
|
||||||
|
machine = { pkgs, ... }: {
|
||||||
|
services.openldap = {
|
||||||
|
enable = true;
|
||||||
|
configDir = "/var/db/slapd.d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = let
|
||||||
|
contents = pkgs.writeText "data.ldif" dbContents;
|
||||||
|
config = pkgs.writeText "config.ldif" ''
|
||||||
|
dn: cn=config
|
||||||
|
cn: config
|
||||||
|
objectClass: olcGlobal
|
||||||
|
olcLogLevel: stats
|
||||||
|
olcPidFile: /run/slapd/slapd.pid
|
||||||
|
|
||||||
|
dn: cn=schema,cn=config
|
||||||
|
cn: schema
|
||||||
|
objectClass: olcSchemaConfig
|
||||||
|
|
||||||
|
include: file://${pkgs.openldap}/etc/schema/core.ldif
|
||||||
|
include: file://${pkgs.openldap}/etc/schema/cosine.ldif
|
||||||
|
include: file://${pkgs.openldap}/etc/schema/inetorgperson.ldif
|
||||||
|
|
||||||
|
dn: olcDatabase={1}mdb,cn=config
|
||||||
|
objectClass: olcDatabaseConfig
|
||||||
|
objectClass: olcMdbConfig
|
||||||
|
olcDatabase: {1}mdb
|
||||||
|
olcDbDirectory: /var/db/openldap
|
||||||
|
olcDbIndex: objectClass eq
|
||||||
|
olcSuffix: dc=example
|
||||||
|
olcRootDN: cn=root,dc=example
|
||||||
|
olcRootPW: notapassword
|
||||||
|
'';
|
||||||
|
in ''
|
||||||
|
machine.succeed(
|
||||||
|
"mkdir -p /var/db/slapd.d /var/db/openldap",
|
||||||
|
"slapadd -F /var/db/slapd.d -n0 -l ${config}",
|
||||||
|
"slapadd -F /var/db/slapd.d -n1 -l ${contents}",
|
||||||
|
"chown -R openldap:openldap /var/db/slapd.d /var/db/openldap",
|
||||||
|
"systemctl restart openldap",
|
||||||
|
)
|
||||||
|
'' + testScript;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ./make-test-python.nix ({ pkgs, ... }:
|
({ pkgs, ... }:
|
||||||
let
|
let
|
||||||
dbDomain = "example.org";
|
dbDomain = "example.org";
|
||||||
dbSuffix = "dc=example,dc=org";
|
dbSuffix = "dc=example,dc=org";
|
||||||
@ -7,8 +7,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
|
|||||||
ldapRootPassword = "foobar";
|
ldapRootPassword = "foobar";
|
||||||
|
|
||||||
testUser = "alice";
|
testUser = "alice";
|
||||||
in
|
in import ./make-test-python.nix {
|
||||||
{
|
|
||||||
name = "sssd-ldap";
|
name = "sssd-ldap";
|
||||||
|
|
||||||
meta = with pkgs.stdenv.lib.maintainers; {
|
meta = with pkgs.stdenv.lib.maintainers; {
|
||||||
@ -18,10 +17,28 @@ import ./make-test-python.nix ({ pkgs, ... }:
|
|||||||
machine = { pkgs, ... }: {
|
machine = { pkgs, ... }: {
|
||||||
services.openldap = {
|
services.openldap = {
|
||||||
enable = true;
|
enable = true;
|
||||||
rootdn = "cn=${ldapRootUser},${dbSuffix}";
|
settings = {
|
||||||
rootpw = ldapRootPassword;
|
children = {
|
||||||
suffix = dbSuffix;
|
"cn=schema".includes = [
|
||||||
declarativeContents = ''
|
"${pkgs.openldap}/etc/schema/core.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/cosine.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/inetorgperson.ldif"
|
||||||
|
"${pkgs.openldap}/etc/schema/nis.ldif"
|
||||||
|
];
|
||||||
|
"olcDatabase={1}mdb" = {
|
||||||
|
attrs = {
|
||||||
|
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
|
||||||
|
olcDatabase = "{1}mdb";
|
||||||
|
olcDbDirectory = "/var/db/openldap";
|
||||||
|
olcSuffix = dbSuffix;
|
||||||
|
olcRootDN = "cn=${ldapRootUser},${dbSuffix}";
|
||||||
|
olcRootPW = ldapRootPassword;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
declarativeContents = {
|
||||||
|
${dbSuffix} = ''
|
||||||
dn: ${dbSuffix}
|
dn: ${dbSuffix}
|
||||||
objectClass: top
|
objectClass: top
|
||||||
objectClass: dcObject
|
objectClass: dcObject
|
||||||
@ -47,6 +64,7 @@ import ./make-test-python.nix ({ pkgs, ... }:
|
|||||||
sn: ""
|
sn: ""
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
services.sssd = {
|
services.sssd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
Loading…
Reference in New Issue
Block a user