python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / config / ldap.nix
blobd2f01fb87d32d4347f0bb41c0e791882a248991f
1 { config, lib, pkgs, ... }:
3 with pkgs;
4 with lib;
6 let
8   cfg = config.users.ldap;
10   # Careful: OpenLDAP seems to be very picky about the indentation of
11   # this file.  Directives HAVE to start in the first column!
12   ldapConfig = {
13     target = "ldap.conf";
14     source = writeText "ldap.conf" ''
15       uri ${config.users.ldap.server}
16       base ${config.users.ldap.base}
17       timelimit ${toString config.users.ldap.timeLimit}
18       bind_timelimit ${toString config.users.ldap.bind.timeLimit}
19       bind_policy ${config.users.ldap.bind.policy}
20       ${optionalString config.users.ldap.useTLS ''
21         ssl start_tls
22       ''}
23       ${optionalString (config.users.ldap.bind.distinguishedName != "") ''
24         binddn ${config.users.ldap.bind.distinguishedName}
25       ''}
26       ${optionalString (cfg.extraConfig != "") cfg.extraConfig }
27     '';
28   };
30   nslcdConfig = writeText "nslcd.conf" ''
31     uri ${cfg.server}
32     base ${cfg.base}
33     timelimit ${toString cfg.timeLimit}
34     bind_timelimit ${toString cfg.bind.timeLimit}
35     ${optionalString (cfg.bind.distinguishedName != "")
36       "binddn ${cfg.bind.distinguishedName}" }
37     ${optionalString (cfg.daemon.rootpwmoddn != "")
38       "rootpwmoddn ${cfg.daemon.rootpwmoddn}" }
39     ${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
40   '';
42   # nslcd normally reads configuration from /etc/nslcd.conf.
43   # this file might contain secrets. We append those at runtime,
44   # so redirect its location to something more temporary.
45   nslcdWrapped = runCommand "nslcd-wrapped" { nativeBuildInputs = [ makeWrapper ]; } ''
46     mkdir -p $out/bin
47     makeWrapper ${nss_pam_ldapd}/sbin/nslcd $out/bin/nslcd \
48       --set LD_PRELOAD    "${pkgs.libredirect}/lib/libredirect.so" \
49       --set NIX_REDIRECTS "/etc/nslcd.conf=/run/nslcd/nslcd.conf"
50   '';
56   ###### interface
58   options = {
60     users.ldap = {
62       enable = mkEnableOption (lib.mdDoc "authentication against an LDAP server");
64       loginPam = mkOption {
65         type = types.bool;
66         default = true;
67         description = lib.mdDoc "Whether to include authentication against LDAP in login PAM.";
68       };
70       nsswitch = mkOption {
71         type = types.bool;
72         default = true;
73         description = lib.mdDoc "Whether to include lookup against LDAP in NSS.";
74       };
76       server = mkOption {
77         type = types.str;
78         example = "ldap://ldap.example.org/";
79         description = lib.mdDoc "The URL of the LDAP server.";
80       };
82       base = mkOption {
83         type = types.str;
84         example = "dc=example,dc=org";
85         description = lib.mdDoc "The distinguished name of the search base.";
86       };
88       useTLS = mkOption {
89         type = types.bool;
90         default = false;
91         description = lib.mdDoc ''
92           If enabled, use TLS (encryption) over an LDAP (port 389)
93           connection.  The alternative is to specify an LDAPS server (port
94           636) in {option}`users.ldap.server` or to forego
95           security.
96         '';
97       };
99       timeLimit = mkOption {
100         default = 0;
101         type = types.int;
102         description = lib.mdDoc ''
103           Specifies the time limit (in seconds) to use when performing
104           searches. A value of zero (0), which is the default, is to
105           wait indefinitely for searches to be completed.
106         '';
107       };
109       daemon = {
110         enable = mkOption {
111           type = types.bool;
112           default = false;
113           description = lib.mdDoc ''
114             Whether to let the nslcd daemon (nss-pam-ldapd) handle the
115             LDAP lookups for NSS and PAM. This can improve performance,
116             and if you need to bind to the LDAP server with a password,
117             it increases security, since only the nslcd user needs to
118             have access to the bindpw file, not everyone that uses NSS
119             and/or PAM. If this option is enabled, a local nscd user is
120             created automatically, and the nslcd service is started
121             automatically when the network get up.
122           '';
123         };
125         extraConfig = mkOption {
126           default =  "";
127           type = types.lines;
128           description = lib.mdDoc ''
129             Extra configuration options that will be added verbatim at
130             the end of the nslcd configuration file (`nslcd.conf(5)`).
131           '' ;
132         } ;
134         rootpwmoddn = mkOption {
135           default = "";
136           example = "cn=admin,dc=example,dc=com";
137           type = types.str;
138           description = lib.mdDoc ''
139             The distinguished name to use to bind to the LDAP server
140             when the root user tries to modify a user's password.
141           '';
142         };
144         rootpwmodpwFile = mkOption {
145           default = "";
146           example = "/run/keys/nslcd.rootpwmodpw";
147           type = types.str;
148           description = lib.mdDoc ''
149             The path to a file containing the credentials with which to bind to
150             the LDAP server if the root user tries to change a user's password.
151           '';
152         };
153       };
155       bind = {
156         distinguishedName = mkOption {
157           default = "";
158           example = "cn=admin,dc=example,dc=com";
159           type = types.str;
160           description = lib.mdDoc ''
161             The distinguished name to bind to the LDAP server with. If this
162             is not specified, an anonymous bind will be done.
163           '';
164         };
166         passwordFile = mkOption {
167           default = "/etc/ldap/bind.password";
168           type = types.str;
169           description = lib.mdDoc ''
170             The path to a file containing the credentials to use when binding
171             to the LDAP server (if not binding anonymously).
172           '';
173         };
175         timeLimit = mkOption {
176           default = 30;
177           type = types.int;
178           description = lib.mdDoc ''
179             Specifies the time limit (in seconds) to use when connecting
180             to the directory server. This is distinct from the time limit
181             specified in {option}`users.ldap.timeLimit` and affects
182             the initial server connection only.
183           '';
184         };
186         policy = mkOption {
187           default = "hard_open";
188           type = types.enum [ "hard_open" "hard_init" "soft" ];
189           description = lib.mdDoc ''
190             Specifies the policy to use for reconnecting to an unavailable
191             LDAP server. The default is `hard_open`, which
192             reconnects if opening the connection to the directory server
193             failed. By contrast, `hard_init` reconnects if
194             initializing the connection failed. Initializing may not
195             actually contact the directory server, and it is possible that
196             a malformed configuration file will trigger reconnection. If
197             `soft` is specified, then
198             `nss_ldap` will return immediately on server
199             failure. All hard reconnect policies block with exponential
200             backoff before retrying.
201           '';
202         };
203       };
205       extraConfig = mkOption {
206         default = "";
207         type = types.lines;
208         description = lib.mdDoc ''
209           Extra configuration options that will be added verbatim at
210           the end of the ldap configuration file (`ldap.conf(5)`).
211           If {option}`users.ldap.daemon` is enabled, this
212           configuration will not be used. In that case, use
213           {option}`users.ldap.daemon.extraConfig` instead.
214         '' ;
215       };
217     };
219   };
221   ###### implementation
223   config = mkIf cfg.enable {
225     environment.etc = optionalAttrs (!cfg.daemon.enable) {
226       "ldap.conf" = ldapConfig;
227     };
229     system.activationScripts = mkIf (!cfg.daemon.enable) {
230       ldap = stringAfter [ "etc" "groups" "users" ] ''
231         if test -f "${cfg.bind.passwordFile}" ; then
232           umask 0077
233           conf="$(mktemp)"
234           printf 'bindpw %s\n' "$(cat ${cfg.bind.passwordFile})" |
235           cat ${ldapConfig.source} - >"$conf"
236           mv -fT "$conf" /etc/ldap.conf
237         fi
238       '';
239     };
241     system.nssModules = mkIf cfg.nsswitch (singleton (
242       if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
243     ));
245     system.nssDatabases.group = optional cfg.nsswitch "ldap";
246     system.nssDatabases.passwd = optional cfg.nsswitch "ldap";
247     system.nssDatabases.shadow = optional cfg.nsswitch "ldap";
249     users = mkIf cfg.daemon.enable {
250       groups.nslcd = {
251         gid = config.ids.gids.nslcd;
252       };
254       users.nslcd = {
255         uid = config.ids.uids.nslcd;
256         description = "nslcd user.";
257         group = "nslcd";
258       };
259     };
261     systemd.services = mkIf cfg.daemon.enable {
262       nslcd = {
263         wantedBy = [ "multi-user.target" ];
265         preStart = ''
266           umask 0077
267           conf="$(mktemp)"
268           {
269             cat ${nslcdConfig}
270             test -z '${cfg.bind.distinguishedName}' -o ! -f '${cfg.bind.passwordFile}' ||
271             printf 'bindpw %s\n' "$(cat '${cfg.bind.passwordFile}')"
272             test -z '${cfg.daemon.rootpwmoddn}' -o ! -f '${cfg.daemon.rootpwmodpwFile}' ||
273             printf 'rootpwmodpw %s\n' "$(cat '${cfg.daemon.rootpwmodpwFile}')"
274           } >"$conf"
275           mv -fT "$conf" /run/nslcd/nslcd.conf
276         '';
278         restartTriggers = [
279           nslcdConfig
280           cfg.bind.passwordFile
281           cfg.daemon.rootpwmodpwFile
282         ];
284         serviceConfig = {
285           ExecStart = "${nslcdWrapped}/bin/nslcd";
286           Type = "forking";
287           Restart = "always";
288           User = "nslcd";
289           Group = "nslcd";
290           RuntimeDirectory = [ "nslcd" ];
291           PIDFile = "/run/nslcd/nslcd.pid";
292           AmbientCapabilities = "CAP_SYS_RESOURCE";
293         };
294       };
296     };
298   };
300   imports =
301     [ (mkRenamedOptionModule [ "users" "ldap" "bind" "password"] [ "users" "ldap" "bind" "passwordFile"])
302     ];