at 24.11-pre 5.3 kB view raw
1{ config, lib, pkgs, utils, ... }: 2 3let 4 5 cfg = config.systemd.sysusers; 6 userCfg = config.users; 7 8 sysusersConfig = pkgs.writeTextDir "00-nixos.conf" '' 9 # Type Name ID GECOS Home directory Shell 10 11 # Users 12 ${lib.concatLines (lib.mapAttrsToList 13 (username: opts: 14 let 15 uid = if opts.uid == null then "-" else toString opts.uid; 16 in 17 ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}'' 18 ) 19 userCfg.users) 20 } 21 22 # Groups 23 ${lib.concatLines (lib.mapAttrsToList 24 (groupname: opts: ''g ${groupname} ${if opts.gid == null then "-" else toString opts.gid}'') userCfg.groups) 25 } 26 27 # Group membership 28 ${lib.concatStrings (lib.mapAttrsToList 29 (groupname: opts: (lib.concatMapStrings (username: "m ${username} ${groupname}\n")) opts.members ) userCfg.groups) 30 } 31 ''; 32 33 staticSysusersCredentials = pkgs.runCommand "static-sysusers-credentials" { } '' 34 mkdir $out; cd $out 35 ${lib.concatLines ( 36 (lib.mapAttrsToList 37 (username: opts: "echo -n '${opts.initialHashedPassword}' > 'passwd.hashed-password.${username}'") 38 (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users)) 39 ++ 40 (lib.mapAttrsToList 41 (username: opts: "echo -n '${opts.initialPassword}' > 'passwd.plaintext-password.${username}'") 42 (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users)) 43 ++ 44 (lib.mapAttrsToList 45 (username: opts: "cat '${opts.hashedPasswordFile}' > 'passwd.hashed-password.${username}'") 46 (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users)) 47 ) 48 } 49 ''; 50 51 staticSysusers = pkgs.runCommand "static-sysusers" 52 { 53 nativeBuildInputs = [ pkgs.systemd ]; 54 } '' 55 mkdir $out 56 export CREDENTIALS_DIRECTORY=${staticSysusersCredentials} 57 systemd-sysusers --root $out ${sysusersConfig}/00-nixos.conf 58 ''; 59 60in 61 62{ 63 64 options = { 65 66 # This module doesn't set it's own user options but reuses the ones from 67 # users-groups.nix 68 69 systemd.sysusers = { 70 enable = lib.mkEnableOption "systemd-sysusers" // { 71 description = '' 72 If enabled, users are created with systemd-sysusers instead of with 73 the custom `update-users-groups.pl` script. 74 75 Note: This is experimental. 76 ''; 77 }; 78 }; 79 80 }; 81 82 config = lib.mkIf cfg.enable { 83 84 assertions = [ 85 { 86 assertion = config.system.activationScripts.users == ""; 87 message = "system.activationScripts.users has to be empty to use systemd-sysusers"; 88 } 89 { 90 assertion = config.users.mutableUsers -> config.system.etc.overlay.enable; 91 message = "config.users.mutableUsers requires config.system.etc.overlay.enable."; 92 } 93 ]; 94 95 systemd = lib.mkMerge [ 96 ({ 97 98 # Create home directories, do not create /var/empty even if that's a user's 99 # home. 100 tmpfiles.settings.home-directories = lib.mapAttrs' 101 (username: opts: lib.nameValuePair opts.home { 102 d = { 103 mode = opts.homeMode; 104 user = username; 105 group = opts.group; 106 }; 107 }) 108 (lib.filterAttrs (_username: opts: opts.home != "/var/empty") userCfg.users); 109 }) 110 111 (lib.mkIf config.users.mutableUsers { 112 additionalUpstreamSystemUnits = [ 113 "systemd-sysusers.service" 114 ]; 115 116 services.systemd-sysusers = { 117 # Enable switch-to-configuration to restart the service. 118 unitConfig.ConditionNeedsUpdate = [ "" ]; 119 requiredBy = [ "sysinit-reactivation.target" ]; 120 before = [ "sysinit-reactivation.target" ]; 121 restartTriggers = [ "${config.environment.etc."sysusers.d".source}" ]; 122 123 serviceConfig = { 124 LoadCredential = lib.mapAttrsToList 125 (username: opts: "passwd.hashed-password.${username}:${opts.hashedPasswordFile}") 126 (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users); 127 SetCredential = (lib.mapAttrsToList 128 (username: opts: "passwd.hashed-password.${username}:${opts.initialHashedPassword}") 129 (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users)) 130 ++ 131 (lib.mapAttrsToList 132 (username: opts: "passwd.plaintext-password.${username}:${opts.initialPassword}") 133 (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users)) 134 ; 135 }; 136 }; 137 }) 138 ]; 139 140 environment.etc = lib.mkMerge [ 141 (lib.mkIf (!userCfg.mutableUsers) { 142 "passwd" = { 143 source = "${staticSysusers}/etc/passwd"; 144 mode = "0644"; 145 }; 146 "group" = { 147 source = "${staticSysusers}/etc/group"; 148 mode = "0644"; 149 }; 150 "shadow" = { 151 source = "${staticSysusers}/etc/shadow"; 152 mode = "0000"; 153 }; 154 "gshadow" = { 155 source = "${staticSysusers}/etc/gshadow"; 156 mode = "0000"; 157 }; 158 }) 159 160 (lib.mkIf userCfg.mutableUsers { 161 "sysusers.d".source = sysusersConfig; 162 }) 163 ]; 164 165 }; 166 167 meta.maintainers = with lib.maintainers; [ nikstur ]; 168 169}