Add support for nslcd (nss-pam-ldapd) as users.ldap.daemon option

This commit is contained in:
Rickard Nilsson 2012-09-16 19:14:19 +02:00
parent 611ebeb1d0
commit 6099451662
3 changed files with 142 additions and 50 deletions

View File

@ -1,8 +1,13 @@
{pkgs, config, ...}: {pkgs, config, ...}:
with pkgs.lib;
with pkgs;
###### interface ###### interface
let let
inherit (pkgs.lib) mkOption mkIf optionalString stringAfter; inherit mkOption mkIf optionalString stringAfter singleton;
cfg = config.users.ldap;
options = { options = {
users = { users = {
@ -41,7 +46,7 @@ let
timeLimit = mkOption { timeLimit = mkOption {
default = 0; default = 0;
type = with pkgs.lib.types; int; type = types.int;
description = " description = "
Specifies the time limit (in seconds) to use when performing Specifies the time limit (in seconds) to use when performing
searches. A value of zero (0), which is the default, is to searches. A value of zero (0), which is the default, is to
@ -49,11 +54,36 @@ let
"; ";
}; };
daemon = {
enable = mkOption {
default = false;
description = ''
Whether to let the nslcd daemon (nss-pam-ldapd) handle the
LDAP lookups for NSS and PAM. This can improve performance,
and if you need to bind to the LDAP server with a password,
it increases security, since only the nslcd user needs to
have access to the bindpw file, not everyone that uses NSS
and/or PAM. If this option is enabled, a local nscd user is
created automatically, and the nslcd service is started
automatically when the network get up.
'';
};
extraConfig = mkOption {
default = "";
type = types.string;
description = ''
Extra configuration options that will be added verbatim at
the end of the nslcd configuration file (nslcd.conf).
'' ;
} ;
};
bind = { bind = {
distinguishedName = mkOption { distinguishedName = mkOption {
default = ""; default = "";
example = "cn=admin,dc=example,dc=com"; example = "cn=admin,dc=example,dc=com";
type = with pkgs.lib.types; string; type = types.string;
description = " description = "
The distinguished name to bind to the LDAP server with. If this The distinguished name to bind to the LDAP server with. If this
is not specified, an anonymous bind will be done. is not specified, an anonymous bind will be done.
@ -62,7 +92,7 @@ let
password = mkOption { password = mkOption {
default = "/etc/ldap/bind.password"; default = "/etc/ldap/bind.password";
type = with pkgs.lib.types; string; type = types.string;
description = " description = "
The path to a file containing the credentials to use when binding The path to a file containing the credentials to use when binding
to the LDAP server (if not binding anonymously). to the LDAP server (if not binding anonymously).
@ -71,7 +101,7 @@ let
timeLimit = mkOption { timeLimit = mkOption {
default = 30; default = 30;
type = with pkgs.lib.types; int; type = types.int;
description = " description = "
Specifies the time limit (in seconds) to use when connecting Specifies the time limit (in seconds) to use when connecting
to the directory server. This is distinct from the time limit to the directory server. This is distinct from the time limit
@ -82,7 +112,7 @@ let
policy = mkOption { policy = mkOption {
default = "hard_open"; default = "hard_open";
type = with pkgs.lib.types; string; type = types.string;
description = " description = "
Specifies the policy to use for reconnecting to an unavailable Specifies the policy to use for reconnecting to an unavailable
LDAP server. The default is <literal>hard_open</literal>, which LDAP server. The default is <literal>hard_open</literal>, which
@ -99,55 +129,120 @@ let
}; };
}; };
extraConfig = mkOption {
default = "" ;
type = types.string ;
description = ''
Extra configuration options that will be added verbatim at
the end of the ldap configuration file (ldap.conf).
If <literal>users.ldap.daemon</literal> is enabled, this
configuration will not be used. In that case, use
<literal>users.ldap.daemon.extraConfig</literal> instead.
'' ;
};
}; };
}; };
}; };
# Careful: OpenLDAP seems to be very picky about the indentation of
# this file. Directives HAVE to start in the first column!
ldapConfig = {
target = "ldap.conf";
source = writeText "ldap.conf" ''
uri ${config.users.ldap.server}
base ${config.users.ldap.base}
timelimit ${toString config.users.ldap.timeLimit}
bind_timelimit ${toString config.users.ldap.bind.timeLimit}
bind_policy ${config.users.ldap.bind.policy}
${optionalString config.users.ldap.useTLS ''
ssl start_tls
tls_checkpeer no
''}
${optionalString (config.users.ldap.bind.distinguishedName != "") ''
binddn ${config.users.ldap.bind.distinguishedName}
''}
${optionalString (cfg.extraConfig != "") cfg.extraConfig }
'';
};
nslcdConfig = {
target = "nslcd.conf";
source = writeText "nslcd.conf" ''
uid nslcd
gid nslcd
uri ${cfg.server}
base ${cfg.base}
timelimit ${toString cfg.timeLimit}
bind_timelimit ${toString cfg.bind.timeLimit}
${optionalString (cfg.bind.distinguishedName != "")
"binddn ${cfg.bind.distinguishedName}" }
${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
'';
};
insertLdapPassword = !config.users.ldap.daemon.enable &&
config.users.ldap.bind.distinguishedName != "";
in in
###### implementation ###### implementation
mkIf cfg.enable {
mkIf config.users.ldap.enable {
require = [ require = [
options options
]; ];
# LDAP configuration. environment.etc = if cfg.daemon.enable then [nslcdConfig] else [ldapConfig];
environment = {
etc = [
# Careful: OpenLDAP seems to be very picky about the indentation of system.activationScripts = mkIf insertLdapPassword {
# this file. Directives HAVE to start in the first column! ldap = stringAfter [ "etc" "groups" "users" ] ''
{ source = pkgs.writeText "ldap.conf" if test -f "${cfg.bind.password}" ; then
'' echo "bindpw "$(cat ${cfg.bind.password})"" | cat ${ldapConfig} - > /etc/ldap.conf.bindpw
uri ${config.users.ldap.server}
base ${config.users.ldap.base}
timelimit ${toString config.users.ldap.timeLimit}
bind_timelimit ${toString config.users.ldap.bind.timeLimit}
bind_policy ${config.users.ldap.bind.policy}
${optionalString config.users.ldap.useTLS ''
ssl start_tls
tls_checkpeer no
''}
${optionalString (config.users.ldap.bind.distinguishedName != "") ''
binddn ${config.users.ldap.bind.distinguishedName}
''}
'';
target = "ldap.conf";
}
];
};
system.activationScripts.ldap = stringAfter [ "etc" ] (
optionalString (config.users.ldap.bind.distinguishedName != "") ''
if test -f "${config.users.ldap.bind.password}" ; then
echo "bindpw $(cat ${config.users.ldap.bind.password})" | cat /etc/ldap.conf - > /etc/ldap.conf.bindpw
mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf
chmod 600 /etc/ldap.conf chmod 600 /etc/ldap.conf
fi fi
'' '';
};
system.nssModules = singleton (
if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
); );
users = mkIf cfg.daemon.enable {
extraGroups.nslcd = {
gid = config.ids.gids.nslcd;
};
extraUsers.nslcd = {
uid = config.ids.uids.nslcd;
description = "nslcd user.";
group = "nslcd";
};
};
jobs = mkIf cfg.daemon.enable {
nslcd = {
startOn = "filesystem";
stopOn = "stopping network-interfaces";
daemonType = "fork";
path = [ nss_pam_ldapd ];
preStart = ''
mkdir -p /run/nslcd
chown nslcd.nslcd /run/nslcd
${optionalString (cfg.bind.distinguishedName != "") ''
if test -s "${cfg.bind.password}" ; then
ln -sfT "${cfg.bind.password}" /run/nslcd/bindpw
fi
''}
'';
postStop = "rm -f /run/nslcd/nslcd.pid";
exec = "nslcd";
};
};
} }

View File

@ -19,14 +19,9 @@ let
"; ";
merge = mergeListOption; merge = mergeListOption;
apply = list: apply = list:
let {
list2 = inherit list;
list path = makeLibraryPath list;
# !!! this should be in the LDAP module
++ optional config.users.ldap.enable pkgs.nss_ldap;
in {
list = list2;
path = makeLibraryPath list2;
}; };
}; };

View File

@ -7,7 +7,9 @@ with pkgs.lib;
let let
inherit (pkgs) pam_ldap pam_krb5 pam_ccreds; inherit (pkgs) pam_krb5 pam_ccreds;
pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
otherService = pkgs.writeText "other.pam" otherService = pkgs.writeText "other.pam"
'' ''