1 # This module provides configuration for the PAM (Pluggable
2 # Authentication Modules) system.
3 { config, lib, pkgs, ... }:
6 moduleSettingsType = with lib.types; attrsOf (nullOr (oneOf [ bool str int pathInStore ]));
7 moduleSettingsDescription = ''
8 Boolean values render just the key if true, and nothing if false.
9 Null values are ignored.
10 All other values are rendered as key-value pairs.
13 mkRulesTypeOption = type: lib.mkOption {
14 # These options are experimental and subject to breaking changes without notice.
16 PAM `${type}` rules for this service.
18 Attribute keys are the name of each rule.
20 type = lib.types.attrsOf (lib.types.submodule ({ name, config, ... }: {
30 enable = lib.mkOption {
31 type = lib.types.bool;
34 Whether this rule is added to the PAM service config file.
37 order = lib.mkOption {
40 Order of this rule in the service file. Rules are arranged in ascending order of this value.
43 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:
47 security.pam.services.login.rules.auth.foo.order =
48 config.security.pam.services.login.rules.auth.unix.order + 10;
54 control = lib.mkOption {
57 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.
60 modulePath = lib.mkOption {
63 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.
67 type = lib.types.listOf lib.types.str;
69 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.
71 Escaping rules for spaces and square brackets are automatically applied.
73 {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.
76 settings = lib.mkOption {
77 type = moduleSettingsType;
80 Settings to add as `module-arguments`.
82 ${moduleSettingsDescription}
88 # Formats an attrset of settings as args for use as `module-arguments`.
89 args = lib.concatLists (lib.flip lib.mapAttrsToList config.settings (name: value:
91 then lib.optional value name
92 else lib.optional (value != null) "${name}=${toString value}"
98 package = config.security.pam.package;
99 parentConfig = config;
101 pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in {
104 (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ])
109 name = lib.mkOption {
111 type = lib.types.str;
112 description = "Name of the PAM service.";
115 rules = lib.mkOption {
116 # This option is experimental and subject to breaking changes without notice.
120 PAM rules for this service.
123 This option and its suboptions are experimental and subject to breaking changes without notice.
125 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.
127 If you share configuration examples that use this option, you MUST include this warning so that users are informed.
129 You may freely use this option within `nixpkgs`, and future changes will account for those use sites.
132 type = lib.types.submodule {
133 options = lib.genAttrs [ "account" "auth" "password" "session" ] mkRulesTypeOption;
137 unixAuth = lib.mkOption {
139 type = lib.types.bool;
141 Whether users can log in with passwords defined in
146 rootOK = lib.mkOption {
148 type = lib.types.bool;
150 If set, root doesn't need to authenticate (e.g. for the
151 {command}`useradd` service).
155 p11Auth = lib.mkOption {
156 default = config.security.pam.p11.enable;
157 defaultText = lib.literalExpression "config.security.pam.p11.enable";
158 type = lib.types.bool;
160 If set, keys listed in
161 {file}`~/.ssh/authorized_keys` and
162 {file}`~/.eid/authorized_certificates`
163 can be used to log in with the associated PKCS#11 tokens.
167 u2fAuth = lib.mkOption {
168 default = config.security.pam.u2f.enable;
169 defaultText = lib.literalExpression "config.security.pam.u2f.enable";
170 type = lib.types.bool;
172 If set, users listed in
173 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
174 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
175 not set) are able to log in with the associated U2F key. Path can be
176 changed using {option}`security.pam.u2f.authFile` option.
180 usshAuth = lib.mkOption {
182 type = lib.types.bool;
184 If set, users with an SSH certificate containing an authorized principal
185 in their SSH agent are able to log in. Specific options are controlled
186 using the {option}`security.pam.ussh` options.
188 Note that the {option}`security.pam.ussh.enable` must also be
189 set for this option to take effect.
193 yubicoAuth = lib.mkOption {
194 default = config.security.pam.yubico.enable;
195 defaultText = lib.literalExpression "config.security.pam.yubico.enable";
196 type = lib.types.bool;
198 If set, users listed in
199 {file}`~/.yubico/authorized_yubikeys`
200 are able to log in with the associated Yubikey tokens.
204 googleAuthenticator = {
205 enable = lib.mkOption {
207 type = lib.types.bool;
209 If set, users with enabled Google Authenticator (created
210 {file}`~/.google_authenticator`) will be required
211 to provide Google Authenticator token to log in.
216 otpwAuth = lib.mkOption {
217 default = config.security.pam.enableOTPW;
218 defaultText = lib.literalExpression "config.security.pam.enableOTPW";
219 type = lib.types.bool;
221 If set, the OTPW system will be used (if
222 {file}`~/.otpw` exists).
226 googleOsLoginAccountVerification = lib.mkOption {
228 type = lib.types.bool;
230 If set, will use the Google OS Login PAM modules
231 (`pam_oslogin_login`,
232 `pam_oslogin_admin`) to verify possible OS Login
233 users and set sudoers configuration accordingly.
234 This only makes sense to enable for the `sshd` PAM
239 googleOsLoginAuthentication = lib.mkOption {
241 type = lib.types.bool;
243 If set, will use the `pam_oslogin_login`'s user
244 authentication methods to authenticate users using 2FA.
245 This only makes sense to enable for the `sshd` PAM
250 mysqlAuth = lib.mkOption {
251 default = config.users.mysql.enable;
252 defaultText = lib.literalExpression "config.users.mysql.enable";
253 type = lib.types.bool;
255 If set, the `pam_mysql` module will be used to
256 authenticate users against a MySQL/MariaDB database.
260 fprintAuth = lib.mkOption {
261 default = config.services.fprintd.enable;
262 defaultText = lib.literalExpression "config.services.fprintd.enable";
263 type = lib.types.bool;
265 If set, fingerprint reader will be used (if exists and
266 your fingerprints are enrolled).
270 oathAuth = lib.mkOption {
271 default = config.security.pam.oath.enable;
272 defaultText = lib.literalExpression "config.security.pam.oath.enable";
273 type = lib.types.bool;
275 If set, the OATH Toolkit will be used.
279 sshAgentAuth = lib.mkOption {
281 type = lib.types.bool;
283 If set, the calling user's SSH agent is used to authenticate
284 against the keys in the calling user's
285 {file}`~/.ssh/authorized_keys`. This is useful
286 for {command}`sudo` on password-less remote systems.
291 enable = lib.mkOption {
293 type = lib.types.bool;
295 If set, use the Duo Security pam module
296 `pam_duo` for authentication. Requires
297 configuration of {option}`security.duosec` options.
302 startSession = lib.mkOption {
304 type = lib.types.bool;
306 If set, the service will register a new session with
307 systemd's login manager. For local sessions, this will give
308 the user access to audio devices, CD-ROM drives. In the
309 default PolicyKit configuration, it also allows the user to
314 setEnvironment = lib.mkOption {
315 type = lib.types.bool;
318 Whether the service should set the environment variables
319 listed in {option}`environment.sessionVariables`
324 setLoginUid = lib.mkOption {
325 type = lib.types.bool;
327 Set the login uid of the process
328 ({file}`/proc/self/loginuid`) for auditing
329 purposes. The login uid is only set by ‘entry points’ like
330 {command}`login` and {command}`sshd`, not by
331 commands like {command}`sudo`.
336 enable = lib.mkOption {
337 type = lib.types.bool;
340 Enable or disable TTY auditing for specified users
344 enablePattern = lib.mkOption {
345 type = lib.types.nullOr lib.types.str;
348 For each user matching one of comma-separated
349 glob patterns, enable TTY auditing
353 disablePattern = lib.mkOption {
354 type = lib.types.nullOr lib.types.str;
357 For each user matching one of comma-separated
358 glob patterns, disable TTY auditing
362 openOnly = lib.mkOption {
363 type = lib.types.bool;
366 Set the TTY audit flag when opening the session,
367 but do not restore it when closing the session.
368 Using this option is necessary for some services
369 that don't fork() to run the authenticated session,
375 forwardXAuth = lib.mkOption {
377 type = lib.types.bool;
379 Whether X authentication keys should be passed from the
380 calling user to the target user (e.g. for
385 pamMount = lib.mkOption {
386 default = config.security.pam.mount.enable;
387 defaultText = lib.literalExpression "config.security.pam.mount.enable";
388 type = lib.types.bool;
390 Enable PAM mount (pam_mount) system to mount filesystems on user login.
394 allowNullPassword = lib.mkOption {
396 type = lib.types.bool;
398 Whether to allow logging into accounts that have no password
399 set (i.e., have an empty password field in
400 {file}`/etc/passwd` or
401 {file}`/etc/group`). This does not enable
402 logging into disabled accounts (i.e., that have the password
403 field set to `!`). Note that regardless of
404 what the pam_unix documentation says, accounts with hashed
405 empty passwords are always allowed to log in.
409 nodelay = lib.mkOption {
411 type = lib.types.bool;
413 Whether the delay after typing a wrong password should be disabled.
417 requireWheel = lib.mkOption {
419 type = lib.types.bool;
421 Whether to permit root access only to members of group wheel.
425 limits = lib.mkOption {
429 Attribute set describing resource limits. Defaults to the
430 value of {option}`security.pam.loginLimits`.
431 The meaning of the values is explained in {manpage}`limits.conf(5)`.
435 showMotd = lib.mkOption {
437 type = lib.types.bool;
438 description = "Whether to show the message of the day.";
441 makeHomeDir = lib.mkOption {
443 type = lib.types.bool;
445 Whether to try to create home directories for users
446 with `$HOME`s pointing to nonexistent
447 locations on session login.
451 updateWtmp = lib.mkOption {
453 type = lib.types.bool;
454 description = "Whether to update {file}`/var/log/wtmp`.";
457 logFailures = lib.mkOption {
459 type = lib.types.bool;
460 description = "Whether to log authentication failures in {file}`/var/log/faillog`.";
463 enableAppArmor = lib.mkOption {
465 type = lib.types.bool;
467 Enable support for attaching AppArmor profiles at the
468 user/group level, e.g., as part of a role based access
474 enable = lib.mkOption {
476 type = lib.types.bool;
478 If enabled, pam_wallet will attempt to automatically unlock the
479 user's default KDE wallet upon login. If the user has no wallet named
480 "kdewallet", or the login password does not match their wallet
481 password, KDE will prompt separately after login.
485 package = lib.mkPackageOption pkgs.plasma5Packages "kwallet-pam" {
486 pkgsText = "pkgs.plasma5Packages";
489 forceRun = lib.mkEnableOption null // {
491 The `force_run` option is used to tell the PAM module for KWallet
492 to forcefully run even if no graphical session (such as a GUI
493 display manager) is detected. This is useful for when you are
494 starting an X Session or a Wayland Session from a TTY. If you
495 intend to log-in from a TTY, it is recommended that you enable
496 this option **and** ensure that `plasma-kwallet-pam.service` is
497 started by `graphical-session.target`.
502 sssdStrictAccess = lib.mkOption {
504 type = lib.types.bool;
505 description = "enforce sssd access control";
508 enableGnomeKeyring = lib.mkOption {
510 type = lib.types.bool;
512 If enabled, pam_gnome_keyring will attempt to automatically unlock the
513 user's default Gnome keyring upon login. If the user login password does
514 not match their keyring password, Gnome Keyring will prompt separately
520 enable = lib.mkOption {
521 type = lib.types.bool;
524 If enabled, this will replace the `FAIL_DELAY` setting from `login.defs`.
525 Change the delay on failure per-application.
529 delay = lib.mkOption {
531 type = lib.types.int;
533 description = "The delay time (in microseconds) on failure.";
538 enable = lib.mkOption {
539 type = lib.types.bool;
542 If enabled, pam_gnupg will attempt to automatically unlock the
543 user's GPG keys with the login password via
544 {command}`gpg-agent`. The keygrips of all keys to be
545 unlocked should be written to {file}`~/.pam-gnupg`,
546 and can be queried with {command}`gpg -K --with-keygrip`.
547 Presetting passphrases must be enabled by adding
548 `allow-preset-passphrase` in
549 {file}`~/.gnupg/gpg-agent.conf`.
553 noAutostart = lib.mkOption {
554 type = lib.types.bool;
557 Don't start {command}`gpg-agent` if it is not running.
558 Useful in conjunction with starting {command}`gpg-agent` as
559 a systemd user service.
563 storeOnly = lib.mkOption {
564 type = lib.types.bool;
567 Don't send the password immediately after login, but store for PAM
574 default = config.security.pam.zfs.enable;
575 defaultText = lib.literalExpression "config.security.pam.zfs.enable";
576 type = lib.types.bool;
578 Enable unlocking and mounting of encrypted ZFS home dataset at login.
582 text = lib.mkOption {
583 type = lib.types.nullOr lib.types.lines;
584 description = "Contents of the PAM service file.";
589 # The resulting /etc/pam.d/* file contents are verified in
590 # nixos/tests/pam/pam-file-contents.nix. Please update tests there when
591 # changing the derivation.
593 name = lib.mkDefault name;
594 setLoginUid = lib.mkDefault cfg.startSession;
595 limits = lib.mkDefault config.security.pam.loginLimits;
598 ensureUniqueOrder = type: rules:
600 checkPair = a: b: assert lib.assertMsg (a.order != b.order) "security.pam.services.${name}.rules.${type}: rules '${a.name}' and '${b.name}' cannot have the same order value (${toString a.order})"; b;
601 checked = lib.zipListsWith checkPair rules (lib.drop 1 rules);
602 in lib.take 1 rules ++ checked;
603 # Formats a string for use in `module-arguments`. See `man pam.conf`.
604 formatModuleArgument = token:
605 if lib.hasInfix " " token
606 then "[${lib.replaceStrings ["]"] ["\\]"] token}]"
608 formatRules = type: lib.pipe cfg.rules.${type} [
610 (lib.filter (rule: rule.enable))
611 (lib.sort (a: b: a.order < b.order))
612 (ensureUniqueOrder type)
613 (map (rule: lib.concatStringsSep " " (
614 [ type rule.control rule.modulePath ]
615 ++ map formatModuleArgument rule.args
616 ++ [ "# ${rule.name} (order ${toString rule.order})" ]
618 (lib.concatStringsSep "\n")
621 # Account management.
622 ${formatRules "account"}
624 # Authentication management.
625 ${formatRules "auth"}
627 # Password management.
628 ${formatRules "password"}
630 # Session management.
631 ${formatRules "session"}
634 # !!! TODO: move the LDAP stuff to the LDAP module, and the
635 # Samba stuff to the Samba module. This requires that the PAM
636 # module provides the right hooks.
638 autoOrderRules = lib.flip lib.pipe [
639 (lib.imap1 (index: rule: rule // { order = lib.mkDefault (10000 + index * 100); } ))
640 (map (rule: lib.nameValuePair rule.name (removeAttrs rule [ "name" ])))
644 account = autoOrderRules [
645 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
646 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
647 config_file = "/etc/security/pam_mysql.conf";
649 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
650 ignore_unknown_user = true;
652 { name = "sss"; enable = config.services.sssd.enable; control = if cfg.sssdStrictAccess then "[default=bad success=ok user_unknown=ignore]" else "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
653 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; }
654 { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; }
655 { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; }
656 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
657 # The required pam_unix.so module has to come after all the sufficient modules
658 # because otherwise, the account lookup will fail if the user does not exist
659 # locally, for example with MySQL- or LDAP-auth.
660 { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
663 auth = autoOrderRules ([
664 { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; control = "[success=done perm_denied=die default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; }
665 { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "${package}/lib/security/pam_rootok.so"; }
666 { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "${package}/lib/security/pam_wheel.so"; settings = {
669 { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "${package}/lib/security/pam_faillock.so"; }
670 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
671 config_file = "/etc/security/pam_mysql.conf";
673 { name = "ssh_agent_auth"; enable = config.security.pam.sshAgentAuth.enable && cfg.sshAgentAuth; control = "sufficient"; modulePath = "${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so"; settings = {
674 file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles;
676 (let p11 = config.security.pam.p11; in { name = "p11"; enable = cfg.p11Auth; control = p11.control; modulePath = "${pkgs.pam_p11}/lib/security/pam_p11.so"; args = [
677 "${pkgs.opensc}/lib/opensc-pkcs11.so"
679 (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; inherit (u2f) settings; })
680 (let ussh = config.security.pam.ussh; in { name = "ussh"; enable = config.security.pam.ussh.enable && cfg.usshAuth; control = ussh.control; modulePath = "${pkgs.pam_ussh}/lib/security/pam_ussh.so"; settings = {
681 ca_file = ussh.caFile;
682 authorized_principals = ussh.authorizedPrincipals;
683 authorized_principals_file = ussh.authorizedPrincipalsFile;
684 inherit (ussh) group;
686 (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; settings = {
687 inherit (oath) window digits;
688 usersfile = oath.usersFile;
690 (let yubi = config.security.pam.yubico; in { name = "yubico"; enable = cfg.yubicoAuth; control = yubi.control; modulePath = "${pkgs.yubico-pam}/lib/security/pam_yubico.so"; settings = {
691 inherit (yubi) mode debug;
692 chalresp_path = yubi.challengeResponsePath;
693 id = lib.mkIf (yubi.mode == "client") yubi.id;
695 (let dp9ik = config.security.pam.dp9ik; in { name = "p9"; enable = dp9ik.enable; control = dp9ik.control; modulePath = "${pkgs.pam_dp9ik}/lib/security/pam_p9.so"; args = [
698 { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${config.services.fprintd.package}/lib/security/pam_fprintd.so"; }
700 # Modules in this block require having the password set in PAM_AUTHTOK.
701 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
702 # after it succeeds. Certain modules need to run after pam_unix
703 # prompts the user for password so we run it once with 'optional' at an
704 # earlier point and it will run again with 'sufficient' further down.
705 # We use try_first_pass the second time to avoid prompting password twice.
707 # The same principle applies to systemd-homed
708 (lib.optionals ((cfg.unixAuth || config.services.homed.enable) &&
709 (config.security.pam.enableEcryptfs
710 || config.security.pam.enableFscrypt
712 || cfg.kwallet.enable
713 || cfg.enableGnomeKeyring
714 || config.services.intune.enable
715 || cfg.googleAuthenticator.enable
717 || cfg.failDelay.enable
718 || cfg.duoSecurity.enable
721 { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
722 { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
723 nullok = cfg.allowNullPassword;
724 inherit (cfg) nodelay;
727 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; settings = {
730 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
731 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
732 inherit (config.security.pam.zfs) homes;
734 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = {
735 disable_interactive = true;
737 { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; }
738 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; }
739 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
740 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
741 store-only = cfg.gnupg.storeOnly;
743 { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${package}/lib/security/pam_faildelay.so"; settings = {
744 inherit (cfg.failDelay) delay;
746 { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = {
747 no_increment_hotp = true;
749 { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; }
751 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
752 { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
753 nullok = cfg.allowNullPassword;
754 inherit (cfg) nodelay;
756 try_first_pass = true;
758 { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; }
759 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = {
760 use_first_pass = true;
762 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
763 ignore_unknown_user = true;
764 use_first_pass = true;
766 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; settings = {
767 use_first_pass = true;
769 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "[default=ignore success=1 service_err=reset]"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = {
770 use_first_pass = true;
772 { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = {
774 use_first_pass = true;
776 { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = {
778 use_first_pass = true;
780 { name = "deny"; control = "required"; modulePath = "${package}/lib/security/pam_deny.so"; }
783 password = autoOrderRules [
784 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
785 { name = "unix"; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
789 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; }
790 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
791 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
792 inherit (config.security.pam.zfs) homes;
794 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; }
795 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
796 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
797 config_file = "/etc/security/pam_mysql.conf";
799 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
800 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
801 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = {
802 use_first_pass = true;
804 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
809 session = autoOrderRules [
810 { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "${package}/lib/security/pam_env.so"; settings = {
811 conffile = "/etc/pam/environment";
814 { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
815 { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "${package}/lib/security/pam_loginuid.so"; }
816 { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${package}/lib/security/pam_tty_audit.so"; settings = {
817 open_only = cfg.ttyAudit.openOnly;
818 enable = cfg.ttyAudit.enablePattern;
819 disable = cfg.ttyAudit.disablePattern;
821 { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
822 { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${package}/lib/security/pam_mkhomedir.so"; settings = {
824 skel = config.security.pam.makeHomeDir.skelDirectory;
825 inherit (config.security.pam.makeHomeDir) umask;
827 { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${package}/lib/security/pam_lastlog.so"; settings = {
830 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; }
831 # Work around https://github.com/systemd/systemd/issues/8598
832 # Skips the pam_fscrypt module for systemd-user sessions which do not have a password
834 # See also https://github.com/google/fscrypt/issues/95
835 { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
836 "service" "=" "systemd-user"
838 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
839 { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
840 "service" "=" "systemd-user"
842 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
843 inherit (config.security.pam.zfs) homes;
844 nounmount = config.security.pam.zfs.noUnmount;
846 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = {
847 disable_interactive = true;
849 { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
850 { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
851 config_file = "/etc/security/pam_mysql.conf";
853 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
854 { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
855 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; }
856 { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; }
857 { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; }
858 { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "${package}/lib/security/pam_xauth.so"; settings = {
859 xauthpath = "${pkgs.xorg.xauth}/bin/xauth";
862 { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${package}/lib/security/pam_limits.so"; settings = {
863 conf = "${makeLimitsConf cfg.limits}";
865 { name = "motd"; enable = cfg.showMotd && (config.users.motd != "" || config.users.motdFile != null); control = "optional"; modulePath = "${package}/lib/security/pam_motd.so"; settings = {
868 { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; settings = {
869 order = "user,group,default";
872 { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; settings = lib.mkIf cfg.kwallet.forceRun { force_run = true; }; }
873 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
876 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
877 no-autostart = cfg.gnupg.noAutostart;
879 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
887 inherit (pkgs) pam_krb5 pam_ccreds;
889 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
890 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
892 # Create a limits.conf(5) file.
893 makeLimitsConf = limits:
894 pkgs.writeText "limits.conf"
895 (lib.concatMapStrings ({ domain, type, item, value }:
896 "${domain} ${type} ${item} ${toString value}\n")
899 limitsType = with lib.types; listOf (submodule ({ ... }: {
901 domain = lib.mkOption {
902 description = "Username, groupname, or wildcard this limit applies to";
907 type = lib.mkOption {
908 description = "Type of this limit";
909 type = enum [ "-" "hard" "soft" ];
913 item = lib.mkOption {
914 description = "Item this limit applies to";
937 value = lib.mkOption {
938 description = "Value of this limit";
939 type = oneOf [ str int ];
944 motd = if config.users.motdFile == null
945 then pkgs.writeText "motd" config.users.motd
946 else config.users.motdFile;
948 makePAMService = name: service:
949 { name = "pam.d/${name}";
950 value.source = pkgs.writeText "${name}.pam" service.text;
953 optionalSudoConfigForSSHAgentAuth = lib.optionalString config.security.pam.sshAgentAuth.enable ''
954 # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
955 Defaults env_keep+=SSH_AUTH_SOCK
962 meta.maintainers = [ lib.maintainers.majiir ];
965 (lib.mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
966 (lib.mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ])
967 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "authFile" ] [ "security" "pam" "u2f" "settings" "authfile" ])
968 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "appId" ] [ "security" "pam" "u2f" "settings" "appid" ])
969 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "origin" ] [ "security" "pam" "u2f" "settings" "origin" ])
970 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "debug" ] [ "security" "pam" "u2f" "settings" "debug" ])
971 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "interactive" ] [ "security" "pam" "u2f" "settings" "interactive" ])
972 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "cue" ] [ "security" "pam" "u2f" "settings" "cue" ])
979 security.pam.package = lib.mkPackageOption pkgs "pam" { };
981 security.pam.loginLimits = lib.mkOption {
990 { domain = "@student";
998 Define resource limits that should apply to users or groups.
999 Each item in the list should be an attribute set with a
1000 {var}`domain`, {var}`type`,
1001 {var}`item`, and {var}`value`
1002 attribute. The syntax and semantics of these attributes
1003 must be that described in {manpage}`limits.conf(5)`.
1005 Note that these limits do not apply to systemd services,
1006 whose limits can be changed via {option}`systemd.extraConfig`
1011 security.pam.services = lib.mkOption {
1013 type = with lib.types; attrsOf (submodule pamOpts);
1015 This option defines the PAM services. A service typically
1016 corresponds to a program that uses PAM,
1017 e.g. {command}`login` or {command}`passwd`.
1018 Each attribute of this set defines a PAM service, with the attribute name
1019 defining the name of the service.
1023 security.pam.makeHomeDir.skelDirectory = lib.mkOption {
1024 type = lib.types.str;
1025 default = "/var/empty";
1026 example = "/etc/skel";
1028 Path to skeleton directory whose contents are copied to home
1029 directories newly created by `pam_mkhomedir`.
1033 security.pam.makeHomeDir.umask = lib.mkOption {
1034 type = lib.types.str;
1038 The user file mode creation mask to use on home directories
1039 newly created by `pam_mkhomedir`.
1043 security.pam.sshAgentAuth = {
1044 enable = lib.mkEnableOption ''
1045 authenticating using a signature performed by the ssh-agent.
1046 This allows using SSH keys exclusively, instead of passwords, for instance on remote machines
1049 authorizedKeysFiles = lib.mkOption {
1050 type = with lib.types; listOf str;
1052 A list of paths to files in OpenSSH's `authorized_keys` format, containing
1053 the keys that will be trusted by the `pam_ssh_agent_auth` module.
1055 The following patterns are expanded when interpreting the path:
1056 - `%f` and `%H` respectively expand to the fully-qualified and short hostname ;
1057 - `%u` expands to the username ;
1058 - `~` or `%h` expands to the user's home directory.
1061 Specifying user-writeable files here result in an insecure configuration: a malicious process
1062 can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
1064 See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611)
1067 default = [ "/etc/ssh/authorized_keys.d/%u" ];
1071 security.pam.enableOTPW = lib.mkEnableOption "the OTPW (one-time password) PAM module";
1073 security.pam.dp9ik = {
1074 enable = lib.mkEnableOption ''
1075 the dp9ik pam module provided by tlsclient.
1077 If set, users can be authenticated against the 9front
1078 authentication server given in {option}`security.pam.dp9ik.authserver`
1080 control = lib.mkOption {
1081 default = "sufficient";
1082 type = lib.types.str;
1084 This option sets the pam "control" used for this module.
1087 authserver = lib.mkOption {
1089 type = with lib.types; nullOr str;
1091 This controls the hostname for the 9front authentication server
1092 that users will be authenticated against.
1097 security.pam.krb5 = {
1098 enable = lib.mkOption {
1099 default = config.security.krb5.enable;
1100 defaultText = lib.literalExpression "config.security.krb5.enable";
1101 type = lib.types.bool;
1103 Enables Kerberos PAM modules (`pam-krb5`,
1106 If set, users can authenticate with their Kerberos password.
1107 This requires a valid Kerberos configuration
1108 (`config.security.krb5.enable` should be set to
1111 Note that the Kerberos PAM modules are not necessary when using SSS
1112 to handle Kerberos authentication.
1117 security.pam.p11 = {
1118 enable = lib.mkOption {
1120 type = lib.types.bool;
1122 Enables P11 PAM (`pam_p11`) module.
1124 If set, users can log in with SSH keys and PKCS#11 tokens.
1126 More information can be found [here](https://github.com/OpenSC/pam_p11).
1130 control = lib.mkOption {
1131 default = "sufficient";
1132 type = lib.types.enum [ "required" "requisite" "sufficient" "lib.optional" ];
1134 This option sets pam "control".
1135 If you want to have multi factor authentication, use "required".
1136 If you want to use the PKCS#11 device instead of the regular password,
1140 {manpage}`pam.conf(5)`
1141 for better understanding of this option.
1146 security.pam.u2f = {
1147 enable = lib.mkOption {
1149 type = lib.types.bool;
1151 Enables U2F PAM (`pam-u2f`) module.
1153 If set, users listed in
1154 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1155 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1156 not set) are able to log in with the associated U2F key. The path can
1157 be changed using {option}`security.pam.u2f.authFile` option.
1160 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
1161 This file can be generated using {command}`pamu2fcfg` command.
1163 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1167 control = lib.mkOption {
1168 default = "sufficient";
1169 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1171 This option sets pam "control".
1172 If you want to have multi factor authentication, use "required".
1173 If you want to use U2F device instead of regular password, use "sufficient".
1176 {manpage}`pam.conf(5)`
1177 for better understanding of this option.
1181 settings = lib.mkOption {
1182 type = lib.types.submodule {
1183 freeformType = moduleSettingsType;
1186 authfile = lib.mkOption {
1188 type = with lib.types; nullOr path;
1190 By default `pam-u2f` module reads the keys from
1191 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1192 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1195 If you want to change auth file locations or centralize database (for
1196 example use {file}`/etc/u2f-mappings`) you can set this
1200 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
1201 This file can be generated using {command}`pamu2fcfg` command.
1203 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1207 appid = lib.mkOption {
1209 type = with lib.types; nullOr str;
1211 By default `pam-u2f` module sets the application
1212 ID to `pam://$HOSTNAME`.
1214 When using {command}`pamu2fcfg`, you can specify your
1215 application ID with the `-i` flag.
1217 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1221 origin = lib.mkOption {
1223 type = with lib.types; nullOr str;
1225 By default `pam-u2f` module sets the origin
1226 to `pam://$HOSTNAME`.
1227 Setting origin to an host independent value will allow you to
1228 reuse credentials across machines
1230 When using {command}`pamu2fcfg`, you can specify your
1231 application ID with the `-o` flag.
1233 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1237 debug = lib.mkOption {
1239 type = lib.types.bool;
1241 Debug output to stderr.
1245 interactive = lib.mkOption {
1247 type = lib.types.bool;
1249 Set to prompt a message and wait before testing the presence of a U2F device.
1250 Recommended if your device doesn’t have a tactile trigger.
1254 cue = lib.mkOption {
1256 type = lib.types.bool;
1258 By default `pam-u2f` module does not inform user
1259 that he needs to use the u2f device, it just waits without a prompt.
1261 If you set this option to `true`,
1262 `cue` option is added to `pam-u2f`
1263 module and reminder message will be displayed.
1270 authfile = "/etc/u2f_keys";
1271 authpending_file = "";
1273 pinverification = 1;
1276 Options to pass to the PAM module.
1278 ${moduleSettingsDescription}
1283 security.pam.ussh = {
1284 enable = lib.mkOption {
1286 type = lib.types.bool;
1288 Enables Uber's USSH PAM (`pam-ussh`) module.
1290 This is similar to `pam-ssh-agent`, except that
1291 the presence of a CA-signed SSH key with a valid principal is checked
1294 Note that this module must both be enabled using this option and on a
1295 per-PAM-service level as well (using `usshAuth`).
1297 More information can be found [here](https://github.com/uber/pam-ussh).
1301 caFile = lib.mkOption {
1303 type = with lib.types; nullOr path;
1305 By default `pam-ussh` reads the trusted user CA keys
1306 from {file}`/etc/ssh/trusted_user_ca`.
1308 This should be set the same as your `TrustedUserCAKeys`
1313 authorizedPrincipals = lib.mkOption {
1315 type = with lib.types; nullOr commas;
1317 Comma-separated list of authorized principals to permit; if the user
1318 presents a certificate with one of these principals, then they will be
1321 Note that `pam-ussh` also requires that the certificate
1322 contain a principal matching the user's username. The principals from
1323 this list are in addition to those principals.
1325 Mutually exclusive with `authorizedPrincipalsFile`.
1329 authorizedPrincipalsFile = lib.mkOption {
1331 type = with lib.types; nullOr path;
1333 Path to a list of principals; if the user presents a certificate with
1334 one of these principals, then they will be authorized.
1336 Note that `pam-ussh` also requires that the certificate
1337 contain a principal matching the user's username. The principals from
1338 this file are in addition to those principals.
1340 Mutually exclusive with `authorizedPrincipals`.
1344 group = lib.mkOption {
1346 type = with lib.types; nullOr str;
1348 If set, then the authenticating user must be a member of this group
1353 control = lib.mkOption {
1354 default = "sufficient";
1355 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1357 This option sets pam "control".
1358 If you want to have multi factor authentication, use "required".
1359 If you want to use the SSH certificate instead of the regular password,
1363 {manpage}`pam.conf(5)`
1364 for better understanding of this option.
1369 security.pam.yubico = {
1370 enable = lib.mkOption {
1372 type = lib.types.bool;
1374 Enables Yubico PAM (`yubico-pam`) module.
1376 If set, users listed in
1377 {file}`~/.yubico/authorized_yubikeys`
1378 are able to log in with the associated Yubikey tokens.
1380 The file must have only one line:
1381 `username:yubikey_token_id1:yubikey_token_id2`
1382 More information can be found [here](https://developers.yubico.com/yubico-pam/).
1385 control = lib.mkOption {
1386 default = "sufficient";
1387 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1389 This option sets pam "control".
1390 If you want to have multi factor authentication, use "required".
1391 If you want to use Yubikey instead of regular password, use "sufficient".
1394 {manpage}`pam.conf(5)`
1395 for better understanding of this option.
1400 type = lib.types.str;
1401 description = "client id";
1404 debug = lib.mkOption {
1406 type = lib.types.bool;
1408 Debug output to stderr.
1411 mode = lib.mkOption {
1413 type = lib.types.enum [ "client" "challenge-response" ];
1417 Use "client" for online validation with a YubiKey validation service such as
1420 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
1421 Challenge-Response configurations. See the man-page ykpamcfg(1) for further
1422 details on how to configure offline Challenge-Response validation.
1424 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
1427 challengeResponsePath = lib.mkOption {
1429 type = lib.types.nullOr lib.types.path;
1431 If not null, set the path used by yubico pam module where the challenge expected response is stored.
1433 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
1438 security.pam.zfs = {
1439 enable = lib.mkOption {
1441 type = lib.types.bool;
1443 Enable unlocking and mounting of encrypted ZFS home dataset at login.
1447 homes = lib.mkOption {
1448 example = "rpool/home";
1449 default = "rpool/home";
1450 type = lib.types.str;
1452 Prefix of home datasets. This value will be concatenated with
1453 `"/" + <username>` in order to determine the home dataset to unlock.
1457 noUnmount = lib.mkOption {
1459 type = lib.types.bool;
1461 Do not unmount home dataset on logout.
1466 security.pam.enableEcryptfs = lib.mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
1467 security.pam.enableFscrypt = lib.mkEnableOption ''
1468 fscrypt, to automatically unlock directories with the user's login password.
1470 This also enables a service at security.pam.services.fscrypt which is used by
1471 fscrypt to verify the user's password when setting up a new protector. If you
1472 use something other than pam_unix to verify user passwords, please remember to
1473 adjust this PAM service
1476 users.motd = lib.mkOption {
1478 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
1479 type = lib.types.lines;
1480 description = "Message of the day shown to users when they log in.";
1483 users.motdFile = lib.mkOption {
1485 example = "/etc/motd";
1486 type = lib.types.nullOr lib.types.path;
1487 description = "A file containing the message of the day shown to users when they log in.";
1492 ###### implementation
1497 assertion = config.users.motd == "" || config.users.motdFile == null;
1499 Only one of users.motd and users.motdFile can be set.
1503 assertion = config.security.pam.zfs.enable -> config.boot.zfs.enabled;
1505 `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled`).
1509 assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != [];
1511 `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list.
1512 Did you forget to set `services.openssh.enable` ?
1517 warnings = lib.optional
1518 (with lib; with config.security.pam.sshAgentAuth;
1519 enable && lib.any (s: lib.hasPrefix "%h" s || lib.hasPrefix "~" s) authorizedKeysFiles)
1520 ''config.security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory.
1522 Specifying user-writeable files there result in an insecure configuration:
1523 a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
1524 See https://github.com/NixOS/nixpkgs/issues/31611
1527 environment.systemPackages =
1528 # Include the PAM modules in the system path mostly for the manpages.
1530 ++ lib.optional config.users.ldap.enable pam_ldap
1531 ++ lib.optional config.services.kanidm.enablePam config.services.kanidm.package
1532 ++ lib.optional config.services.sssd.enable pkgs.sssd
1533 ++ lib.optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds]
1534 ++ lib.optionals config.security.pam.enableOTPW [ pkgs.otpw ]
1535 ++ lib.optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ]
1536 ++ lib.optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
1537 ++ lib.optionals config.security.pam.enableFscrypt [ pkgs.fscrypt-experimental ]
1538 ++ lib.optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
1540 boot.supportedFilesystems = lib.optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
1542 security.wrappers = {
1547 source = "${package}/bin/unix_chkpwd";
1551 environment.etc = lib.mapAttrs' makePAMService config.security.pam.services;
1553 security.pam.services =
1556 auth required pam_warn.so
1557 auth required pam_deny.so
1558 account required pam_warn.so
1559 account required pam_deny.so
1560 password required pam_warn.so
1561 password required pam_deny.so
1562 session required pam_warn.so
1563 session required pam_deny.so
1566 # Most of these should be moved to specific modules.
1573 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
1575 /* FIXME: should runuser -l start a systemd session? Currently
1576 it complains "Cannot create session: Already running in a
1578 runuser-l = { rootOK = true; unixAuth = false; };
1579 } // lib.optionalAttrs (config.security.pam.enableFscrypt) {
1580 # Allow fscrypt to verify login passphrase
1584 security.apparmor.includes."abstractions/pam" =
1585 lib.concatMapStrings
1586 (name: "r ${config.environment.etc."pam.d/${name}".source},\n")
1587 (lib.attrNames config.security.pam.services) +
1588 (with lib; pipe config.security.pam.services [
1591 (lib.concatMap lib.attrValues)
1592 (lib.concatMap lib.attrValues)
1593 (lib.filter (rule: rule.enable))
1594 (lib.catAttrs "modulePath")
1595 # TODO(@uninsane): replace this warning + lib.filter with just an assertion
1596 (map (modulePath: lib.warnIfNot
1597 (lib.hasPrefix "/" modulePath)
1598 ''non-absolute PAM modulePath "${modulePath}" is unsupported by apparmor and will be treated as an error by future versions of nixpkgs; see <https://github.com/NixOS/nixpkgs/pull/314791>''
1601 (lib.filter (lib.hasPrefix "/"))
1603 (map (module: "mr ${module},"))
1607 security.sudo.extraConfig = optionalSudoConfigForSSHAgentAuth;
1608 security.sudo-rs.extraConfig = optionalSudoConfigForSSHAgentAuth;