at 24.11-pre 63 kB view raw
1# This module provides configuration for the PAM (Pluggable 2# Authentication Modules) system. 3 4{ config, lib, pkgs, ... }: 5 6with lib; 7 8let 9 10 mkRulesTypeOption = type: mkOption { 11 # These options are experimental and subject to breaking changes without notice. 12 description = '' 13 PAM `${type}` rules for this service. 14 15 Attribute keys are the name of each rule. 16 ''; 17 type = types.attrsOf (types.submodule ({ name, config, ... }: { 18 options = { 19 name = mkOption { 20 type = types.str; 21 description = '' 22 Name of this rule. 23 ''; 24 internal = true; 25 readOnly = true; 26 }; 27 enable = mkOption { 28 type = types.bool; 29 default = true; 30 description = '' 31 Whether this rule is added to the PAM service config file. 32 ''; 33 }; 34 order = mkOption { 35 type = types.int; 36 description = '' 37 Order of this rule in the service file. Rules are arranged in ascending order of this value. 38 39 ::: {.warning} 40 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: 41 42 ```nix 43 { 44 security.pam.services.login.rules.auth.foo.order = 45 config.security.pam.services.login.rules.auth.unix.order + 10; 46 } 47 ``` 48 ::: 49 ''; 50 }; 51 control = mkOption { 52 type = types.str; 53 description = '' 54 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. 55 ''; 56 }; 57 modulePath = mkOption { 58 type = types.str; 59 description = '' 60 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. 61 ''; 62 }; 63 args = mkOption { 64 type = types.listOf types.str; 65 description = '' 66 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. 67 68 Escaping rules for spaces and square brackets are automatically applied. 69 70 {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. 71 ''; 72 }; 73 settings = mkOption { 74 type = with types; attrsOf (nullOr (oneOf [ bool str int pathInStore ])); 75 default = {}; 76 description = '' 77 Settings to add as `module-arguments`. 78 79 Boolean values render just the key if true, and nothing if false. Null values are ignored. All other values are rendered as key-value pairs. 80 ''; 81 }; 82 }; 83 config = { 84 inherit name; 85 # Formats an attrset of settings as args for use as `module-arguments`. 86 args = concatLists (flip mapAttrsToList config.settings (name: value: 87 if isBool value 88 then optional value name 89 else optional (value != null) "${name}=${toString value}" 90 )); 91 }; 92 })); 93 }; 94 95 parentConfig = config; 96 97 pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in { 98 99 imports = [ 100 (lib.mkRenamedOptionModule [ "enableKwallet" ] [ "kwallet" "enable" ]) 101 ]; 102 103 options = { 104 105 name = mkOption { 106 example = "sshd"; 107 type = types.str; 108 description = "Name of the PAM service."; 109 }; 110 111 rules = mkOption { 112 # This option is experimental and subject to breaking changes without notice. 113 visible = false; 114 115 description = '' 116 PAM rules for this service. 117 118 ::: {.warning} 119 This option and its suboptions are experimental and subject to breaking changes without notice. 120 121 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. 122 123 If you share configuration examples that use this option, you MUST include this warning so that users are informed. 124 125 You may freely use this option within `nixpkgs`, and future changes will account for those use sites. 126 ::: 127 ''; 128 type = types.submodule { 129 options = genAttrs [ "account" "auth" "password" "session" ] mkRulesTypeOption; 130 }; 131 }; 132 133 unixAuth = mkOption { 134 default = true; 135 type = types.bool; 136 description = '' 137 Whether users can log in with passwords defined in 138 {file}`/etc/shadow`. 139 ''; 140 }; 141 142 rootOK = mkOption { 143 default = false; 144 type = types.bool; 145 description = '' 146 If set, root doesn't need to authenticate (e.g. for the 147 {command}`useradd` service). 148 ''; 149 }; 150 151 p11Auth = mkOption { 152 default = config.security.pam.p11.enable; 153 defaultText = literalExpression "config.security.pam.p11.enable"; 154 type = types.bool; 155 description = '' 156 If set, keys listed in 157 {file}`~/.ssh/authorized_keys` and 158 {file}`~/.eid/authorized_certificates` 159 can be used to log in with the associated PKCS#11 tokens. 160 ''; 161 }; 162 163 u2fAuth = mkOption { 164 default = config.security.pam.u2f.enable; 165 defaultText = literalExpression "config.security.pam.u2f.enable"; 166 type = types.bool; 167 description = '' 168 If set, users listed in 169 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or 170 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is 171 not set) are able to log in with the associated U2F key. Path can be 172 changed using {option}`security.pam.u2f.authFile` option. 173 ''; 174 }; 175 176 usshAuth = mkOption { 177 default = false; 178 type = types.bool; 179 description = '' 180 If set, users with an SSH certificate containing an authorized principal 181 in their SSH agent are able to log in. Specific options are controlled 182 using the {option}`security.pam.ussh` options. 183 184 Note that the {option}`security.pam.ussh.enable` must also be 185 set for this option to take effect. 186 ''; 187 }; 188 189 yubicoAuth = mkOption { 190 default = config.security.pam.yubico.enable; 191 defaultText = literalExpression "config.security.pam.yubico.enable"; 192 type = types.bool; 193 description = '' 194 If set, users listed in 195 {file}`~/.yubico/authorized_yubikeys` 196 are able to log in with the associated Yubikey tokens. 197 ''; 198 }; 199 200 googleAuthenticator = { 201 enable = mkOption { 202 default = false; 203 type = types.bool; 204 description = '' 205 If set, users with enabled Google Authenticator (created 206 {file}`~/.google_authenticator`) will be required 207 to provide Google Authenticator token to log in. 208 ''; 209 }; 210 }; 211 212 otpwAuth = mkOption { 213 default = config.security.pam.enableOTPW; 214 defaultText = literalExpression "config.security.pam.enableOTPW"; 215 type = types.bool; 216 description = '' 217 If set, the OTPW system will be used (if 218 {file}`~/.otpw` exists). 219 ''; 220 }; 221 222 googleOsLoginAccountVerification = mkOption { 223 default = false; 224 type = types.bool; 225 description = '' 226 If set, will use the Google OS Login PAM modules 227 (`pam_oslogin_login`, 228 `pam_oslogin_admin`) to verify possible OS Login 229 users and set sudoers configuration accordingly. 230 This only makes sense to enable for the `sshd` PAM 231 service. 232 ''; 233 }; 234 235 googleOsLoginAuthentication = mkOption { 236 default = false; 237 type = types.bool; 238 description = '' 239 If set, will use the `pam_oslogin_login`'s user 240 authentication methods to authenticate users using 2FA. 241 This only makes sense to enable for the `sshd` PAM 242 service. 243 ''; 244 }; 245 246 mysqlAuth = mkOption { 247 default = config.users.mysql.enable; 248 defaultText = literalExpression "config.users.mysql.enable"; 249 type = types.bool; 250 description = '' 251 If set, the `pam_mysql` module will be used to 252 authenticate users against a MySQL/MariaDB database. 253 ''; 254 }; 255 256 fprintAuth = mkOption { 257 default = config.services.fprintd.enable; 258 defaultText = literalExpression "config.services.fprintd.enable"; 259 type = types.bool; 260 description = '' 261 If set, fingerprint reader will be used (if exists and 262 your fingerprints are enrolled). 263 ''; 264 }; 265 266 oathAuth = mkOption { 267 default = config.security.pam.oath.enable; 268 defaultText = literalExpression "config.security.pam.oath.enable"; 269 type = types.bool; 270 description = '' 271 If set, the OATH Toolkit will be used. 272 ''; 273 }; 274 275 sshAgentAuth = mkOption { 276 default = false; 277 type = types.bool; 278 description = '' 279 If set, the calling user's SSH agent is used to authenticate 280 against the keys in the calling user's 281 {file}`~/.ssh/authorized_keys`. This is useful 282 for {command}`sudo` on password-less remote systems. 283 ''; 284 }; 285 286 duoSecurity = { 287 enable = mkOption { 288 default = false; 289 type = types.bool; 290 description = '' 291 If set, use the Duo Security pam module 292 `pam_duo` for authentication. Requires 293 configuration of {option}`security.duosec` options. 294 ''; 295 }; 296 }; 297 298 startSession = mkOption { 299 default = false; 300 type = types.bool; 301 description = '' 302 If set, the service will register a new session with 303 systemd's login manager. For local sessions, this will give 304 the user access to audio devices, CD-ROM drives. In the 305 default PolicyKit configuration, it also allows the user to 306 reboot the system. 307 ''; 308 }; 309 310 setEnvironment = mkOption { 311 type = types.bool; 312 default = true; 313 description = '' 314 Whether the service should set the environment variables 315 listed in {option}`environment.sessionVariables` 316 using `pam_env.so`. 317 ''; 318 }; 319 320 setLoginUid = mkOption { 321 type = types.bool; 322 description = '' 323 Set the login uid of the process 324 ({file}`/proc/self/loginuid`) for auditing 325 purposes. The login uid is only set by entry points like 326 {command}`login` and {command}`sshd`, not by 327 commands like {command}`sudo`. 328 ''; 329 }; 330 331 ttyAudit = { 332 enable = mkOption { 333 type = types.bool; 334 default = false; 335 description = '' 336 Enable or disable TTY auditing for specified users 337 ''; 338 }; 339 340 enablePattern = mkOption { 341 type = types.nullOr types.str; 342 default = null; 343 description = '' 344 For each user matching one of comma-separated 345 glob patterns, enable TTY auditing 346 ''; 347 }; 348 349 disablePattern = mkOption { 350 type = types.nullOr types.str; 351 default = null; 352 description = '' 353 For each user matching one of comma-separated 354 glob patterns, disable TTY auditing 355 ''; 356 }; 357 358 openOnly = mkOption { 359 type = types.bool; 360 default = false; 361 description = '' 362 Set the TTY audit flag when opening the session, 363 but do not restore it when closing the session. 364 Using this option is necessary for some services 365 that don't fork() to run the authenticated session, 366 such as sudo. 367 ''; 368 }; 369 }; 370 371 forwardXAuth = mkOption { 372 default = false; 373 type = types.bool; 374 description = '' 375 Whether X authentication keys should be passed from the 376 calling user to the target user (e.g. for 377 {command}`su`) 378 ''; 379 }; 380 381 pamMount = mkOption { 382 default = config.security.pam.mount.enable; 383 defaultText = literalExpression "config.security.pam.mount.enable"; 384 type = types.bool; 385 description = '' 386 Enable PAM mount (pam_mount) system to mount filesystems on user login. 387 ''; 388 }; 389 390 allowNullPassword = mkOption { 391 default = false; 392 type = types.bool; 393 description = '' 394 Whether to allow logging into accounts that have no password 395 set (i.e., have an empty password field in 396 {file}`/etc/passwd` or 397 {file}`/etc/group`). This does not enable 398 logging into disabled accounts (i.e., that have the password 399 field set to `!`). Note that regardless of 400 what the pam_unix documentation says, accounts with hashed 401 empty passwords are always allowed to log in. 402 ''; 403 }; 404 405 nodelay = mkOption { 406 default = false; 407 type = types.bool; 408 description = '' 409 Whether the delay after typing a wrong password should be disabled. 410 ''; 411 }; 412 413 requireWheel = mkOption { 414 default = false; 415 type = types.bool; 416 description = '' 417 Whether to permit root access only to members of group wheel. 418 ''; 419 }; 420 421 limits = mkOption { 422 default = []; 423 type = limitsType; 424 description = '' 425 Attribute set describing resource limits. Defaults to the 426 value of {option}`security.pam.loginLimits`. 427 The meaning of the values is explained in {manpage}`limits.conf(5)`. 428 ''; 429 }; 430 431 showMotd = mkOption { 432 default = false; 433 type = types.bool; 434 description = "Whether to show the message of the day."; 435 }; 436 437 makeHomeDir = mkOption { 438 default = false; 439 type = types.bool; 440 description = '' 441 Whether to try to create home directories for users 442 with `$HOME`s pointing to nonexistent 443 locations on session login. 444 ''; 445 }; 446 447 updateWtmp = mkOption { 448 default = false; 449 type = types.bool; 450 description = "Whether to update {file}`/var/log/wtmp`."; 451 }; 452 453 logFailures = mkOption { 454 default = false; 455 type = types.bool; 456 description = "Whether to log authentication failures in {file}`/var/log/faillog`."; 457 }; 458 459 enableAppArmor = mkOption { 460 default = false; 461 type = types.bool; 462 description = '' 463 Enable support for attaching AppArmor profiles at the 464 user/group level, e.g., as part of a role based access 465 control scheme. 466 ''; 467 }; 468 469 kwallet = { 470 enable = mkOption { 471 default = false; 472 type = types.bool; 473 description = '' 474 If enabled, pam_wallet will attempt to automatically unlock the 475 user's default KDE wallet upon login. If the user has no wallet named 476 "kdewallet", or the login password does not match their wallet 477 password, KDE will prompt separately after login. 478 ''; 479 }; 480 481 package = mkPackageOption pkgs.plasma5Packages "kwallet-pam" { 482 pkgsText = "pkgs.plasma5Packages"; 483 }; 484 }; 485 486 sssdStrictAccess = mkOption { 487 default = false; 488 type = types.bool; 489 description = "enforce sssd access control"; 490 }; 491 492 enableGnomeKeyring = mkOption { 493 default = false; 494 type = types.bool; 495 description = '' 496 If enabled, pam_gnome_keyring will attempt to automatically unlock the 497 user's default Gnome keyring upon login. If the user login password does 498 not match their keyring password, Gnome Keyring will prompt separately 499 after login. 500 ''; 501 }; 502 503 failDelay = { 504 enable = mkOption { 505 type = types.bool; 506 default = false; 507 description = '' 508 If enabled, this will replace the `FAIL_DELAY` setting from `login.defs`. 509 Change the delay on failure per-application. 510 ''; 511 }; 512 513 delay = mkOption { 514 default = 3000000; 515 type = types.int; 516 example = 1000000; 517 description = "The delay time (in microseconds) on failure."; 518 }; 519 }; 520 521 gnupg = { 522 enable = mkOption { 523 type = types.bool; 524 default = false; 525 description = '' 526 If enabled, pam_gnupg will attempt to automatically unlock the 527 user's GPG keys with the login password via 528 {command}`gpg-agent`. The keygrips of all keys to be 529 unlocked should be written to {file}`~/.pam-gnupg`, 530 and can be queried with {command}`gpg -K --with-keygrip`. 531 Presetting passphrases must be enabled by adding 532 `allow-preset-passphrase` in 533 {file}`~/.gnupg/gpg-agent.conf`. 534 ''; 535 }; 536 537 noAutostart = mkOption { 538 type = types.bool; 539 default = false; 540 description = '' 541 Don't start {command}`gpg-agent` if it is not running. 542 Useful in conjunction with starting {command}`gpg-agent` as 543 a systemd user service. 544 ''; 545 }; 546 547 storeOnly = mkOption { 548 type = types.bool; 549 default = false; 550 description = '' 551 Don't send the password immediately after login, but store for PAM 552 `session`. 553 ''; 554 }; 555 }; 556 557 zfs = mkOption { 558 default = config.security.pam.zfs.enable; 559 defaultText = literalExpression "config.security.pam.zfs.enable"; 560 type = types.bool; 561 description = '' 562 Enable unlocking and mounting of encrypted ZFS home dataset at login. 563 ''; 564 }; 565 566 text = mkOption { 567 type = types.nullOr types.lines; 568 description = "Contents of the PAM service file."; 569 }; 570 571 }; 572 573 # The resulting /etc/pam.d/* file contents are verified in 574 # nixos/tests/pam/pam-file-contents.nix. Please update tests there when 575 # changing the derivation. 576 config = { 577 name = mkDefault name; 578 setLoginUid = mkDefault cfg.startSession; 579 limits = mkDefault config.security.pam.loginLimits; 580 581 text = let 582 ensureUniqueOrder = type: rules: 583 let 584 checkPair = a: b: assert 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; 585 checked = zipListsWith checkPair rules (drop 1 rules); 586 in take 1 rules ++ checked; 587 # Formats a string for use in `module-arguments`. See `man pam.conf`. 588 formatModuleArgument = token: 589 if hasInfix " " token 590 then "[${replaceStrings ["]"] ["\\]"] token}]" 591 else token; 592 formatRules = type: pipe cfg.rules.${type} [ 593 attrValues 594 (filter (rule: rule.enable)) 595 (sort (a: b: a.order < b.order)) 596 (ensureUniqueOrder type) 597 (map (rule: concatStringsSep " " ( 598 [ type rule.control rule.modulePath ] 599 ++ map formatModuleArgument rule.args 600 ++ [ "# ${rule.name} (order ${toString rule.order})" ] 601 ))) 602 (concatStringsSep "\n") 603 ]; 604 in mkDefault '' 605 # Account management. 606 ${formatRules "account"} 607 608 # Authentication management. 609 ${formatRules "auth"} 610 611 # Password management. 612 ${formatRules "password"} 613 614 # Session management. 615 ${formatRules "session"} 616 ''; 617 618 # !!! TODO: move the LDAP stuff to the LDAP module, and the 619 # Samba stuff to the Samba module. This requires that the PAM 620 # module provides the right hooks. 621 rules = let 622 autoOrderRules = flip pipe [ 623 (imap1 (index: rule: rule // { order = mkDefault (10000 + index * 100); } )) 624 (map (rule: nameValuePair rule.name (removeAttrs rule [ "name" ]))) 625 listToAttrs 626 ]; 627 in { 628 account = autoOrderRules [ 629 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; } 630 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { 631 config_file = "/etc/security/pam_mysql.conf"; 632 }; } 633 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { 634 ignore_unknown_user = true; 635 }; } 636 { 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"; } 637 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; } 638 { name = "oslogin_login"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok ignore=ignore default=die]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; } 639 { name = "oslogin_admin"; enable = cfg.googleOsLoginAccountVerification; control = "[success=ok default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_admin.so"; } 640 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } 641 # The required pam_unix.so module has to come after all the sufficient modules 642 # because otherwise, the account lookup will fail if the user does not exist 643 # locally, for example with MySQL- or LDAP-auth. 644 { name = "unix"; control = "required"; modulePath = "pam_unix.so"; } 645 ]; 646 647 auth = autoOrderRules ([ 648 { 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"; } 649 { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; } 650 { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; settings = { 651 use_uid = true; 652 }; } 653 { name = "faillock"; enable = cfg.logFailures; control = "required"; modulePath = "pam_faillock.so"; } 654 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { 655 config_file = "/etc/security/pam_mysql.conf"; 656 }; } 657 { 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 = { 658 file = lib.concatStringsSep ":" config.security.pam.sshAgentAuth.authorizedKeysFiles; 659 }; } 660 (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 = [ 661 "${pkgs.opensc}/lib/opensc-pkcs11.so" 662 ]; }) 663 (let u2f = config.security.pam.u2f; in { name = "u2f"; enable = cfg.u2fAuth; control = u2f.control; modulePath = "${pkgs.pam_u2f}/lib/security/pam_u2f.so"; settings = { 664 inherit (u2f) debug interactive cue origin; 665 authfile = u2f.authFile; 666 appid = u2f.appId; 667 }; }) 668 (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 = { 669 ca_file = ussh.caFile; 670 authorized_principals = ussh.authorizedPrincipals; 671 authorized_principals_file = ussh.authorizedPrincipalsFile; 672 inherit (ussh) group; 673 }; }) 674 (let oath = config.security.pam.oath; in { name = "oath"; enable = cfg.oathAuth; control = "requisite"; modulePath = "${pkgs.oath-toolkit}/lib/security/pam_oath.so"; settings = { 675 inherit (oath) window digits; 676 usersfile = oath.usersFile; 677 }; }) 678 (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 = { 679 inherit (yubi) mode debug; 680 chalresp_path = yubi.challengeResponsePath; 681 id = mkIf (yubi.mode == "client") yubi.id; 682 }; }) 683 (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 = [ 684 dp9ik.authserver 685 ]; }) 686 { name = "fprintd"; enable = cfg.fprintAuth; control = "sufficient"; modulePath = "${config.services.fprintd.package}/lib/security/pam_fprintd.so"; } 687 ] ++ 688 # Modules in this block require having the password set in PAM_AUTHTOK. 689 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run 690 # after it succeeds. Certain modules need to run after pam_unix 691 # prompts the user for password so we run it once with 'optional' at an 692 # earlier point and it will run again with 'sufficient' further down. 693 # We use try_first_pass the second time to avoid prompting password twice. 694 # 695 # The same principle applies to systemd-homed 696 (optionals ((cfg.unixAuth || config.services.homed.enable) && 697 (config.security.pam.enableEcryptfs 698 || config.security.pam.enableFscrypt 699 || cfg.pamMount 700 || cfg.kwallet.enable 701 || cfg.enableGnomeKeyring 702 || config.services.intune.enable 703 || cfg.googleAuthenticator.enable 704 || cfg.gnupg.enable 705 || cfg.failDelay.enable 706 || cfg.duoSecurity.enable 707 || cfg.zfs)) 708 [ 709 { name = "systemd_home-early"; enable = config.services.homed.enable; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } 710 { name = "unix-early"; enable = cfg.unixAuth; control = "optional"; modulePath = "pam_unix.so"; settings = { 711 nullok = cfg.allowNullPassword; 712 inherit (cfg) nodelay; 713 likeauth = true; 714 }; } 715 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; settings = { 716 unwrap = true; 717 }; } 718 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; } 719 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { 720 inherit (config.security.pam.zfs) homes; 721 }; } 722 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { 723 disable_interactive = true; 724 }; } 725 { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; } 726 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; } 727 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; } 728 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { 729 store-only = cfg.gnupg.storeOnly; 730 }; } 731 { name = "faildelay"; enable = cfg.failDelay.enable; control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_faildelay.so"; settings = { 732 inherit (cfg.failDelay) delay; 733 }; } 734 { name = "google_authenticator"; enable = cfg.googleAuthenticator.enable; control = "required"; modulePath = "${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so"; settings = { 735 no_increment_hotp = true; 736 }; } 737 { name = "duo"; enable = cfg.duoSecurity.enable; control = "required"; modulePath = "${pkgs.duo-unix}/lib/security/pam_duo.so"; } 738 ]) ++ [ 739 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } 740 { name = "unix"; enable = cfg.unixAuth; control = "sufficient"; modulePath = "pam_unix.so"; settings = { 741 nullok = cfg.allowNullPassword; 742 inherit (cfg) nodelay; 743 likeauth = true; 744 try_first_pass = true; 745 }; } 746 { name = "otpw"; enable = cfg.otpwAuth; control = "sufficient"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; } 747 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; settings = { 748 use_first_pass = true; 749 }; } 750 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; settings = { 751 ignore_unknown_user = true; 752 use_first_pass = true; 753 }; } 754 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; settings = { 755 use_first_pass = true; 756 }; } 757 { 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 = { 758 use_first_pass = true; 759 }; } 760 { name = "ccreds-validate"; enable = config.security.pam.krb5.enable; control = "[default=die success=done]"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { 761 action = "validate"; 762 use_first_pass = true; 763 }; } 764 { name = "ccreds-store"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_ccreds}/lib/security/pam_ccreds.so"; settings = { 765 action = "store"; 766 use_first_pass = true; 767 }; } 768 { name = "deny"; control = "required"; modulePath = "pam_deny.so"; } 769 ]); 770 771 password = autoOrderRules [ 772 { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } 773 { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; settings = { 774 nullok = true; 775 yescrypt = true; 776 }; } 777 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; } 778 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; } 779 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { 780 inherit (config.security.pam.zfs) homes; 781 }; } 782 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; } 783 { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; } 784 { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { 785 config_file = "/etc/security/pam_mysql.conf"; 786 }; } 787 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "sufficient"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; } 788 { name = "sss"; enable = config.services.sssd.enable; control = "sufficient"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; } 789 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "sufficient"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; settings = { 790 use_first_pass = true; 791 }; } 792 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { 793 use_authtok = true; 794 }; } 795 ]; 796 797 session = autoOrderRules [ 798 { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = { 799 conffile = "/etc/pam/environment"; 800 readenv = 0; 801 }; } 802 { name = "unix"; control = "required"; modulePath = "pam_unix.so"; } 803 { name = "loginuid"; enable = cfg.setLoginUid; control = if config.boot.isContainer then "optional" else "required"; modulePath = "pam_loginuid.so"; } 804 { name = "tty_audit"; enable = cfg.ttyAudit.enable; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_tty_audit.so"; settings = { 805 open_only = cfg.ttyAudit.openOnly; 806 enable = cfg.ttyAudit.enablePattern; 807 disable = cfg.ttyAudit.disablePattern; 808 }; } 809 { name = "systemd_home"; enable = config.services.homed.enable; control = "required"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } 810 { name = "mkhomedir"; enable = cfg.makeHomeDir; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_mkhomedir.so"; settings = { 811 silent = true; 812 skel = config.security.pam.makeHomeDir.skelDirectory; 813 inherit (config.security.pam.makeHomeDir) umask; 814 }; } 815 { name = "lastlog"; enable = cfg.updateWtmp; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_lastlog.so"; settings = { 816 silent = true; 817 }; } 818 { name = "ecryptfs"; enable = config.security.pam.enableEcryptfs; control = "optional"; modulePath = "${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"; } 819 # Work around https://github.com/systemd/systemd/issues/8598 820 # Skips the pam_fscrypt module for systemd-user sessions which do not have a password 821 # anyways. 822 # See also https://github.com/google/fscrypt/issues/95 823 { name = "fscrypt-skip-systemd"; enable = config.security.pam.enableFscrypt; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [ 824 "service" "=" "systemd-user" 825 ]; } 826 { name = "fscrypt"; enable = config.security.pam.enableFscrypt; control = "optional"; modulePath = "${pkgs.fscrypt-experimental}/lib/security/pam_fscrypt.so"; } 827 { name = "zfs_key-skip-systemd"; enable = cfg.zfs; control = "[success=1 default=ignore]"; modulePath = "pam_succeed_if.so"; args = [ 828 "service" "=" "systemd-user" 829 ]; } 830 { name = "zfs_key"; enable = cfg.zfs; control = "optional"; modulePath = "${config.boot.zfs.package}/lib/security/pam_zfs_key.so"; settings = { 831 inherit (config.security.pam.zfs) homes; 832 nounmount = config.security.pam.zfs.noUnmount; 833 }; } 834 { name = "mount"; enable = cfg.pamMount; control = "optional"; modulePath = "${pkgs.pam_mount}/lib/security/pam_mount.so"; settings = { 835 disable_interactive = true; 836 }; } 837 { name = "ldap"; enable = use_ldap; control = "optional"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; } 838 { name = "mysql"; enable = cfg.mysqlAuth; control = "optional"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { 839 config_file = "/etc/security/pam_mysql.conf"; 840 }; } 841 { name = "kanidm"; enable = config.services.kanidm.enablePam; control = "optional"; modulePath = "${pkgs.kanidm}/lib/pam_kanidm.so"; } 842 { name = "sss"; enable = config.services.sssd.enable; control = "optional"; modulePath = "${pkgs.sssd}/lib/security/pam_sss.so"; } 843 { name = "krb5"; enable = config.security.pam.krb5.enable; control = "optional"; modulePath = "${pam_krb5}/lib/security/pam_krb5.so"; } 844 { name = "otpw"; enable = cfg.otpwAuth; control = "optional"; modulePath = "${pkgs.otpw}/lib/security/pam_otpw.so"; } 845 { name = "systemd"; enable = cfg.startSession; control = "optional"; modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; } 846 { name = "xauth"; enable = cfg.forwardXAuth; control = "optional"; modulePath = "pam_xauth.so"; settings = { 847 xauthpath = "${pkgs.xorg.xauth}/bin/xauth"; 848 systemuser = 99; 849 }; } 850 { name = "limits"; enable = cfg.limits != []; control = "required"; modulePath = "${pkgs.pam}/lib/security/pam_limits.so"; settings = { 851 conf = "${makeLimitsConf cfg.limits}"; 852 }; } 853 { name = "motd"; enable = cfg.showMotd && (config.users.motd != null || config.users.motdFile != null); control = "optional"; modulePath = "${pkgs.pam}/lib/security/pam_motd.so"; settings = { 854 inherit motd; 855 }; } 856 { name = "apparmor"; enable = cfg.enableAppArmor && config.security.apparmor.enable; control = "optional"; modulePath = "${pkgs.apparmor-pam}/lib/security/pam_apparmor.so"; settings = { 857 order = "user,group,default"; 858 debug = true; 859 }; } 860 { name = "kwallet"; enable = cfg.kwallet.enable; control = "optional"; modulePath = "${cfg.kwallet.package}/lib/security/pam_kwallet5.so"; } 861 { name = "gnome_keyring"; enable = cfg.enableGnomeKeyring; control = "optional"; modulePath = "${pkgs.gnome.gnome-keyring}/lib/security/pam_gnome_keyring.so"; settings = { 862 auto_start = true; 863 }; } 864 { name = "gnupg"; enable = cfg.gnupg.enable; control = "optional"; modulePath = "${pkgs.pam_gnupg}/lib/security/pam_gnupg.so"; settings = { 865 no-autostart = cfg.gnupg.noAutostart; 866 }; } 867 { name = "intune"; enable = config.services.intune.enable; control = "optional"; modulePath = "${pkgs.intune-portal}/lib/security/pam_intune.so"; } 868 ]; 869 }; 870 }; 871 872 }; 873 874 875 inherit (pkgs) pam_krb5 pam_ccreds; 876 877 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam); 878 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; 879 880 # Create a limits.conf(5) file. 881 makeLimitsConf = limits: 882 pkgs.writeText "limits.conf" 883 (concatMapStrings ({ domain, type, item, value }: 884 "${domain} ${type} ${item} ${toString value}\n") 885 limits); 886 887 limitsType = with lib.types; listOf (submodule ({ ... }: { 888 options = { 889 domain = mkOption { 890 description = "Username, groupname, or wildcard this limit applies to"; 891 example = "@wheel"; 892 type = str; 893 }; 894 895 type = mkOption { 896 description = "Type of this limit"; 897 type = enum [ "-" "hard" "soft" ]; 898 default = "-"; 899 }; 900 901 item = mkOption { 902 description = "Item this limit applies to"; 903 type = enum [ 904 "core" 905 "data" 906 "fsize" 907 "memlock" 908 "nofile" 909 "rss" 910 "stack" 911 "cpu" 912 "nproc" 913 "as" 914 "maxlogins" 915 "maxsyslogins" 916 "priority" 917 "locks" 918 "sigpending" 919 "msgqueue" 920 "nice" 921 "rtprio" 922 ]; 923 }; 924 925 value = mkOption { 926 description = "Value of this limit"; 927 type = oneOf [ str int ]; 928 }; 929 }; 930 })); 931 932 motd = if config.users.motdFile == null 933 then pkgs.writeText "motd" config.users.motd 934 else config.users.motdFile; 935 936 makePAMService = name: service: 937 { name = "pam.d/${name}"; 938 value.source = pkgs.writeText "${name}.pam" service.text; 939 }; 940 941 optionalSudoConfigForSSHAgentAuth = optionalString config.security.pam.sshAgentAuth.enable '' 942 # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic. 943 Defaults env_keep+=SSH_AUTH_SOCK 944 ''; 945 946in 947 948{ 949 950 meta.maintainers = [ maintainers.majiir ]; 951 952 imports = [ 953 (mkRenamedOptionModule [ "security" "pam" "enableU2F" ] [ "security" "pam" "u2f" "enable" ]) 954 (mkRenamedOptionModule [ "security" "pam" "enableSSHAgentAuth" ] [ "security" "pam" "sshAgentAuth" "enable" ]) 955 ]; 956 957 ###### interface 958 959 options = { 960 961 security.pam.loginLimits = mkOption { 962 default = []; 963 type = limitsType; 964 example = 965 [ { domain = "ftp"; 966 type = "hard"; 967 item = "nproc"; 968 value = "0"; 969 } 970 { domain = "@student"; 971 type = "-"; 972 item = "maxlogins"; 973 value = "4"; 974 } 975 ]; 976 977 description = '' 978 Define resource limits that should apply to users or groups. 979 Each item in the list should be an attribute set with a 980 {var}`domain`, {var}`type`, 981 {var}`item`, and {var}`value` 982 attribute. The syntax and semantics of these attributes 983 must be that described in {manpage}`limits.conf(5)`. 984 985 Note that these limits do not apply to systemd services, 986 whose limits can be changed via {option}`systemd.extraConfig` 987 instead. 988 ''; 989 }; 990 991 security.pam.services = mkOption { 992 default = {}; 993 type = with types; attrsOf (submodule pamOpts); 994 description = '' 995 This option defines the PAM services. A service typically 996 corresponds to a program that uses PAM, 997 e.g. {command}`login` or {command}`passwd`. 998 Each attribute of this set defines a PAM service, with the attribute name 999 defining the name of the service. 1000 ''; 1001 }; 1002 1003 security.pam.makeHomeDir.skelDirectory = mkOption { 1004 type = types.str; 1005 default = "/var/empty"; 1006 example = "/etc/skel"; 1007 description = '' 1008 Path to skeleton directory whose contents are copied to home 1009 directories newly created by `pam_mkhomedir`. 1010 ''; 1011 }; 1012 1013 security.pam.makeHomeDir.umask = mkOption { 1014 type = types.str; 1015 default = "0077"; 1016 example = "0022"; 1017 description = '' 1018 The user file mode creation mask to use on home directories 1019 newly created by `pam_mkhomedir`. 1020 ''; 1021 }; 1022 1023 security.pam.sshAgentAuth = { 1024 enable = mkEnableOption '' 1025 authenticating using a signature performed by the ssh-agent. 1026 This allows using SSH keys exclusively, instead of passwords, for instance on remote machines 1027 ''; 1028 1029 authorizedKeysFiles = mkOption { 1030 type = with types; listOf str; 1031 description = '' 1032 A list of paths to files in OpenSSH's `authorized_keys` format, containing 1033 the keys that will be trusted by the `pam_ssh_agent_auth` module. 1034 1035 The following patterns are expanded when interpreting the path: 1036 - `%f` and `%H` respectively expand to the fully-qualified and short hostname ; 1037 - `%u` expands to the username ; 1038 - `~` or `%h` expands to the user's home directory. 1039 1040 ::: {.note} 1041 Specifying user-writeable files here result in an insecure configuration: a malicious process 1042 can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. 1043 1044 See [issue #31611](https://github.com/NixOS/nixpkgs/issues/31611) 1045 ::: 1046 ''; 1047 default = [ "/etc/ssh/authorized_keys.d/%u" ]; 1048 }; 1049 }; 1050 1051 security.pam.enableOTPW = mkEnableOption "the OTPW (one-time password) PAM module"; 1052 1053 security.pam.dp9ik = { 1054 enable = mkEnableOption '' 1055 the dp9ik pam module provided by tlsclient. 1056 1057 If set, users can be authenticated against the 9front 1058 authentication server given in {option}`security.pam.dp9ik.authserver`. 1059 ''; 1060 control = mkOption { 1061 default = "sufficient"; 1062 type = types.str; 1063 description = '' 1064 This option sets the pam "control" used for this module. 1065 ''; 1066 }; 1067 authserver = mkOption { 1068 default = null; 1069 type = with types; nullOr str; 1070 description = '' 1071 This controls the hostname for the 9front authentication server 1072 that users will be authenticated against. 1073 ''; 1074 }; 1075 }; 1076 1077 security.pam.krb5 = { 1078 enable = mkOption { 1079 default = config.security.krb5.enable; 1080 defaultText = literalExpression "config.security.krb5.enable"; 1081 type = types.bool; 1082 description = '' 1083 Enables Kerberos PAM modules (`pam-krb5`, 1084 `pam-ccreds`). 1085 1086 If set, users can authenticate with their Kerberos password. 1087 This requires a valid Kerberos configuration 1088 (`config.security.krb5.enable` should be set to 1089 `true`). 1090 1091 Note that the Kerberos PAM modules are not necessary when using SSS 1092 to handle Kerberos authentication. 1093 ''; 1094 }; 1095 }; 1096 1097 security.pam.p11 = { 1098 enable = mkOption { 1099 default = false; 1100 type = types.bool; 1101 description = '' 1102 Enables P11 PAM (`pam_p11`) module. 1103 1104 If set, users can log in with SSH keys and PKCS#11 tokens. 1105 1106 More information can be found [here](https://github.com/OpenSC/pam_p11). 1107 ''; 1108 }; 1109 1110 control = mkOption { 1111 default = "sufficient"; 1112 type = types.enum [ "required" "requisite" "sufficient" "optional" ]; 1113 description = '' 1114 This option sets pam "control". 1115 If you want to have multi factor authentication, use "required". 1116 If you want to use the PKCS#11 device instead of the regular password, 1117 use "sufficient". 1118 1119 Read 1120 {manpage}`pam.conf(5)` 1121 for better understanding of this option. 1122 ''; 1123 }; 1124 }; 1125 1126 security.pam.u2f = { 1127 enable = mkOption { 1128 default = false; 1129 type = types.bool; 1130 description = '' 1131 Enables U2F PAM (`pam-u2f`) module. 1132 1133 If set, users listed in 1134 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or 1135 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is 1136 not set) are able to log in with the associated U2F key. The path can 1137 be changed using {option}`security.pam.u2f.authFile` option. 1138 1139 File format is: 1140 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key` 1141 This file can be generated using {command}`pamu2fcfg` command. 1142 1143 More information can be found [here](https://developers.yubico.com/pam-u2f/). 1144 ''; 1145 }; 1146 1147 authFile = mkOption { 1148 default = null; 1149 type = with types; nullOr path; 1150 description = '' 1151 By default `pam-u2f` module reads the keys from 1152 {file}`$XDG_CONFIG_HOME/Yubico/u2f_keys` (or 1153 {file}`$HOME/.config/Yubico/u2f_keys` if XDG variable is 1154 not set). 1155 1156 If you want to change auth file locations or centralize database (for 1157 example use {file}`/etc/u2f-mappings`) you can set this 1158 option. 1159 1160 File format is: 1161 `username:first_keyHandle,first_public_key: second_keyHandle,second_public_key` 1162 This file can be generated using {command}`pamu2fcfg` command. 1163 1164 More information can be found [here](https://developers.yubico.com/pam-u2f/). 1165 ''; 1166 }; 1167 1168 appId = mkOption { 1169 default = null; 1170 type = with types; nullOr str; 1171 description = '' 1172 By default `pam-u2f` module sets the application 1173 ID to `pam://$HOSTNAME`. 1174 1175 When using {command}`pamu2fcfg`, you can specify your 1176 application ID with the `-i` flag. 1177 1178 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html) 1179 ''; 1180 }; 1181 1182 origin = mkOption { 1183 default = null; 1184 type = with types; nullOr str; 1185 description = '' 1186 By default `pam-u2f` module sets the origin 1187 to `pam://$HOSTNAME`. 1188 Setting origin to an host independent value will allow you to 1189 reuse credentials across machines 1190 1191 When using {command}`pamu2fcfg`, you can specify your 1192 application ID with the `-o` flag. 1193 1194 More information can be found [here](https://developers.yubico.com/pam-u2f/Manuals/pam_u2f.8.html) 1195 ''; 1196 }; 1197 1198 control = mkOption { 1199 default = "sufficient"; 1200 type = types.enum [ "required" "requisite" "sufficient" "optional" ]; 1201 description = '' 1202 This option sets pam "control". 1203 If you want to have multi factor authentication, use "required". 1204 If you want to use U2F device instead of regular password, use "sufficient". 1205 1206 Read 1207 {manpage}`pam.conf(5)` 1208 for better understanding of this option. 1209 ''; 1210 }; 1211 1212 debug = mkOption { 1213 default = false; 1214 type = types.bool; 1215 description = '' 1216 Debug output to stderr. 1217 ''; 1218 }; 1219 1220 interactive = mkOption { 1221 default = false; 1222 type = types.bool; 1223 description = '' 1224 Set to prompt a message and wait before testing the presence of a U2F device. 1225 Recommended if your device doesnt have a tactile trigger. 1226 ''; 1227 }; 1228 1229 cue = mkOption { 1230 default = false; 1231 type = types.bool; 1232 description = '' 1233 By default `pam-u2f` module does not inform user 1234 that he needs to use the u2f device, it just waits without a prompt. 1235 1236 If you set this option to `true`, 1237 `cue` option is added to `pam-u2f` 1238 module and reminder message will be displayed. 1239 ''; 1240 }; 1241 }; 1242 1243 security.pam.ussh = { 1244 enable = mkOption { 1245 default = false; 1246 type = types.bool; 1247 description = '' 1248 Enables Uber's USSH PAM (`pam-ussh`) module. 1249 1250 This is similar to `pam-ssh-agent`, except that 1251 the presence of a CA-signed SSH key with a valid principal is checked 1252 instead. 1253 1254 Note that this module must both be enabled using this option and on a 1255 per-PAM-service level as well (using `usshAuth`). 1256 1257 More information can be found [here](https://github.com/uber/pam-ussh). 1258 ''; 1259 }; 1260 1261 caFile = mkOption { 1262 default = null; 1263 type = with types; nullOr path; 1264 description = '' 1265 By default `pam-ussh` reads the trusted user CA keys 1266 from {file}`/etc/ssh/trusted_user_ca`. 1267 1268 This should be set the same as your `TrustedUserCAKeys` 1269 option for sshd. 1270 ''; 1271 }; 1272 1273 authorizedPrincipals = mkOption { 1274 default = null; 1275 type = with types; nullOr commas; 1276 description = '' 1277 Comma-separated list of authorized principals to permit; if the user 1278 presents a certificate with one of these principals, then they will be 1279 authorized. 1280 1281 Note that `pam-ussh` also requires that the certificate 1282 contain a principal matching the user's username. The principals from 1283 this list are in addition to those principals. 1284 1285 Mutually exclusive with `authorizedPrincipalsFile`. 1286 ''; 1287 }; 1288 1289 authorizedPrincipalsFile = mkOption { 1290 default = null; 1291 type = with types; nullOr path; 1292 description = '' 1293 Path to a list of principals; if the user presents a certificate with 1294 one of these principals, then they will be authorized. 1295 1296 Note that `pam-ussh` also requires that the certificate 1297 contain a principal matching the user's username. The principals from 1298 this file are in addition to those principals. 1299 1300 Mutually exclusive with `authorizedPrincipals`. 1301 ''; 1302 }; 1303 1304 group = mkOption { 1305 default = null; 1306 type = with types; nullOr str; 1307 description = '' 1308 If set, then the authenticating user must be a member of this group 1309 to use this module. 1310 ''; 1311 }; 1312 1313 control = mkOption { 1314 default = "sufficient"; 1315 type = types.enum [ "required" "requisite" "sufficient" "optional" ]; 1316 description = '' 1317 This option sets pam "control". 1318 If you want to have multi factor authentication, use "required". 1319 If you want to use the SSH certificate instead of the regular password, 1320 use "sufficient". 1321 1322 Read 1323 {manpage}`pam.conf(5)` 1324 for better understanding of this option. 1325 ''; 1326 }; 1327 }; 1328 1329 security.pam.yubico = { 1330 enable = mkOption { 1331 default = false; 1332 type = types.bool; 1333 description = '' 1334 Enables Yubico PAM (`yubico-pam`) module. 1335 1336 If set, users listed in 1337 {file}`~/.yubico/authorized_yubikeys` 1338 are able to log in with the associated Yubikey tokens. 1339 1340 The file must have only one line: 1341 `username:yubikey_token_id1:yubikey_token_id2` 1342 More information can be found [here](https://developers.yubico.com/yubico-pam/). 1343 ''; 1344 }; 1345 control = mkOption { 1346 default = "sufficient"; 1347 type = types.enum [ "required" "requisite" "sufficient" "optional" ]; 1348 description = '' 1349 This option sets pam "control". 1350 If you want to have multi factor authentication, use "required". 1351 If you want to use Yubikey instead of regular password, use "sufficient". 1352 1353 Read 1354 {manpage}`pam.conf(5)` 1355 for better understanding of this option. 1356 ''; 1357 }; 1358 id = mkOption { 1359 example = "42"; 1360 type = types.str; 1361 description = "client id"; 1362 }; 1363 1364 debug = mkOption { 1365 default = false; 1366 type = types.bool; 1367 description = '' 1368 Debug output to stderr. 1369 ''; 1370 }; 1371 mode = mkOption { 1372 default = "client"; 1373 type = types.enum [ "client" "challenge-response" ]; 1374 description = '' 1375 Mode of operation. 1376 1377 Use "client" for online validation with a YubiKey validation service such as 1378 the YubiCloud. 1379 1380 Use "challenge-response" for offline validation using YubiKeys with HMAC-SHA-1 1381 Challenge-Response configurations. See the man-page ykpamcfg(1) for further 1382 details on how to configure offline Challenge-Response validation. 1383 1384 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html). 1385 ''; 1386 }; 1387 challengeResponsePath = mkOption { 1388 default = null; 1389 type = types.nullOr types.path; 1390 description = '' 1391 If not null, set the path used by yubico pam module where the challenge expected response is stored. 1392 1393 More information can be found [here](https://developers.yubico.com/yubico-pam/Authentication_Using_Challenge-Response.html). 1394 ''; 1395 }; 1396 }; 1397 1398 security.pam.zfs = { 1399 enable = mkOption { 1400 default = false; 1401 type = types.bool; 1402 description = '' 1403 Enable unlocking and mounting of encrypted ZFS home dataset at login. 1404 ''; 1405 }; 1406 1407 homes = mkOption { 1408 example = "rpool/home"; 1409 default = "rpool/home"; 1410 type = types.str; 1411 description = '' 1412 Prefix of home datasets. This value will be concatenated with 1413 `"/" + <username>` in order to determine the home dataset to unlock. 1414 ''; 1415 }; 1416 1417 noUnmount = mkOption { 1418 default = false; 1419 type = types.bool; 1420 description = '' 1421 Do not unmount home dataset on logout. 1422 ''; 1423 }; 1424 }; 1425 1426 security.pam.enableEcryptfs = mkEnableOption "eCryptfs PAM module (mounting ecryptfs home directory on login)"; 1427 security.pam.enableFscrypt = mkEnableOption '' 1428 fscrypt, to automatically unlock directories with the user's login password. 1429 1430 This also enables a service at security.pam.services.fscrypt which is used by 1431 fscrypt to verify the user's password when setting up a new protector. If you 1432 use something other than pam_unix to verify user passwords, please remember to 1433 adjust this PAM service 1434 ''; 1435 1436 users.motd = mkOption { 1437 default = null; 1438 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; 1439 type = types.nullOr types.lines; 1440 description = "Message of the day shown to users when they log in."; 1441 }; 1442 1443 users.motdFile = mkOption { 1444 default = null; 1445 example = "/etc/motd"; 1446 type = types.nullOr types.path; 1447 description = "A file containing the message of the day shown to users when they log in."; 1448 }; 1449 }; 1450 1451 1452 ###### implementation 1453 1454 config = { 1455 assertions = [ 1456 { 1457 assertion = config.users.motd == null || config.users.motdFile == null; 1458 message = '' 1459 Only one of users.motd and users.motdFile can be set. 1460 ''; 1461 } 1462 { 1463 assertion = config.security.pam.zfs.enable -> config.boot.zfs.enabled; 1464 message = '' 1465 `security.pam.zfs.enable` requires enabling ZFS (`boot.zfs.enabled`). 1466 ''; 1467 } 1468 { 1469 assertion = with config.security.pam.sshAgentAuth; enable -> authorizedKeysFiles != []; 1470 message = '' 1471 `security.pam.enableSSHAgentAuth` requires `services.openssh.authorizedKeysFiles` to be a non-empty list. 1472 Did you forget to set `services.openssh.enable` ? 1473 ''; 1474 } 1475 ]; 1476 1477 warnings = optional 1478 (with lib; with config.security.pam.sshAgentAuth; 1479 enable && any (s: hasPrefix "%h" s || hasPrefix "~" s) authorizedKeysFiles) 1480 ''config.security.pam.sshAgentAuth.authorizedKeysFiles contains files in the user's home directory. 1481 1482 Specifying user-writeable files there result in an insecure configuration: 1483 a malicious process can then edit such an authorized_keys file and bypass the ssh-agent-based authentication. 1484 See https://github.com/NixOS/nixpkgs/issues/31611 1485 ''; 1486 1487 environment.systemPackages = 1488 # Include the PAM modules in the system path mostly for the manpages. 1489 [ pkgs.pam ] 1490 ++ optional config.users.ldap.enable pam_ldap 1491 ++ optional config.services.kanidm.enablePam pkgs.kanidm 1492 ++ optional config.services.sssd.enable pkgs.sssd 1493 ++ optionals config.security.pam.krb5.enable [pam_krb5 pam_ccreds] 1494 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] 1495 ++ optionals config.security.pam.oath.enable [ pkgs.oath-toolkit ] 1496 ++ optionals config.security.pam.p11.enable [ pkgs.pam_p11 ] 1497 ++ optionals config.security.pam.enableFscrypt [ pkgs.fscrypt-experimental ] 1498 ++ optionals config.security.pam.u2f.enable [ pkgs.pam_u2f ]; 1499 1500 boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ]; 1501 1502 security.wrappers = { 1503 unix_chkpwd = { 1504 setuid = true; 1505 owner = "root"; 1506 group = "root"; 1507 source = "${pkgs.pam}/bin/unix_chkpwd"; 1508 }; 1509 }; 1510 1511 environment.etc = mapAttrs' makePAMService config.security.pam.services; 1512 1513 security.pam.services = 1514 { other.text = 1515 '' 1516 auth required pam_warn.so 1517 auth required pam_deny.so 1518 account required pam_warn.so 1519 account required pam_deny.so 1520 password required pam_warn.so 1521 password required pam_deny.so 1522 session required pam_warn.so 1523 session required pam_deny.so 1524 ''; 1525 1526 # Most of these should be moved to specific modules. 1527 i3lock = {}; 1528 i3lock-color = {}; 1529 vlock = {}; 1530 xlock = {}; 1531 xscreensaver = {}; 1532 1533 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; }; 1534 1535 /* FIXME: should runuser -l start a systemd session? Currently 1536 it complains "Cannot create session: Already running in a 1537 session". */ 1538 runuser-l = { rootOK = true; unixAuth = false; }; 1539 } // optionalAttrs (config.security.pam.enableFscrypt) { 1540 # Allow fscrypt to verify login passphrase 1541 fscrypt = {}; 1542 }; 1543 1544 security.apparmor.includes."abstractions/pam" = 1545 lib.concatMapStrings 1546 (name: "r ${config.environment.etc."pam.d/${name}".source},\n") 1547 (attrNames config.security.pam.services) + 1548 '' 1549 mr ${getLib pkgs.pam}/lib/security/pam_filter/*, 1550 mr ${getLib pkgs.pam}/lib/security/pam_*.so, 1551 r ${getLib pkgs.pam}/lib/security/, 1552 '' + 1553 (with lib; pipe config.security.pam.services [ 1554 attrValues 1555 (catAttrs "rules") 1556 (concatMap attrValues) 1557 (concatMap attrValues) 1558 (filter (rule: rule.enable)) 1559 (catAttrs "modulePath") 1560 (filter (hasPrefix "/")) 1561 unique 1562 (map (module: "mr ${module},")) 1563 concatLines 1564 ]); 1565 1566 security.sudo.extraConfig = optionalSudoConfigForSSHAgentAuth; 1567 security.sudo-rs.extraConfig = optionalSudoConfigForSSHAgentAuth; 1568 }; 1569}