1# This module provides configuration for the PAM (Pluggable
2# Authentication Modules) system.
3{
4 config,
5 lib,
6 pkgs,
7 ...
8}:
9let
10
11 moduleSettingsType =
12 with lib.types;
13 attrsOf (
14 nullOr (oneOf [
15 bool
16 str
17 int
18 pathInStore
19 ])
20 );
21 moduleSettingsDescription = ''
22 Boolean values render just the key if true, and nothing if false.
23 Null values are ignored.
24 All other values are rendered as key-value pairs.
25 '';
26
27 mkRulesTypeOption =
28 type:
29 lib.mkOption {
30 # These options are experimental and subject to breaking changes without notice.
31 description = ''
32 PAM `${type}` rules for this service.
33
34 Attribute keys are the name of each rule.
35 '';
36 type = lib.types.attrsOf (
37 lib.types.submodule (
38 { name, config, ... }:
39 {
40 options = {
41 name = lib.mkOption {
42 type = lib.types.str;
43 description = ''
44 Name of this rule.
45 '';
46 internal = true;
47 readOnly = true;
48 };
49 enable = lib.mkOption {
50 type = lib.types.bool;
51 default = true;
52 description = ''
53 Whether this rule is added to the PAM service config file.
54 '';
55 };
56 order = lib.mkOption {
57 type = lib.types.int;
58 description = ''
59 Order of this rule in the service file. Rules are arranged in ascending order of this value.
60
61 ::: {.warning}
62 The `order` values for the built-in rules are subject to change. If you assign a constant value to this option, a system update could silently reorder your rule. You could be locked out of your system, or your system could be left wide open. When using this option, set it to a relative offset from another rule's `order` value:
63
64 ```nix
65 {
66 security.pam.services.login.rules.auth.foo.order =
67 config.security.pam.services.login.rules.auth.unix.order + 10;
68 }
69 ```
70 :::
71 '';
72 };
73 control = lib.mkOption {
74 type = lib.types.str;
75 description = ''
76 Indicates the behavior of the PAM-API should the module fail to succeed in its authentication task. See `control` in {manpage}`pam.conf(5)` for details.
77 '';
78 };
79 modulePath = lib.mkOption {
80 type = lib.types.str;
81 description = ''
82 Either the full filename of the PAM to be used by the application (it begins with a '/'), or a relative pathname from the default module location. See `module-path` in {manpage}`pam.conf(5)` for details.
83 '';
84 };
85 args = lib.mkOption {
86 type = lib.types.listOf lib.types.str;
87 description = ''
88 Tokens that can be used to modify the specific behavior of the given PAM. Such arguments will be documented for each individual module. See `module-arguments` in {manpage}`pam.conf(5)` for details.
89
90 Escaping rules for spaces and square brackets are automatically applied.
91
92 {option}`settings` are automatically added as {option}`args`. It's recommended to use the {option}`settings` option whenever possible so that arguments can be overridden.
93 '';
94 };
95 settings = lib.mkOption {
96 type = moduleSettingsType;
97 default = { };
98 description = ''
99 Settings to add as `module-arguments`.
100
101 ${moduleSettingsDescription}
102 '';
103 };
104 };
105 config = {
106 inherit name;
107 # Formats an attrset of settings as args for use as `module-arguments`.
108 args = lib.concatLists (
109 lib.flip lib.mapAttrsToList config.settings (
110 name: value:
111 if lib.isBool value then
112 lib.optional value name
113 else
114 lib.optional (value != null) "${name}=${toString value}"
115 )
116 );
117 };
118 }
119 )
120 );
121 };
122
123 package = config.security.pam.package;
124 parentConfig = config;
125
126 pamOpts =
127 { config, name, ... }:
128 let
129 cfg = config;
130 in
131 let
132 config = parentConfig;
133 in
134 {
135
136 imports = [
137 (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ])
138 ];
139
140 options = {
141
142 name = lib.mkOption {
143 example = "sshd";
144 type = lib.types.str;
145 description = "Name of the PAM service.";
146 };
147
148 enable = lib.mkEnableOption "this PAM service" // {
149 default = true;
150 example = false;
151 };
152
153 rules = lib.mkOption {
154 # This option is experimental and subject to breaking changes without notice.
155 visible = false;
156
157 description = ''
158 PAM rules for this service.
159
160 ::: {.warning}
161 This option and its suboptions are experimental and subject to breaking changes without notice.
162
163 If you use this option in your system configuration, you will need to manually monitor this module for any changes. Otherwise, failure to adjust your configuration properly could lead to you being locked out of your system, or worse, your system could be left wide open to attackers.
164
165 If you share configuration examples that use this option, you MUST include this warning so that users are informed.
166
167 You may freely use this option within `nixpkgs`, and future changes will account for those use sites.
168 :::
169 '';
170 type = lib.types.submodule {
171 options = lib.genAttrs [ "account" "auth" "password" "session" ] mkRulesTypeOption;
172 };
173 };
174
175 unixAuth = lib.mkOption {
176 default = true;
177 type = lib.types.bool;
178 description = ''
179 Whether users can log in with passwords defined in
180 {file}`/etc/shadow`.
181 '';
182 };
183
184 rootOK = lib.mkOption {
185 default = false;
186 type = lib.types.bool;
187 description = ''
188 If set, root doesn't need to authenticate (e.g. for the
189 {command}`useradd` service).
190 '';
191 };
192
193 p11Auth = lib.mkOption {
194 default = config.security.pam.p11.enable;
195 defaultText = lib.literalExpression "config.security.pam.p11.enable";
196 type = lib.types.bool;
197 description = ''
198 If set, keys listed in
199 {file}`~/.ssh/authorized_keys` and
200 {file}`~/.eid/authorized_certificates`
201 can be used to log in with the associated PKCS#11 tokens.
202 '';
203 };
204
205 u2fAuth = lib.mkOption {
206 default = config.security.pam.u2f.enable;
207 defaultText = lib.literalExpression "config.security.pam.u2f.enable";
208 type = lib.types.bool;
209 description = ''
210 If set, users listed in
211 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
212 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
213 not set) are able to log in with the associated U2F key. Path can be
214 changed using {option}`security.pam.u2f.authFile` option.
215 '';
216 };
217
218 usshAuth = lib.mkOption {
219 default = false;
220 type = lib.types.bool;
221 description = ''
222 If set, users with an SSH certificate containing an authorized principal
223 in their SSH agent are able to log in. Specific options are controlled
224 using the {option}`security.pam.ussh` options.
225
226 Note that the {option}`security.pam.ussh.enable` must also be
227 set for this option to take effect.
228 '';
229 };
230
231 yubicoAuth = lib.mkOption {
232 default = config.security.pam.yubico.enable;
233 defaultText = lib.literalExpression "config.security.pam.yubico.enable";
234 type = lib.types.bool;
235 description = ''
236 If set, users listed in
237 {file}`~/.yubico/authorized_yubikeys`
238 are able to log in with the associated Yubikey tokens.
239 '';
240 };
241
242 googleAuthenticator = {
243 enable = lib.mkOption {
244 default = false;
245 type = lib.types.bool;
246 description = ''
247 If set, users with enabled Google Authenticator (created
248 {file}`~/.google_authenticator`) will be required
249 to provide Google Authenticator token to log in.
250 '';
251 };
252 allowNullOTP = lib.mkOption {
253 type = lib.types.bool;
254 default = false;
255 description = ''
256 Whether to allow login for accounts that have no OTP set
257 (i.e., accounts with no OTP configured or no existing
258 {file}`~/.google_authenticator`).
259 '';
260 };
261 forwardPass = lib.mkOption {
262 type = lib.types.bool;
263 default = false;
264 description = ''
265 The authentication provides a single field requiring
266 the user's password followed by the one-time password (OTP).
267 '';
268 };
269 };
270
271 otpwAuth = lib.mkOption {
272 default = config.security.pam.enableOTPW;
273 defaultText = lib.literalExpression "config.security.pam.enableOTPW";
274 type = lib.types.bool;
275 description = ''
276 If set, the OTPW system will be used (if
277 {file}`~/.otpw` exists).
278 '';
279 };
280
281 googleOsLoginAccountVerification = lib.mkOption {
282 default = false;
283 type = lib.types.bool;
284 description = ''
285 If set, will use the Google OS Login PAM modules
286 (`pam_oslogin_login`,
287 `pam_oslogin_admin`) to verify possible OS Login
288 users and set sudoers configuration accordingly.
289 This only makes sense to enable for the `sshd` PAM
290 service.
291 '';
292 };
293
294 googleOsLoginAuthentication = lib.mkOption {
295 default = false;
296 type = lib.types.bool;
297 description = ''
298 If set, will use the `pam_oslogin_login`'s user
299 authentication methods to authenticate users using 2FA.
300 This only makes sense to enable for the `sshd` PAM
301 service.
302 '';
303 };
304
305 mysqlAuth = lib.mkOption {
306 default = config.users.mysql.enable;
307 defaultText = lib.literalExpression "config.users.mysql.enable";
308 type = lib.types.bool;
309 description = ''
310 If set, the `pam_mysql` module will be used to
311 authenticate users against a MySQL/MariaDB database.
312 '';
313 };
314
315 fprintAuth = lib.mkOption {
316 default = config.services.fprintd.enable;
317 defaultText = lib.literalExpression "config.services.fprintd.enable";
318 type = lib.types.bool;
319 description = ''
320 If set, fingerprint reader will be used (if exists and
321 your fingerprints are enrolled).
322 '';
323 };
324
325 oathAuth = lib.mkOption {
326 default = config.security.pam.oath.enable;
327 defaultText = lib.literalExpression "config.security.pam.oath.enable";
328 type = lib.types.bool;
329 description = ''
330 If set, the OATH Toolkit will be used.
331 '';
332 };
333
334 sshAgentAuth = lib.mkOption {
335 default = false;
336 type = lib.types.bool;
337 description = ''
338 If set, the calling user's SSH agent is used to authenticate
339 against the keys in the calling user's
340 {file}`~/.ssh/authorized_keys`. This is useful
341 for {command}`sudo` on password-less remote systems.
342 '';
343 };
344
345 rssh = lib.mkOption {
346 default = false;
347 type = lib.types.bool;
348 description = ''
349 If set, the calling user's SSH agent is used to authenticate
350 against the configured keys. This module works in a manner
351 similar to pam_ssh_agent_auth, but supports a wider range
352 of SSH key types, including those protected by security
353 keys (FIDO2).
354 '';
355 };
356
357 duoSecurity = {
358 enable = lib.mkOption {
359 default = false;
360 type = lib.types.bool;
361 description = ''
362 If set, use the Duo Security pam module
363 `pam_duo` for authentication. Requires
364 configuration of {option}`security.duosec` options.
365 '';
366 };
367 };
368
369 startSession = lib.mkOption {
370 default = false;
371 type = lib.types.bool;
372 description = ''
373 If set, the service will register a new session with
374 systemd's login manager. For local sessions, this will give
375 the user access to audio devices, CD-ROM drives. In the
376 default PolicyKit configuration, it also allows the user to
377 reboot the system.
378 '';
379 };
380
381 setEnvironment = lib.mkOption {
382 type = lib.types.bool;
383 default = true;
384 description = ''
385 Whether the service should set the environment variables
386 listed in {option}`environment.sessionVariables`
387 using `pam_env.so`.
388 '';
389 };
390
391 setLoginUid = lib.mkOption {
392 type = lib.types.bool;
393 description = ''
394 Set the login uid of the process
395 ({file}`/proc/self/loginuid`) for auditing
396 purposes. The login uid is only set by ‘entry points’ like
397 {command}`login` and {command}`sshd`, not by
398 commands like {command}`sudo`.
399 '';
400 };
401
402 ttyAudit = {
403 enable = lib.mkOption {
404 type = lib.types.bool;
405 default = false;
406 description = ''
407 Enable or disable TTY auditing for specified users
408 '';
409 };
410
411 enablePattern = lib.mkOption {
412 type = lib.types.nullOr lib.types.str;
413 default = null;
414 description = ''
415 For each user matching one of comma-separated
416 glob patterns, enable TTY auditing
417 '';
418 };
419
420 disablePattern = lib.mkOption {
421 type = lib.types.nullOr lib.types.str;
422 default = null;
423 description = ''
424 For each user matching one of comma-separated
425 glob patterns, disable TTY auditing
426 '';
427 };
428
429 openOnly = lib.mkOption {
430 type = lib.types.bool;
431 default = false;
432 description = ''
433 Set the TTY audit flag when opening the session,
434 but do not restore it when closing the session.
435 Using this option is necessary for some services
436 that don't fork() to run the authenticated session,
437 such as sudo.
438 '';
439 };
440 };
441
442 forwardXAuth = lib.mkOption {
443 default = false;
444 type = lib.types.bool;
445 description = ''
446 Whether X authentication keys should be passed from the
447 calling user to the target user (e.g. for
448 {command}`su`)
449 '';
450 };
451
452 pamMount = lib.mkOption {
453 default = config.security.pam.mount.enable;
454 defaultText = lib.literalExpression "config.security.pam.mount.enable";
455 type = lib.types.bool;
456 description = ''
457 Enable PAM mount (pam_mount) system to mount filesystems on user login.
458 '';
459 };
460
461 allowNullPassword = lib.mkOption {
462 default = false;
463 type = lib.types.bool;
464 description = ''
465 Whether to allow logging into accounts that have no password
466 set (i.e., have an empty password field in
467 {file}`/etc/passwd` or
468 {file}`/etc/group`). This does not enable
469 logging into disabled accounts (i.e., that have the password
470 field set to `!`). Note that regardless of
471 what the pam_unix documentation says, accounts with hashed
472 empty passwords are always allowed to log in.
473 '';
474 };
475
476 nodelay = lib.mkOption {
477 default = false;
478 type = lib.types.bool;
479 description = ''
480 Whether the delay after typing a wrong password should be disabled.
481 '';
482 };
483
484 requireWheel = lib.mkOption {
485 default = false;
486 type = lib.types.bool;
487 description = ''
488 Whether to permit root access only to members of group wheel.
489 '';
490 };
491
492 limits = lib.mkOption {
493 default = [ ];
494 type = limitsType;
495 description = ''
496 Attribute set describing resource limits. Defaults to the
497 value of {option}`security.pam.loginLimits`.
498 The meaning of the values is explained in {manpage}`limits.conf(5)`.
499 '';
500 };
501
502 showMotd = lib.mkOption {
503 default = false;
504 type = lib.types.bool;
505 description = "Whether to show the message of the day.";
506 };
507
508 makeHomeDir = lib.mkOption {
509 default = false;
510 type = lib.types.bool;
511 description = ''
512 Whether to try to create home directories for users
513 with `$HOME`s pointing to nonexistent
514 locations on session login.
515 '';
516 };
517
518 updateWtmp = lib.mkOption {
519 default = false;
520 type = lib.types.bool;
521 description = "Whether to update {file}`/var/log/wtmp`.";
522 };
523
524 logFailures = lib.mkOption {
525 default = false;
526 type = lib.types.bool;
527 description = "Whether to log authentication failures in {file}`/var/log/faillog`.";
528 };
529
530 enableAppArmor = lib.mkOption {
531 default = false;
532 type = lib.types.bool;
533 description = ''
534 Enable support for attaching AppArmor profiles at the
535 user/group level, e.g., as part of a role based access
536 control scheme.
537 '';
538 };
539
540 kwallet = {
541 enable = lib.mkOption {
542 default = false;
543 type = lib.types.bool;
544 description = ''
545 If enabled, pam_wallet will attempt to automatically unlock the
546 user's default KDE wallet upon login. If the user has no wallet named
547 "kdewallet", or the login password does not match their wallet
548 password, KDE will prompt separately after login.
549 '';
550 };
551
552 package = lib.mkPackageOption pkgs.kdePackages "kwallet-pam" {
553 pkgsText = "pkgs.kdePackages";
554 };
555
556 forceRun = lib.mkEnableOption null // {
557 description = ''
558 The `force_run` option is used to tell the PAM module for KWallet
559 to forcefully run even if no graphical session (such as a GUI
560 display manager) is detected. This is useful for when you are
561 starting an X Session or a Wayland Session from a TTY. If you
562 intend to log-in from a TTY, it is recommended that you enable
563 this option **and** ensure that `plasma-kwallet-pam.service` is
564 started by `graphical-session.target`.
565 '';
566 };
567 };
568
569 sssdStrictAccess = lib.mkOption {
570 default = false;
571 type = lib.types.bool;
572 description = "enforce sssd access control";
573 };
574
575 enableGnomeKeyring = lib.mkOption {
576 default = false;
577 type = lib.types.bool;
578 description = ''
579 If enabled, pam_gnome_keyring will attempt to automatically unlock the
580 user's default Gnome keyring upon login. If the user login password does
581 not match their keyring password, Gnome Keyring will prompt separately
582 after login.
583 '';
584 };
585
586 failDelay = {
587 enable = lib.mkOption {
588 type = lib.types.bool;
589 default = false;
590 description = ''
591 If enabled, this will replace the `FAIL_DELAY` setting from `login.defs`.
592 Change the delay on failure per-application.
593 '';
594 };
595
596 delay = lib.mkOption {
597 default = 3000000;
598 type = lib.types.int;
599 example = 1000000;
600 description = "The delay time (in microseconds) on failure.";
601 };
602 };
603
604 gnupg = {
605 enable = lib.mkOption {
606 type = lib.types.bool;
607 default = false;
608 description = ''
609 If enabled, pam_gnupg will attempt to automatically unlock the
610 user's GPG keys with the login password via
611 {command}`gpg-agent`. The keygrips of all keys to be
612 unlocked should be written to {file}`~/.pam-gnupg`,
613 and can be queried with {command}`gpg -K --with-keygrip`.
614 Presetting passphrases must be enabled by adding
615 `allow-preset-passphrase` in
616 {file}`~/.gnupg/gpg-agent.conf`.
617 '';
618 };
619
620 noAutostart = lib.mkOption {
621 type = lib.types.bool;
622 default = false;
623 description = ''
624 Don't start {command}`gpg-agent` if it is not running.
625 Useful in conjunction with starting {command}`gpg-agent` as
626 a systemd user service.
627 '';
628 };
629
630 storeOnly = lib.mkOption {
631 type = lib.types.bool;
632 default = false;
633 description = ''
634 Don't send the password immediately after login, but store for PAM
635 `session`.
636 '';
637 };
638 };
639
640 zfs = lib.mkOption {
641 default = config.security.pam.zfs.enable;
642 defaultText = lib.literalExpression "config.security.pam.zfs.enable";
643 type = lib.types.bool;
644 description = ''
645 Enable unlocking and mounting of encrypted ZFS home dataset at login.
646 '';
647 };
648
649 text = lib.mkOption {
650 type = lib.types.nullOr lib.types.lines;
651 description = "Contents of the PAM service file.";
652 };
653
654 };
655
656 # The resulting /etc/pam.d/* file contents are verified in
657 # nixos/tests/pam/pam-file-contents.nix. Please update tests there when
658 # changing the derivation.
659 config = {
660 name = lib.mkDefault name;
661 setLoginUid = lib.mkDefault cfg.startSession;
662 limits = lib.mkDefault config.security.pam.loginLimits;
663
664 text =
665 let
666 ensureUniqueOrder =
667 type: rules:
668 let
669 checkPair =
670 a: b:
671 assert lib.assertMsg (a.order != b.order)
672 "security.pam.services.${name}.rules.${type}: rules '${a.name}' and '${b.name}' cannot have the same order value (${toString a.order})";
673 b;
674 checked = lib.zipListsWith checkPair rules (lib.drop 1 rules);
675 in
676 lib.take 1 rules ++ checked;
677 # Formats a string for use in `module-arguments`. See `man pam.conf`.
678 formatModuleArgument =
679 token: if lib.hasInfix " " token then "[${lib.replaceStrings [ "]" ] [ "\\]" ] token}]" else token;
680 formatRules =
681 type:
682 lib.pipe cfg.rules.${type} [
683 lib.attrValues
684 (lib.filter (rule: rule.enable))
685 (lib.sort (a: b: a.order < b.order))
686 (ensureUniqueOrder type)
687 (map (
688 rule:
689 lib.concatStringsSep " " (
690 [
691 type
692 rule.control
693 rule.modulePath
694 ]
695 ++ map formatModuleArgument rule.args
696 ++ [ "# ${rule.name} (order ${toString rule.order})" ]
697 )
698 ))
699 (lib.concatStringsSep "\n")
700 ];
701 in
702 lib.mkDefault ''
703 # Account management.
704 ${formatRules "account"}
705
706 # Authentication management.
707 ${formatRules "auth"}
708
709 # Password management.
710 ${formatRules "password"}
711
712 # Session management.
713 ${formatRules "session"}
714 '';
715
716 # !!! TODO: move the LDAP stuff to the LDAP module, and the
717 # Samba stuff to the Samba module. This requires that the PAM
718 # module provides the right hooks.
719 rules =
720 let
721 autoOrderRules = lib.flip lib.pipe [
722 (lib.imap1 (index: rule: rule // { order = lib.mkDefault (10000 + index * 100); }))
723 (map (rule: lib.nameValuePair rule.name (removeAttrs rule [ "name" ])))
724 lib.listToAttrs
725 ];
726 in
727 {
728 account = autoOrderRules [
729 {
730 name = "ldap";
731 enable = use_ldap;
732 control = "sufficient";
733 modulePath = "${pam_ldap}/lib/security/pam_ldap.so";
734 }
735 {
736 name = "mysql";
737 enable = cfg.mysqlAuth;
738 control = "sufficient";
739 modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so";
740 settings = {
741 config_file = "/etc/security/pam_mysql.conf";
742 };
743 }
744 {
745 name = "kanidm";
746 enable = config.services.kanidm.enablePam;
747 control = "sufficient";
748 modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so";
749 settings = {
750 ignore_unknown_user = true;
751 };
752 }
753 {
754 name = "sss";
755 enable = config.services.sssd.enable;
756 control =
757 if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient";
758 modulePath = "${pkgs.sssd}/lib/security/pam_sss.so";
759 }
760 {
761 name = "krb5";
762 enable = config.security.pam.krb5.enable;
763 control = "sufficient";
764 modulePath = "${pam_krb5}/lib/security/pam_krb5.so";
765 }
766 {
767 name = "oslogin_login";
768 enable = cfg.googleOsLoginAccountVerification;
769 control = "[success=ok ignore=ignore default=die]";
770 modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so";
771 }
772 {
773 name = "oslogin_admin";
774 enable = cfg.googleOsLoginAccountVerification;
775 control = "[success=ok default=ignore]";
776 modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so";
777 }
778 {
779 name = "systemd_home";
780 enable = config.services.homed.enable;
781 control = "sufficient";
782 modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so";
783 }
784 # The required pam_unix.so module has to come after all the sufficient modules
785 # because otherwise, the account lookup will fail if the user does not exist
786 # locally, for example with MySQL- or LDAP-auth.
787 {
788 name = "unix";
789 control = "required";
790 modulePath = "${package}/lib/security/pam_unix.so";
791 }
792 ];
793
794 auth = autoOrderRules (
795 [
796 {
797 name = "oslogin_login";
798 enable = cfg.googleOsLoginAuthentication;
799 control = "[success=done perm_denied=die default=ignore]";
800 modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so";
801 }
802 {
803 name = "rootok";
804 enable = cfg.rootOK;
805 control = "sufficient";
806 modulePath = "${package}/lib/security/pam_rootok.so";
807 }
808 {
809 name = "wheel";
810 enable = cfg.requireWheel;
811 control = "required";
812 modulePath = "${package}/lib/security/pam_wheel.so";
813 settings = {
814 use_uid = true;
815 };
816 }
817 {
818 name = "faillock";
819 enable = cfg.logFailures;
820 control = "required";
821 modulePath = "${package}/lib/security/pam_faillock.so";
822 }
823 {
824 name = "mysql";
825 enable = cfg.mysqlAuth;
826 control = "sufficient";
827 modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so";
828 settings = {
829 config_file = "/etc/security/pam_mysql.conf";
830 };
831 }
832 {
833 name = "ssh_agent_auth";
834 enable = config.security.pam.sshAgentAuth.enable && cfg.sshAgentAuth;
835 control = "sufficient";
836 modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so";
837 settings = {
838 file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles;
839 };
840 }
841 (
842 let
843 inherit (config.security.pam) rssh;
844 in
845 {
846 name = "rssh";
847 enable = rssh.enable && cfg.rssh;
848 control = "sufficient";
849 modulePath = "${pkgs.pam_rssh}/lib/libpam_rssh.so";
850 inherit (rssh) settings;
851 }
852 )
853 (
854 let
855 p11 = config.security.pam.p11;
856 in
857 {
858 name = "p11";
859 enable = cfg.p11Auth;
860 control = p11.control;
861 modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so";
862 args = [
863 "${pkgs.opensc}/lib/opensc-pkcs11.so"
864 ];
865 }
866 )
867 (
868 let
869 u2f = config.security.pam.u2f;
870 in
871 {
872 name = "u2f";
873 enable = cfg.u2fAuth;
874 control = u2f.control;
875 modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so";
876 inherit (u2f) settings;
877 }
878 )
879 (
880 let
881 ussh = config.security.pam.ussh;
882 in
883 {
884 name = "ussh";
885 enable = config.security.pam.ussh.enable && cfg.usshAuth;
886 control = ussh.control;
887 modulePath = "${pkgs.pam_ussh}/lib/security/pam_ussh.so";
888 settings = {
889 ca_file = ussh.caFile;
890 authorized_principals = ussh.authorizedPrincipals;
891 authorized_principals_file = ussh.authorizedPrincipalsFile;
892 inherit (ussh) group;
893 };
894 }
895 )
896 (
897 let
898 oath = config.security.pam.oath;
899 in
900 {
901 name = "oath";
902 enable = cfg.oathAuth;
903 control = "requisite";
904 modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so";
905 settings = {
906 inherit (oath) window digits;
907 usersfile = oath.usersFile;
908 };
909 }
910 )
911 (
912 let
913 yubi = config.security.pam.yubico;
914 in
915 {
916 name = "yubico";
917 enable = cfg.yubicoAuth;
918 control = yubi.control;
919 modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so";
920 settings = {
921 inherit (yubi) mode debug;
922 chalresp_path = yubi.challengeResponsePath;
923 id = lib.mkIf (yubi.mode == "client") yubi.id;
924 };
925 }
926 )
927 (
928 let
929 dp9ik = config.security.pam.dp9ik;
930 in
931 {
932 name = "p9";
933 enable = dp9ik.enable;
934 control = dp9ik.control;
935 modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so";
936 args = [
937 dp9ik.authserver
938 ];
939 }
940 )
941 {
942 name = "fprintd";
943 enable = cfg.fprintAuth;
944 control = "sufficient";
945 modulePath = "${config.services.fprintd.package}/lib/security/pam_fprintd.so";
946 }
947 ]
948 ++
949 # Modules in this block require having the password set in PAM_AUTHTOK.
950 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
951 # after it succeeds. Certain modules need to run after pam_unix
952 # prompts the user for password so we run it once with 'optional' at an
953 # earlier point and it will run again with 'sufficient' further down.
954 # We use try_first_pass the second time to avoid prompting password twice.
955 #
956 # The same principle applies to systemd-homed
957 (lib.optionals
958 (
959 (cfg.unixAuth || config.services.homed.enable)
960 && (
961 config.security.pam.enableEcryptfs
962 || config.security.pam.enableFscrypt
963 || cfg.pamMount
964 || cfg.kwallet.enable
965 || cfg.enableGnomeKeyring
966 || config.services.intune.enable
967 || cfg.googleAuthenticator.enable
968 || cfg.gnupg.enable
969 || cfg.failDelay.enable
970 || cfg.duoSecurity.enable
971 || cfg.zfs
972 )
973 )
974 [
975 {
976 name = "systemd_home-early";
977 enable = config.services.homed.enable;
978 control = "optional";
979 modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so";
980 }
981 {
982 name = "unix-early";
983 enable = cfg.unixAuth;
984 control = "optional";
985 modulePath = "${package}/lib/security/pam_unix.so";
986 settings = {
987 nullok = cfg.allowNullPassword;
988 inherit (cfg) nodelay;
989 likeauth = true;
990 };
991 }
992 {
993 name = "ecryptfs";
994 enable = config.security.pam.enableEcryptfs;
995 control = "optional";
996 modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so";
997 settings = {
998 unwrap = true;
999 };
1000 }
1001 {
1002 name = "fscrypt";
1003 enable = config.security.pam.enableFscrypt;
1004 control = "optional";
1005 modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so";
1006 }
1007 {
1008 name = "zfs_key";
1009 enable = cfg.zfs;
1010 control = "optional";
1011 modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so";
1012 settings = {
1013 inherit (config.security.pam.zfs) homes;
1014 };
1015 }
1016 {
1017 name = "mount";
1018 enable = cfg.pamMount;
1019 control = "optional";
1020 modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so";
1021 settings = {
1022 disable_interactive = true;
1023 };
1024 }
1025 {
1026 name = "kwallet";
1027 enable = cfg.kwallet.enable;
1028 control = "optional";
1029 modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so";
1030 }
1031 {
1032 name = "gnome_keyring";
1033 enable = cfg.enableGnomeKeyring;
1034 control = "optional";
1035 modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so";
1036 }
1037 {
1038 name = "intune";
1039 enable = config.services.intune.enable;
1040 control = "optional";
1041 modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so";
1042 }
1043 {
1044 name = "gnupg";
1045 enable = cfg.gnupg.enable;
1046 control = "optional";
1047 modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so";
1048 settings = {
1049 store-only = cfg.gnupg.storeOnly;
1050 };
1051 }
1052 {
1053 name = "faildelay";
1054 enable = cfg.failDelay.enable;
1055 control = "optional";
1056 modulePath = "${package}/lib/security/pam_faildelay.so";
1057 settings = {
1058 inherit (cfg.failDelay) delay;
1059 };
1060 }
1061 {
1062 name = "google_authenticator";
1063 enable = cfg.googleAuthenticator.enable;
1064 control = "required";
1065 modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so";
1066 settings = {
1067 no_increment_hotp = true;
1068 forward_pass = cfg.googleAuthenticator.forwardPass;
1069 nullok = cfg.googleAuthenticator.allowNullOTP;
1070 };
1071 }
1072 {
1073 name = "duo";
1074 enable = cfg.duoSecurity.enable;
1075 control = "required";
1076 modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so";
1077 }
1078 ]
1079 )
1080 ++ [
1081 {
1082 name = "systemd_home";
1083 enable = config.services.homed.enable;
1084 control = "sufficient";
1085 modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so";
1086 }
1087 {
1088 name = "unix";
1089 enable = cfg.unixAuth;
1090 control = "sufficient";
1091 modulePath = "${package}/lib/security/pam_unix.so";
1092 settings = {
1093 nullok = cfg.allowNullPassword;
1094 inherit (cfg) nodelay;
1095 likeauth = true;
1096 try_first_pass = true;
1097 };
1098 }
1099 {
1100 name = "otpw";
1101 enable = cfg.otpwAuth;
1102 control = "sufficient";
1103 modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so";
1104 }
1105 {
1106 name = "ldap";
1107 enable = use_ldap;
1108 control = "sufficient";
1109 modulePath = "${pam_ldap}/lib/security/pam_ldap.so";
1110 settings = {
1111 use_first_pass = true;
1112 };
1113 }
1114 {
1115 name = "kanidm";
1116 enable = config.services.kanidm.enablePam;
1117 control = "sufficient";
1118 modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so";
1119 settings = {
1120 ignore_unknown_user = true;
1121 use_first_pass = true;
1122 };
1123 }
1124 {
1125 name = "sss";
1126 enable = config.services.sssd.enable;
1127 control = "sufficient";
1128 modulePath = "${pkgs.sssd}/lib/security/pam_sss.so";
1129 settings = {
1130 use_first_pass = true;
1131 };
1132 }
1133 {
1134 name = "krb5";
1135 enable = config.security.pam.krb5.enable;
1136 control = "[default=ignore success=1 service_err=reset]";
1137 modulePath = "${pam_krb5}/lib/security/pam_krb5.so";
1138 settings = {
1139 use_first_pass = true;
1140 };
1141 }
1142 {
1143 name = "ccreds-validate";
1144 enable = config.security.pam.krb5.enable;
1145 control = "[default=die success=done]";
1146 modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so";
1147 settings = {
1148 action = "validate";
1149 use_first_pass = true;
1150 };
1151 }
1152 {
1153 name = "ccreds-store";
1154 enable = config.security.pam.krb5.enable;
1155 control = "sufficient";
1156 modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so";
1157 settings = {
1158 action = "store";
1159 use_first_pass = true;
1160 };
1161 }
1162 {
1163 name = "deny";
1164 control = "required";
1165 modulePath = "${package}/lib/security/pam_deny.so";
1166 }
1167 ]
1168 );
1169
1170 password = autoOrderRules [
1171 {
1172 name = "systemd_home";
1173 enable = config.services.homed.enable;
1174 control = "sufficient";
1175 modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so";
1176 }
1177 {
1178 name = "unix";
1179 control = "sufficient";
1180 modulePath = "${package}/lib/security/pam_unix.so";
1181 settings = {
1182 nullok = true;
1183 yescrypt = true;
1184 };
1185 }
1186 {
1187 name = "ecryptfs";
1188 enable = config.security.pam.enableEcryptfs;
1189 control = "optional";
1190 modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so";
1191 }
1192 {
1193 name = "fscrypt";
1194 enable = config.security.pam.enableFscrypt;
1195 control = "optional";
1196 modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so";
1197 }
1198 {
1199 name = "zfs_key";
1200 enable = cfg.zfs;
1201 control = "optional";
1202 modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so";
1203 settings = {
1204 inherit (config.security.pam.zfs) homes;
1205 };
1206 }
1207 {
1208 name = "mount";
1209 enable = cfg.pamMount;
1210 control = "optional";
1211 modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so";
1212 }
1213 {
1214 name = "ldap";
1215 enable = use_ldap;
1216 control = "sufficient";
1217 modulePath = "${pam_ldap}/lib/security/pam_ldap.so";
1218 }
1219 {
1220 name = "mysql";
1221 enable = cfg.mysqlAuth;
1222 control = "sufficient";
1223 modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so";
1224 settings = {
1225 config_file = "/etc/security/pam_mysql.conf";
1226 };
1227 }
1228 {
1229 name = "kanidm";
1230 enable = config.services.kanidm.enablePam;
1231 control = "sufficient";
1232 modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so";
1233 }
1234 {
1235 name = "sss";
1236 enable = config.services.sssd.enable;
1237 control = "sufficient";
1238 modulePath = "${pkgs.sssd}/lib/security/pam_sss.so";
1239 }
1240 {
1241 name = "krb5";
1242 enable = config.security.pam.krb5.enable;
1243 control = "sufficient";
1244 modulePath = "${pam_krb5}/lib/security/pam_krb5.so";
1245 settings = {
1246 use_first_pass = true;
1247 };
1248 }
1249 {
1250 name = "gnome_keyring";
1251 enable = cfg.enableGnomeKeyring;
1252 control = "optional";
1253 modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so";
1254 settings = {
1255 use_authtok = true;
1256 };
1257 }
1258 ];
1259
1260 session = autoOrderRules [
1261 {
1262 name = "env";
1263 enable = cfg.setEnvironment;
1264 control = "required";
1265 modulePath = "${package}/lib/security/pam_env.so";
1266 settings = {
1267 conffile = "/etc/pam/environment";
1268 readenv = 0;
1269 };
1270 }
1271 {
1272 name = "unix";
1273 control = "required";
1274 modulePath = "${package}/lib/security/pam_unix.so";
1275 }
1276 {
1277 name = "loginuid";
1278 enable = cfg.setLoginUid;
1279 control = if config.boot.isContainer then "optional" else "required";
1280 modulePath = "${package}/lib/security/pam_loginuid.so";
1281 }
1282 {
1283 name = "tty_audit";
1284 enable = cfg.ttyAudit.enable;
1285 control = "required";
1286 modulePath = "${package}/lib/security/pam_tty_audit.so";
1287 settings = {
1288 open_only = cfg.ttyAudit.openOnly;
1289 enable = cfg.ttyAudit.enablePattern;
1290 disable = cfg.ttyAudit.disablePattern;
1291 };
1292 }
1293 {
1294 name = "systemd_home";
1295 enable = config.services.homed.enable;
1296 control = "required";
1297 modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so";
1298 }
1299 {
1300 name = "mkhomedir";
1301 enable = cfg.makeHomeDir;
1302 control = "required";
1303 modulePath = "${package}/lib/security/pam_mkhomedir.so";
1304 settings = {
1305 silent = true;
1306 skel = config.security.pam.makeHomeDir.skelDirectory;
1307 inherit (config.security.pam.makeHomeDir) umask;
1308 };
1309 }
1310 {
1311 name = "lastlog";
1312 enable = cfg.updateWtmp;
1313 control = "required";
1314 modulePath = "${pkgs.util-linux.lastlog}/lib/security/pam_lastlog2.so";
1315 settings = {
1316 silent = true;
1317 };
1318 }
1319 {
1320 name = "ecryptfs";
1321 enable = config.security.pam.enableEcryptfs;
1322 control = "optional";
1323 modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so";
1324 }
1325 # Work around https://github.com/systemd/systemd/issues/8598
1326 # Skips the pam_fscrypt module for systemd-user sessions which do not have a password
1327 # anyways.
1328 # See also https://github.com/google/fscrypt/issues/95
1329 {
1330 name = "fscrypt-skip-systemd";
1331 enable = config.security.pam.enableFscrypt;
1332 control = "[success=1 default=ignore]";
1333 modulePath = "${package}/lib/security/pam_succeed_if.so";
1334 args = [
1335 "service"
1336 "="
1337 "systemd-user"
1338 ];
1339 }
1340 {
1341 name = "fscrypt";
1342 enable = config.security.pam.enableFscrypt;
1343 control = "optional";
1344 modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so";
1345 }
1346 {
1347 name = "zfs_key-skip-systemd";
1348 enable = cfg.zfs;
1349 control = "[success=1 default=ignore]";
1350 modulePath = "${package}/lib/security/pam_succeed_if.so";
1351 args = [
1352 "service"
1353 "="
1354 "systemd-user"
1355 ];
1356 }
1357 {
1358 name = "zfs_key";
1359 enable = cfg.zfs;
1360 control = "optional";
1361 modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so";
1362 settings = {
1363 inherit (config.security.pam.zfs) homes;
1364 nounmount = config.security.pam.zfs.noUnmount;
1365 };
1366 }
1367 {
1368 name = "mount";
1369 enable = cfg.pamMount;
1370 control = "optional";
1371 modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so";
1372 settings = {
1373 disable_interactive = true;
1374 };
1375 }
1376 {
1377 name = "ldap";
1378 enable = use_ldap;
1379 control = "optional";
1380 modulePath = "${pam_ldap}/lib/security/pam_ldap.so";
1381 }
1382 {
1383 name = "mysql";
1384 enable = cfg.mysqlAuth;
1385 control = "optional";
1386 modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so";
1387 settings = {
1388 config_file = "/etc/security/pam_mysql.conf";
1389 };
1390 }
1391 {
1392 name = "kanidm";
1393 enable = config.services.kanidm.enablePam;
1394 control = "optional";
1395 modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so";
1396 }
1397 {
1398 name = "sss";
1399 enable = config.services.sssd.enable;
1400 control = "optional";
1401 modulePath = "${pkgs.sssd}/lib/security/pam_sss.so";
1402 }
1403 {
1404 name = "krb5";
1405 enable = config.security.pam.krb5.enable;
1406 control = "optional";
1407 modulePath = "${pam_krb5}/lib/security/pam_krb5.so";
1408 }
1409 {
1410 name = "otpw";
1411 enable = cfg.otpwAuth;
1412 control = "optional";
1413 modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so";
1414 }
1415 {
1416 name = "systemd";
1417 enable = cfg.startSession;
1418 control = "optional";
1419 modulePath = "${config.systemd.package}/lib/security/pam_systemd.so";
1420 }
1421 {
1422 name = "xauth";
1423 enable = cfg.forwardXAuth;
1424 control = "optional";
1425 modulePath = "${package}/lib/security/pam_xauth.so";
1426 settings = {
1427 xauthpath = "${pkgs.xorg.xauth}/bin/xauth";
1428 systemuser = 99;
1429 };
1430 }
1431 {
1432 name = "limits";
1433 enable = cfg.limits != [ ];
1434 control = "required";
1435 modulePath = "${package}/lib/security/pam_limits.so";
1436 settings = {
1437 conf = "${makeLimitsConf cfg.limits}";
1438 };
1439 }
1440 {
1441 name = "motd";
1442 enable = cfg.showMotd && (config.users.motd != "" || config.users.motdFile != null);
1443 control = "optional";
1444 modulePath = "${package}/lib/security/pam_motd.so";
1445 settings = {
1446 inherit motd;
1447 };
1448 }
1449 {
1450 name = "apparmor";
1451 enable = cfg.enableAppArmor && config.security.apparmor.enable;
1452 control = "optional";
1453 modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so";
1454 settings = {
1455 order = "user,group,default";
1456 debug = true;
1457 };
1458 }
1459 {
1460 name = "kwallet";
1461 enable = cfg.kwallet.enable;
1462 control = "optional";
1463 modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so";
1464 settings = lib.mkIf cfg.kwallet.forceRun { force_run = true; };
1465 }
1466 {
1467 name = "gnome_keyring";
1468 enable = cfg.enableGnomeKeyring;
1469 control = "optional";
1470 modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so";
1471 settings = {
1472 auto_start = true;
1473 };
1474 }
1475 {
1476 name = "gnupg";
1477 enable = cfg.gnupg.enable;
1478 control = "optional";
1479 modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so";
1480 settings = {
1481 no-autostart = cfg.gnupg.noAutostart;
1482 };
1483 }
1484 {
1485 name = "intune";
1486 enable = config.services.intune.enable;
1487 control = "optional";
1488 modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so";
1489 }
1490 ];
1491 };
1492 };
1493
1494 };
1495
1496 inherit (pkgs) pam_krb5 pam_ccreds;
1497
1498 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
1499 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
1500
1501 # Create a limits.conf(5) file.
1502 makeLimitsConf =
1503 limits:
1504 pkgs.writeText "limits.conf" (
1505 lib.concatMapStrings (
1506 {
1507 domain,
1508 type,
1509 item,
1510 value,
1511 }:
1512 "${domain} ${type} ${item} ${toString value}\n"
1513 ) limits
1514 );
1515
1516 limitsType =
1517 with lib.types;
1518 listOf (
1519 submodule (
1520 { ... }:
1521 {
1522 options = {
1523 domain = lib.mkOption {
1524 description = "Username, groupname, or wildcard this limit applies to";
1525 example = "@wheel";
1526 type = str;
1527 };
1528
1529 type = lib.mkOption {
1530 description = "Type of this limit";
1531 type = enum [
1532 "-"
1533 "hard"
1534 "soft"
1535 ];
1536 default = "-";
1537 };
1538
1539 item = lib.mkOption {
1540 description = "Item this limit applies to";
1541 type = enum [
1542 "core"
1543 "data"
1544 "fsize"
1545 "memlock"
1546 "nofile"
1547 "rss"
1548 "stack"
1549 "cpu"
1550 "nproc"
1551 "as"
1552 "maxlogins"
1553 "maxsyslogins"
1554 "priority"
1555 "locks"
1556 "sigpending"
1557 "msgqueue"
1558 "nice"
1559 "rtprio"
1560 ];
1561 };
1562
1563 value = lib.mkOption {
1564 description = "Value of this limit";
1565 type = oneOf [
1566 str
1567 int
1568 ];
1569 };
1570 };
1571 }
1572 )
1573 );
1574
1575 motd =
1576 if config.users.motdFile == null then
1577 pkgs.writeText "motd" config.users.motd
1578 else
1579 config.users.motdFile;
1580
1581 makePAMService = name: service: {
1582 name = "pam.d/${name}";
1583 value.source = pkgs.writeText "${name}.pam" service.text;
1584 };
1585
1586 optionalSudoConfigForSSHAgentAuth =
1587 lib.optionalString (config.security.pam.sshAgentAuth.enable || config.security.pam.rssh.enable)
1588 ''
1589 # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so and libpam_rssh.so can do their magic.
1590 Defaults env_keep+=SSH_AUTH_SOCK
1591 '';
1592
1593 enabledServices = lib.filterAttrs (name: svc: svc.enable) config.security.pam.services;
1594
1595in
1596
1597{
1598
1599 meta.maintainers = [ lib.maintainers.majiir ];
1600
1601 imports = [
1602 (lib.mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
1603 (lib.mkRenamedOptionModule
1604 [ "security" "pam" "enableSSHAgentAuth" ]
1605 [ "security" "pam" "sshAgentAuth" "enable" ]
1606 )
1607 (lib.mkRenamedOptionModule
1608 [ "security" "pam" "u2f" "authFile" ]
1609 [ "security" "pam" "u2f" "settings" "authfile" ]
1610 )
1611 (lib.mkRenamedOptionModule
1612 [ "security" "pam" "u2f" "appId" ]
1613 [ "security" "pam" "u2f" "settings" "appid" ]
1614 )
1615 (lib.mkRenamedOptionModule
1616 [ "security" "pam" "u2f" "origin" ]
1617 [ "security" "pam" "u2f" "settings" "origin" ]
1618 )
1619 (lib.mkRenamedOptionModule
1620 [ "security" "pam" "u2f" "debug" ]
1621 [ "security" "pam" "u2f" "settings" "debug" ]
1622 )
1623 (lib.mkRenamedOptionModule
1624 [ "security" "pam" "u2f" "interactive" ]
1625 [ "security" "pam" "u2f" "settings" "interactive" ]
1626 )
1627 (lib.mkRenamedOptionModule
1628 [ "security" "pam" "u2f" "cue" ]
1629 [ "security" "pam" "u2f" "settings" "cue" ]
1630 )
1631 ];
1632
1633 ###### interface
1634
1635 options = {
1636
1637 security.pam.package = lib.mkPackageOption pkgs "pam" { };
1638
1639 security.pam.loginLimits = lib.mkOption {
1640 default = [ ];
1641 type = limitsType;
1642 example = [
1643 {
1644 domain = "ftp";
1645 type = "hard";
1646 item = "nproc";
1647 value = "0";
1648 }
1649 {
1650 domain = "@student";
1651 type = "-";
1652 item = "maxlogins";
1653 value = "4";
1654 }
1655 ];
1656
1657 description = ''
1658 Define resource limits that should apply to users or groups.
1659 Each item in the list should be an attribute set with a
1660 {var}`domain`, {var}`type`,
1661 {var}`item`, and {var}`value`
1662 attribute. The syntax and semantics of these attributes
1663 must be that described in {manpage}`limits.conf(5)`.
1664
1665 Note that these limits do not apply to systemd services,
1666 whose limits can be changed via {option}`systemd.settings.Manager`
1667 instead.
1668 '';
1669 };
1670
1671 security.pam.services = lib.mkOption {
1672 default = { };
1673 type = with lib.types; attrsOf (submodule pamOpts);
1674 description = ''
1675 This option defines the PAM services. A service typically
1676 corresponds to a program that uses PAM,
1677 e.g. {command}`login` or {command}`passwd`.
1678 Each attribute of this set defines a PAM service, with the attribute name
1679 defining the name of the service.
1680 '';
1681 };
1682
1683 security.pam.makeHomeDir.skelDirectory = lib.mkOption {
1684 type = lib.types.str;
1685 default = "/var/empty";
1686 example = "/etc/skel";
1687 description = ''
1688 Path to skeleton directory whose contents are copied to home
1689 directories newly created by `pam_mkhomedir`.
1690 '';
1691 };
1692
1693 security.pam.makeHomeDir.umask = lib.mkOption {
1694 type = lib.types.str;
1695 default = "0077";
1696 example = "0022";
1697 description = ''
1698 The user file mode creation mask to use on home directories
1699 newly created by `pam_mkhomedir`.
1700 '';
1701 };
1702
1703 security.pam.sshAgentAuth = {
1704 enable = lib.mkEnableOption ''
1705 authenticating using a signature performed by the ssh-agent.
1706 This allows using SSH keys exclusively, instead of passwords, for instance on remote machines
1707 '';
1708
1709 authorizedKeysFiles = lib.mkOption {
1710 type = with lib.types; listOf str;
1711 description = ''
1712 A list of paths to files in OpenSSH's `authorized_keys` format, containing
1713 the keys that will be trusted by the `pam_ssh_agent_auth` module.
1714
1715 The following patterns are expanded when interpreting the path:
1716 - `%f` and `%H` respectively expand to the fully-qualified and short hostname ;
1717 - `%u` expands to the username ;
1718 - `~` or `%h` expands to the user's home directory.
1719
1720 ::: {.note}
1721 Specifying user-writeable files here result in an insecure configuration: a malicious process
1722 can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
1723
1724 See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611)
1725 :::
1726 '';
1727 default = [ "/etc/ssh/authorized_keys.d/%u" ];
1728 };
1729 };
1730
1731 security.pam.rssh = {
1732 enable = lib.mkEnableOption "authenticating using a signature performed by the ssh-agent";
1733
1734 settings = lib.mkOption {
1735 type = lib.types.submodule {
1736 freeformType = moduleSettingsType;
1737 options = {
1738 auth_key_file = lib.mkOption {
1739 type = with lib.types; nullOr nonEmptyStr;
1740 description = ''
1741 Path to file with trusted public keys in OpenSSH's `authorized_keys` format. The following
1742 variables are expanded to the respective PAM items:
1743
1744 - `service`: `PAM_SERVICE`, the service name,
1745 - `user`: `PAM_USER`, the username of the entity under whose identity service will be given,
1746 - `tty`: `PAM_TTY`, the terminal name,
1747 - `rhost`: `PAM_RHOST`, the requesting hostname, and
1748 - `ruser`: `PAM_RUSER`, the requesting entity.
1749
1750 These PAM items are explained in {manpage}`pam_get_item(3)`.
1751
1752 Variables may be specified as `$var`, `''${var}` or `''${var:defaultValue}`.
1753
1754 ::: {.note}
1755 Specifying user-writeable files here results in an insecure configuration: a malicious process
1756 can then edit such an `authorized_keys` file and bypass the ssh-agent-based authentication.
1757
1758 This option is ignored if {option}`security.pam.rssh.settings.authorized_keys_command` is set.
1759
1760 If both this option and {option}`security.pam.rssh.settings.authorized_keys_command` are unset,
1761 the keys will be read from `''${HOME}/.ssh/authorized_keys`, which should be considered
1762 insecure.
1763 '';
1764 default = "/etc/ssh/authorized_keys.d/$ruser";
1765 };
1766 };
1767 };
1768
1769 default = { };
1770 description = ''
1771 Options to pass to the pam_rssh module. Refer to
1772 <https://github.com/z4yx/pam_rssh/blob/main/README.md#optional-arguments>
1773 for supported values.
1774
1775 ${moduleSettingsDescription}
1776 '';
1777 };
1778 };
1779
1780 security.pam.enableOTPW = lib.mkEnableOption "the OTPW (one-time password) PAM module";
1781
1782 security.pam.dp9ik = {
1783 enable = lib.mkEnableOption ''
1784 the dp9ik pam module provided by tlsclient.
1785
1786 If set, users can be authenticated against the 9front
1787 authentication server given in {option}`security.pam.dp9ik.authserver`
1788 '';
1789 control = lib.mkOption {
1790 default = "sufficient";
1791 type = lib.types.str;
1792 description = ''
1793 This option sets the pam "control" used for this module.
1794 '';
1795 };
1796 authserver = lib.mkOption {
1797 default = null;
1798 type = with lib.types; nullOr str;
1799 description = ''
1800 This controls the hostname for the 9front authentication server
1801 that users will be authenticated against.
1802 '';
1803 };
1804 };
1805
1806 security.pam.krb5 = {
1807 enable = lib.mkOption {
1808 default = config.security.krb5.enable;
1809 defaultText = lib.literalExpression "config.security.krb5.enable";
1810 type = lib.types.bool;
1811 description = ''
1812 Enables Kerberos PAM modules (`pam-krb5`,
1813 `pam-ccreds`).
1814
1815 If set, users can authenticate with their Kerberos password.
1816 This requires a valid Kerberos configuration
1817 (`security.krb5.enable` should be set to `true`).
1818
1819 Note that the Kerberos PAM modules are not necessary when using SSS
1820 to handle Kerberos authentication.
1821 '';
1822 };
1823 };
1824
1825 security.pam.p11 = {
1826 enable = lib.mkOption {
1827 default = false;
1828 type = lib.types.bool;
1829 description = ''
1830 Enables P11 PAM (`pam_p11`) module.
1831
1832 If set, users can log in with SSH keys and PKCS#11 tokens.
1833
1834 More information can be found [here](https://github.com/OpenSC/pam_p11).
1835 '';
1836 };
1837
1838 control = lib.mkOption {
1839 default = "sufficient";
1840 type = lib.types.enum [
1841 "required"
1842 "requisite"
1843 "sufficient"
1844 "optional"
1845 ];
1846 description = ''
1847 This option sets pam "control".
1848 If you want to have multi factor authentication, use "required".
1849 If you want to use the PKCS#11 device instead of the regular password,
1850 use "sufficient".
1851
1852 Read
1853 {manpage}`pam.conf(5)`
1854 for better understanding of this option.
1855 '';
1856 };
1857 };
1858
1859 security.pam.u2f = {
1860 enable = lib.mkOption {
1861 default = false;
1862 type = lib.types.bool;
1863 description = ''
1864 Enables U2F PAM (`pam-u2f`) module.
1865
1866 If set, users listed in
1867 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1868 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1869 not set) are able to log in with the associated U2F key. The path can
1870 be changed using {option}`security.pam.u2f.authFile` option.
1871
1872 File format is:
1873 ```
1874 <username1>:<KeyHandle1>,<UserKey1>,<CoseType1>,<Options1>:<KeyHandle2>,<UserKey2>,<CoseType2>,<Options2>:...
1875 <username2>:<KeyHandle1>,<UserKey1>,<CoseType1>,<Options1>:<KeyHandle2>,<UserKey2>,<CoseType2>,<Options2>:...
1876 ```
1877 This file can be generated using {command}`pamu2fcfg` command.
1878
1879 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1880 '';
1881 };
1882
1883 control = lib.mkOption {
1884 default = "sufficient";
1885 type = lib.types.enum [
1886 "required"
1887 "requisite"
1888 "sufficient"
1889 "optional"
1890 ];
1891 description = ''
1892 This option sets pam "control".
1893 If you want to have multi factor authentication, use "required".
1894 If you want to use U2F device instead of regular password, use "sufficient".
1895
1896 Read
1897 {manpage}`pam.conf(5)`
1898 for better understanding of this option.
1899 '';
1900 };
1901
1902 settings = lib.mkOption {
1903 type = lib.types.submodule {
1904 freeformType = moduleSettingsType;
1905
1906 options = {
1907 authfile = lib.mkOption {
1908 default = null;
1909 type = with lib.types; nullOr path;
1910 description = ''
1911 By default `pam-u2f` module reads the keys from
1912 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1913 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1914 not set).
1915
1916 If you want to change auth file locations or centralize database (for
1917 example use {file}`/etc/u2f-mappings`) you can set this
1918 option.
1919
1920 File format is:
1921 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
1922 This file can be generated using {command}`pamu2fcfg` command.
1923
1924 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1925 '';
1926 };
1927
1928 appid = lib.mkOption {
1929 default = null;
1930 type = with lib.types; nullOr str;
1931 description = ''
1932 By default `pam-u2f` module sets the application
1933 ID to `pam://$HOSTNAME`.
1934
1935 When using {command}`pamu2fcfg`, you can specify your
1936 application ID with the `-i` flag.
1937
1938 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1939 '';
1940 };
1941
1942 origin = lib.mkOption {
1943 default = null;
1944 type = with lib.types; nullOr str;
1945 description = ''
1946 By default `pam-u2f` module sets the origin
1947 to `pam://$HOSTNAME`.
1948 Setting origin to an host independent value will allow you to
1949 reuse credentials across machines
1950
1951 When using {command}`pamu2fcfg`, you can specify your
1952 application ID with the `-o` flag.
1953
1954 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1955 '';
1956 };
1957
1958 debug = lib.mkOption {
1959 default = false;
1960 type = lib.types.bool;
1961 description = ''
1962 Debug output to stderr.
1963 '';
1964 };
1965
1966 interactive = lib.mkOption {
1967 default = false;
1968 type = lib.types.bool;
1969 description = ''
1970 Set to prompt a message and wait before testing the presence of a U2F device.
1971 Recommended if your device doesn’t have a tactile trigger.
1972 '';
1973 };
1974
1975 cue = lib.mkOption {
1976 default = false;
1977 type = lib.types.bool;
1978 description = ''
1979 By default `pam-u2f` module does not inform user
1980 that he needs to use the u2f device, it just waits without a prompt.
1981
1982 If you set this option to `true`,
1983 `cue` option is added to `pam-u2f`
1984 module and reminder message will be displayed.
1985 '';
1986 };
1987 };
1988 };
1989 default = { };
1990 example = {
1991 authfile = "/etc/u2f_keys";
1992 authpending_file = "";
1993 userpresence = 0;
1994 pinverification = 1;
1995 };
1996 description = ''
1997 Options to pass to the PAM module.
1998
1999 ${moduleSettingsDescription}
2000 '';
2001 };
2002 };
2003
2004 security.pam.ussh = {
2005 enable = lib.mkOption {
2006 default = false;
2007 type = lib.types.bool;
2008 description = ''
2009 Enables Uber's USSH PAM (`pam-ussh`) module.
2010
2011 This is similar to `pam-ssh-agent`, except that
2012 the presence of a CA-signed SSH key with a valid principal is checked
2013 instead.
2014
2015 Note that this module must both be enabled using this option and on a
2016 per-PAM-service level as well (using `usshAuth`).
2017
2018 More information can be found [here](https://github.com/uber/pam-ussh).
2019 '';
2020 };
2021
2022 caFile = lib.mkOption {
2023 default = null;
2024 type = with lib.types; nullOr path;
2025 description = ''
2026 By default `pam-ussh` reads the trusted user CA keys
2027 from {file}`/etc/ssh/trusted_user_ca`.
2028
2029 This should be set the same as your `TrustedUserCAKeys`
2030 option for sshd.
2031 '';
2032 };
2033
2034 authorizedPrincipals = lib.mkOption {
2035 default = null;
2036 type = with lib.types; nullOr commas;
2037 description = ''
2038 Comma-separated list of authorized principals to permit; if the user
2039 presents a certificate with one of these principals, then they will be
2040 authorized.
2041
2042 Note that `pam-ussh` also requires that the certificate
2043 contain a principal matching the user's username. The principals from
2044 this list are in addition to those principals.
2045
2046 Mutually exclusive with `authorizedPrincipalsFile`.
2047 '';
2048 };
2049
2050 authorizedPrincipalsFile = lib.mkOption {
2051 default = null;
2052 type = with lib.types; nullOr path;
2053 description = ''
2054 Path to a list of principals; if the user presents a certificate with
2055 one of these principals, then they will be authorized.
2056
2057 Note that `pam-ussh` also requires that the certificate
2058 contain a principal matching the user's username. The principals from
2059 this file are in addition to those principals.
2060
2061 Mutually exclusive with `authorizedPrincipals`.
2062 '';
2063 };
2064
2065 group = lib.mkOption {
2066 default = null;
2067 type = with lib.types; nullOr str;
2068 description = ''
2069 If set, then the authenticating user must be a member of this group
2070 to use this module.
2071 '';
2072 };
2073
2074 control = lib.mkOption {
2075 default = "sufficient";
2076 type = lib.types.enum [
2077 "required"
2078 "requisite"
2079 "sufficient"
2080 "optional"
2081 ];
2082 description = ''
2083 This option sets pam "control".
2084 If you want to have multi factor authentication, use "required".
2085 If you want to use the SSH certificate instead of the regular password,
2086 use "sufficient".
2087
2088 Read
2089 {manpage}`pam.conf(5)`
2090 for better understanding of this option.
2091 '';
2092 };
2093 };
2094
2095 security.pam.yubico = {
2096 enable = lib.mkOption {
2097 default = false;
2098 type = lib.types.bool;
2099 description = ''
2100 Enables Yubico PAM (`yubico-pam`) module.
2101
2102 If set, users listed in
2103 {file}`~/.yubico/authorized_yubikeys`
2104 are able to log in with the associated Yubikey tokens.
2105
2106 The file must have only one line:
2107 `username:yubikey_token_id1:yubikey_token_id2`
2108 More information can be found [here](https://developers.yubico.com/yubico-pam/).
2109 '';
2110 };
2111 control = lib.mkOption {
2112 default = "sufficient";
2113 type = lib.types.enum [
2114 "required"
2115 "requisite"
2116 "sufficient"
2117 "optional"
2118 ];
2119 description = ''
2120 This option sets pam "control".
2121 If you want to have multi factor authentication, use "required".
2122 If you want to use Yubikey instead of regular password, use "sufficient".
2123
2124 Read
2125 {manpage}`pam.conf(5)`
2126 for better understanding of this option.
2127 '';
2128 };
2129 id = lib.mkOption {
2130 example = "42";
2131 type = lib.types.str;
2132 description = "client id";
2133 };
2134
2135 debug = lib.mkOption {
2136 default = false;
2137 type = lib.types.bool;
2138 description = ''
2139 Debug output to stderr.
2140 '';
2141 };
2142 mode = lib.mkOption {
2143 default = "client";
2144 type = lib.types.enum [
2145 "client"
2146 "challenge-response"
2147 ];
2148 description = ''
2149 Mode of operation.
2150
2151 Use "client" for online validation with a YubiKey validation service such as
2152 the YubiCloud.
2153
2154 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
2155 Challenge-Response configurations. See the man-page {manpage}`ykpamcfg(1)` for further
2156 details on how to configure offline Challenge-Response validation.
2157
2158 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
2159 '';
2160 };
2161 challengeResponsePath = lib.mkOption {
2162 default = null;
2163 type = lib.types.nullOr lib.types.path;
2164 description = ''
2165 If not null, set the path used by yubico pam module where the challenge expected response is stored.
2166
2167 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
2168 '';
2169 };
2170 };
2171
2172 security.pam.zfs = {
2173 enable = lib.mkOption {
2174 default = false;
2175 type = lib.types.bool;
2176 description = ''
2177 Enable unlocking and mounting of encrypted ZFS home dataset at login.
2178 '';
2179 };
2180
2181 homes = lib.mkOption {
2182 example = "rpool/home";
2183 default = "rpool/home";
2184 type = lib.types.str;
2185 description = ''
2186 Prefix of home datasets. This value will be concatenated with
2187 `"/" + <username>` in order to determine the home dataset to unlock.
2188 '';
2189 };
2190
2191 noUnmount = lib.mkOption {
2192 default = false;
2193 type = lib.types.bool;
2194 description = ''
2195 Do not unmount home dataset on logout.
2196 '';
2197 };
2198 };
2199
2200 security.pam.enableEcryptfs = lib.mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
2201 security.pam.enableFscrypt = lib.mkEnableOption ''
2202 fscrypt, to automatically unlock directories with the user's login password.
2203
2204 This also enables a service at security.pam.services.fscrypt which is used by
2205 fscrypt to verify the user's password when setting up a new protector. If you
2206 use something other than pam_unix to verify user passwords, please remember to
2207 adjust this PAM service
2208 '';
2209
2210 users.motd = lib.mkOption {
2211 default = "";
2212 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
2213 type = lib.types.lines;
2214 description = "Message of the day shown to users when they log in.";
2215 };
2216
2217 users.motdFile = lib.mkOption {
2218 default = null;
2219 example = "/etc/motd";
2220 type = lib.types.nullOr lib.types.path;
2221 description = "A file containing the message of the day shown to users when they log in.";
2222 };
2223 };
2224
2225 ###### implementation
2226
2227 config = {
2228 assertions = [
2229 {
2230 assertion = config.users.motd == "" || config.users.motdFile == null;
2231 message = ''
2232 Only one of users.motd and users.motdFile can be set.
2233 '';
2234 }
2235 {
2236 assertion = config.security.pam.zfs.enable -> config.boot.zfs.enabled;
2237 message = ''
2238 `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled`).
2239 '';
2240 }
2241 {
2242 assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != [ ];
2243 message = ''
2244 `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list.
2245 Did you forget to set `services.openssh.enable` ?
2246 '';
2247 }
2248 {
2249 assertion =
2250 with config.security.pam.rssh;
2251 enable
2252 -> (settings.auth_key_file or null != null || settings.authorized_keys_command or null != null);
2253 message = ''
2254 security.pam.rssh.enable requires either security.pam.rssh.settings.auth_key_file or
2255 security.pam.rssh.settings.authorized_keys_command to be set.
2256 '';
2257 }
2258 ];
2259
2260 warnings =
2261 lib.optional
2262 (
2263 with config.security.pam.sshAgentAuth;
2264 enable && lib.any (s: lib.hasPrefix "%h" s || lib.hasPrefix "~" s) authorizedKeysFiles
2265 )
2266 ''
2267 security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory.
2268
2269 Specifying user-writeable files there result in an insecure configuration:
2270 a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
2271 See https://github.com/NixOS/nixpkgs/issues/31611
2272 ''
2273 ++
2274 lib.optional
2275 (
2276 with config.security.pam.rssh;
2277 enable && settings.auth_key_file or null != null && settings.authorized_keys_command or null != null
2278 )
2279 ''
2280 security.pam.rssh.settings.auth_key_file will be ignored as
2281 security.pam.rssh.settings.authorized_keys_command has been specified.
2282 Explictly set the former to null to silence this warning.
2283 '';
2284
2285 environment.systemPackages =
2286 # Include the PAM modules in the system path mostly for the manpages.
2287 [ package ]
2288 ++ lib.optional config.users.ldap.enable pam_ldap
2289 ++ lib.optional config.services.kanidm.enablePam config.services.kanidm.package
2290 ++ lib.optional config.services.sssd.enable pkgs.sssd
2291 ++ lib.optionals config.security.pam.krb5.enable [
2292 pam_krb5
2293 pam_ccreds
2294 ]
2295 ++ lib.optionals config.security.pam.enableOTPW [ pkgs.otpw ]
2296 ++ lib.optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ]
2297 ++ lib.optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
2298 ++ lib.optionals config.security.pam.enableFscrypt [ pkgs.fscrypt-experimental ]
2299 ++ lib.optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
2300
2301 boot.supportedFilesystems = lib.optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
2302
2303 security.wrappers = {
2304 unix_chkpwd = {
2305 setuid = true;
2306 owner = "root";
2307 group = "root";
2308 source = "${package}/bin/unix_chkpwd";
2309 };
2310 };
2311
2312 environment.etc = lib.mapAttrs' makePAMService enabledServices;
2313
2314 systemd =
2315 lib.optionalAttrs
2316 (lib.any (service: service.updateWtmp) (lib.attrValues config.security.pam.services))
2317 {
2318 tmpfiles.packages = [ pkgs.util-linux.lastlog ]; # /lib/tmpfiles.d/lastlog2-tmpfiles.conf
2319 services.lastlog2-import = {
2320 enable = true;
2321 wantedBy = [ "default.target" ];
2322 after = [
2323 "local-fs.target"
2324 "systemd-tmpfiles-setup.service"
2325 ];
2326 # TODO: ${pkgs.util-linux.lastlog}/lib/systemd/system/lastlog2-import.service
2327 # uses unpatched /usr/bin/mv, needs to be fixed on staging
2328 # in the meantime, use a service drop-in here
2329 serviceConfig.ExecStartPost = [
2330 ""
2331 "${lib.getExe' pkgs.coreutils "mv"} /var/log/lastlog /var/log/lastlog.migrated"
2332 ];
2333 };
2334 packages = [ pkgs.util-linux.lastlog ]; # lib/systemd/system/lastlog2-import.service
2335 };
2336
2337 security.pam.services = {
2338 other.text = ''
2339 auth required pam_warn.so
2340 auth required pam_deny.so
2341 account required pam_warn.so
2342 account required pam_deny.so
2343 password required pam_warn.so
2344 password required pam_deny.so
2345 session required pam_warn.so
2346 session required pam_deny.so
2347 '';
2348
2349 # Most of these should be moved to specific modules.
2350 i3lock.enable = lib.mkDefault config.programs.i3lock.enable;
2351 i3lock-color.enable = lib.mkDefault config.programs.i3lock.enable;
2352 vlock.enable = lib.mkDefault config.console.enable;
2353 xlock.enable = lib.mkDefault config.services.xserver.enable;
2354 xscreensaver.enable = lib.mkDefault config.services.xscreensaver.enable;
2355
2356 runuser = {
2357 rootOK = true;
2358 unixAuth = false;
2359 setEnvironment = false;
2360 };
2361
2362 /*
2363 FIXME: should runuser -l start a systemd session? Currently
2364 it complains "Cannot create session: Already running in a
2365 session".
2366 */
2367 runuser-l = {
2368 rootOK = true;
2369 unixAuth = false;
2370 };
2371 }
2372 // lib.optionalAttrs (config.security.pam.enableFscrypt) {
2373 # Allow fscrypt to verify login passphrase
2374 fscrypt = { };
2375 };
2376
2377 security.apparmor.includes."abstractions/pam" =
2378 lib.concatMapStrings (name: "r ${config.environment.etc."pam.d/${name}".source},\n") (
2379 lib.attrNames enabledServices
2380 )
2381 + (
2382 with lib;
2383 pipe enabledServices [
2384 lib.attrValues
2385 (catAttrs "rules")
2386 (lib.concatMap lib.attrValues)
2387 (lib.concatMap lib.attrValues)
2388 (lib.filter (rule: rule.enable))
2389 (lib.catAttrs "modulePath")
2390 (map (
2391 modulePath:
2392 lib.throwIfNot (lib.hasPrefix "/" modulePath)
2393 ''non-absolute PAM modulePath "${modulePath}" is unsupported by apparmor''
2394 modulePath
2395 ))
2396 lib.unique
2397 (map (module: "mr ${module},"))
2398 concatLines
2399 ]
2400 );
2401
2402 security.sudo.extraConfig = optionalSudoConfigForSSHAgentAuth;
2403 security.sudo-rs.extraConfig = optionalSudoConfigForSSHAgentAuth;
2404 };
2405}