1# This module provides configuration for the PAM (Pluggable
2# Authentication Modules) system.
3
4{ config, lib, pkgs, ... }:
5
6with lib;
7
8let
9 parentConfig = config;
10
11 pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in {
12
13 options = {
14
15 name = mkOption {
16 example = "sshd";
17 type = types.str;
18 description = "Name of the PAM service.";
19 };
20
21 unixAuth = mkOption {
22 default = true;
23 type = types.bool;
24 description = ''
25 Whether users can log in with passwords defined in
26 <filename>/etc/shadow</filename>.
27 '';
28 };
29
30 rootOK = mkOption {
31 default = false;
32 type = types.bool;
33 description = ''
34 If set, root doesn't need to authenticate (e.g. for the
35 <command>useradd</command> service).
36 '';
37 };
38
39 p11Auth = mkOption {
40 default = config.security.pam.p11.enable;
41 type = types.bool;
42 description = ''
43 If set, keys listed in
44 <filename>~/.ssh/authorized_keys</filename> and
45 <filename>~/.eid/authorized_certificates</filename>
46 can be used to log in with the associated PKCS#11 tokens.
47 '';
48 };
49
50 u2fAuth = mkOption {
51 default = config.security.pam.u2f.enable;
52 type = types.bool;
53 description = ''
54 If set, users listed in
55 <filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
56 <filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
57 not set) are able to log in with the associated U2F key. Path can be
58 changed using <option>security.pam.u2f.authFile</option> option.
59 '';
60 };
61
62 yubicoAuth = mkOption {
63 default = config.security.pam.yubico.enable;
64 type = types.bool;
65 description = ''
66 If set, users listed in
67 <filename>~/.yubico/authorized_yubikeys</filename>
68 are able to log in with the associated Yubikey tokens.
69 '';
70 };
71
72 googleAuthenticator = {
73 enable = mkOption {
74 default = false;
75 type = types.bool;
76 description = ''
77 If set, users with enabled Google Authenticator (created
78 <filename>~/.google_authenticator</filename>) will be required
79 to provide Google Authenticator token to log in.
80 '';
81 };
82 };
83
84 usbAuth = mkOption {
85 default = config.security.pam.usb.enable;
86 type = types.bool;
87 description = ''
88 If set, users listed in
89 <filename>/etc/pamusb.conf</filename> are able to log in
90 with the associated USB key.
91 '';
92 };
93
94 otpwAuth = mkOption {
95 default = config.security.pam.enableOTPW;
96 type = types.bool;
97 description = ''
98 If set, the OTPW system will be used (if
99 <filename>~/.otpw</filename> exists).
100 '';
101 };
102
103 googleOsLoginAccountVerification = mkOption {
104 default = false;
105 type = types.bool;
106 description = ''
107 If set, will use the Google OS Login PAM modules
108 (<literal>pam_oslogin_login</literal>,
109 <literal>pam_oslogin_admin</literal>) to verify possible OS Login
110 users and set sudoers configuration accordingly.
111 This only makes sense to enable for the <literal>sshd</literal> PAM
112 service.
113 '';
114 };
115
116 googleOsLoginAuthentication = mkOption {
117 default = false;
118 type = types.bool;
119 description = ''
120 If set, will use the <literal>pam_oslogin_login</literal>'s user
121 authentication methods to authenticate users using 2FA.
122 This only makes sense to enable for the <literal>sshd</literal> PAM
123 service.
124 '';
125 };
126
127 fprintAuth = mkOption {
128 default = config.services.fprintd.enable;
129 type = types.bool;
130 description = ''
131 If set, fingerprint reader will be used (if exists and
132 your fingerprints are enrolled).
133 '';
134 };
135
136 oathAuth = mkOption {
137 default = config.security.pam.oath.enable;
138 type = types.bool;
139 description = ''
140 If set, the OATH Toolkit will be used.
141 '';
142 };
143
144 sshAgentAuth = mkOption {
145 default = false;
146 type = types.bool;
147 description = ''
148 If set, the calling user's SSH agent is used to authenticate
149 against the keys in the calling user's
150 <filename>~/.ssh/authorized_keys</filename>. This is useful
151 for <command>sudo</command> on password-less remote systems.
152 '';
153 };
154
155 duoSecurity = {
156 enable = mkOption {
157 default = false;
158 type = types.bool;
159 description = ''
160 If set, use the Duo Security pam module
161 <literal>pam_duo</literal> for authentication. Requires
162 configuration of <option>security.duosec</option> options.
163 '';
164 };
165 };
166
167 startSession = mkOption {
168 default = false;
169 type = types.bool;
170 description = ''
171 If set, the service will register a new session with
172 systemd's login manager. For local sessions, this will give
173 the user access to audio devices, CD-ROM drives. In the
174 default PolicyKit configuration, it also allows the user to
175 reboot the system.
176 '';
177 };
178
179 setEnvironment = mkOption {
180 type = types.bool;
181 default = true;
182 description = ''
183 Whether the service should set the environment variables
184 listed in <option>environment.sessionVariables</option>
185 using <literal>pam_env.so</literal>.
186 '';
187 };
188
189 setLoginUid = mkOption {
190 type = types.bool;
191 description = ''
192 Set the login uid of the process
193 (<filename>/proc/self/loginuid</filename>) for auditing
194 purposes. The login uid is only set by ‘entry points’ like
195 <command>login</command> and <command>sshd</command>, not by
196 commands like <command>sudo</command>.
197 '';
198 };
199
200 forwardXAuth = mkOption {
201 default = false;
202 type = types.bool;
203 description = ''
204 Whether X authentication keys should be passed from the
205 calling user to the target user (e.g. for
206 <command>su</command>)
207 '';
208 };
209
210 pamMount = mkOption {
211 default = config.security.pam.mount.enable;
212 type = types.bool;
213 description = ''
214 Enable PAM mount (pam_mount) system to mount fileystems on user login.
215 '';
216 };
217
218 allowNullPassword = mkOption {
219 default = false;
220 type = types.bool;
221 description = ''
222 Whether to allow logging into accounts that have no password
223 set (i.e., have an empty password field in
224 <filename>/etc/passwd</filename> or
225 <filename>/etc/group</filename>). This does not enable
226 logging into disabled accounts (i.e., that have the password
227 field set to <literal>!</literal>). Note that regardless of
228 what the pam_unix documentation says, accounts with hashed
229 empty passwords are always allowed to log in.
230 '';
231 };
232
233 nodelay = mkOption {
234 default = false;
235 type = types.bool;
236 description = ''
237 Wheather the delay after typing a wrong password should be disabled.
238 '';
239 };
240
241 requireWheel = mkOption {
242 default = false;
243 type = types.bool;
244 description = ''
245 Whether to permit root access only to members of group wheel.
246 '';
247 };
248
249 limits = mkOption {
250 description = ''
251 Attribute set describing resource limits. Defaults to the
252 value of <option>security.pam.loginLimits</option>.
253 '';
254 };
255
256 showMotd = mkOption {
257 default = false;
258 type = types.bool;
259 description = "Whether to show the message of the day.";
260 };
261
262 makeHomeDir = mkOption {
263 default = false;
264 type = types.bool;
265 description = ''
266 Whether to try to create home directories for users
267 with <literal>$HOME</literal>s pointing to nonexistent
268 locations on session login.
269 '';
270 };
271
272 updateWtmp = mkOption {
273 default = false;
274 type = types.bool;
275 description = "Whether to update <filename>/var/log/wtmp</filename>.";
276 };
277
278 logFailures = mkOption {
279 default = false;
280 type = types.bool;
281 description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>.";
282 };
283
284 enableAppArmor = mkOption {
285 default = false;
286 type = types.bool;
287 description = ''
288 Enable support for attaching AppArmor profiles at the
289 user/group level, e.g., as part of a role based access
290 control scheme.
291 '';
292 };
293
294 enableKwallet = mkOption {
295 default = false;
296 type = types.bool;
297 description = ''
298 If enabled, pam_wallet will attempt to automatically unlock the
299 user's default KDE wallet upon login. If the user has no wallet named
300 "kdewallet", or the login password does not match their wallet
301 password, KDE will prompt separately after login.
302 '';
303 };
304 sssdStrictAccess = mkOption {
305 default = false;
306 type = types.bool;
307 description = "enforce sssd access control";
308 };
309
310 enableGnomeKeyring = mkOption {
311 default = false;
312 type = types.bool;
313 description = ''
314 If enabled, pam_gnome_keyring will attempt to automatically unlock the
315 user's default Gnome keyring upon login. If the user login password does
316 not match their keyring password, Gnome Keyring will prompt separately
317 after login.
318 '';
319 };
320
321 gnupg = {
322 enable = mkOption {
323 type = types.bool;
324 default = false;
325 description = ''
326 If enabled, pam_gnupg will attempt to automatically unlock the
327 user's GPG keys with the login password via
328 <command>gpg-agent</command>. The keygrips of all keys to be
329 unlocked should be written to <filename>~/.pam-gnupg</filename>,
330 and can be queried with <command>gpg -K --with-keygrip</command>.
331 Presetting passphrases must be enabled by adding
332 <literal>allow-preset-passphrase</literal> in
333 <filename>~/.gnupg/gpg-agent.conf</filename>.
334 '';
335 };
336
337 noAutostart = mkOption {
338 type = types.bool;
339 default = false;
340 description = ''
341 Don't start <command>gpg-agent</command> if it is not running.
342 Useful in conjunction with starting <command>gpg-agent</command> as
343 a systemd user service.
344 '';
345 };
346
347 storeOnly = mkOption {
348 type = types.bool;
349 default = false;
350 description = ''
351 Don't send the password immediately after login, but store for PAM
352 <literal>session</literal>.
353 '';
354 };
355 };
356
357 text = mkOption {
358 type = types.nullOr types.lines;
359 description = "Contents of the PAM service file.";
360 };
361
362 };
363
364 config = {
365 name = mkDefault name;
366 setLoginUid = mkDefault cfg.startSession;
367 limits = mkDefault config.security.pam.loginLimits;
368
369 # !!! TODO: move the LDAP stuff to the LDAP module, and the
370 # Samba stuff to the Samba module. This requires that the PAM
371 # module provides the right hooks.
372 text = mkDefault
373 (''
374 # Account management.
375 account required pam_unix.so
376 ${optionalString use_ldap
377 "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
378 ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false)
379 "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"}
380 ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess)
381 "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"}
382 ${optionalString config.krb5.enable
383 "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
384 ${optionalString cfg.googleOsLoginAccountVerification ''
385 account [success=ok ignore=ignore default=die] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
386 account [success=ok default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so
387 ''}
388
389 # Authentication management.
390 ${optionalString cfg.googleOsLoginAuthentication
391 "auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so"}
392 ${optionalString cfg.rootOK
393 "auth sufficient pam_rootok.so"}
394 ${optionalString cfg.requireWheel
395 "auth required pam_wheel.so use_uid"}
396 ${optionalString cfg.logFailures
397 "auth required pam_faillock.so"}
398 ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth)
399 "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}"}
400 ${optionalString cfg.fprintAuth
401 "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"}
402 ${let p11 = config.security.pam.p11; in optionalString cfg.p11Auth
403 "auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so"}
404 ${let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth
405 "auth ${u2f.control} ${pkgs.pam_u2f}/lib/security/pam_u2f.so ${optionalString u2f.debug "debug"} ${optionalString (u2f.authFile != null) "authfile=${u2f.authFile}"} ${optionalString u2f.interactive "interactive"} ${optionalString u2f.cue "cue"} ${optionalString (u2f.appId != null) "appid=${u2f.appId}"}"}
406 ${optionalString cfg.usbAuth
407 "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
408 ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
409 "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
410 ${let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth
411 "auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}"}
412 '' +
413 # Modules in this block require having the password set in PAM_AUTHTOK.
414 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
415 # after it succeeds. Certain modules need to run after pam_unix
416 # prompts the user for password so we run it once with 'required' at an
417 # earlier point and it will run again with 'sufficient' further down.
418 # We use try_first_pass the second time to avoid prompting password twice
419 (optionalString (cfg.unixAuth &&
420 (config.security.pam.enableEcryptfs
421 || cfg.pamMount
422 || cfg.enableKwallet
423 || cfg.enableGnomeKeyring
424 || cfg.googleAuthenticator.enable
425 || cfg.gnupg.enable
426 || cfg.duoSecurity.enable)) ''
427 auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
428 ${optionalString config.security.pam.enableEcryptfs
429 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"}
430 ${optionalString cfg.pamMount
431 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
432 ${optionalString cfg.enableKwallet
433 ("auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so" +
434 " kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5")}
435 ${optionalString cfg.enableGnomeKeyring
436 "auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"}
437 ${optionalString cfg.gnupg.enable
438 "auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
439 + optionalString cfg.gnupg.storeOnly " store-only"
440 }
441 ${optionalString cfg.googleAuthenticator.enable
442 "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"}
443 ${optionalString cfg.duoSecurity.enable
444 "auth required ${pkgs.duo-unix}/lib/security/pam_duo.so"}
445 '') + ''
446 ${optionalString cfg.unixAuth
447 "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass"}
448 ${optionalString cfg.otpwAuth
449 "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
450 ${optionalString use_ldap
451 "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
452 ${optionalString config.services.sssd.enable
453 "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"}
454 ${optionalString config.krb5.enable ''
455 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
456 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
457 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
458 ''}
459 auth required pam_deny.so
460
461 # Password management.
462 password sufficient pam_unix.so nullok sha512
463 ${optionalString config.security.pam.enableEcryptfs
464 "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
465 ${optionalString cfg.pamMount
466 "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
467 ${optionalString use_ldap
468 "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
469 ${optionalString config.services.sssd.enable
470 "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"}
471 ${optionalString config.krb5.enable
472 "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
473 ${optionalString cfg.enableGnomeKeyring
474 "password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok"}
475
476 # Session management.
477 ${optionalString cfg.setEnvironment ''
478 session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
479 ''}
480 session required pam_unix.so
481 ${optionalString cfg.setLoginUid
482 "session ${
483 if config.boot.isContainer then "optional" else "required"
484 } pam_loginuid.so"}
485 ${optionalString cfg.makeHomeDir
486 "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0022"}
487 ${optionalString cfg.updateWtmp
488 "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
489 ${optionalString config.security.pam.enableEcryptfs
490 "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"}
491 ${optionalString cfg.pamMount
492 "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"}
493 ${optionalString use_ldap
494 "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
495 ${optionalString config.services.sssd.enable
496 "session optional ${pkgs.sssd}/lib/security/pam_sss.so"}
497 ${optionalString config.krb5.enable
498 "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
499 ${optionalString cfg.otpwAuth
500 "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
501 ${optionalString cfg.startSession
502 "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
503 ${optionalString cfg.forwardXAuth
504 "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
505 ${optionalString (cfg.limits != [])
506 "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"}
507 ${optionalString (cfg.showMotd && config.users.motd != null)
508 "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
509 ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable)
510 "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"}
511 ${optionalString (cfg.enableKwallet)
512 ("session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so" +
513 " kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5")}
514 ${optionalString (cfg.enableGnomeKeyring)
515 "session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"}
516 ${optionalString cfg.gnupg.enable
517 "session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"
518 + optionalString cfg.gnupg.noAutostart " no-autostart"
519 }
520 ${optionalString (config.virtualisation.lxc.lxcfs.enable)
521 "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"}
522 '');
523 };
524
525 };
526
527
528 inherit (pkgs) pam_krb5 pam_ccreds;
529
530 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
531 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
532
533 # Create a limits.conf(5) file.
534 makeLimitsConf = limits:
535 pkgs.writeText "limits.conf"
536 (concatMapStrings ({ domain, type, item, value }:
537 "${domain} ${type} ${item} ${toString value}\n")
538 limits);
539
540 motd = pkgs.writeText "motd" config.users.motd;
541
542 makePAMService = name: service:
543 { name = "pam.d/${name}";
544 value.source = pkgs.writeText "${name}.pam" service.text;
545 };
546
547in
548
549{
550
551 imports = [
552 (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
553 ];
554
555 ###### interface
556
557 options = {
558
559 security.pam.loginLimits = mkOption {
560 default = [];
561 example =
562 [ { domain = "ftp";
563 type = "hard";
564 item = "nproc";
565 value = "0";
566 }
567 { domain = "@student";
568 type = "-";
569 item = "maxlogins";
570 value = "4";
571 }
572 ];
573
574 description =
575 '' Define resource limits that should apply to users or groups.
576 Each item in the list should be an attribute set with a
577 <varname>domain</varname>, <varname>type</varname>,
578 <varname>item</varname>, and <varname>value</varname>
579 attribute. The syntax and semantics of these attributes
580 must be that described in the limits.conf(5) man page.
581
582 Note that these limits do not apply to systemd services,
583 whose limits can be changed via <option>systemd.extraConfig</option>
584 instead.
585 '';
586 };
587
588 security.pam.services = mkOption {
589 default = [];
590 type = with types; attrsOf (submodule pamOpts);
591 description =
592 ''
593 This option defines the PAM services. A service typically
594 corresponds to a program that uses PAM,
595 e.g. <command>login</command> or <command>passwd</command>.
596 Each attribute of this set defines a PAM service, with the attribute name
597 defining the name of the service.
598 '';
599 };
600
601 security.pam.makeHomeDir.skelDirectory = mkOption {
602 type = types.str;
603 default = "/var/empty";
604 example = "/etc/skel";
605 description = ''
606 Path to skeleton directory whose contents are copied to home
607 directories newly created by <literal>pam_mkhomedir</literal>.
608 '';
609 };
610
611 security.pam.enableSSHAgentAuth = mkOption {
612 type = types.bool;
613 default = false;
614 description =
615 ''
616 Enable sudo logins if the user's SSH agent provides a key
617 present in <filename>~/.ssh/authorized_keys</filename>.
618 This allows machines to exclusively use SSH keys instead of
619 passwords.
620 '';
621 };
622
623 security.pam.enableOTPW = mkEnableOption "the OTPW (one-time password) PAM module";
624
625 security.pam.p11 = {
626 enable = mkOption {
627 default = false;
628 type = types.bool;
629 description = ''
630 Enables P11 PAM (<literal>pam_p11</literal>) module.
631
632 If set, users can log in with SSH keys and PKCS#11 tokens.
633
634 More information can be found <link
635 xlink:href="https://github.com/OpenSC/pam_p11">here</link>.
636 '';
637 };
638
639 control = mkOption {
640 default = "sufficient";
641 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
642 description = ''
643 This option sets pam "control".
644 If you want to have multi factor authentication, use "required".
645 If you want to use the PKCS#11 device instead of the regular password,
646 use "sufficient".
647
648 Read
649 <citerefentry>
650 <refentrytitle>pam.conf</refentrytitle>
651 <manvolnum>5</manvolnum>
652 </citerefentry>
653 for better understanding of this option.
654 '';
655 };
656 };
657
658 security.pam.u2f = {
659 enable = mkOption {
660 default = false;
661 type = types.bool;
662 description = ''
663 Enables U2F PAM (<literal>pam-u2f</literal>) module.
664
665 If set, users listed in
666 <filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
667 <filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
668 not set) are able to log in with the associated U2F key. The path can
669 be changed using <option>security.pam.u2f.authFile</option> option.
670
671 File format is:
672 <literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
673 This file can be generated using <command>pamu2fcfg</command> command.
674
675 More information can be found <link
676 xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
677 '';
678 };
679
680 authFile = mkOption {
681 default = null;
682 type = with types; nullOr path;
683 description = ''
684 By default <literal>pam-u2f</literal> module reads the keys from
685 <filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
686 <filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
687 not set).
688
689 If you want to change auth file locations or centralize database (for
690 example use <filename>/etc/u2f-mappings</filename>) you can set this
691 option.
692
693 File format is:
694 <literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
695 This file can be generated using <command>pamu2fcfg</command> command.
696
697 More information can be found <link
698 xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
699 '';
700 };
701
702 appId = mkOption {
703 default = null;
704 type = with types; nullOr str;
705 description = ''
706 By default <literal>pam-u2f</literal> module sets the application
707 ID to <literal>pam://$HOSTNAME</literal>.
708
709 When using <command>pamu2fcfg</command>, you can specify your
710 application ID with the <literal>-i</literal> flag.
711
712 More information can be found <link
713 xlink:href="https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html">
714 here</link>
715 '';
716 };
717
718 control = mkOption {
719 default = "sufficient";
720 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
721 description = ''
722 This option sets pam "control".
723 If you want to have multi factor authentication, use "required".
724 If you want to use U2F device instead of regular password, use "sufficient".
725
726 Read
727 <citerefentry>
728 <refentrytitle>pam.conf</refentrytitle>
729 <manvolnum>5</manvolnum>
730 </citerefentry>
731 for better understanding of this option.
732 '';
733 };
734
735 debug = mkOption {
736 default = false;
737 type = types.bool;
738 description = ''
739 Debug output to stderr.
740 '';
741 };
742
743 interactive = mkOption {
744 default = false;
745 type = types.bool;
746 description = ''
747 Set to prompt a message and wait before testing the presence of a U2F device.
748 Recommended if your device doesn’t have a tactile trigger.
749 '';
750 };
751
752 cue = mkOption {
753 default = false;
754 type = types.bool;
755 description = ''
756 By default <literal>pam-u2f</literal> module does not inform user
757 that he needs to use the u2f device, it just waits without a prompt.
758
759 If you set this option to <literal>true</literal>,
760 <literal>cue</literal> option is added to <literal>pam-u2f</literal>
761 module and reminder message will be displayed.
762 '';
763 };
764 };
765
766 security.pam.yubico = {
767 enable = mkOption {
768 default = false;
769 type = types.bool;
770 description = ''
771 Enables Yubico PAM (<literal>yubico-pam</literal>) module.
772
773 If set, users listed in
774 <filename>~/.yubico/authorized_yubikeys</filename>
775 are able to log in with the associated Yubikey tokens.
776
777 The file must have only one line:
778 <literal>username:yubikey_token_id1:yubikey_token_id2</literal>
779 More information can be found <link
780 xlink:href="https://developers.yubico.com/yubico-pam/">here</link>.
781 '';
782 };
783 control = mkOption {
784 default = "sufficient";
785 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
786 description = ''
787 This option sets pam "control".
788 If you want to have multi factor authentication, use "required".
789 If you want to use Yubikey instead of regular password, use "sufficient".
790
791 Read
792 <citerefentry>
793 <refentrytitle>pam.conf</refentrytitle>
794 <manvolnum>5</manvolnum>
795 </citerefentry>
796 for better understanding of this option.
797 '';
798 };
799 id = mkOption {
800 example = "42";
801 type = types.str;
802 description = "client id";
803 };
804
805 debug = mkOption {
806 default = false;
807 type = types.bool;
808 description = ''
809 Debug output to stderr.
810 '';
811 };
812 mode = mkOption {
813 default = "client";
814 type = types.enum [ "client" "challenge-response" ];
815 description = ''
816 Mode of operation.
817
818 Use "client" for online validation with a YubiKey validation service such as
819 the YubiCloud.
820
821 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
822 Challenge-Response configurations. See the man-page ykpamcfg(1) for further
823 details on how to configure offline Challenge-Response validation.
824
825 More information can be found <link
826 xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
827 '';
828 };
829 };
830
831 security.pam.enableEcryptfs = mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
832
833 users.motd = mkOption {
834 default = null;
835 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
836 type = types.nullOr types.lines;
837 description = "Message of the day shown to users when they log in.";
838 };
839
840 };
841
842
843 ###### implementation
844
845 config = {
846
847 environment.systemPackages =
848 # Include the PAM modules in the system path mostly for the manpages.
849 [ pkgs.pam ]
850 ++ optional config.users.ldap.enable pam_ldap
851 ++ optional config.services.sssd.enable pkgs.sssd
852 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
853 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
854 ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ]
855 ++ optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
856 ++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
857
858 boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
859
860 security.wrappers = {
861 unix_chkpwd = {
862 source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
863 owner = "root";
864 setuid = true;
865 };
866 };
867
868 environment.etc = mapAttrs' makePAMService config.security.pam.services;
869
870 security.pam.services =
871 { other.text =
872 ''
873 auth required pam_warn.so
874 auth required pam_deny.so
875 account required pam_warn.so
876 account required pam_deny.so
877 password required pam_warn.so
878 password required pam_deny.so
879 session required pam_warn.so
880 session required pam_deny.so
881 '';
882
883 # Most of these should be moved to specific modules.
884 i3lock = {};
885 i3lock-color = {};
886 vlock = {};
887 xlock = {};
888 xscreensaver = {};
889
890 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
891
892 /* FIXME: should runuser -l start a systemd session? Currently
893 it complains "Cannot create session: Already running in a
894 session". */
895 runuser-l = { rootOK = true; unixAuth = false; };
896 };
897
898 security.apparmor.includes."abstractions/pam" = let
899 isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
900 in
901 lib.concatMapStringsSep "\n"
902 (name: "r ${config.environment.etc."pam.d/${name}".source},")
903 (attrNames config.security.pam.services) +
904 ''
905 mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
906 mr ${getLib pkgs.pam}/lib/security/pam_*.so,
907 r ${getLib pkgs.pam}/lib/security/,
908 '' +
909 optionalString use_ldap ''
910 mr ${pam_ldap}/lib/security/pam_ldap.so,
911 '' +
912 optionalString config.services.sssd.enable ''
913 mr ${pkgs.sssd}/lib/security/pam_sss.so,
914 '' +
915 optionalString config.krb5.enable ''
916 mr ${pam_krb5}/lib/security/pam_krb5.so,
917 mr ${pam_ccreds}/lib/security/pam_ccreds.so,
918 '' +
919 optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
920 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
921 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
922 '' +
923 optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) ''
924 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
925 '' +
926 optionalString (config.security.pam.enableSSHAgentAuth
927 && isEnabled (cfg: cfg.sshAgentAuth)) ''
928 mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,
929 '' +
930 optionalString (isEnabled (cfg: cfg.fprintAuth)) ''
931 mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,
932 '' +
933 optionalString (isEnabled (cfg: cfg.u2fAuth)) ''
934 mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,
935 '' +
936 optionalString (isEnabled (cfg: cfg.usbAuth)) ''
937 mr ${pkgs.pam_usb}/lib/security/pam_usb.so,
938 '' +
939 optionalString (isEnabled (cfg: cfg.oathAuth)) ''
940 "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,
941 '' +
942 optionalString (isEnabled (cfg: cfg.yubicoAuth)) ''
943 mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,
944 '' +
945 optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) ''
946 mr ${pkgs.duo-unix}/lib/security/pam_duo.so,
947 '' +
948 optionalString (isEnabled (cfg: cfg.otpwAuth)) ''
949 mr ${pkgs.otpw}/lib/security/pam_otpw.so,
950 '' +
951 optionalString config.security.pam.enableEcryptfs ''
952 mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,
953 '' +
954 optionalString (isEnabled (cfg: cfg.pamMount)) ''
955 mr ${pkgs.pam_mount}/lib/security/pam_mount.so,
956 '' +
957 optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) ''
958 mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,
959 '' +
960 optionalString (isEnabled (cfg: cfg.startSession)) ''
961 mr ${pkgs.systemd}/lib/security/pam_systemd.so,
962 '' +
963 optionalString (isEnabled (cfg: cfg.enableAppArmor)
964 && config.security.apparmor.enable) ''
965 mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,
966 '' +
967 optionalString (isEnabled (cfg: cfg.enableKwallet)) ''
968 mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,
969 '' +
970 optionalString config.virtualisation.lxc.lxcfs.enable ''
971 mr ${pkgs.lxc}/lib/security/pam_cgfs.so
972 '';
973 };
974
975}