Merge pull request #53762 from ju1m/nslcd
Improving integration of `nslcd`, PAM and `openldap`.
This commit is contained in:
commit
d3c2ed21d0
|
@ -331,17 +331,29 @@
|
||||||
<para>
|
<para>
|
||||||
The <literal>pam_unix</literal> account module is now loaded with its
|
The <literal>pam_unix</literal> account module is now loaded with its
|
||||||
control field set to <literal>required</literal> instead of
|
control field set to <literal>required</literal> instead of
|
||||||
<literal>sufficient</literal>, so that later pam account modules that
|
<literal>sufficient</literal>, so that later PAM account modules that
|
||||||
might do more extensive checks are being executed.
|
might do more extensive checks are being executed.
|
||||||
Previously, the whole account module verification was exited prematurely
|
Previously, the whole account module verification was exited prematurely
|
||||||
in case a nss module provided the account name to
|
in case a nss module provided the account name to
|
||||||
<literal>pam_unix</literal>.
|
<literal>pam_unix</literal>.
|
||||||
The LDAP and SSSD NixOS modules already add their NSS modules when
|
The LDAP and SSSD NixOS modules already add their NSS modules when
|
||||||
enabled. In case your setup breaks due to some later pam account module
|
enabled. In case your setup breaks due to some later PAM account module
|
||||||
previosuly shadowed, or failing NSS lookups, please file a bug. You can
|
previosuly shadowed, or failing NSS lookups, please file a bug. You can
|
||||||
get back the old behaviour by manually setting
|
get back the old behaviour by manually setting
|
||||||
<literal><![CDATA[security.pam.services.<name?>.text]]></literal>.
|
<literal><![CDATA[security.pam.services.<name?>.text]]></literal>.
|
||||||
</para>
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The <literal>pam_unix</literal> password module is now loaded with its
|
||||||
|
control field set to <literal>sufficient</literal> instead of
|
||||||
|
<literal>required</literal>, so that password managed only
|
||||||
|
by later PAM password modules are being executed.
|
||||||
|
Previously, for example, changing an LDAP account's password through PAM
|
||||||
|
was not possible: the whole password module verification
|
||||||
|
was exited prematurely by <literal>pam_unix</literal>,
|
||||||
|
preventing <literal>pam_ldap</literal> to manage the password as it should.
|
||||||
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
|
|
|
@ -38,6 +38,8 @@ let
|
||||||
bind_timelimit ${toString cfg.bind.timeLimit}
|
bind_timelimit ${toString cfg.bind.timeLimit}
|
||||||
${optionalString (cfg.bind.distinguishedName != "")
|
${optionalString (cfg.bind.distinguishedName != "")
|
||||||
"binddn ${cfg.bind.distinguishedName}" }
|
"binddn ${cfg.bind.distinguishedName}" }
|
||||||
|
${optionalString (cfg.daemon.rootpwmoddn != "")
|
||||||
|
"rootpwmoddn ${cfg.daemon.rootpwmoddn}" }
|
||||||
${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
|
${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
@ -126,6 +128,26 @@ in
|
||||||
the end of the nslcd configuration file (nslcd.conf).
|
the end of the nslcd configuration file (nslcd.conf).
|
||||||
'' ;
|
'' ;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
rootpwmoddn = mkOption {
|
||||||
|
default = "";
|
||||||
|
example = "cn=admin,dc=example,dc=com";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The distinguished name to use to bind to the LDAP server
|
||||||
|
when the root user tries to modify a user's password.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
rootpwmodpw = mkOption {
|
||||||
|
default = "";
|
||||||
|
example = "/run/keys/nslcd.rootpwmodpw";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The path to a file containing the credentials with which
|
||||||
|
to bind to the LDAP server if the root user tries to change a user's password
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
bind = {
|
bind = {
|
||||||
|
@ -203,9 +225,11 @@ in
|
||||||
system.activationScripts = mkIf insertLdapPassword {
|
system.activationScripts = mkIf insertLdapPassword {
|
||||||
ldap = stringAfter [ "etc" "groups" "users" ] ''
|
ldap = stringAfter [ "etc" "groups" "users" ] ''
|
||||||
if test -f "${cfg.bind.password}" ; then
|
if test -f "${cfg.bind.password}" ; then
|
||||||
echo "bindpw "$(cat ${cfg.bind.password})"" | cat ${ldapConfig.source} - > /etc/ldap.conf.bindpw
|
umask 0077
|
||||||
mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf
|
conf="$(mktemp)"
|
||||||
chmod 600 /etc/ldap.conf
|
printf 'bindpw %s\n' "$(cat ${cfg.bind.password})" |
|
||||||
|
cat ${ldapConfig.source} - >"$conf"
|
||||||
|
mv -fT "$conf" /etc/ldap.conf
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
@ -232,21 +256,31 @@ in
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
preStart = ''
|
preStart = ''
|
||||||
mkdir -p /run/nslcd
|
umask 0077
|
||||||
rm -f /run/nslcd/nslcd.pid;
|
conf="$(mktemp)"
|
||||||
chown nslcd.nslcd /run/nslcd
|
{
|
||||||
${optionalString (cfg.bind.distinguishedName != "") ''
|
cat ${nslcdConfig.source}
|
||||||
if test -s "${cfg.bind.password}" ; then
|
test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.password}' ||
|
||||||
ln -sfT "${cfg.bind.password}" /run/nslcd/bindpw
|
printf 'bindpw %s\n' "$(cat '${cfg.bind.password}')"
|
||||||
fi
|
test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpw}' ||
|
||||||
''}
|
printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpw}')"
|
||||||
|
} >"$conf"
|
||||||
|
mv -fT "$conf" /etc/nslcd.conf
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# NOTE: because one cannot pass a custom config path to `nslcd`
|
||||||
|
# (which is only able to use `/etc/nslcd.conf`)
|
||||||
|
# changes in `nslcdConfig` won't change `serviceConfig`,
|
||||||
|
# and thus won't restart `nslcd`.
|
||||||
|
# Therefore `restartTriggers` is used on `/etc/nslcd.conf`.
|
||||||
|
restartTriggers = [ nslcdConfig.source ];
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${nss_pam_ldapd}/sbin/nslcd";
|
ExecStart = "${nss_pam_ldapd}/sbin/nslcd";
|
||||||
Type = "forking";
|
Type = "forking";
|
||||||
PIDFile = "/run/nslcd/nslcd.pid";
|
PIDFile = "/run/nslcd/nslcd.pid";
|
||||||
Restart = "always";
|
Restart = "always";
|
||||||
|
RuntimeDirectory = [ "nslcd" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -370,7 +370,7 @@ let
|
||||||
auth required pam_deny.so
|
auth required pam_deny.so
|
||||||
|
|
||||||
# Password management.
|
# Password management.
|
||||||
password requisite pam_unix.so nullok sha512
|
password sufficient pam_unix.so nullok sha512
|
||||||
${optionalString config.security.pam.enableEcryptfs
|
${optionalString config.security.pam.enableEcryptfs
|
||||||
"password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
|
"password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
|
||||||
${optionalString cfg.pamMount
|
${optionalString cfg.pamMount
|
||||||
|
|
|
@ -1,41 +1,23 @@
|
||||||
import ./make-test.nix ({ pkgs, lib, ...} :
|
import ./make-test.nix ({ pkgs, lib, ...} :
|
||||||
|
|
||||||
let
|
let
|
||||||
|
unlines = lib.concatStringsSep "\n";
|
||||||
|
unlinesAttrs = f: as: unlines (lib.mapAttrsToList f as);
|
||||||
|
|
||||||
|
dbDomain = "example.com";
|
||||||
dbSuffix = "dc=example,dc=com";
|
dbSuffix = "dc=example,dc=com";
|
||||||
dbPath = "/var/db/openldap";
|
|
||||||
dbAdminDn = "cn=admin,${dbSuffix}";
|
dbAdminDn = "cn=admin,${dbSuffix}";
|
||||||
dbAdminPwd = "test";
|
dbAdminPwd = "admin-password";
|
||||||
serverUri = "ldap:///";
|
# NOTE: slappasswd -h "{SSHA}" -s '${dbAdminPwd}'
|
||||||
|
dbAdminPwdHash = "{SSHA}i7FopSzkFQMrHzDMB1vrtkI0rBnwouP8";
|
||||||
ldapUser = "test-ldap-user";
|
ldapUser = "test-ldap-user";
|
||||||
ldapUserId = 10000;
|
ldapUserId = 10000;
|
||||||
ldapUserPwd = "test";
|
ldapUserPwd = "user-password";
|
||||||
|
# NOTE: slappasswd -h "{SSHA}" -s '${ldapUserPwd}'
|
||||||
|
ldapUserPwdHash = "{SSHA}v12XICMZNGT6r2KJ26rIkN8Vvvp4QX6i";
|
||||||
ldapGroup = "test-ldap-group";
|
ldapGroup = "test-ldap-group";
|
||||||
ldapGroupId = 10000;
|
ldapGroupId = 10000;
|
||||||
setupLdif = pkgs.writeText "test-ldap.ldif" ''
|
|
||||||
dn: ${dbSuffix}
|
|
||||||
dc: ${with lib; let dc = head (splitString "," dbSuffix); dcName = head (tail (splitString "=" dc)); in dcName}
|
|
||||||
o: ${dbSuffix}
|
|
||||||
objectclass: top
|
|
||||||
objectclass: dcObject
|
|
||||||
objectclass: organization
|
|
||||||
|
|
||||||
dn: cn=${ldapUser},${dbSuffix}
|
|
||||||
sn: ${ldapUser}
|
|
||||||
objectClass: person
|
|
||||||
objectClass: posixAccount
|
|
||||||
uid: ${ldapUser}
|
|
||||||
uidNumber: ${toString ldapUserId}
|
|
||||||
gidNumber: ${toString ldapGroupId}
|
|
||||||
homeDirectory: /home/${ldapUser}
|
|
||||||
loginShell: /bin/sh
|
|
||||||
userPassword: ${ldapUserPwd}
|
|
||||||
|
|
||||||
dn: cn=${ldapGroup},${dbSuffix}
|
|
||||||
objectClass: posixGroup
|
|
||||||
gidNumber: ${toString ldapGroupId}
|
|
||||||
memberUid: ${ldapUser}
|
|
||||||
'';
|
|
||||||
mkClient = useDaemon:
|
mkClient = useDaemon:
|
||||||
{ lib, ... }:
|
{ lib, ... }:
|
||||||
{
|
{
|
||||||
|
@ -43,13 +25,24 @@ let
|
||||||
virtualisation.vlans = [ 1 ];
|
virtualisation.vlans = [ 1 ];
|
||||||
security.pam.services.su.rootOK = lib.mkForce false;
|
security.pam.services.su.rootOK = lib.mkForce false;
|
||||||
users.ldap.enable = true;
|
users.ldap.enable = true;
|
||||||
users.ldap.daemon.enable = useDaemon;
|
users.ldap.daemon = {
|
||||||
|
enable = useDaemon;
|
||||||
|
rootpwmoddn = "cn=admin,${dbSuffix}";
|
||||||
|
rootpwmodpw = "/etc/nslcd.rootpwmodpw";
|
||||||
|
};
|
||||||
|
# NOTE: password stored in clear in Nix's store, but this is a test.
|
||||||
|
environment.etc."nslcd.rootpwmodpw".source = pkgs.writeText "rootpwmodpw" dbAdminPwd;
|
||||||
users.ldap.loginPam = true;
|
users.ldap.loginPam = true;
|
||||||
users.ldap.nsswitch = true;
|
users.ldap.nsswitch = true;
|
||||||
users.ldap.server = "ldap://server";
|
users.ldap.server = "ldap://server";
|
||||||
users.ldap.base = "${dbSuffix}";
|
users.ldap.base = "ou=posix,${dbSuffix}";
|
||||||
|
users.ldap.bind = {
|
||||||
|
distinguishedName = "cn=admin,${dbSuffix}";
|
||||||
|
password = "/etc/ldap/bind.password";
|
||||||
|
};
|
||||||
|
# NOTE: password stored in clear in Nix's store, but this is a test.
|
||||||
|
environment.etc."ldap/bind.password".source = pkgs.writeText "password" dbAdminPwd;
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -61,28 +54,237 @@ in
|
||||||
nodes = {
|
nodes = {
|
||||||
|
|
||||||
server =
|
server =
|
||||||
{ pkgs, ... }:
|
{ pkgs, config, ... }:
|
||||||
|
let
|
||||||
|
inherit (config.services) openldap;
|
||||||
|
|
||||||
|
slapdConfig = 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
|
||||||
|
#olcRootPW:
|
||||||
|
# NOTE: 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
|
||||||
|
|
||||||
|
dn: cn=module{0},cn=config
|
||||||
|
objectClass: olcModuleList
|
||||||
|
# Where the dynamically loaded modules are stored
|
||||||
|
#olcModulePath: /usr/lib/ldap
|
||||||
|
olcModuleLoad: back_mdb
|
||||||
|
|
||||||
|
''
|
||||||
|
+ unlinesAttrs (olcSuffix: {conf, ...}:
|
||||||
|
"include: file://" + pkgs.writeText "config.ldif" conf
|
||||||
|
) slapdDatabases
|
||||||
|
);
|
||||||
|
|
||||||
|
slapdDatabases = {
|
||||||
|
"${dbSuffix}" = {
|
||||||
|
conf = ''
|
||||||
|
dn: olcBackend={1}mdb,cn=config
|
||||||
|
objectClass: olcBackendConfig
|
||||||
|
|
||||||
|
dn: olcDatabase={1}mdb,cn=config
|
||||||
|
olcSuffix: ${dbSuffix}
|
||||||
|
olcDbDirectory: ${openldap.dataDir}/${dbSuffix}
|
||||||
|
objectClass: olcDatabaseConfig
|
||||||
|
objectClass: olcMdbConfig
|
||||||
|
# NOTE: checkpoint the database periodically in case of system failure
|
||||||
|
# and to speed up slapd shutdown.
|
||||||
|
olcDbCheckpoint: 512 30
|
||||||
|
# Database max size is 1G
|
||||||
|
olcDbMaxSize: 1073741824
|
||||||
|
olcLastMod: TRUE
|
||||||
|
# NOTE: database superuser. Needed for syncrepl,
|
||||||
|
# and used to auth as admin through a TCP connection.
|
||||||
|
olcRootDN: cn=admin,${dbSuffix}
|
||||||
|
olcRootPW: ${dbAdminPwdHash}
|
||||||
|
#
|
||||||
|
olcDbIndex: objectClass eq
|
||||||
|
olcDbIndex: cn,uid eq
|
||||||
|
olcDbIndex: uidNumber,gidNumber eq
|
||||||
|
olcDbIndex: member,memberUid eq
|
||||||
|
#
|
||||||
|
olcAccess: to attrs=userPassword
|
||||||
|
by self write
|
||||||
|
by anonymous auth
|
||||||
|
by dn="cn=admin,${dbSuffix}" write
|
||||||
|
by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
|
||||||
|
by * none
|
||||||
|
olcAccess: to attrs=shadowLastChange
|
||||||
|
by self write
|
||||||
|
by dn="cn=admin,${dbSuffix}" write
|
||||||
|
by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" write
|
||||||
|
by * none
|
||||||
|
olcAccess: to dn.sub="ou=posix,${dbSuffix}"
|
||||||
|
by self read
|
||||||
|
by dn="cn=admin,${dbSuffix}" read
|
||||||
|
by dn="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
|
||||||
|
olcAccess: to *
|
||||||
|
by self read
|
||||||
|
by * none
|
||||||
|
'';
|
||||||
|
data = ''
|
||||||
|
dn: ${dbSuffix}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: dcObject
|
||||||
|
objectClass: organization
|
||||||
|
o: ${dbDomain}
|
||||||
|
|
||||||
|
dn: cn=admin,${dbSuffix}
|
||||||
|
objectClass: simpleSecurityObject
|
||||||
|
objectClass: organizationalRole
|
||||||
|
description: ${dbDomain} LDAP administrator
|
||||||
|
roleOccupant: ${dbSuffix}
|
||||||
|
userPassword: ${ldapUserPwdHash}
|
||||||
|
|
||||||
|
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
|
||||||
|
''
|
||||||
|
+ lib.concatMapStrings posixAccount [
|
||||||
|
{ uid=ldapUser; uidNumber=ldapUserId; gidNumber=ldapGroupId; userPassword=ldapUserPwdHash; }
|
||||||
|
]
|
||||||
|
+ lib.concatMapStrings posixGroup [
|
||||||
|
{ gid=ldapGroup; gidNumber=ldapGroupId; members=[]; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# NOTE: create a user account using the posixAccount objectClass.
|
||||||
|
posixAccount =
|
||||||
|
{ uid
|
||||||
|
, uidNumber ? null
|
||||||
|
, gidNumber ? null
|
||||||
|
, cn ? ""
|
||||||
|
, sn ? ""
|
||||||
|
, userPassword ? ""
|
||||||
|
, loginShell ? "/bin/sh"
|
||||||
|
}: ''
|
||||||
|
|
||||||
|
dn: uid=${uid},ou=accounts,ou=posix,${dbSuffix}
|
||||||
|
objectClass: person
|
||||||
|
objectClass: posixAccount
|
||||||
|
objectClass: shadowAccount
|
||||||
|
cn: ${cn}
|
||||||
|
gecos:
|
||||||
|
${if gidNumber == null then "#" else "gidNumber: ${toString gidNumber}"}
|
||||||
|
homeDirectory: /home/${uid}
|
||||||
|
loginShell: ${loginShell}
|
||||||
|
sn: ${sn}
|
||||||
|
${if uidNumber == null then "#" else "uidNumber: ${toString uidNumber}"}
|
||||||
|
${if userPassword == "" then "#" else "userPassword: ${userPassword}"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
# NOTE: create a group using the posixGroup objectClass.
|
||||||
|
posixGroup =
|
||||||
|
{ gid
|
||||||
|
, gidNumber
|
||||||
|
, members
|
||||||
|
}: ''
|
||||||
|
|
||||||
|
dn: cn=${gid},ou=groups,ou=posix,${dbSuffix}
|
||||||
|
objectClass: top
|
||||||
|
objectClass: posixGroup
|
||||||
|
gidNumber: ${toString gidNumber}
|
||||||
|
${lib.concatMapStrings (member: "memberUid: ${member}\n") members}
|
||||||
|
'';
|
||||||
|
in
|
||||||
{
|
{
|
||||||
virtualisation.memorySize = 256;
|
virtualisation.memorySize = 256;
|
||||||
virtualisation.vlans = [ 1 ];
|
virtualisation.vlans = [ 1 ];
|
||||||
networking.firewall.allowedTCPPorts = [ 389 ];
|
networking.firewall.allowedTCPPorts = [ 389 ];
|
||||||
services.openldap.enable = true;
|
services.openldap.enable = true;
|
||||||
services.openldap.dataDir = dbPath;
|
services.openldap.dataDir = "/var/db/openldap";
|
||||||
|
services.openldap.configDir = "/var/db/slapd";
|
||||||
services.openldap.urlList = [
|
services.openldap.urlList = [
|
||||||
serverUri
|
"ldap:///"
|
||||||
|
"ldapi:///"
|
||||||
];
|
];
|
||||||
services.openldap.extraConfig = ''
|
systemd.services.openldap = {
|
||||||
include ${pkgs.openldap.out}/etc/schema/core.schema
|
preStart = ''
|
||||||
include ${pkgs.openldap.out}/etc/schema/cosine.schema
|
set -e
|
||||||
include ${pkgs.openldap.out}/etc/schema/inetorgperson.schema
|
# NOTE: slapd's config is always re-initialized.
|
||||||
include ${pkgs.openldap.out}/etc/schema/nis.schema
|
rm -rf "${openldap.configDir}"/cn=config \
|
||||||
|
"${openldap.configDir}"/cn=config.ldif
|
||||||
database mdb
|
install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" "${openldap.configDir}"
|
||||||
suffix ${dbSuffix}
|
# NOTE: olcDbDirectory must be created before adding the config.
|
||||||
rootdn ${dbAdminDn}
|
'' +
|
||||||
rootpw ${dbAdminPwd}
|
unlinesAttrs (olcSuffix: {data, ...}: ''
|
||||||
directory ${dbPath}
|
# NOTE: database is always re-initialized.
|
||||||
'';
|
rm -rf "${openldap.dataDir}/${olcSuffix}"
|
||||||
|
install -D -d -m 0700 -o "${openldap.user}" -g "${openldap.group}" \
|
||||||
|
"${openldap.dataDir}/${olcSuffix}"
|
||||||
|
'') slapdDatabases
|
||||||
|
+ ''
|
||||||
|
# NOTE: slapd is supposed to be stopped while in preStart,
|
||||||
|
# hence slap* commands can safely be used.
|
||||||
|
umask 0077
|
||||||
|
${pkgs.openldap}/bin/slapadd -n 0 \
|
||||||
|
-F "${openldap.configDir}" \
|
||||||
|
-l ${slapdConfig}
|
||||||
|
chown -R "${openldap.user}:${openldap.group}" "${openldap.configDir}"
|
||||||
|
# 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.
|
||||||
|
'' +
|
||||||
|
unlinesAttrs (olcSuffix: {data, ...}: ''
|
||||||
|
# NOTE: load database ${olcSuffix}
|
||||||
|
# (as root to avoid depending on sudo or chpst)
|
||||||
|
${pkgs.openldap}/bin/slapadd \
|
||||||
|
-F "${openldap.configDir}" \
|
||||||
|
-l ${pkgs.writeText "data.ldif" data}
|
||||||
|
'' + ''
|
||||||
|
# NOTE: redundant with default openldap's preStart, but do not harm.
|
||||||
|
chown -R "${openldap.user}:${openldap.group}" \
|
||||||
|
"${openldap.dataDir}/${olcSuffix}"
|
||||||
|
'') slapdDatabases;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
client1 = mkClient true; # use nss_pam_ldapd
|
client1 = mkClient true; # use nss_pam_ldapd
|
||||||
|
@ -91,15 +293,91 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = ''
|
testScript = ''
|
||||||
startAll;
|
$server->start;
|
||||||
$server->waitForUnit("default.target");
|
$server->waitForUnit("default.target");
|
||||||
|
|
||||||
|
subtest "slapd", sub {
|
||||||
|
subtest "auth as database admin with SASL and check a POSIX account", sub {
|
||||||
|
$server->succeed(join ' ', 'test',
|
||||||
|
'"$(ldapsearch -LLL -H ldapi:// -Y EXTERNAL',
|
||||||
|
'-b \'uid=${ldapUser},ou=accounts,ou=posix,${dbSuffix}\' ',
|
||||||
|
'-s base uidNumber |',
|
||||||
|
'sed -ne \'s/^uidNumber: \\(.*\\)/\\1/p\' ',
|
||||||
|
')" -eq ${toString ldapUserId}');
|
||||||
|
};
|
||||||
|
subtest "auth as database admin with password and check a POSIX account", sub {
|
||||||
|
$server->succeed(join ' ', 'test',
|
||||||
|
'"$(ldapsearch -LLL -H ldap://server',
|
||||||
|
'-D \'cn=admin,${dbSuffix}\' -w \'${dbAdminPwd}\' ',
|
||||||
|
'-b \'uid=${ldapUser},ou=accounts,ou=posix,${dbSuffix}\' ',
|
||||||
|
'-s base uidNumber |',
|
||||||
|
'sed -ne \'s/^uidNumber: \\(.*\\)/\\1/p\' ',
|
||||||
|
')" -eq ${toString ldapUserId}');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$client1->start;
|
||||||
$client1->waitForUnit("default.target");
|
$client1->waitForUnit("default.target");
|
||||||
|
|
||||||
|
subtest "password", sub {
|
||||||
|
subtest "su with password to a POSIX account", sub {
|
||||||
|
$client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';',
|
||||||
|
'spawn su "${ldapUser}"',
|
||||||
|
'expect "Password:"',
|
||||||
|
'send "${ldapUserPwd}\n"',
|
||||||
|
'expect "*"',
|
||||||
|
'send "whoami\n"',
|
||||||
|
'expect -ex "${ldapUser}" {exit}',
|
||||||
|
'exit 1' . "'");
|
||||||
|
};
|
||||||
|
subtest "change password of a POSIX account as root", sub {
|
||||||
|
$client1->succeed("chpasswd <<<'${ldapUser}:new-password'");
|
||||||
|
$client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';',
|
||||||
|
'spawn su "${ldapUser}"',
|
||||||
|
'expect "Password:"',
|
||||||
|
'send "new-password\n"',
|
||||||
|
'expect "*"',
|
||||||
|
'send "whoami\n"',
|
||||||
|
'expect -ex "${ldapUser}" {exit}',
|
||||||
|
'exit 1' . "'");
|
||||||
|
$client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' ');
|
||||||
|
};
|
||||||
|
subtest "change password of a POSIX account from itself", sub {
|
||||||
|
$client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' ');
|
||||||
|
$client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';',
|
||||||
|
'spawn su --login ${ldapUser} -c passwd',
|
||||||
|
'expect "Password: "',
|
||||||
|
'send "${ldapUserPwd}\n"',
|
||||||
|
'expect "(current) UNIX password: "',
|
||||||
|
'send "${ldapUserPwd}\n"',
|
||||||
|
'expect "New password: "',
|
||||||
|
'send "new-password\n"',
|
||||||
|
'expect "Retype new password: "',
|
||||||
|
'send "new-password\n"',
|
||||||
|
'expect "passwd: password updated successfully" {exit}',
|
||||||
|
'exit 1' . "'");
|
||||||
|
$client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';',
|
||||||
|
'spawn su "${ldapUser}"',
|
||||||
|
'expect "Password:"',
|
||||||
|
'send "${ldapUserPwd}\n"',
|
||||||
|
'expect "su: Authentication failure" {exit}',
|
||||||
|
'exit 1' . "'");
|
||||||
|
$client1->succeed("${pkgs.expect}/bin/expect -c '" . join ';',
|
||||||
|
'spawn su "${ldapUser}"',
|
||||||
|
'expect "Password:"',
|
||||||
|
'send "new-password\n"',
|
||||||
|
'expect "*"',
|
||||||
|
'send "whoami\n"',
|
||||||
|
'expect -ex "${ldapUser}" {exit}',
|
||||||
|
'exit 1' . "'");
|
||||||
|
$client1->succeed('chpasswd <<<\'${ldapUser}:${ldapUserPwd}\' ');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
$client2->start;
|
||||||
$client2->waitForUnit("default.target");
|
$client2->waitForUnit("default.target");
|
||||||
|
|
||||||
$server->succeed("ldapadd -D '${dbAdminDn}' -w ${dbAdminPwd} -H ${serverUri} -f '${setupLdif}'");
|
subtest "NSS", sub {
|
||||||
|
|
||||||
# NSS tests
|
|
||||||
subtest "nss", sub {
|
|
||||||
$client1->succeed("test \"\$(id -u '${ldapUser}')\" -eq ${toString ldapUserId}");
|
$client1->succeed("test \"\$(id -u '${ldapUser}')\" -eq ${toString ldapUserId}");
|
||||||
$client1->succeed("test \"\$(id -u -n '${ldapUser}')\" = '${ldapUser}'");
|
$client1->succeed("test \"\$(id -u -n '${ldapUser}')\" = '${ldapUser}'");
|
||||||
$client1->succeed("test \"\$(id -g '${ldapUser}')\" -eq ${toString ldapGroupId}");
|
$client1->succeed("test \"\$(id -g '${ldapUser}')\" -eq ${toString ldapGroupId}");
|
||||||
|
@ -110,8 +388,7 @@ in
|
||||||
$client2->succeed("test \"\$(id -g -n '${ldapUser}')\" = '${ldapGroup}'");
|
$client2->succeed("test \"\$(id -g -n '${ldapUser}')\" = '${ldapGroup}'");
|
||||||
};
|
};
|
||||||
|
|
||||||
# PAM tests
|
subtest "PAM", sub {
|
||||||
subtest "pam", sub {
|
|
||||||
$client1->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true");
|
$client1->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true");
|
||||||
$client2->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true");
|
$client2->succeed("echo ${ldapUserPwd} | su -l '${ldapUser}' -c true");
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue