at v192 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.enableOATH; 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 config.users.ldap.enable 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 ${optionalString cfg.unixAuth 245 "auth ${if (config.security.pam.enableEcryptfs || cfg.pamMount) then "required" else "sufficient"} pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth"} 246 ${optionalString cfg.pamMount 247 "auth optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 248 ${optionalString config.security.pam.enableEcryptfs 249 "auth required ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so unwrap"} 250 ${optionalString cfg.otpwAuth 251 "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"} 252 ${optionalString cfg.oathAuth 253 "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"} 254 ${optionalString config.users.ldap.enable 255 "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"} 256 ${optionalString config.krb5.enable '' 257 auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass 258 auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass 259 auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass 260 ''} 261 ${optionalString (!(config.security.pam.enableEcryptfs || cfg.pamMount)) "auth required pam_deny.so"} 262 263 # Password management. 264 password requisite pam_unix.so nullok sha512 265 ${optionalString config.security.pam.enableEcryptfs 266 "password optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 267 ${optionalString cfg.pamMount 268 "password optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 269 ${optionalString config.users.ldap.enable 270 "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"} 271 ${optionalString config.krb5.enable 272 "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"} 273 ${optionalString config.services.samba.syncPasswordsByPam 274 "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"} 275 276 # Session management. 277 session required pam_env.so envfile=${config.system.build.pamEnvironment} 278 session required pam_unix.so 279 ${optionalString cfg.setLoginUid 280 "session ${ 281 if config.boot.isContainer then "optional" else "required" 282 } pam_loginuid.so"} 283 ${optionalString cfg.makeHomeDir 284 "session required ${pkgs.pam}/lib/security/pam_mkhomedir.so silent skel=/etc/skel umask=0022"} 285 ${optionalString cfg.updateWtmp 286 "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"} 287 ${optionalString config.security.pam.enableEcryptfs 288 "session optional ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so"} 289 ${optionalString config.users.ldap.enable 290 "session optional ${pam_ldap}/lib/security/pam_ldap.so"} 291 ${optionalString config.krb5.enable 292 "session optional ${pam_krb5}/lib/security/pam_krb5.so"} 293 ${optionalString cfg.otpwAuth 294 "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"} 295 ${optionalString cfg.oathAuth 296 "session optional ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"} 297 ${optionalString cfg.startSession 298 "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"} 299 ${optionalString cfg.forwardXAuth 300 "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"} 301 ${optionalString (cfg.limits != []) 302 "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf cfg.limits}"} 303 ${optionalString (cfg.showMotd && config.users.motd != null) 304 "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"} 305 ${optionalString cfg.pamMount 306 "session optional ${pkgs.pam_mount}/lib/security/pam_mount.so"} 307 ${optionalString (cfg.enableAppArmor && config.security.apparmor.enable) 308 "session optional ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so order=user,group,default debug"} 309 ''; 310 }; 311 312 }; 313 314 315 inherit (pkgs) pam_krb5 pam_ccreds; 316 317 pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap; 318 319 # Create a limits.conf(5) file. 320 makeLimitsConf = limits: 321 pkgs.writeText "limits.conf" 322 (concatMapStrings ({ domain, type, item, value }: 323 "${domain} ${type} ${item} ${toString value}\n") 324 limits); 325 326 motd = pkgs.writeText "motd" config.users.motd; 327 328 makePAMService = pamService: 329 { source = pkgs.writeText "${pamService.name}.pam" pamService.text; 330 target = "pam.d/${pamService.name}"; 331 }; 332 333in 334 335{ 336 337 ###### interface 338 339 options = { 340 341 security.pam.loginLimits = mkOption { 342 default = []; 343 example = 344 [ { domain = "ftp"; 345 type = "hard"; 346 item = "nproc"; 347 value = "0"; 348 } 349 { domain = "@student"; 350 type = "-"; 351 item = "maxlogins"; 352 value = "4"; 353 } 354 ]; 355 356 description = 357 '' Define resource limits that should apply to users or groups. 358 Each item in the list should be an attribute set with a 359 <varname>domain</varname>, <varname>type</varname>, 360 <varname>item</varname>, and <varname>value</varname> 361 attribute. The syntax and semantics of these attributes 362 must be that described in the limits.conf(5) man page. 363 ''; 364 }; 365 366 security.pam.services = mkOption { 367 default = []; 368 type = types.loaOf types.optionSet; 369 options = [ pamOpts ]; 370 description = 371 '' 372 This option defines the PAM services. A service typically 373 corresponds to a program that uses PAM, 374 e.g. <command>login</command> or <command>passwd</command>. 375 Each attribute of this set defines a PAM service, with the attribute name 376 defining the name of the service. 377 ''; 378 }; 379 380 security.pam.enableSSHAgentAuth = mkOption { 381 default = false; 382 description = 383 '' 384 Enable sudo logins if the user's SSH agent provides a key 385 present in <filename>~/.ssh/authorized_keys</filename>. 386 This allows machines to exclusively use SSH keys instead of 387 passwords. 388 ''; 389 }; 390 391 security.pam.enableOTPW = mkOption { 392 default = false; 393 description = '' 394 Enable the OTPW (one-time password) PAM module. 395 ''; 396 }; 397 398 security.pam.enableOATH = mkOption { 399 default = false; 400 description = '' 401 Enable the OATH (one-time password) PAM module. 402 ''; 403 }; 404 405 security.pam.enableU2F = mkOption { 406 default = false; 407 description = '' 408 Enable the U2F PAM module. 409 ''; 410 }; 411 412 security.pam.enableEcryptfs = mkOption { 413 default = false; 414 description = '' 415 Enable eCryptfs PAM module (mounting ecryptfs home directory on login). 416 ''; 417 }; 418 419 users.motd = mkOption { 420 default = null; 421 example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178."; 422 type = types.nullOr types.string; 423 description = "Message of the day shown to users when they log in."; 424 }; 425 426 }; 427 428 429 ###### implementation 430 431 config = { 432 433 environment.systemPackages = 434 # Include the PAM modules in the system path mostly for the manpages. 435 [ pkgs.pam ] 436 ++ optional config.users.ldap.enable pam_ldap 437 ++ optionals config.krb5.enable [pam_krb5 pam_ccreds] 438 ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ] 439 ++ optionals config.security.pam.enableOATH [ pkgs.oathToolkit ] 440 ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ] 441 ++ optionals config.security.pam.enableEcryptfs [ pkgs.ecryptfs ]; 442 443 security.setuidPrograms = 444 optionals config.security.pam.enableEcryptfs [ "mount.ecryptfs_private" "umount.ecryptfs_private" ]; 445 446 environment.etc = 447 mapAttrsToList (n: v: makePAMService v) config.security.pam.services; 448 449 security.setuidOwners = [ { 450 program = "unix_chkpwd"; 451 source = "${pkgs.pam}/sbin/unix_chkpwd.orig"; 452 owner = "root"; 453 setuid = true; 454 } ]; 455 456 security.pam.services = 457 { other.text = 458 '' 459 auth required pam_warn.so 460 auth required pam_deny.so 461 account required pam_warn.so 462 account required pam_deny.so 463 password required pam_warn.so 464 password required pam_deny.so 465 session required pam_warn.so 466 session required pam_deny.so 467 ''; 468 469 # Most of these should be moved to specific modules. 470 cups = {}; 471 ftp = {}; 472 i3lock = {}; 473 screen = {}; 474 vlock = {}; 475 xlock = {}; 476 xscreensaver = {}; 477 }; 478 479 }; 480 481}