at 18.09-beta 22 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 parentConfig = config; 10 11 pamOpts = { config, name, ... }: let cfg = config; in let config = parentConfig; in { 12 13 options = { 14 15 name = mkOption { 16 example = "sshd"; 17 type = types.str; 18 description = "Name of the PAM service."; 19 }; 20 21 unixAuth = mkOption { 22 default = true; 23 type = types.bool; 24 description = '' 25 Whether users can log in with passwords defined in 26 <filename>/etc/shadow</filename>. 27 ''; 28 }; 29 30 rootOK = mkOption { 31 default = false; 32 type = types.bool; 33 description = '' 34 If set, root doesn't need to authenticate (e.g. for the 35 <command>useradd</command> service). 36 ''; 37 }; 38 39 u2fAuth = mkOption { 40 default = config.security.pam.enableU2F; 41 type = types.bool; 42 description = '' 43 If set, users listed in 44 <filename>~/.config/Yubico/u2f_keys</filename> are able to log in 45 with the associated U2F key. 46 ''; 47 }; 48 49 googleAuthenticator = { 50 enable = mkOption { 51 default = false; 52 type = types.bool; 53 description = '' 54 If set, users with enabled Google Authenticator (created 55 <filename>~/.google_authenticator</filename>) will be required 56 to provide Google Authenticator token to log in. 57 ''; 58 }; 59 }; 60 61 usbAuth = mkOption { 62 default = config.security.pam.usb.enable; 63 type = types.bool; 64 description = '' 65 If set, users listed in 66 <filename>/etc/pamusb.conf</filename> are able to log in 67 with the associated USB key. 68 ''; 69 }; 70 71 otpwAuth = mkOption { 72 default = config.security.pam.enableOTPW; 73 type = types.bool; 74 description = '' 75 If set, the OTPW system will be used (if 76 <filename>~/.otpw</filename> exists). 77 ''; 78 }; 79 80 fprintAuth = mkOption { 81 default = config.services.fprintd.enable; 82 type = types.bool; 83 description = '' 84 If set, fingerprint reader will be used (if exists and 85 your fingerprints are enrolled). 86 ''; 87 }; 88 89 oathAuth = mkOption { 90 default = config.security.pam.oath.enable; 91 type = types.bool; 92 description = '' 93 If set, the OATH Toolkit will be used. 94 ''; 95 }; 96 97 sshAgentAuth = mkOption { 98 default = false; 99 type = types.bool; 100 description = '' 101 If set, the calling user's SSH agent is used to authenticate 102 against the keys in the calling user's 103 <filename>~/.ssh/authorized_keys</filename>. This is useful 104 for <command>sudo</command> on password-less remote systems. 105 ''; 106 }; 107 108 startSession = mkOption { 109 default = false; 110 type = types.bool; 111 description = '' 112 If set, the service will register a new session with 113 systemd's login manager. For local sessions, this will give 114 the user access to audio devices, CD-ROM drives. In the 115 default PolicyKit configuration, it also allows the user to 116 reboot the system. 117 ''; 118 }; 119 120 setEnvironment = mkOption { 121 type = types.bool; 122 default = true; 123 description = '' 124 Whether the service should set the environment variables 125 listed in <option>environment.sessionVariables</option> 126 using <literal>pam_env.so</literal>. 127 ''; 128 }; 129 130 setLoginUid = mkOption { 131 type = types.bool; 132 description = '' 133 Set the login uid of the process 134 (<filename>/proc/self/loginuid</filename>) for auditing 135 purposes. The login uid is only set by entry points like 136 <command>login</command> and <command>sshd</command>, not by 137 commands like <command>sudo</command>. 138 ''; 139 }; 140 141 forwardXAuth = mkOption { 142 default = false; 143 type = types.bool; 144 description = '' 145 Whether X authentication keys should be passed from the 146 calling user to the target user (e.g. for 147 <command>su</command>) 148 ''; 149 }; 150 151 pamMount = mkOption { 152 default = config.security.pam.mount.enable; 153 type = types.bool; 154 description = '' 155 Enable PAM mount (pam_mount) system to mount fileystems on user login. 156 ''; 157 }; 158 159 allowNullPassword = mkOption { 160 default = false; 161 type = types.bool; 162 description = '' 163 Whether to allow logging into accounts that have no password 164 set (i.e., have an empty password field in 165 <filename>/etc/passwd</filename> or 166 <filename>/etc/group</filename>). This does not enable 167 logging into disabled accounts (i.e., that have the password 168 field set to <literal>!</literal>). Note that regardless of 169 what the pam_unix documentation says, accounts with hashed 170 empty passwords are always allowed to log in. 171 ''; 172 }; 173 174 requireWheel = mkOption { 175 default = false; 176 type = types.bool; 177 description = '' 178 Whether to permit root access only to members of group wheel. 179 ''; 180 }; 181 182 limits = mkOption { 183 description = '' 184 Attribute set describing resource limits. Defaults to the 185 value of <option>security.pam.loginLimits</option>. 186 ''; 187 }; 188 189 showMotd = mkOption { 190 default = false; 191 type = types.bool; 192 description = "Whether to show the message of the day."; 193 }; 194 195 makeHomeDir = mkOption { 196 default = false; 197 type = types.bool; 198 description = '' 199 Whether to try to create home directories for users 200 with <literal>$HOME</literal>s pointing to nonexistent 201 locations on session login. 202 ''; 203 }; 204 205 updateWtmp = mkOption { 206 default = false; 207 type = types.bool; 208 description = "Whether to update <filename>/var/log/wtmp</filename>."; 209 }; 210 211 logFailures = mkOption { 212 default = false; 213 type = types.bool; 214 description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>."; 215 }; 216 217 enableAppArmor = mkOption { 218 default = false; 219 type = types.bool; 220 description = '' 221 Enable support for attaching AppArmor profiles at the 222 user/group level, e.g., as part of a role based access 223 control scheme. 224 ''; 225 }; 226 227 enableKwallet = mkOption { 228 default = false; 229 type = types.bool; 230 description = '' 231 If enabled, pam_wallet will attempt to automatically unlock the 232 user's default KDE wallet upon login. If the user has no wallet named 233 "kdewallet", or the login password does not match their wallet 234 password, KDE will prompt separately after login. 235 ''; 236 }; 237 sssdStrictAccess = mkOption { 238 default = false; 239 type = types.bool; 240 description = "enforce sssd access control"; 241 }; 242 243 enableGnomeKeyring = mkOption { 244 default = false; 245 type = types.bool; 246 description = '' 247 If enabled, pam_gnome_keyring will attempt to automatically unlock the 248 user's default Gnome keyring upon login. If the user login password does 249 not match their keyring password, Gnome Keyring will prompt separately 250 after login. 251 ''; 252 }; 253 254 text = mkOption { 255 type = types.nullOr types.lines; 256 description = "Contents of the PAM service file."; 257 }; 258 259 }; 260 261 config = { 262 name = mkDefault name; 263 setLoginUid = mkDefault cfg.startSession; 264 limits = mkDefault config.security.pam.loginLimits; 265 266 # !!! TODO: move the LDAP stuff to the LDAP module, and the 267 # Samba stuff to the Samba module. This requires that the PAM 268 # module provides the right hooks. 269 text = mkDefault 270 ('' 271 # Account management. 272 account ${if cfg.sssdStrictAccess then "required" else "sufficient"} pam_unix.so 273 ${optionalString use_ldap 274 "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 275 ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess==false) 276 "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"} 277 ${optionalString (config.services.sssd.enable && cfg.sssdStrictAccess) 278 "account [default=bad success=ok user_unknown=ignore] ${pkgs.sssd}/lib/security/pam_sss.so"} 279 ${optionalString config.krb5.enable 280 "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} 281 282 # Authentication management. 283 ${optionalString cfg.rootOK 284 "auth sufficient pam_rootok.so"} 285 ${optionalString cfg.requireWheel 286 "auth required pam_wheel.so use_uid"} 287 ${optionalString cfg.logFailures 288 "auth required pam_tally.so"} 289 ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) 290 "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"} 291 ${optionalString cfg.fprintAuth 292 "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"} 293 ${optionalString cfg.u2fAuth 294 "auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"} 295 ${optionalString cfg.usbAuth 296 "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"} 297 ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth 298 "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"} 299 '' + 300 # Modules in this block require having the password set in PAM_AUTHTOK. 301 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run 302 # after it succeeds. Certain modules need to run after pam_unix 303 # prompts the user for password so we run it once with 'required' at an 304 # earlier point and it will run again with 'sufficient' further down. 305 # We use try_first_pass the second time to avoid prompting password twice 306 (optionalString (cfg.unixAuth && 307 (config.security.pam.enableEcryptfs 308 || cfg.pamMount 309 || cfg.enableKwallet 310 || cfg.enableGnomeKeyring 311 || cfg.googleAuthenticator.enable)) '' 312 auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth 313 ${optionalString config.security.pam.enableEcryptfs 314 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"} 315 ${optionalString cfg.pamMount 316 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 317 ${optionalString cfg.enableKwallet 318 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" + 319 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")} 320 ${optionalString cfg.enableGnomeKeyring 321 ("auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so")} 322 ${optionalString cfg.googleAuthenticator.enable 323 "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"} 324 '') + '' 325 ${optionalString cfg.unixAuth 326 "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"} 327 ${optionalString cfg.otpwAuth 328 "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} 329 ${optionalString use_ldap 330 "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} 331 ${optionalString config.services.sssd.enable 332 "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"} 333 ${optionalString config.krb5.enable '' 334 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass 335 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass 336 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass 337 ''} 338 auth required pam_deny.so 339 340 # Password management. 341 password requisite pam_unix.so nullok sha512 342 ${optionalString config.security.pam.enableEcryptfs 343 "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 344 ${optionalString cfg.pamMount 345 "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 346 ${optionalString use_ldap 347 "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 348 ${optionalString config.services.sssd.enable 349 "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"} 350 ${optionalString config.krb5.enable 351 "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"} 352 ${optionalString config.services.samba.syncPasswordsByPam 353 "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"} 354 355 # Session management. 356 ${optionalString cfg.setEnvironment '' 357 session required pam_env.so envfile=${config.system.build.pamEnvironment} 358 ''} 359 session required pam_unix.so 360 ${optionalString cfg.setLoginUid 361 "session ${ 362 if config.boot.isContainer then "optional" else "required" 363 } pam_loginuid.so"} 364 ${optionalString cfg.makeHomeDir 365 "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0022"} 366 ${optionalString cfg.updateWtmp 367 "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} 368 ${optionalString config.security.pam.enableEcryptfs 369 "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 370 ${optionalString use_ldap 371 "session optional ${pam_ldap}/lib/security/pam_ldap.so"} 372 ${optionalString config.services.sssd.enable 373 "session optional ${pkgs.sssd}/lib/security/pam_sss.so"} 374 ${optionalString config.krb5.enable 375 "session optional ${pam_krb5}/lib/security/pam_krb5.so"} 376 ${optionalString cfg.otpwAuth 377 "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} 378 ${optionalString cfg.startSession 379 "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} 380 ${optionalString cfg.forwardXAuth 381 "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} 382 ${optionalString (cfg.limits != []) 383 "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} 384 ${optionalString (cfg.showMotd && config.users.motd != null) 385 "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} 386 ${optionalString cfg.pamMount 387 "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 388 ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable) 389 "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"} 390 ${optionalString (cfg.enableKwallet) 391 ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" + 392 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")} 393 ${optionalString (cfg.enableGnomeKeyring) 394 "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"} 395 ${optionalString (config.virtualisation.lxc.lxcfs.enable) 396 "session optional ${pkgs.lxc}/lib/security/pam_cgfs.so -c all"} 397 ''); 398 }; 399 400 }; 401 402 403 inherit (pkgs) pam_krb5 pam_ccreds; 404 405 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam); 406 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; 407 408 # Create a limits.conf(5) file. 409 makeLimitsConf = limits: 410 pkgs.writeText "limits.conf" 411 (concatMapStrings ({ domain, type, item, value }: 412 "${domain} ${type} ${item} ${toString value}\n") 413 limits); 414 415 motd = pkgs.writeText "motd" config.users.motd; 416 417 makePAMService = pamService: 418 { source = pkgs.writeText "${pamService.name}.pam" pamService.text; 419 target = "pam.d/${pamService.name}"; 420 }; 421 422in 423 424{ 425 426 ###### interface 427 428 options = { 429 430 security.pam.loginLimits = mkOption { 431 default = []; 432 example = 433 [ { domain = "ftp"; 434 type = "hard"; 435 item = "nproc"; 436 value = "0"; 437 } 438 { domain = "@student"; 439 type = "-"; 440 item = "maxlogins"; 441 value = "4"; 442 } 443 ]; 444 445 description = 446 '' Define resource limits that should apply to users or groups. 447 Each item in the list should be an attribute set with a 448 <varname>domain</varname>, <varname>type</varname>, 449 <varname>item</varname>, and <varname>value</varname> 450 attribute. The syntax and semantics of these attributes 451 must be that described in the limits.conf(5) man page. 452 453 Note that these limits do not apply to systemd services, 454 whose limits can be changed via <option>systemd.extraConfig</option> 455 instead. 456 ''; 457 }; 458 459 security.pam.services = mkOption { 460 default = []; 461 type = with types; loaOf (submodule pamOpts); 462 description = 463 '' 464 This option defines the PAM services. A service typically 465 corresponds to a program that uses PAM, 466 e.g. <command>login</command> or <command>passwd</command>. 467 Each attribute of this set defines a PAM service, with the attribute name 468 defining the name of the service. 469 ''; 470 }; 471 472 security.pam.makeHomeDir.skelDirectory = mkOption { 473 type = types.str; 474 default = "/var/empty"; 475 example = "/etc/skel"; 476 description = '' 477 Path to skeleton directory whose contents are copied to home 478 directories newly created by <literal>pam_mkhomedir</literal>. 479 ''; 480 }; 481 482 security.pam.enableSSHAgentAuth = mkOption { 483 default = false; 484 description = 485 '' 486 Enable sudo logins if the user's SSH agent provides a key 487 present in <filename>~/.ssh/authorized_keys</filename>. 488 This allows machines to exclusively use SSH keys instead of 489 passwords. 490 ''; 491 }; 492 493 security.pam.enableOTPW = mkOption { 494 default = false; 495 description = '' 496 Enable the OTPW (one-time password) PAM module. 497 ''; 498 }; 499 500 security.pam.enableU2F = mkOption { 501 default = false; 502 description = '' 503 Enable the U2F PAM module. 504 ''; 505 }; 506 507 security.pam.enableEcryptfs = mkOption { 508 default = false; 509 description = '' 510 Enable eCryptfs PAM module (mounting ecryptfs home directory on login). 511 ''; 512 }; 513 514 users.motd = mkOption { 515 default = null; 516 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; 517 type = types.nullOr types.lines; 518 description = "Message of the day shown to users when they log in."; 519 }; 520 521 }; 522 523 524 ###### implementation 525 526 config = { 527 528 environment.systemPackages = 529 # Include the PAM modules in the system path mostly for the manpages. 530 [ pkgs.pam ] 531 ++ optional config.users.ldap.enable pam_ldap 532 ++ optional config.services.sssd.enable pkgs.sssd 533 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] 534 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] 535 ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ] 536 ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ]; 537 538 boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ]; 539 540 security.wrappers = { 541 unix_chkpwd = { 542 source = "${pkgs.pam}/sbin/unix_chkpwd.orig"; 543 owner = "root"; 544 setuid = true; 545 }; 546 }; 547 548 environment.etc = 549 mapAttrsToList (n: v: makePAMService v) config.security.pam.services; 550 551 security.pam.services = 552 { other.text = 553 '' 554 auth required pam_warn.so 555 auth required pam_deny.so 556 account required pam_warn.so 557 account required pam_deny.so 558 password required pam_warn.so 559 password required pam_deny.so 560 session required pam_warn.so 561 session required pam_deny.so 562 ''; 563 564 # Most of these should be moved to specific modules. 565 cups = {}; 566 ftp = {}; 567 i3lock = {}; 568 i3lock-color = {}; 569 screen = {}; 570 vlock = {}; 571 xlock = {}; 572 xscreensaver = {}; 573 574 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; }; 575 576 /* FIXME: should runuser -l start a systemd session? Currently 577 it complains "Cannot create session: Already running in a 578 session". */ 579 runuser-l = { rootOK = true; unixAuth = false; }; 580 }; 581 582 }; 583 584}