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