at 18.03-beta 21 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 238 enableGnomeKeyring = mkOption { 239 default = false; 240 type = types.bool; 241 description = '' 242 If enabled, pam_gnome_keyring will attempt to automatically unlock the 243 user's default Gnome keyring upon login. If the user login password does 244 not match their keyring password, Gnome Keyring will prompt separately 245 after login. 246 ''; 247 }; 248 249 text = mkOption { 250 type = types.nullOr types.lines; 251 description = "Contents of the PAM service file."; 252 }; 253 254 }; 255 256 config = { 257 name = mkDefault name; 258 setLoginUid = mkDefault cfg.startSession; 259 limits = mkDefault config.security.pam.loginLimits; 260 261 # !!! TODO: move the LDAP stuff to the LDAP module, and the 262 # Samba stuff to the Samba module. This requires that the PAM 263 # module provides the right hooks. 264 text = mkDefault 265 ('' 266 # Account management. 267 account sufficient pam_unix.so 268 ${optionalString use_ldap 269 "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 270 ${optionalString config.services.sssd.enable 271 "account sufficient ${pkgs.sssd}/lib/security/pam_sss.so"} 272 ${optionalString config.krb5.enable 273 "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} 274 275 # Authentication management. 276 ${optionalString cfg.rootOK 277 "auth sufficient pam_rootok.so"} 278 ${optionalString cfg.requireWheel 279 "auth required pam_wheel.so use_uid"} 280 ${optionalString cfg.logFailures 281 "auth required pam_tally.so"} 282 ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) 283 "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"} 284 ${optionalString cfg.fprintAuth 285 "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"} 286 ${optionalString cfg.u2fAuth 287 "auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"} 288 ${optionalString cfg.usbAuth 289 "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"} 290 ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth 291 "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"} 292 '' + 293 # Modules in this block require having the password set in PAM_AUTHTOK. 294 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run 295 # after it succeeds. Certain modules need to run after pam_unix 296 # prompts the user for password so we run it once with 'required' at an 297 # earlier point and it will run again with 'sufficient' further down. 298 # We use try_first_pass the second time to avoid prompting password twice 299 (optionalString (cfg.unixAuth && 300 (config.security.pam.enableEcryptfs 301 || cfg.pamMount 302 || cfg.enableKwallet 303 || cfg.enableGnomeKeyring 304 || cfg.googleAuthenticator.enable)) '' 305 auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth 306 ${optionalString config.security.pam.enableEcryptfs 307 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"} 308 ${optionalString cfg.pamMount 309 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 310 ${optionalString cfg.enableKwallet 311 ("auth optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" + 312 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")} 313 ${optionalString cfg.enableGnomeKeyring 314 ("auth optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so")} 315 ${optionalString cfg.googleAuthenticator.enable 316 "auth required ${pkgs.googleAuthenticator}/lib/security/pam_google_authenticator.so no_increment_hotp"} 317 '') + '' 318 ${optionalString cfg.unixAuth 319 "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"} 320 ${optionalString cfg.otpwAuth 321 "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} 322 ${optionalString use_ldap 323 "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} 324 ${optionalString config.services.sssd.enable 325 "auth sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_first_pass"} 326 ${optionalString config.krb5.enable '' 327 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass 328 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass 329 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass 330 ''} 331 auth required pam_deny.so 332 333 # Password management. 334 password requisite pam_unix.so nullok sha512 335 ${optionalString config.security.pam.enableEcryptfs 336 "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 337 ${optionalString cfg.pamMount 338 "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 339 ${optionalString use_ldap 340 "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 341 ${optionalString config.services.sssd.enable 342 "password sufficient ${pkgs.sssd}/lib/security/pam_sss.so use_authtok"} 343 ${optionalString config.krb5.enable 344 "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"} 345 ${optionalString config.services.samba.syncPasswordsByPam 346 "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"} 347 348 # Session management. 349 ${optionalString cfg.setEnvironment '' 350 session required pam_env.so envfile=${config.system.build.pamEnvironment} 351 ''} 352 session required pam_unix.so 353 ${optionalString cfg.setLoginUid 354 "session ${ 355 if config.boot.isContainer then "optional" else "required" 356 } pam_loginuid.so"} 357 ${optionalString cfg.makeHomeDir 358 "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=${config.security.pam.makeHomeDir.skelDirectory} umask=0022"} 359 ${optionalString cfg.updateWtmp 360 "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} 361 ${optionalString config.security.pam.enableEcryptfs 362 "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 363 ${optionalString use_ldap 364 "session optional ${pam_ldap}/lib/security/pam_ldap.so"} 365 ${optionalString config.services.sssd.enable 366 "session optional ${pkgs.sssd}/lib/security/pam_sss.so"} 367 ${optionalString config.krb5.enable 368 "session optional ${pam_krb5}/lib/security/pam_krb5.so"} 369 ${optionalString cfg.otpwAuth 370 "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} 371 ${optionalString cfg.startSession 372 "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} 373 ${optionalString cfg.forwardXAuth 374 "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} 375 ${optionalString (cfg.limits != []) 376 "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} 377 ${optionalString (cfg.showMotd && config.users.motd != null) 378 "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} 379 ${optionalString cfg.pamMount 380 "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 381 ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable) 382 "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"} 383 ${optionalString (cfg.enableKwallet) 384 ("session optional ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so" + 385 " kwalletd=${pkgs.libsForQt5.kwallet.bin}/bin/kwalletd5")} 386 ${optionalString (cfg.enableGnomeKeyring) 387 "session optional ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so auto_start"} 388 ${optionalString (config.virtualisation.lxc.lxcfs.enable) 389 "session optional ${pkgs.lxcfs}/lib/security/pam_cgfs.so -c freezer,memory,name=systemd,unified,cpuset"} 390 ''); 391 }; 392 393 }; 394 395 396 inherit (pkgs) pam_krb5 pam_ccreds; 397 398 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam); 399 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; 400 401 # Create a limits.conf(5) file. 402 makeLimitsConf = limits: 403 pkgs.writeText "limits.conf" 404 (concatMapStrings ({ domain, type, item, value }: 405 "${domain} ${type} ${item} ${toString value}\n") 406 limits); 407 408 motd = pkgs.writeText "motd" config.users.motd; 409 410 makePAMService = pamService: 411 { source = pkgs.writeText "${pamService.name}.pam" pamService.text; 412 target = "pam.d/${pamService.name}"; 413 }; 414 415in 416 417{ 418 419 ###### interface 420 421 options = { 422 423 security.pam.loginLimits = mkOption { 424 default = []; 425 example = 426 [ { domain = "ftp"; 427 type = "hard"; 428 item = "nproc"; 429 value = "0"; 430 } 431 { domain = "@student"; 432 type = "-"; 433 item = "maxlogins"; 434 value = "4"; 435 } 436 ]; 437 438 description = 439 '' Define resource limits that should apply to users or groups. 440 Each item in the list should be an attribute set with a 441 <varname>domain</varname>, <varname>type</varname>, 442 <varname>item</varname>, and <varname>value</varname> 443 attribute. The syntax and semantics of these attributes 444 must be that described in the limits.conf(5) man page. 445 ''; 446 }; 447 448 security.pam.services = mkOption { 449 default = []; 450 type = with types; loaOf (submodule pamOpts); 451 description = 452 '' 453 This option defines the PAM services. A service typically 454 corresponds to a program that uses PAM, 455 e.g. <command>login</command> or <command>passwd</command>. 456 Each attribute of this set defines a PAM service, with the attribute name 457 defining the name of the service. 458 ''; 459 }; 460 461 security.pam.makeHomeDir.skelDirectory = mkOption { 462 type = types.str; 463 default = "/var/empty"; 464 example = "/etc/skel"; 465 description = '' 466 Path to skeleton directory whose contents are copied to home 467 directories newly created by <literal>pam_mkhomedir</literal>. 468 ''; 469 }; 470 471 security.pam.enableSSHAgentAuth = mkOption { 472 default = false; 473 description = 474 '' 475 Enable sudo logins if the user's SSH agent provides a key 476 present in <filename>~/.ssh/authorized_keys</filename>. 477 This allows machines to exclusively use SSH keys instead of 478 passwords. 479 ''; 480 }; 481 482 security.pam.enableOTPW = mkOption { 483 default = false; 484 description = '' 485 Enable the OTPW (one-time password) PAM module. 486 ''; 487 }; 488 489 security.pam.enableU2F = mkOption { 490 default = false; 491 description = '' 492 Enable the U2F PAM module. 493 ''; 494 }; 495 496 security.pam.enableEcryptfs = mkOption { 497 default = false; 498 description = '' 499 Enable eCryptfs PAM module (mounting ecryptfs home directory on login). 500 ''; 501 }; 502 503 users.motd = mkOption { 504 default = null; 505 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; 506 type = types.nullOr types.lines; 507 description = "Message of the day shown to users when they log in."; 508 }; 509 510 }; 511 512 513 ###### implementation 514 515 config = { 516 517 environment.systemPackages = 518 # Include the PAM modules in the system path mostly for the manpages. 519 [ pkgs.pam ] 520 ++ optional config.users.ldap.enable pam_ldap 521 ++ optional config.services.sssd.enable pkgs.sssd 522 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] 523 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] 524 ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ] 525 ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ]; 526 527 boot.supportedFilesystems = optionals config.security.pam.enableEcryptfs [ "ecryptfs" ]; 528 529 security.wrappers = { 530 unix_chkpwd = { 531 source = "${pkgs.pam}/sbin/unix_chkpwd.orig"; 532 owner = "root"; 533 setuid = true; 534 }; 535 }; 536 537 environment.etc = 538 mapAttrsToList (n: v: makePAMService v) config.security.pam.services; 539 540 security.pam.services = 541 { other.text = 542 '' 543 auth required pam_warn.so 544 auth required pam_deny.so 545 account required pam_warn.so 546 account required pam_deny.so 547 password required pam_warn.so 548 password required pam_deny.so 549 session required pam_warn.so 550 session required pam_deny.so 551 ''; 552 553 # Most of these should be moved to specific modules. 554 cups = {}; 555 ftp = {}; 556 i3lock = {}; 557 i3lock-color = {}; 558 screen = {}; 559 vlock = {}; 560 xlock = {}; 561 xscreensaver = {}; 562 563 runuser = { rootOK = true; unixAuth = false; setEnvironment = false; }; 564 565 /* FIXME: should runuser -l start a systemd session? Currently 566 it complains "Cannot create session: Already running in a 567 session". */ 568 runuser-l = { rootOK = true; unixAuth = false; }; 569 }; 570 571 }; 572 573}