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 ttyAudit = {
201 enable = mkOption {
202 type = types.bool;
203 default = false;
204 description = ''
205 Enable or disable TTY auditing for specified users
206 '';
207 };
208
209 enablePattern = mkOption {
210 type = types.nullOr types.str;
211 default = null;
212 description = ''
213 For each user matching one of comma-separated
214 glob patterns, enable TTY auditing
215 '';
216 };
217
218 disablePattern = mkOption {
219 type = types.nullOr types.str;
220 default = null;
221 description = ''
222 For each user matching one of comma-separated
223 glob patterns, disable TTY auditing
224 '';
225 };
226
227 openOnly = mkOption {
228 type = types.bool;
229 default = false;
230 description = ''
231 Set the TTY audit flag when opening the session,
232 but do not restore it when closing the session.
233 Using this option is necessary for some services
234 that don't fork() to run the authenticated session,
235 such as sudo.
236 '';
237 };
238 };
239
240 forwardXAuth = mkOption {
241 default = false;
242 type = types.bool;
243 description = ''
244 Whether X authentication keys should be passed from the
245 calling user to the target user (e.g. for
246 <command>su</command>)
247 '';
248 };
249
250 pamMount = mkOption {
251 default = config.security.pam.mount.enable;
252 type = types.bool;
253 description = ''
254 Enable PAM mount (pam_mount) system to mount fileystems on user login.
255 '';
256 };
257
258 allowNullPassword = mkOption {
259 default = false;
260 type = types.bool;
261 description = ''
262 Whether to allow logging into accounts that have no password
263 set (i.e., have an empty password field in
264 <filename>/etc/passwd</filename> or
265 <filename>/etc/group</filename>). This does not enable
266 logging into disabled accounts (i.e., that have the password
267 field set to <literal>!</literal>). Note that regardless of
268 what the pam_unix documentation says, accounts with hashed
269 empty passwords are always allowed to log in.
270 '';
271 };
272
273 nodelay = mkOption {
274 default = false;
275 type = types.bool;
276 description = ''
277 Wheather the delay after typing a wrong password should be disabled.
278 '';
279 };
280
281 requireWheel = mkOption {
282 default = false;
283 type = types.bool;
284 description = ''
285 Whether to permit root access only to members of group wheel.
286 '';
287 };
288
289 limits = mkOption {
290 description = ''
291 Attribute set describing resource limits. Defaults to the
292 value of <option>security.pam.loginLimits</option>.
293 '';
294 };
295
296 showMotd = mkOption {
297 default = false;
298 type = types.bool;
299 description = "Whether to show the message of the day.";
300 };
301
302 makeHomeDir = mkOption {
303 default = false;
304 type = types.bool;
305 description = ''
306 Whether to try to create home directories for users
307 with <literal>$HOME</literal>s pointing to nonexistent
308 locations on session login.
309 '';
310 };
311
312 updateWtmp = mkOption {
313 default = false;
314 type = types.bool;
315 description = "Whether to update <filename>/var/log/wtmp</filename>.";
316 };
317
318 logFailures = mkOption {
319 default = false;
320 type = types.bool;
321 description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>.";
322 };
323
324 enableAppArmor = mkOption {
325 default = false;
326 type = types.bool;
327 description = ''
328 Enable support for attaching AppArmor profiles at the
329 user/group level, e.g., as part of a role based access
330 control scheme.
331 '';
332 };
333
334 enableKwallet = mkOption {
335 default = false;
336 type = types.bool;
337 description = ''
338 If enabled, pam_wallet will attempt to automatically unlock the
339 user's default KDE wallet upon login. If the user has no wallet named
340 "kdewallet", or the login password does not match their wallet
341 password, KDE will prompt separately after login.
342 '';
343 };
344 sssdStrictAccess = mkOption {
345 default = false;
346 type = types.bool;
347 description = "enforce sssd access control";
348 };
349
350 enableGnomeKeyring = mkOption {
351 default = false;
352 type = types.bool;
353 description = ''
354 If enabled, pam_gnome_keyring will attempt to automatically unlock the
355 user's default Gnome keyring upon login. If the user login password does
356 not match their keyring password, Gnome Keyring will prompt separately
357 after login.
358 '';
359 };
360
361 gnupg = {
362 enable = mkOption {
363 type = types.bool;
364 default = false;
365 description = ''
366 If enabled, pam_gnupg will attempt to automatically unlock the
367 user's GPG keys with the login password via
368 <command>gpg-agent</command>. The keygrips of all keys to be
369 unlocked should be written to <filename>~/.pam-gnupg</filename>,
370 and can be queried with <command>gpg -K --with-keygrip</command>.
371 Presetting passphrases must be enabled by adding
372 <literal>allow-preset-passphrase</literal> in
373 <filename>~/.gnupg/gpg-agent.conf</filename>.
374 '';
375 };
376
377 noAutostart = mkOption {
378 type = types.bool;
379 default = false;
380 description = ''
381 Don't start <command>gpg-agent</command> if it is not running.
382 Useful in conjunction with starting <command>gpg-agent</command> as
383 a systemd user service.
384 '';
385 };
386
387 storeOnly = mkOption {
388 type = types.bool;
389 default = false;
390 description = ''
391 Don't send the password immediately after login, but store for PAM
392 <literal>session</literal>.
393 '';
394 };
395 };
396
397 text = mkOption {
398 type = types.nullOr types.lines;
399 description = "Contents of the PAM service file.";
400 };
401
402 };
403
404 config = {
405 name = mkDefault name;
406 setLoginUid = mkDefault cfg.startSession;
407 limits = mkDefault config.security.pam.loginLimits;
408
409 # !!! TODO: move the LDAP stuff to the LDAP module, and the
410 # Samba stuff to the Samba module. This requires that the PAM
411 # module provides the right hooks.
412 text = mkDefault
413 (
414 ''
415 # Account management.
416 account required pam_unix.so
417 '' +
418 optionalString use_ldap ''
419 account sufficient ${pam_ldap}/lib/security/pam_ldap.so
420 '' +
421 optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) ''
422 account sufficient ${pkgs.sssd}/lib/security/pam_sss.so
423 '' +
424 optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) ''
425 account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so
426 '' +
427 optionalString config.krb5.enable ''
428 account sufficient ${pam_krb5}/lib/security/pam_krb5.so
429 '' +
430 optionalString cfg.googleOsLoginAccountVerification ''
431 account [success=ok ignore=ignore default=die] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
432 account [success=ok default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so
433 '' +
434 ''
435
436 # Authentication management.
437 '' +
438 optionalString cfg.googleOsLoginAuthentication ''
439 auth [success=done perm_denied=bad default=ignore] ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so
440 '' +
441 optionalString cfg.rootOK ''
442 auth sufficient pam_rootok.so
443 '' +
444 optionalString cfg.requireWheel ''
445 auth required pam_wheel.so use_uid
446 '' +
447 optionalString cfg.logFailures ''
448 auth required pam_faillock.so
449 '' +
450 optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) ''
451 auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=${lib.concatStringsSep ":" config.services.openssh.authorizedKeysFiles}
452 '' +
453 (let p11 = config.security.pam.p11; in optionalString cfg.p11Auth ''
454 auth ${p11.control} ${pkgs.pam_p11}/lib/security/pam_p11.so ${pkgs.opensc}/lib/opensc-pkcs11.so
455 '') +
456 (let u2f = config.security.pam.u2f; in optionalString cfg.u2fAuth ''
457 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}"}
458 '') +
459 optionalString cfg.usbAuth ''
460 auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so
461 '' +
462 (let oath = config.security.pam.oath; in optionalString cfg.oathAuth ''
463 auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}
464 '') +
465 (let yubi = config.security.pam.yubico; in optionalString cfg.yubicoAuth ''
466 auth ${yubi.control} ${pkgs.yubico-pam}/lib/security/pam_yubico.so mode=${toString yubi.mode} ${optionalString (yubi.challengeResponsePath != null) "chalresp_path=${yubi.challengeResponsePath}"} ${optionalString (yubi.mode == "client") "id=${toString yubi.id}"} ${optionalString yubi.debug "debug"}
467 '') +
468 optionalString cfg.fprintAuth ''
469 auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so
470 '' +
471 # Modules in this block require having the password set in PAM_AUTHTOK.
472 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
473 # after it succeeds. Certain modules need to run after pam_unix
474 # prompts the user for password so we run it once with 'required' at an
475 # earlier point and it will run again with 'sufficient' further down.
476 # We use try_first_pass the second time to avoid prompting password twice
477 (optionalString (cfg.unixAuth &&
478 (config.security.pam.enableEcryptfs
479 || cfg.pamMount
480 || cfg.enableKwallet
481 || cfg.enableGnomeKeyring
482 || cfg.googleAuthenticator.enable
483 || cfg.gnupg.enable
484 || cfg.duoSecurity.enable))
485 (
486 ''
487 auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth
488 '' +
489 optionalString config.security.pam.enableEcryptfs ''
490 auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap
491 '' +
492 optionalString cfg.pamMount ''
493 auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
494 '' +
495 optionalString cfg.enableKwallet ''
496 auth optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5
497 '' +
498 optionalString cfg.enableGnomeKeyring ''
499 auth optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so
500 '' +
501 optionalString cfg.gnupg.enable ''
502 auth optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.storeOnly " store-only"}
503 '' +
504 optionalString cfg.googleAuthenticator.enable ''
505 auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp
506 '' +
507 optionalString cfg.duoSecurity.enable ''
508 auth required ${pkgs.duo-unix}/lib/security/pam_duo.so
509 ''
510 )) +
511 optionalString cfg.unixAuth ''
512 auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} ${optionalString cfg.nodelay "nodelay"} likeauth try_first_pass
513 '' +
514 optionalString cfg.otpwAuth ''
515 auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so
516 '' +
517 optionalString use_ldap ''
518 auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass
519 '' +
520 optionalString config.services.sssd.enable ''
521 auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass
522 '' +
523 optionalString config.krb5.enable ''
524 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
525 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
526 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
527 '' +
528 ''
529 auth required pam_deny.so
530
531 # Password management.
532 password sufficient pam_unix.so nullok sha512
533 '' +
534 optionalString config.security.pam.enableEcryptfs ''
535 password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
536 '' +
537 optionalString cfg.pamMount ''
538 password optional ${pkgs.pam_mount}/lib/security/pam_mount.so
539 '' +
540 optionalString use_ldap ''
541 password sufficient ${pam_ldap}/lib/security/pam_ldap.so
542 '' +
543 optionalString config.services.sssd.enable ''
544 password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok
545 '' +
546 optionalString config.krb5.enable ''
547 password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
548 '' +
549 optionalString cfg.enableGnomeKeyring ''
550 password optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so use_authtok
551 '' +
552 ''
553
554 # Session management.
555 '' +
556 optionalString cfg.setEnvironment ''
557 session required pam_env.so conffile=/etc/pam/environment readenv=0
558 '' +
559 ''
560 session required pam_unix.so
561 '' +
562 optionalString cfg.setLoginUid ''
563 session ${if config.boot.isContainer then "optional" else "required"} pam_loginuid.so
564 '' +
565 optionalString cfg.ttyAudit.enable ''
566 session required ${pkgs.pam}/lib/security/pam_tty_audit.so
567 open_only=${toString cfg.ttyAudit.openOnly}
568 ${optionalString (cfg.ttyAudit.enablePattern != null) "enable=${cfg.ttyAudit.enablePattern}"}
569 ${optionalString (cfg.ttyAudit.disablePattern != null) "disable=${cfg.ttyAudit.disablePattern}"}
570 '' +
571 optionalString cfg.makeHomeDir ''
572 session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0077
573 '' +
574 optionalString cfg.updateWtmp ''
575 session required ${pkgs.pam}/lib/security/pam_lastlog.so silent
576 '' +
577 optionalString config.security.pam.enableEcryptfs ''
578 session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so
579 '' +
580 optionalString cfg.pamMount ''
581 session optional ${pkgs.pam_mount}/lib/security/pam_mount.so disable_interactive
582 '' +
583 optionalString use_ldap ''
584 session optional ${pam_ldap}/lib/security/pam_ldap.so
585 '' +
586 optionalString config.services.sssd.enable ''
587 session optional ${pkgs.sssd}/lib/security/pam_sss.so
588 '' +
589 optionalString config.krb5.enable ''
590 session optional ${pam_krb5}/lib/security/pam_krb5.so
591 '' +
592 optionalString cfg.otpwAuth ''
593 session optional ${pkgs.otpw}/lib/security/pam_otpw.so
594 '' +
595 optionalString cfg.startSession ''
596 session optional ${pkgs.systemd}/lib/security/pam_systemd.so
597 '' +
598 optionalString cfg.forwardXAuth ''
599 session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99
600 '' +
601 optionalString (cfg.limits != []) ''
602 session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}
603 '' +
604 optionalString (cfg.showMotd && config.users.motd != null) ''
605 session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}
606 '' +
607 optionalString (cfg.enableAppArmor && config.security.apparmor.enable) ''
608 session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug
609 '' +
610 optionalString (cfg.enableKwallet) ''
611 session optional ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so kwalletd=${pkgs.plasma5Packages.kwallet.bin}/bin/kwalletd5
612 '' +
613 optionalString (cfg.enableGnomeKeyring) ''
614 session optional ${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start
615 '' +
616 optionalString cfg.gnupg.enable ''
617 session optional ${pkgs.pam_gnupg}/lib/security/pam_gnupg.so ${optionalString cfg.gnupg.noAutostart " no-autostart"}
618 '' +
619 optionalString (config.virtualisation.lxc.lxcfs.enable) ''
620 session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all
621 ''
622 );
623 };
624
625 };
626
627
628 inherit (pkgs) pam_krb5 pam_ccreds;
629
630 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
631 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
632
633 # Create a limits.conf(5) file.
634 makeLimitsConf = limits:
635 pkgs.writeText "limits.conf"
636 (concatMapStrings ({ domain, type, item, value }:
637 "${domain} ${type} ${item} ${toString value}\n")
638 limits);
639
640 motd = pkgs.writeText "motd" config.users.motd;
641
642 makePAMService = name: service:
643 { name = "pam.d/${name}";
644 value.source = pkgs.writeText "${name}.pam" service.text;
645 };
646
647in
648
649{
650
651 imports = [
652 (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
653 ];
654
655 ###### interface
656
657 options = {
658
659 security.pam.loginLimits = mkOption {
660 default = [];
661 example =
662 [ { domain = "ftp";
663 type = "hard";
664 item = "nproc";
665 value = "0";
666 }
667 { domain = "@student";
668 type = "-";
669 item = "maxlogins";
670 value = "4";
671 }
672 ];
673
674 description =
675 '' Define resource limits that should apply to users or groups.
676 Each item in the list should be an attribute set with a
677 <varname>domain</varname>, <varname>type</varname>,
678 <varname>item</varname>, and <varname>value</varname>
679 attribute. The syntax and semantics of these attributes
680 must be that described in the limits.conf(5) man page.
681
682 Note that these limits do not apply to systemd services,
683 whose limits can be changed via <option>systemd.extraConfig</option>
684 instead.
685 '';
686 };
687
688 security.pam.services = mkOption {
689 default = {};
690 type = with types; attrsOf (submodule pamOpts);
691 description =
692 ''
693 This option defines the PAM services. A service typically
694 corresponds to a program that uses PAM,
695 e.g. <command>login</command> or <command>passwd</command>.
696 Each attribute of this set defines a PAM service, with the attribute name
697 defining the name of the service.
698 '';
699 };
700
701 security.pam.makeHomeDir.skelDirectory = mkOption {
702 type = types.str;
703 default = "/var/empty";
704 example = "/etc/skel";
705 description = ''
706 Path to skeleton directory whose contents are copied to home
707 directories newly created by <literal>pam_mkhomedir</literal>.
708 '';
709 };
710
711 security.pam.enableSSHAgentAuth = mkOption {
712 type = types.bool;
713 default = false;
714 description =
715 ''
716 Enable sudo logins if the user's SSH agent provides a key
717 present in <filename>~/.ssh/authorized_keys</filename>.
718 This allows machines to exclusively use SSH keys instead of
719 passwords.
720 '';
721 };
722
723 security.pam.enableOTPW = mkEnableOption "the OTPW (one-time password) PAM module";
724
725 security.pam.p11 = {
726 enable = mkOption {
727 default = false;
728 type = types.bool;
729 description = ''
730 Enables P11 PAM (<literal>pam_p11</literal>) module.
731
732 If set, users can log in with SSH keys and PKCS#11 tokens.
733
734 More information can be found <link
735 xlink:href="https://github.com/OpenSC/pam_p11">here</link>.
736 '';
737 };
738
739 control = mkOption {
740 default = "sufficient";
741 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
742 description = ''
743 This option sets pam "control".
744 If you want to have multi factor authentication, use "required".
745 If you want to use the PKCS#11 device instead of the regular password,
746 use "sufficient".
747
748 Read
749 <citerefentry>
750 <refentrytitle>pam.conf</refentrytitle>
751 <manvolnum>5</manvolnum>
752 </citerefentry>
753 for better understanding of this option.
754 '';
755 };
756 };
757
758 security.pam.u2f = {
759 enable = mkOption {
760 default = false;
761 type = types.bool;
762 description = ''
763 Enables U2F PAM (<literal>pam-u2f</literal>) module.
764
765 If set, users listed in
766 <filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
767 <filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
768 not set) are able to log in with the associated U2F key. The path can
769 be changed using <option>security.pam.u2f.authFile</option> option.
770
771 File format is:
772 <literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
773 This file can be generated using <command>pamu2fcfg</command> command.
774
775 More information can be found <link
776 xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
777 '';
778 };
779
780 authFile = mkOption {
781 default = null;
782 type = with types; nullOr path;
783 description = ''
784 By default <literal>pam-u2f</literal> module reads the keys from
785 <filename>$XDG_CONFIG_HOME/Yubico/u2f_keys</filename> (or
786 <filename>$HOME/.config/Yubico/u2f_keys</filename> if XDG variable is
787 not set).
788
789 If you want to change auth file locations or centralize database (for
790 example use <filename>/etc/u2f-mappings</filename>) you can set this
791 option.
792
793 File format is:
794 <literal>username:first_keyHandle,first_public_key: second_keyHandle,second_public_key</literal>
795 This file can be generated using <command>pamu2fcfg</command> command.
796
797 More information can be found <link
798 xlink:href="https://developers.yubico.com/pam-u2f/">here</link>.
799 '';
800 };
801
802 appId = mkOption {
803 default = null;
804 type = with types; nullOr str;
805 description = ''
806 By default <literal>pam-u2f</literal> module sets the application
807 ID to <literal>pam://$HOSTNAME</literal>.
808
809 When using <command>pamu2fcfg</command>, you can specify your
810 application ID with the <literal>-i</literal> flag.
811
812 More information can be found <link
813 xlink:href="https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html">
814 here</link>
815 '';
816 };
817
818 control = mkOption {
819 default = "sufficient";
820 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
821 description = ''
822 This option sets pam "control".
823 If you want to have multi factor authentication, use "required".
824 If you want to use U2F device instead of regular password, use "sufficient".
825
826 Read
827 <citerefentry>
828 <refentrytitle>pam.conf</refentrytitle>
829 <manvolnum>5</manvolnum>
830 </citerefentry>
831 for better understanding of this option.
832 '';
833 };
834
835 debug = mkOption {
836 default = false;
837 type = types.bool;
838 description = ''
839 Debug output to stderr.
840 '';
841 };
842
843 interactive = mkOption {
844 default = false;
845 type = types.bool;
846 description = ''
847 Set to prompt a message and wait before testing the presence of a U2F device.
848 Recommended if your device doesn’t have a tactile trigger.
849 '';
850 };
851
852 cue = mkOption {
853 default = false;
854 type = types.bool;
855 description = ''
856 By default <literal>pam-u2f</literal> module does not inform user
857 that he needs to use the u2f device, it just waits without a prompt.
858
859 If you set this option to <literal>true</literal>,
860 <literal>cue</literal> option is added to <literal>pam-u2f</literal>
861 module and reminder message will be displayed.
862 '';
863 };
864 };
865
866 security.pam.yubico = {
867 enable = mkOption {
868 default = false;
869 type = types.bool;
870 description = ''
871 Enables Yubico PAM (<literal>yubico-pam</literal>) module.
872
873 If set, users listed in
874 <filename>~/.yubico/authorized_yubikeys</filename>
875 are able to log in with the associated Yubikey tokens.
876
877 The file must have only one line:
878 <literal>username:yubikey_token_id1:yubikey_token_id2</literal>
879 More information can be found <link
880 xlink:href="https://developers.yubico.com/yubico-pam/">here</link>.
881 '';
882 };
883 control = mkOption {
884 default = "sufficient";
885 type = types.enum [ "required" "requisite" "sufficient" "optional" ];
886 description = ''
887 This option sets pam "control".
888 If you want to have multi factor authentication, use "required".
889 If you want to use Yubikey instead of regular password, use "sufficient".
890
891 Read
892 <citerefentry>
893 <refentrytitle>pam.conf</refentrytitle>
894 <manvolnum>5</manvolnum>
895 </citerefentry>
896 for better understanding of this option.
897 '';
898 };
899 id = mkOption {
900 example = "42";
901 type = types.str;
902 description = "client id";
903 };
904
905 debug = mkOption {
906 default = false;
907 type = types.bool;
908 description = ''
909 Debug output to stderr.
910 '';
911 };
912 mode = mkOption {
913 default = "client";
914 type = types.enum [ "client" "challenge-response" ];
915 description = ''
916 Mode of operation.
917
918 Use "client" for online validation with a YubiKey validation service such as
919 the YubiCloud.
920
921 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
922 Challenge-Response configurations. See the man-page ykpamcfg(1) for further
923 details on how to configure offline Challenge-Response validation.
924
925 More information can be found <link
926 xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
927 '';
928 };
929 challengeResponsePath = mkOption {
930 default = null;
931 type = types.nullOr types.path;
932 description = ''
933 If not null, set the path used by yubico pam module where the challenge expected response is stored.
934
935 More information can be found <link
936 xlink:href="https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html">here</link>.
937 '';
938 };
939 };
940
941 security.pam.enableEcryptfs = mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
942
943 users.motd = mkOption {
944 default = null;
945 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
946 type = types.nullOr types.lines;
947 description = "Message of the day shown to users when they log in.";
948 };
949
950 };
951
952
953 ###### implementation
954
955 config = {
956
957 environment.systemPackages =
958 # Include the PAM modules in the system path mostly for the manpages.
959 [ pkgs.pam ]
960 ++ optional config.users.ldap.enable pam_ldap
961 ++ optional config.services.sssd.enable pkgs.sssd
962 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
963 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
964 ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ]
965 ++ optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
966 ++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
967
968 boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
969
970 security.wrappers = {
971 unix_chkpwd = {
972 setuid = true;
973 owner = "root";
974 group = "root";
975 source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
976 };
977 };
978
979 environment.etc = mapAttrs' makePAMService config.security.pam.services;
980
981 security.pam.services =
982 { other.text =
983 ''
984 auth required pam_warn.so
985 auth required pam_deny.so
986 account required pam_warn.so
987 account required pam_deny.so
988 password required pam_warn.so
989 password required pam_deny.so
990 session required pam_warn.so
991 session required pam_deny.so
992 '';
993
994 # Most of these should be moved to specific modules.
995 i3lock = {};
996 i3lock-color = {};
997 vlock = {};
998 xlock = {};
999 xscreensaver = {};
1000
1001 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
1002
1003 /* FIXME: should runuser -l start a systemd session? Currently
1004 it complains "Cannot create session: Already running in a
1005 session". */
1006 runuser-l = { rootOK = true; unixAuth = false; };
1007 };
1008
1009 security.apparmor.includes."abstractions/pam" = let
1010 isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
1011 in
1012 lib.concatMapStringsSep "\n"
1013 (name: "r ${config.environment.etc."pam.d/${name}".source},")
1014 (attrNames config.security.pam.services) +
1015 ''
1016 mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
1017 mr ${getLib pkgs.pam}/lib/security/pam_*.so,
1018 r ${getLib pkgs.pam}/lib/security/,
1019 '' +
1020 optionalString use_ldap ''
1021 mr ${pam_ldap}/lib/security/pam_ldap.so,
1022 '' +
1023 optionalString config.services.sssd.enable ''
1024 mr ${pkgs.sssd}/lib/security/pam_sss.so,
1025 '' +
1026 optionalString config.krb5.enable ''
1027 mr ${pam_krb5}/lib/security/pam_krb5.so,
1028 mr ${pam_ccreds}/lib/security/pam_ccreds.so,
1029 '' +
1030 optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
1031 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
1032 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
1033 '' +
1034 optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) ''
1035 mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
1036 '' +
1037 optionalString (config.security.pam.enableSSHAgentAuth
1038 && isEnabled (cfg: cfg.sshAgentAuth)) ''
1039 mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,
1040 '' +
1041 optionalString (isEnabled (cfg: cfg.fprintAuth)) ''
1042 mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,
1043 '' +
1044 optionalString (isEnabled (cfg: cfg.u2fAuth)) ''
1045 mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,
1046 '' +
1047 optionalString (isEnabled (cfg: cfg.usbAuth)) ''
1048 mr ${pkgs.pam_usb}/lib/security/pam_usb.so,
1049 '' +
1050 optionalString (isEnabled (cfg: cfg.oathAuth)) ''
1051 "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,
1052 '' +
1053 optionalString (isEnabled (cfg: cfg.yubicoAuth)) ''
1054 mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,
1055 '' +
1056 optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) ''
1057 mr ${pkgs.duo-unix}/lib/security/pam_duo.so,
1058 '' +
1059 optionalString (isEnabled (cfg: cfg.otpwAuth)) ''
1060 mr ${pkgs.otpw}/lib/security/pam_otpw.so,
1061 '' +
1062 optionalString config.security.pam.enableEcryptfs ''
1063 mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,
1064 '' +
1065 optionalString (isEnabled (cfg: cfg.pamMount)) ''
1066 mr ${pkgs.pam_mount}/lib/security/pam_mount.so,
1067 '' +
1068 optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) ''
1069 mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,
1070 '' +
1071 optionalString (isEnabled (cfg: cfg.startSession)) ''
1072 mr ${pkgs.systemd}/lib/security/pam_systemd.so,
1073 '' +
1074 optionalString (isEnabled (cfg: cfg.enableAppArmor)
1075 && config.security.apparmor.enable) ''
1076 mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,
1077 '' +
1078 optionalString (isEnabled (cfg: cfg.enableKwallet)) ''
1079 mr ${pkgs.plasma5Packages.kwallet-pam}/lib/security/pam_kwallet5.so,
1080 '' +
1081 optionalString config.virtualisation.lxc.lxcfs.enable ''
1082 mr ${pkgs.lxc}/lib/security/pam_cgfs.so
1083 '';
1084 };
1085
1086}