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