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