diff options
Diffstat (limited to 'nixpkgs/nixos/modules/config/ldap.nix')
-rw-r--r-- | nixpkgs/nixos/modules/config/ldap.nix | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/config/ldap.nix b/nixpkgs/nixos/modules/config/ldap.nix new file mode 100644 index 000000000000..e008497a2a6e --- /dev/null +++ b/nixpkgs/nixos/modules/config/ldap.nix @@ -0,0 +1,293 @@ +{ config, lib, pkgs, ... }: + +with pkgs; +with lib; + +let + + cfg = config.users.ldap; + + # 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 + ''} + ${optionalString (config.users.ldap.bind.distinguishedName != "") '' + binddn ${config.users.ldap.bind.distinguishedName} + ''} + ${optionalString (cfg.extraConfig != "") cfg.extraConfig } + ''; + }; + + nslcdConfig = 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.rootpwmoddn != "") + "rootpwmoddn ${cfg.daemon.rootpwmoddn}" } + ${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig } + ''; + + # nslcd normally reads configuration from /etc/nslcd.conf. + # this file might contain secrets. We append those at runtime, + # so redirect its location to something more temporary. + nslcdWrapped = runCommandNoCC "nslcd-wrapped" { nativeBuildInputs = [ makeWrapper ]; } '' + mkdir -p $out/bin + makeWrapper ${nss_pam_ldapd}/sbin/nslcd $out/bin/nslcd \ + --set LD_PRELOAD "${pkgs.libredirect}/lib/libredirect.so" \ + --set NIX_REDIRECTS "/etc/nslcd.conf=/run/nslcd/nslcd.conf" + ''; + +in + +{ + + ###### interface + + options = { + + users.ldap = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable authentication against an LDAP server."; + }; + + loginPam = mkOption { + type = types.bool; + default = true; + description = "Whether to include authentication against LDAP in login PAM"; + }; + + nsswitch = mkOption { + type = types.bool; + default = true; + description = "Whether to include lookup against LDAP in NSS"; + }; + + server = mkOption { + example = "ldap://ldap.example.org/"; + description = "The URL of the LDAP server."; + }; + + base = mkOption { + example = "dc=example,dc=org"; + description = "The distinguished name of the search base."; + }; + + useTLS = mkOption { + default = false; + description = '' + If enabled, use TLS (encryption) over an LDAP (port 389) + connection. The alternative is to specify an LDAPS server (port + 636) in <option>users.ldap.server</option> or to forego + security. + ''; + }; + + timeLimit = mkOption { + default = 0; + type = types.int; + description = '' + Specifies the time limit (in seconds) to use when performing + searches. A value of zero (0), which is the default, is to + wait indefinitely for searches to be completed. + ''; + }; + + 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.lines; + description = '' + Extra configuration options that will be added verbatim at + 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. + ''; + }; + + rootpwmodpwFile = 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 = { + distinguishedName = mkOption { + default = ""; + example = "cn=admin,dc=example,dc=com"; + type = types.str; + description = '' + The distinguished name to bind to the LDAP server with. If this + is not specified, an anonymous bind will be done. + ''; + }; + + passwordFile = mkOption { + default = "/etc/ldap/bind.password"; + type = types.str; + description = '' + The path to a file containing the credentials to use when binding + to the LDAP server (if not binding anonymously). + ''; + }; + + timeLimit = mkOption { + default = 30; + type = types.int; + description = '' + Specifies the time limit (in seconds) to use when connecting + to the directory server. This is distinct from the time limit + specified in <literal>users.ldap.timeLimit</literal> and affects + the initial server connection only. + ''; + }; + + policy = mkOption { + default = "hard_open"; + type = types.enum [ "hard_open" "hard_init" "soft" ]; + description = '' + Specifies the policy to use for reconnecting to an unavailable + LDAP server. The default is <literal>hard_open</literal>, which + reconnects if opening the connection to the directory server + failed. By contrast, <literal>hard_init</literal> reconnects if + initializing the connection failed. Initializing may not + actually contact the directory server, and it is possible that + a malformed configuration file will trigger reconnection. If + <literal>soft</literal> is specified, then + <literal>nss_ldap</literal> will return immediately on server + failure. All hard reconnect policies block with exponential + backoff before retrying. + ''; + }; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + 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. + '' ; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.etc = optional (!cfg.daemon.enable) ldapConfig; + + system.activationScripts = mkIf (!cfg.daemon.enable) { + ldap = stringAfter [ "etc" "groups" "users" ] '' + if test -f "${cfg.bind.passwordFile}" ; then + umask 0077 + conf="$(mktemp)" + printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" | + cat ${ldapConfig.source} - >"$conf" + mv -fT "$conf" /etc/ldap.conf + fi + ''; + }; + + system.nssModules = singleton ( + if cfg.daemon.enable then nss_pam_ldapd else nss_ldap + ); + + users = mkIf cfg.daemon.enable { + groups.nslcd = { + gid = config.ids.gids.nslcd; + }; + + users.nslcd = { + uid = config.ids.uids.nslcd; + description = "nslcd user."; + group = "nslcd"; + }; + }; + + systemd.services = mkIf cfg.daemon.enable { + nslcd = { + wantedBy = [ "multi-user.target" ]; + + preStart = '' + umask 0077 + conf="$(mktemp)" + { + cat ${nslcdConfig} + test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' || + printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')" + test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' || + printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')" + } >"$conf" + mv -fT "$conf" /run/nslcd/nslcd.conf + ''; + restartTriggers = [ "/run/nslcd/nslcd.conf" ]; + + serviceConfig = { + ExecStart = "${nslcdWrapped}/bin/nslcd"; + Type = "forking"; + Restart = "always"; + User = "nslcd"; + Group = "nslcd"; + RuntimeDirectory = [ "nslcd" ]; + PIDFile = "/run/nslcd/nslcd.pid"; + }; + }; + + }; + + }; + + imports = + [ (mkRenamedOptionModule [ "users" "ldap" "bind" "password"] [ "users" "ldap" "bind" "passwordFile"]) + ]; +} |