at 16.09-beta 17 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 setLoginUid = mkOption { 109 type = types.bool; 110 description = '' 111 Set the login uid of the process 112 (<filename>/proc/self/loginuid</filename>) for auditing 113 purposes. The login uid is only set by entry points like 114 <command>login</command> and <command>sshd</command>, not by 115 commands like <command>sudo</command>. 116 ''; 117 }; 118 119 forwardXAuth = mkOption { 120 default = false; 121 type = types.bool; 122 description = '' 123 Whether X authentication keys should be passed from the 124 calling user to the target user (e.g. for 125 <command>su</command>) 126 ''; 127 }; 128 129 pamMount = mkOption { 130 default = config.security.pam.mount.enable; 131 type = types.bool; 132 description = '' 133 Enable PAM mount (pam_mount) system to mount fileystems on user login. 134 ''; 135 }; 136 137 allowNullPassword = mkOption { 138 default = false; 139 type = types.bool; 140 description = '' 141 Whether to allow logging into accounts that have no password 142 set (i.e., have an empty password field in 143 <filename>/etc/passwd</filename> or 144 <filename>/etc/group</filename>). This does not enable 145 logging into disabled accounts (i.e., that have the password 146 field set to <literal>!</literal>). Note that regardless of 147 what the pam_unix documentation says, accounts with hashed 148 empty passwords are always allowed to log in. 149 ''; 150 }; 151 152 requireWheel = mkOption { 153 default = false; 154 type = types.bool; 155 description = '' 156 Whether to permit root access only to members of group wheel. 157 ''; 158 }; 159 160 limits = mkOption { 161 description = '' 162 Attribute set describing resource limits. Defaults to the 163 value of <option>security.pam.loginLimits</option>. 164 ''; 165 }; 166 167 showMotd = mkOption { 168 default = false; 169 type = types.bool; 170 description = "Whether to show the message of the day."; 171 }; 172 173 makeHomeDir = mkOption { 174 default = false; 175 type = types.bool; 176 description = '' 177 Whether to try to create home directories for users 178 with <literal>$HOME</literal>s pointing to nonexistent 179 locations on session login. 180 ''; 181 }; 182 183 updateWtmp = mkOption { 184 default = false; 185 type = types.bool; 186 description = "Whether to update <filename>/var/log/wtmp</filename>."; 187 }; 188 189 logFailures = mkOption { 190 default = false; 191 type = types.bool; 192 description = "Whether to log authentication failures in <filename>/var/log/faillog</filename>."; 193 }; 194 195 enableAppArmor = mkOption { 196 default = false; 197 type = types.bool; 198 description = '' 199 Enable support for attaching AppArmor profiles at the 200 user/group level, e.g., as part of a role based access 201 control scheme. 202 ''; 203 }; 204 205 text = mkOption { 206 type = types.nullOr types.lines; 207 description = "Contents of the PAM service file."; 208 }; 209 210 }; 211 212 config = { 213 name = mkDefault name; 214 setLoginUid = mkDefault cfg.startSession; 215 limits = mkDefault config.security.pam.loginLimits; 216 217 # !!! TODO: move the LDAP stuff to the LDAP module, and the 218 # Samba stuff to the Samba module. This requires that the PAM 219 # module provides the right hooks. 220 text = mkDefault 221 ('' 222 # Account management. 223 account sufficient pam_unix.so 224 ${optionalString use_ldap 225 "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 226 ${optionalString config.krb5.enable 227 "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"} 228 229 # Authentication management. 230 ${optionalString cfg.rootOK 231 "auth sufficient pam_rootok.so"} 232 ${optionalString cfg.requireWheel 233 "auth required pam_wheel.so use_uid"} 234 ${optionalString cfg.logFailures 235 "auth required pam_tally.so"} 236 ${optionalString (config.security.pam.enableSSHAgentAuth && cfg.sshAgentAuth) 237 "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"} 238 ${optionalString cfg.fprintAuth 239 "auth sufficient ${pkgs.fprintd}/lib/security/pam_fprintd.so"} 240 ${optionalString cfg.u2fAuth 241 "auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"} 242 ${optionalString cfg.usbAuth 243 "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"} 244 '' + 245 # Modules in this block require having the password set in PAM_AUTHTOK. 246 # pam_unix is marked as 'sufficient' on NixOS which means nothing will run 247 # after it succeeds. Certain modules need to run after pam_unix 248 # prompts the user for password so we run it once with 'required' at an 249 # earlier point and it will run again with 'sufficient' further down. 250 # We use try_first_pass the second time to avoid prompting password twice 251 (optionalString (cfg.unixAuth && (config.security.pam.enableEcryptfs || cfg.pamMount)) '' 252 auth required pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth 253 ${optionalString config.security.pam.enableEcryptfs 254 "auth optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"} 255 ${optionalString cfg.pamMount 256 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 257 '') + '' 258 ${optionalString cfg.unixAuth 259 "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"} 260 ${optionalString cfg.otpwAuth 261 "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} 262 ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth 263 "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"} 264 ${optionalString use_ldap 265 "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} 266 ${optionalString config.krb5.enable '' 267 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass 268 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass 269 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass 270 ''} 271 auth required pam_deny.so 272 273 # Password management. 274 password requisite pam_unix.so nullok sha512 275 ${optionalString config.security.pam.enableEcryptfs 276 "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 277 ${optionalString cfg.pamMount 278 "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 279 ${optionalString use_ldap 280 "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 281 ${optionalString config.krb5.enable 282 "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"} 283 ${optionalString config.services.samba.syncPasswordsByPam 284 "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"} 285 286 # Session management. 287 session required pam_env.so envfile=${config.system.build.pamEnvironment} 288 session required pam_unix.so 289 ${optionalString cfg.setLoginUid 290 "session ${ 291 if config.boot.isContainer then "optional" else "required" 292 } pam_loginuid.so"} 293 ${optionalString cfg.makeHomeDir 294 "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=/etc/skel umask=0022"} 295 ${optionalString cfg.updateWtmp 296 "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} 297 ${optionalString config.security.pam.enableEcryptfs 298 "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 299 ${optionalString use_ldap 300 "session optional ${pam_ldap}/lib/security/pam_ldap.so"} 301 ${optionalString config.krb5.enable 302 "session optional ${pam_krb5}/lib/security/pam_krb5.so"} 303 ${optionalString cfg.otpwAuth 304 "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} 305 ${optionalString cfg.startSession 306 "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} 307 ${optionalString cfg.forwardXAuth 308 "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} 309 ${optionalString (cfg.limits != []) 310 "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} 311 ${optionalString (cfg.showMotd && config.users.motd != null) 312 "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} 313 ${optionalString cfg.pamMount 314 "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 315 ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable) 316 "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"} 317 ''); 318 }; 319 320 }; 321 322 323 inherit (pkgs) pam_krb5 pam_ccreds; 324 325 use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam); 326 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; 327 328 # Create a limits.conf(5) file. 329 makeLimitsConf = limits: 330 pkgs.writeText "limits.conf" 331 (concatMapStrings ({ domain, type, item, value }: 332 "${domain} ${type} ${item} ${toString value}\n") 333 limits); 334 335 motd = pkgs.writeText "motd" config.users.motd; 336 337 makePAMService = pamService: 338 { source = pkgs.writeText "${pamService.name}.pam" pamService.text; 339 target = "pam.d/${pamService.name}"; 340 }; 341 342in 343 344{ 345 346 ###### interface 347 348 options = { 349 350 security.pam.loginLimits = mkOption { 351 default = []; 352 example = 353 [ { domain = "ftp"; 354 type = "hard"; 355 item = "nproc"; 356 value = "0"; 357 } 358 { domain = "@student"; 359 type = "-"; 360 item = "maxlogins"; 361 value = "4"; 362 } 363 ]; 364 365 description = 366 '' Define resource limits that should apply to users or groups. 367 Each item in the list should be an attribute set with a 368 <varname>domain</varname>, <varname>type</varname>, 369 <varname>item</varname>, and <varname>value</varname> 370 attribute. The syntax and semantics of these attributes 371 must be that described in the limits.conf(5) man page. 372 ''; 373 }; 374 375 security.pam.services = mkOption { 376 default = []; 377 type = types.loaOf types.optionSet; 378 options = [ pamOpts ]; 379 description = 380 '' 381 This option defines the PAM services. A service typically 382 corresponds to a program that uses PAM, 383 e.g. <command>login</command> or <command>passwd</command>. 384 Each attribute of this set defines a PAM service, with the attribute name 385 defining the name of the service. 386 ''; 387 }; 388 389 security.pam.enableSSHAgentAuth = mkOption { 390 default = false; 391 description = 392 '' 393 Enable sudo logins if the user's SSH agent provides a key 394 present in <filename>~/.ssh/authorized_keys</filename>. 395 This allows machines to exclusively use SSH keys instead of 396 passwords. 397 ''; 398 }; 399 400 security.pam.enableOTPW = mkOption { 401 default = false; 402 description = '' 403 Enable the OTPW (one-time password) PAM module. 404 ''; 405 }; 406 407 security.pam.enableU2F = mkOption { 408 default = false; 409 description = '' 410 Enable the U2F PAM module. 411 ''; 412 }; 413 414 security.pam.enableEcryptfs = mkOption { 415 default = false; 416 description = '' 417 Enable eCryptfs PAM module (mounting ecryptfs home directory on login). 418 ''; 419 }; 420 421 users.motd = mkOption { 422 default = null; 423 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; 424 type = types.nullOr types.lines; 425 description = "Message of the day shown to users when they log in."; 426 }; 427 428 }; 429 430 431 ###### implementation 432 433 config = { 434 435 environment.systemPackages = 436 # Include the PAM modules in the system path mostly for the manpages. 437 [ pkgs.pam ] 438 ++ optional config.users.ldap.enable pam_ldap 439 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] 440 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] 441 ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ] 442 ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ] 443 ++ optionals config.security.pam.enableEcryptfs [ pkgs.ecryptfs ]; 444 445 security.setuidPrograms = 446 optionals config.security.pam.enableEcryptfs [ "mount.ecryptfs_private" "umount.ecryptfs_private" ]; 447 448 environment.etc = 449 mapAttrsToList (n: v: makePAMService v) config.security.pam.services; 450 451 security.setuidOwners = [ { 452 program = "unix_chkpwd"; 453 source = "${pkgs.pam}/sbin/unix_chkpwd.orig"; 454 owner = "root"; 455 setuid = true; 456 } ]; 457 458 security.pam.services = 459 { other.text = 460 '' 461 auth required pam_warn.so 462 auth required pam_deny.so 463 account required pam_warn.so 464 account required pam_deny.so 465 password required pam_warn.so 466 password required pam_deny.so 467 session required pam_warn.so 468 session required pam_deny.so 469 ''; 470 471 # Most of these should be moved to specific modules. 472 cups = {}; 473 ftp = {}; 474 i3lock = {}; 475 i3lock-color = {}; 476 screen = {}; 477 vlock = {}; 478 xlock = {}; 479 xscreensaver = {}; 480 }; 481 482 }; 483 484}