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.
290 rssh = lib.mkOption {
292 type = lib.types.bool;
294 If set, the calling user's SSH agent is used to authenticate
295 against the configured keys. This module works in a manner
296 similar to pam_ssh_agent_auth, but supports a wider range
297 of SSH key types, including those protected by security
303 enable = lib.mkOption {
305 type = lib.types.bool;
307 If set, use the Duo Security pam module
308 `pam_duo` for authentication. Requires
309 configuration of {option}`security.duosec` options.
314 startSession = lib.mkOption {
316 type = lib.types.bool;
318 If set, the service will register a new session with
319 systemd's login manager. For local sessions, this will give
320 the user access to audio devices, CD-ROM drives. In the
321 default PolicyKit configuration, it also allows the user to
326 setEnvironment = lib.mkOption {
327 type = lib.types.bool;
330 Whether the service should set the environment variables
331 listed in {option}`environment.sessionVariables`
336 setLoginUid = lib.mkOption {
337 type = lib.types.bool;
339 Set the login uid of the process
340 ({file}`/proc/self/loginuid`) for auditing
341 purposes. The login uid is only set by ‘entry points’ like
342 {command}`login` and {command}`sshd`, not by
343 commands like {command}`sudo`.
348 enable = lib.mkOption {
349 type = lib.types.bool;
352 Enable or disable TTY auditing for specified users
356 enablePattern = lib.mkOption {
357 type = lib.types.nullOr lib.types.str;
360 For each user matching one of comma-separated
361 glob patterns, enable TTY auditing
365 disablePattern = lib.mkOption {
366 type = lib.types.nullOr lib.types.str;
369 For each user matching one of comma-separated
370 glob patterns, disable TTY auditing
374 openOnly = lib.mkOption {
375 type = lib.types.bool;
378 Set the TTY audit flag when opening the session,
379 but do not restore it when closing the session.
380 Using this option is necessary for some services
381 that don't fork() to run the authenticated session,
387 forwardXAuth = lib.mkOption {
389 type = lib.types.bool;
391 Whether X authentication keys should be passed from the
392 calling user to the target user (e.g. for
397 pamMount = lib.mkOption {
398 default = config.security.pam.mount.enable;
399 defaultText = lib.literalExpression "config.security.pam.mount.enable";
400 type = lib.types.bool;
402 Enable PAM mount (pam_mount) system to mount filesystems on user login.
406 allowNullPassword = lib.mkOption {
408 type = lib.types.bool;
410 Whether to allow logging into accounts that have no password
411 set (i.e., have an empty password field in
412 {file}`/etc/passwd` or
413 {file}`/etc/group`). This does not enable
414 logging into disabled accounts (i.e., that have the password
415 field set to `!`). Note that regardless of
416 what the pam_unix documentation says, accounts with hashed
417 empty passwords are always allowed to log in.
421 nodelay = lib.mkOption {
423 type = lib.types.bool;
425 Whether the delay after typing a wrong password should be disabled.
429 requireWheel = lib.mkOption {
431 type = lib.types.bool;
433 Whether to permit root access only to members of group wheel.
437 limits = lib.mkOption {
441 Attribute set describing resource limits. Defaults to the
442 value of {option}`security.pam.loginLimits`.
443 The meaning of the values is explained in {manpage}`limits.conf(5)`.
447 showMotd = lib.mkOption {
449 type = lib.types.bool;
450 description = "Whether to show the message of the day.";
453 makeHomeDir = lib.mkOption {
455 type = lib.types.bool;
457 Whether to try to create home directories for users
458 with `$HOME`s pointing to nonexistent
459 locations on session login.
463 updateWtmp = lib.mkOption {
465 type = lib.types.bool;
466 description = "Whether to update {file}`/var/log/wtmp`.";
469 logFailures = lib.mkOption {
471 type = lib.types.bool;
472 description = "Whether to log authentication failures in {file}`/var/log/faillog`.";
475 enableAppArmor = lib.mkOption {
477 type = lib.types.bool;
479 Enable support for attaching AppArmor profiles at the
480 user/group level, e.g., as part of a role based access
486 enable = lib.mkOption {
488 type = lib.types.bool;
490 If enabled, pam_wallet will attempt to automatically unlock the
491 user's default KDE wallet upon login. If the user has no wallet named
492 "kdewallet", or the login password does not match their wallet
493 password, KDE will prompt separately after login.
497 package = lib.mkPackageOption pkgs.plasma5Packages "kwallet-pam" {
498 pkgsText = "pkgs.plasma5Packages";
501 forceRun = lib.mkEnableOption null // {
503 The `force_run` option is used to tell the PAM module for KWallet
504 to forcefully run even if no graphical session (such as a GUI
505 display manager) is detected. This is useful for when you are
506 starting an X Session or a Wayland Session from a TTY. If you
507 intend to log-in from a TTY, it is recommended that you enable
508 this option **and** ensure that `plasma-kwallet-pam.service` is
509 started by `graphical-session.target`.
514 sssdStrictAccess = lib.mkOption {
516 type = lib.types.bool;
517 description = "enforce sssd access control";
520 enableGnomeKeyring = lib.mkOption {
522 type = lib.types.bool;
524 If enabled, pam_gnome_keyring will attempt to automatically unlock the
525 user's default Gnome keyring upon login. If the user login password does
526 not match their keyring password, Gnome Keyring will prompt separately
532 enable = lib.mkOption {
533 type = lib.types.bool;
536 If enabled, this will replace the `FAIL_DELAY` setting from `login.defs`.
537 Change the delay on failure per-application.
541 delay = lib.mkOption {
543 type = lib.types.int;
545 description = "The delay time (in microseconds) on failure.";
550 enable = lib.mkOption {
551 type = lib.types.bool;
554 If enabled, pam_gnupg will attempt to automatically unlock the
555 user's GPG keys with the login password via
556 {command}`gpg-agent`. The keygrips of all keys to be
557 unlocked should be written to {file}`~/.pam-gnupg`,
558 and can be queried with {command}`gpg -K --with-keygrip`.
559 Presetting passphrases must be enabled by adding
560 `allow-preset-passphrase` in
561 {file}`~/.gnupg/gpg-agent.conf`.
565 noAutostart = lib.mkOption {
566 type = lib.types.bool;
569 Don't start {command}`gpg-agent` if it is not running.
570 Useful in conjunction with starting {command}`gpg-agent` as
571 a systemd user service.
575 storeOnly = lib.mkOption {
576 type = lib.types.bool;
579 Don't send the password immediately after login, but store for PAM
586 default = config.security.pam.zfs.enable;
587 defaultText = lib.literalExpression "config.security.pam.zfs.enable";
588 type = lib.types.bool;
590 Enable unlocking and mounting of encrypted ZFS home dataset at login.
594 text = lib.mkOption {
595 type = lib.types.nullOr lib.types.lines;
596 description = "Contents of the PAM service file.";
601 # The resulting /etc/pam.d/* file contents are verified in
602 # nixos/tests/pam/pam-file-contents.nix. Please update tests there when
603 # changing the derivation.
605 name = lib.mkDefault name;
606 setLoginUid = lib.mkDefault cfg.startSession;
607 limits = lib.mkDefault config.security.pam.loginLimits;
610 ensureUniqueOrder = type: rules:
612 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;
613 checked = lib.zipListsWith checkPair rules (lib.drop 1 rules);
614 in lib.take 1 rules ++ checked;
615 # Formats a string for use in `module-arguments`. See `man pam.conf`.
616 formatModuleArgument = token:
617 if lib.hasInfix " " token
618 then "[${lib.replaceStrings ["]"] ["\\]"] token}]"
620 formatRules = type: lib.pipe cfg.rules.${type} [
622 (lib.filter (rule: rule.enable))
623 (lib.sort (a: b: a.order < b.order))
624 (ensureUniqueOrder type)
625 (map (rule: lib.concatStringsSep " " (
626 [ type rule.control rule.modulePath ]
627 ++ map formatModuleArgument rule.args
628 ++ [ "# ${rule.name} (order ${toString rule.order})" ]
630 (lib.concatStringsSep "\n")
633 # Account management.
634 ${formatRules "account"}
636 # Authentication management.
637 ${formatRules "auth"}
639 # Password management.
640 ${formatRules "password"}
642 # Session management.
643 ${formatRules "session"}
646 # !!! TODO: move the LDAP stuff to the LDAP module, and the
647 # Samba stuff to the Samba module. This requires that the PAM
648 # module provides the right hooks.
650 autoOrderRules = lib.flip lib.pipe [
651 (lib.imap1 (index: rule: rule // { order = lib.mkDefault (10000 + index * 100); } ))
652 (map (rule: lib.nameValuePair rule.name (removeAttrs rule [ "name" ])))
656 account = autoOrderRules [
657 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
658 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
659 config_file = "/etc/security/pam_mysql.conf";
661 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
662 ignore_unknown_user = true;
664 { 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"; }
665 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; }
666 { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; }
667 { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; }
668 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
669 # The required pam_unix.so module has to come after all the sufficient modules
670 # because otherwise, the account lookup will fail if the user does not exist
671 # locally, for example with MySQL- or LDAP-auth.
672 { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
675 auth = autoOrderRules ([
676 { 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"; }
677 { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "${package}/lib/security/pam_rootok.so"; }
678 { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "${package}/lib/security/pam_wheel.so"; settings = {
681 { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "${package}/lib/security/pam_faillock.so"; }
682 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
683 config_file = "/etc/security/pam_mysql.conf";
685 { 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 = {
686 file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles;
688 (let inherit (config.security.pam) rssh; in { name = "rssh"; enable = rssh.enable && cfg.rssh; control = "sufficient"; modulePath = "${pkgs.pam_rssh}/lib/libpam_rssh.so"; inherit (rssh) settings; })
689 (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 = [
690 "${pkgs.opensc}/lib/opensc-pkcs11.so"
692 (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; })
693 (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 = {
694 ca_file = ussh.caFile;
695 authorized_principals = ussh.authorizedPrincipals;
696 authorized_principals_file = ussh.authorizedPrincipalsFile;
697 inherit (ussh) group;
699 (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; settings = {
700 inherit (oath) window digits;
701 usersfile = oath.usersFile;
703 (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 = {
704 inherit (yubi) mode debug;
705 chalresp_path = yubi.challengeResponsePath;
706 id = lib.mkIf (yubi.mode == "client") yubi.id;
708 (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 = [
711 { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${config.services.fprintd.package}/lib/security/pam_fprintd.so"; }
713 # Modules in this block require having the password set in PAM_AUTHTOK.
714 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
715 # after it succeeds. Certain modules need to run after pam_unix
716 # prompts the user for password so we run it once with 'optional' at an
717 # earlier point and it will run again with 'sufficient' further down.
718 # We use try_first_pass the second time to avoid prompting password twice.
720 # The same principle applies to systemd-homed
721 (lib.optionals ((cfg.unixAuth || config.services.homed.enable) &&
722 (config.security.pam.enableEcryptfs
723 || config.security.pam.enableFscrypt
725 || cfg.kwallet.enable
726 || cfg.enableGnomeKeyring
727 || config.services.intune.enable
728 || cfg.googleAuthenticator.enable
730 || cfg.failDelay.enable
731 || cfg.duoSecurity.enable
734 { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
735 { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
736 nullok = cfg.allowNullPassword;
737 inherit (cfg) nodelay;
740 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; settings = {
743 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
744 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
745 inherit (config.security.pam.zfs) homes;
747 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = {
748 disable_interactive = true;
750 { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; }
751 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; }
752 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
753 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
754 store-only = cfg.gnupg.storeOnly;
756 { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${package}/lib/security/pam_faildelay.so"; settings = {
757 inherit (cfg.failDelay) delay;
759 { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = {
760 no_increment_hotp = true;
762 { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; }
764 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
765 { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
766 nullok = cfg.allowNullPassword;
767 inherit (cfg) nodelay;
769 try_first_pass = true;
771 { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; }
772 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = {
773 use_first_pass = true;
775 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; settings = {
776 ignore_unknown_user = true;
777 use_first_pass = true;
779 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; settings = {
780 use_first_pass = true;
782 { 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 = {
783 use_first_pass = true;
785 { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = {
787 use_first_pass = true;
789 { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = {
791 use_first_pass = true;
793 { name = "deny"; control = "required"; modulePath = "${package}/lib/security/pam_deny.so"; }
796 password = autoOrderRules [
797 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
798 { name = "unix"; control = "sufficient"; modulePath = "${package}/lib/security/pam_unix.so"; settings = {
802 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; }
803 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
804 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
805 inherit (config.security.pam.zfs) homes;
807 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; }
808 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
809 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
810 config_file = "/etc/security/pam_mysql.conf";
812 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
813 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
814 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = {
815 use_first_pass = true;
817 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
822 session = autoOrderRules [
823 { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "${package}/lib/security/pam_env.so"; settings = {
824 conffile = "/etc/pam/environment";
827 { name = "unix"; control = "required"; modulePath = "${package}/lib/security/pam_unix.so"; }
828 { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "${package}/lib/security/pam_loginuid.so"; }
829 { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${package}/lib/security/pam_tty_audit.so"; settings = {
830 open_only = cfg.ttyAudit.openOnly;
831 enable = cfg.ttyAudit.enablePattern;
832 disable = cfg.ttyAudit.disablePattern;
834 { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; }
835 { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${package}/lib/security/pam_mkhomedir.so"; settings = {
837 skel = config.security.pam.makeHomeDir.skelDirectory;
838 inherit (config.security.pam.makeHomeDir) umask;
840 { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${package}/lib/security/pam_lastlog.so"; settings = {
843 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; }
844 # Work around https://github.com/systemd/systemd/issues/8598
845 # Skips the pam_fscrypt module for systemd-user sessions which do not have a password
847 # See also https://github.com/google/fscrypt/issues/95
848 { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
849 "service" "=" "systemd-user"
851 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; }
852 { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "${package}/lib/security/pam_succeed_if.so"; args = [
853 "service" "=" "systemd-user"
855 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = {
856 inherit (config.security.pam.zfs) homes;
857 nounmount = config.security.pam.zfs.noUnmount;
859 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = {
860 disable_interactive = true;
862 { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; }
863 { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = {
864 config_file = "/etc/security/pam_mysql.conf";
866 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${config.services.kanidm.package}/lib/pam_kanidm.so"; }
867 { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; }
868 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; }
869 { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; }
870 { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; }
871 { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "${package}/lib/security/pam_xauth.so"; settings = {
872 xauthpath = "${pkgs.xorg.xauth}/bin/xauth";
875 { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${package}/lib/security/pam_limits.so"; settings = {
876 conf = "${makeLimitsConf cfg.limits}";
878 { name = "motd"; enable = cfg.showMotd && (config.users.motd != "" || config.users.motdFile != null); control = "optional"; modulePath = "${package}/lib/security/pam_motd.so"; settings = {
881 { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; settings = {
882 order = "user,group,default";
885 { 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; }; }
886 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = {
889 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = {
890 no-autostart = cfg.gnupg.noAutostart;
892 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; }
900 inherit (pkgs) pam_krb5 pam_ccreds;
902 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
903 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
905 # Create a limits.conf(5) file.
906 makeLimitsConf = limits:
907 pkgs.writeText "limits.conf"
908 (lib.concatMapStrings ({ domain, type, item, value }:
909 "${domain} ${type} ${item} ${toString value}\n")
912 limitsType = with lib.types; listOf (submodule ({ ... }: {
914 domain = lib.mkOption {
915 description = "Username, groupname, or wildcard this limit applies to";
920 type = lib.mkOption {
921 description = "Type of this limit";
922 type = enum [ "-" "hard" "soft" ];
926 item = lib.mkOption {
927 description = "Item this limit applies to";
950 value = lib.mkOption {
951 description = "Value of this limit";
952 type = oneOf [ str int ];
957 motd = if config.users.motdFile == null
958 then pkgs.writeText "motd" config.users.motd
959 else config.users.motdFile;
961 makePAMService = name: service:
962 { name = "pam.d/${name}";
963 value.source = pkgs.writeText "${name}.pam" service.text;
966 optionalSudoConfigForSSHAgentAuth = lib.optionalString
967 (config.security.pam.sshAgentAuth.enable || config.security.pam.rssh.enable) ''
968 # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so and libpam_rssh.so can do their magic.
969 Defaults env_keep+=SSH_AUTH_SOCK
976 meta.maintainers = [ lib.maintainers.majiir ];
979 (lib.mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ])
980 (lib.mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ])
981 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "authFile" ] [ "security" "pam" "u2f" "settings" "authfile" ])
982 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "appId" ] [ "security" "pam" "u2f" "settings" "appid" ])
983 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "origin" ] [ "security" "pam" "u2f" "settings" "origin" ])
984 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "debug" ] [ "security" "pam" "u2f" "settings" "debug" ])
985 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "interactive" ] [ "security" "pam" "u2f" "settings" "interactive" ])
986 (lib.mkRenamedOptionModule [ "security" "pam" "u2f" "cue" ] [ "security" "pam" "u2f" "settings" "cue" ])
993 security.pam.package = lib.mkPackageOption pkgs "pam" { };
995 security.pam.loginLimits = lib.mkOption {
1004 { domain = "@student";
1012 Define resource limits that should apply to users or groups.
1013 Each item in the list should be an attribute set with a
1014 {var}`domain`, {var}`type`,
1015 {var}`item`, and {var}`value`
1016 attribute. The syntax and semantics of these attributes
1017 must be that described in {manpage}`limits.conf(5)`.
1019 Note that these limits do not apply to systemd services,
1020 whose limits can be changed via {option}`systemd.extraConfig`
1025 security.pam.services = lib.mkOption {
1027 type = with lib.types; attrsOf (submodule pamOpts);
1029 This option defines the PAM services. A service typically
1030 corresponds to a program that uses PAM,
1031 e.g. {command}`login` or {command}`passwd`.
1032 Each attribute of this set defines a PAM service, with the attribute name
1033 defining the name of the service.
1037 security.pam.makeHomeDir.skelDirectory = lib.mkOption {
1038 type = lib.types.str;
1039 default = "/var/empty";
1040 example = "/etc/skel";
1042 Path to skeleton directory whose contents are copied to home
1043 directories newly created by `pam_mkhomedir`.
1047 security.pam.makeHomeDir.umask = lib.mkOption {
1048 type = lib.types.str;
1052 The user file mode creation mask to use on home directories
1053 newly created by `pam_mkhomedir`.
1057 security.pam.sshAgentAuth = {
1058 enable = lib.mkEnableOption ''
1059 authenticating using a signature performed by the ssh-agent.
1060 This allows using SSH keys exclusively, instead of passwords, for instance on remote machines
1063 authorizedKeysFiles = lib.mkOption {
1064 type = with lib.types; listOf str;
1066 A list of paths to files in OpenSSH's `authorized_keys` format, containing
1067 the keys that will be trusted by the `pam_ssh_agent_auth` module.
1069 The following patterns are expanded when interpreting the path:
1070 - `%f` and `%H` respectively expand to the fully-qualified and short hostname ;
1071 - `%u` expands to the username ;
1072 - `~` or `%h` expands to the user's home directory.
1075 Specifying user-writeable files here result in an insecure configuration: a malicious process
1076 can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
1078 See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611)
1081 default = [ "/etc/ssh/authorized_keys.d/%u" ];
1085 security.pam.rssh = {
1086 enable = lib.mkEnableOption "authenticating using a signature performed by the ssh-agent";
1088 settings = lib.mkOption {
1089 type = lib.types.submodule {
1090 freeformType = moduleSettingsType;
1092 auth_key_file = lib.mkOption {
1093 type = with lib.types; nullOr nonEmptyStr;
1095 Path to file with trusted public keys in OpenSSH's `authorized_keys` format. The following
1096 variables are expanded to the respective PAM items:
1098 - `service`: `PAM_SERVICE`, the service name,
1099 - `user`: `PAM_USER`, the username of the entity under whose identity service will be given,
1100 - `tty`: `PAM_TTY`, the terminal name,
1101 - `rhost`: `PAM_RHOST`, the requesting hostname, and
1102 - `ruser`: `PAM_RUSER`, the requesting entity.
1104 These PAM items are explained in {manpage}`pam_get_item(3)`.
1106 Variables may be specified as `$var`, `''${var}` or `''${var:defaultValue}`.
1109 Specifying user-writeable files here results in an insecure configuration: a malicious process
1110 can then edit such an `authorized_keys` file and bypass the ssh-agent-based authentication.
1112 This option is ignored if {option}`security.pam.rssh.settings.authorized_keys_command` is set.
1114 If both this option and {option}`security.pam.rssh.settings.authorized_keys_command` are unset,
1115 the keys will be read from `''${HOME}/.ssh/authorized_keys`, which should be considered
1118 default = "/etc/ssh/authorized_keys.d/$ruser";
1125 Options to pass to the pam_rssh module. Refer to
1126 <https://github.com/z4yx/pam_rssh/blob/main/README.md#optional-arguments>
1127 for supported values.
1129 ${moduleSettingsDescription}
1134 security.pam.enableOTPW = lib.mkEnableOption "the OTPW (one-time password) PAM module";
1136 security.pam.dp9ik = {
1137 enable = lib.mkEnableOption ''
1138 the dp9ik pam module provided by tlsclient.
1140 If set, users can be authenticated against the 9front
1141 authentication server given in {option}`security.pam.dp9ik.authserver`
1143 control = lib.mkOption {
1144 default = "sufficient";
1145 type = lib.types.str;
1147 This option sets the pam "control" used for this module.
1150 authserver = lib.mkOption {
1152 type = with lib.types; nullOr str;
1154 This controls the hostname for the 9front authentication server
1155 that users will be authenticated against.
1160 security.pam.krb5 = {
1161 enable = lib.mkOption {
1162 default = config.security.krb5.enable;
1163 defaultText = lib.literalExpression "config.security.krb5.enable";
1164 type = lib.types.bool;
1166 Enables Kerberos PAM modules (`pam-krb5`,
1169 If set, users can authenticate with their Kerberos password.
1170 This requires a valid Kerberos configuration
1171 (`config.security.krb5.enable` should be set to
1174 Note that the Kerberos PAM modules are not necessary when using SSS
1175 to handle Kerberos authentication.
1180 security.pam.p11 = {
1181 enable = lib.mkOption {
1183 type = lib.types.bool;
1185 Enables P11 PAM (`pam_p11`) module.
1187 If set, users can log in with SSH keys and PKCS#11 tokens.
1189 More information can be found [here](https://github.com/OpenSC/pam_p11).
1193 control = lib.mkOption {
1194 default = "sufficient";
1195 type = lib.types.enum [ "required" "requisite" "sufficient" "lib.optional" ];
1197 This option sets pam "control".
1198 If you want to have multi factor authentication, use "required".
1199 If you want to use the PKCS#11 device instead of the regular password,
1203 {manpage}`pam.conf(5)`
1204 for better understanding of this option.
1209 security.pam.u2f = {
1210 enable = lib.mkOption {
1212 type = lib.types.bool;
1214 Enables U2F PAM (`pam-u2f`) module.
1216 If set, users listed in
1217 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1218 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1219 not set) are able to log in with the associated U2F key. The path can
1220 be changed using {option}`security.pam.u2f.authFile` option.
1223 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
1224 This file can be generated using {command}`pamu2fcfg` command.
1226 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1230 control = lib.mkOption {
1231 default = "sufficient";
1232 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1234 This option sets pam "control".
1235 If you want to have multi factor authentication, use "required".
1236 If you want to use U2F device instead of regular password, use "sufficient".
1239 {manpage}`pam.conf(5)`
1240 for better understanding of this option.
1244 settings = lib.mkOption {
1245 type = lib.types.submodule {
1246 freeformType = moduleSettingsType;
1249 authfile = lib.mkOption {
1251 type = with lib.types; nullOr path;
1253 By default `pam-u2f` module reads the keys from
1254 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or
1255 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is
1258 If you want to change auth file locations or centralize database (for
1259 example use {file}`/etc/u2f-mappings`) you can set this
1263 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key`
1264 This file can be generated using {command}`pamu2fcfg` command.
1266 More information can be found [here](https://developers.yubico.com/pam-u2f/).
1270 appid = lib.mkOption {
1272 type = with lib.types; nullOr str;
1274 By default `pam-u2f` module sets the application
1275 ID to `pam://$HOSTNAME`.
1277 When using {command}`pamu2fcfg`, you can specify your
1278 application ID with the `-i` flag.
1280 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1284 origin = lib.mkOption {
1286 type = with lib.types; nullOr str;
1288 By default `pam-u2f` module sets the origin
1289 to `pam://$HOSTNAME`.
1290 Setting origin to an host independent value will allow you to
1291 reuse credentials across machines
1293 When using {command}`pamu2fcfg`, you can specify your
1294 application ID with the `-o` flag.
1296 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html)
1300 debug = lib.mkOption {
1302 type = lib.types.bool;
1304 Debug output to stderr.
1308 interactive = lib.mkOption {
1310 type = lib.types.bool;
1312 Set to prompt a message and wait before testing the presence of a U2F device.
1313 Recommended if your device doesn’t have a tactile trigger.
1317 cue = lib.mkOption {
1319 type = lib.types.bool;
1321 By default `pam-u2f` module does not inform user
1322 that he needs to use the u2f device, it just waits without a prompt.
1324 If you set this option to `true`,
1325 `cue` option is added to `pam-u2f`
1326 module and reminder message will be displayed.
1333 authfile = "/etc/u2f_keys";
1334 authpending_file = "";
1336 pinverification = 1;
1339 Options to pass to the PAM module.
1341 ${moduleSettingsDescription}
1346 security.pam.ussh = {
1347 enable = lib.mkOption {
1349 type = lib.types.bool;
1351 Enables Uber's USSH PAM (`pam-ussh`) module.
1353 This is similar to `pam-ssh-agent`, except that
1354 the presence of a CA-signed SSH key with a valid principal is checked
1357 Note that this module must both be enabled using this option and on a
1358 per-PAM-service level as well (using `usshAuth`).
1360 More information can be found [here](https://github.com/uber/pam-ussh).
1364 caFile = lib.mkOption {
1366 type = with lib.types; nullOr path;
1368 By default `pam-ussh` reads the trusted user CA keys
1369 from {file}`/etc/ssh/trusted_user_ca`.
1371 This should be set the same as your `TrustedUserCAKeys`
1376 authorizedPrincipals = lib.mkOption {
1378 type = with lib.types; nullOr commas;
1380 Comma-separated list of authorized principals to permit; if the user
1381 presents a certificate with one of these principals, then they will be
1384 Note that `pam-ussh` also requires that the certificate
1385 contain a principal matching the user's username. The principals from
1386 this list are in addition to those principals.
1388 Mutually exclusive with `authorizedPrincipalsFile`.
1392 authorizedPrincipalsFile = lib.mkOption {
1394 type = with lib.types; nullOr path;
1396 Path to a list of principals; if the user presents a certificate with
1397 one of these principals, then they will be authorized.
1399 Note that `pam-ussh` also requires that the certificate
1400 contain a principal matching the user's username. The principals from
1401 this file are in addition to those principals.
1403 Mutually exclusive with `authorizedPrincipals`.
1407 group = lib.mkOption {
1409 type = with lib.types; nullOr str;
1411 If set, then the authenticating user must be a member of this group
1416 control = lib.mkOption {
1417 default = "sufficient";
1418 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1420 This option sets pam "control".
1421 If you want to have multi factor authentication, use "required".
1422 If you want to use the SSH certificate instead of the regular password,
1426 {manpage}`pam.conf(5)`
1427 for better understanding of this option.
1432 security.pam.yubico = {
1433 enable = lib.mkOption {
1435 type = lib.types.bool;
1437 Enables Yubico PAM (`yubico-pam`) module.
1439 If set, users listed in
1440 {file}`~/.yubico/authorized_yubikeys`
1441 are able to log in with the associated Yubikey tokens.
1443 The file must have only one line:
1444 `username:yubikey_token_id1:yubikey_token_id2`
1445 More information can be found [here](https://developers.yubico.com/yubico-pam/).
1448 control = lib.mkOption {
1449 default = "sufficient";
1450 type = lib.types.enum [ "required" "requisite" "sufficient" "optional" ];
1452 This option sets pam "control".
1453 If you want to have multi factor authentication, use "required".
1454 If you want to use Yubikey instead of regular password, use "sufficient".
1457 {manpage}`pam.conf(5)`
1458 for better understanding of this option.
1463 type = lib.types.str;
1464 description = "client id";
1467 debug = lib.mkOption {
1469 type = lib.types.bool;
1471 Debug output to stderr.
1474 mode = lib.mkOption {
1476 type = lib.types.enum [ "client" "challenge-response" ];
1480 Use "client" for online validation with a YubiKey validation service such as
1483 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1
1484 Challenge-Response configurations. See the man-page ykpamcfg(1) for further
1485 details on how to configure offline Challenge-Response validation.
1487 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
1490 challengeResponsePath = lib.mkOption {
1492 type = lib.types.nullOr lib.types.path;
1494 If not null, set the path used by yubico pam module where the challenge expected response is stored.
1496 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html).
1501 security.pam.zfs = {
1502 enable = lib.mkOption {
1504 type = lib.types.bool;
1506 Enable unlocking and mounting of encrypted ZFS home dataset at login.
1510 homes = lib.mkOption {
1511 example = "rpool/home";
1512 default = "rpool/home";
1513 type = lib.types.str;
1515 Prefix of home datasets. This value will be concatenated with
1516 `"/" + <username>` in order to determine the home dataset to unlock.
1520 noUnmount = lib.mkOption {
1522 type = lib.types.bool;
1524 Do not unmount home dataset on logout.
1529 security.pam.enableEcryptfs = lib.mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)";
1530 security.pam.enableFscrypt = lib.mkEnableOption ''
1531 fscrypt, to automatically unlock directories with the user's login password.
1533 This also enables a service at security.pam.services.fscrypt which is used by
1534 fscrypt to verify the user's password when setting up a new protector. If you
1535 use something other than pam_unix to verify user passwords, please remember to
1536 adjust this PAM service
1539 users.motd = lib.mkOption {
1541 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
1542 type = lib.types.lines;
1543 description = "Message of the day shown to users when they log in.";
1546 users.motdFile = lib.mkOption {
1548 example = "/etc/motd";
1549 type = lib.types.nullOr lib.types.path;
1550 description = "A file containing the message of the day shown to users when they log in.";
1555 ###### implementation
1560 assertion = config.users.motd == "" || config.users.motdFile == null;
1562 Only one of users.motd and users.motdFile can be set.
1566 assertion = config.security.pam.zfs.enable -> config.boot.zfs.enabled;
1568 `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled`).
1572 assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != [];
1574 `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list.
1575 Did you forget to set `services.openssh.enable` ?
1579 assertion = with config.security.pam.rssh;
1580 enable -> (settings.auth_key_file or null != null || settings.authorized_keys_command or null != null);
1582 security.pam.rssh.enable requires either security.pam.rssh.settings.auth_key_file or
1583 security.pam.rssh.settings.authorized_keys_command to be set.
1588 warnings = lib.optional
1589 (with config.security.pam.sshAgentAuth;
1590 enable && lib.any (s: lib.hasPrefix "%h" s || lib.hasPrefix "~" s) authorizedKeysFiles)
1591 ''config.security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory.
1593 Specifying user-writeable files there result in an insecure configuration:
1594 a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication.
1595 See https://github.com/NixOS/nixpkgs/issues/31611
1597 (with config.security.pam.rssh;
1598 enable && settings.auth_key_file or null != null && settings.authorized_keys_command or null != null) ''
1599 security.pam.rssh.settings.auth_key_file will be ignored as
1600 security.pam.rssh.settings.authorized_keys_command has been specified.
1601 Explictly set the former to null to silence this warning.
1604 environment.systemPackages =
1605 # Include the PAM modules in the system path mostly for the manpages.
1607 ++ lib.optional config.users.ldap.enable pam_ldap
1608 ++ lib.optional config.services.kanidm.enablePam config.services.kanidm.package
1609 ++ lib.optional config.services.sssd.enable pkgs.sssd
1610 ++ lib.optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds]
1611 ++ lib.optionals config.security.pam.enableOTPW [ pkgs.otpw ]
1612 ++ lib.optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ]
1613 ++ lib.optionals config.security.pam.p11.enable [ pkgs.pam_p11 ]
1614 ++ lib.optionals config.security.pam.enableFscrypt [ pkgs.fscrypt-experimental ]
1615 ++ lib.optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ];
1617 boot.supportedFilesystems = lib.optionals config.security.pam.enableEcryptfs [ "ecryptfs" ];
1619 security.wrappers = {
1624 source = "${package}/bin/unix_chkpwd";
1628 environment.etc = lib.mapAttrs' makePAMService config.security.pam.services;
1630 security.pam.services =
1633 auth required pam_warn.so
1634 auth required pam_deny.so
1635 account required pam_warn.so
1636 account required pam_deny.so
1637 password required pam_warn.so
1638 password required pam_deny.so
1639 session required pam_warn.so
1640 session required pam_deny.so
1643 # Most of these should be moved to specific modules.
1650 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; };
1652 /* FIXME: should runuser -l start a systemd session? Currently
1653 it complains "Cannot create session: Already running in a
1655 runuser-l = { rootOK = true; unixAuth = false; };
1656 } // lib.optionalAttrs (config.security.pam.enableFscrypt) {
1657 # Allow fscrypt to verify login passphrase
1661 security.apparmor.includes."abstractions/pam" =
1662 lib.concatMapStrings
1663 (name: "r ${config.environment.etc."pam.d/${name}".source},\n")
1664 (lib.attrNames config.security.pam.services) +
1665 (with lib; pipe config.security.pam.services [
1668 (lib.concatMap lib.attrValues)
1669 (lib.concatMap lib.attrValues)
1670 (lib.filter (rule: rule.enable))
1671 (lib.catAttrs "modulePath")
1672 # TODO(@uninsane): replace this warning + lib.filter with just an assertion
1673 (map (modulePath: lib.warnIfNot
1674 (lib.hasPrefix "/" modulePath)
1675 ''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>''
1678 (lib.filter (lib.hasPrefix "/"))
1680 (map (module: "mr ${module},"))
1684 security.sudo.extraConfig = optionalSudoConfigForSSHAgentAuth;
1685 security.sudo-rs.extraConfig = optionalSudoConfigForSSHAgentAuth;