Update lean 4.15 (#373564)
[NixPkgs.git] / nixos / modules / system / boot / systemd / sysusers.nix
blobd7b700f5748cd7b18d3091b413000098994fc41f
2   config,
3   lib,
4   pkgs,
5   utils,
6   ...
7 }:
9 let
11   cfg = config.systemd.sysusers;
12   userCfg = config.users;
14   systemUsers = lib.filterAttrs (_username: opts: !opts.isNormalUser) userCfg.users;
16   sysusersConfig = pkgs.writeTextDir "00-nixos.conf" ''
17     # Type Name ID GECOS Home directory Shell
19     # Users
20     ${lib.concatLines (
21       lib.mapAttrsToList (
22         username: opts:
23         let
24           uid = if opts.uid == null then "/var/lib/nixos/uid/${username}" else toString opts.uid;
25         in
26         ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}''
27       ) systemUsers
28     )}
30     # Groups
31     ${lib.concatLines (
32       lib.mapAttrsToList (
33         groupname: opts:
34         ''g ${groupname} ${
35           if opts.gid == null then "/var/lib/nixos/gid/${groupname}" else toString opts.gid
36         }''
37       ) userCfg.groups
38     )}
40     # Group membership
41     ${lib.concatStrings (
42       lib.mapAttrsToList (
43         groupname: opts: (lib.concatMapStrings (username: "m ${username} ${groupname}\n")) opts.members
44       ) userCfg.groups
45     )}
46   '';
48   immutableEtc = config.system.etc.overlay.enable && !config.system.etc.overlay.mutable;
49   # The location of the password files when using an immutable /etc.
50   immutablePasswordFilesLocation = "/var/lib/nixos/etc";
51   passwordFilesLocation = if immutableEtc then immutablePasswordFilesLocation else "/etc";
52   # The filenames created by systemd-sysusers.
53   passwordFiles = [
54     "passwd"
55     "group"
56     "shadow"
57     "gshadow"
58   ];
64   options = {
66     # This module doesn't set it's own user options but reuses the ones from
67     # users-groups.nix
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.
75           Note: This is experimental.
76         '';
77       };
78     };
80   };
82   config = lib.mkIf cfg.enable {
84     assertions =
85       [
86         {
87           assertion = config.system.activationScripts.users == "";
88           message = "system.activationScripts.users has to be empty to use systemd-sysusers";
89         }
90       ]
91       ++ (lib.mapAttrsToList (username: opts: {
92         assertion = !opts.isNormalUser;
93         message = "${username} is a normal user. systemd-sysusers doesn't create normal users, only system users.";
94       }) userCfg.users)
95       ++ lib.mapAttrsToList (username: opts: {
96         assertion =
97           (opts.password == opts.initialPassword || opts.password == null)
98           && (opts.hashedPassword == opts.initialHashedPassword || opts.hashedPassword == null);
99         message = "user '${username}' uses password or hashedPassword. systemd-sysupdate only supports initial passwords. It'll never update your passwords.";
100       }) systemUsers;
102     systemd = {
104       # Create home directories, do not create /var/empty even if that's a user's
105       # home.
106       tmpfiles.settings.home-directories = lib.mapAttrs' (
107         username: opts:
108         lib.nameValuePair opts.home {
109           d = {
110             mode = opts.homeMode;
111             user = username;
112             group = opts.group;
113           };
114         }
115       ) (lib.filterAttrs (_username: opts: opts.home != "/var/empty") systemUsers);
117       # Create uid/gid marker files for those without an explicit id
118       tmpfiles.settings.nixos-uid = lib.mapAttrs' (
119         username: opts:
120         lib.nameValuePair "/var/lib/nixos/uid/${username}" {
121           f = {
122             user = username;
123           };
124         }
125       ) (lib.filterAttrs (_username: opts: opts.uid == null) systemUsers);
127       tmpfiles.settings.nixos-gid = lib.mapAttrs' (
128         groupname: opts:
129         lib.nameValuePair "/var/lib/nixos/gid/${groupname}" {
130           f = {
131             group = groupname;
132           };
133         }
134       ) (lib.filterAttrs (_groupname: opts: opts.gid == null) userCfg.groups);
136       additionalUpstreamSystemUnits = [
137         "systemd-sysusers.service"
138       ];
140       services.systemd-sysusers = {
141         # Enable switch-to-configuration to restart the service.
142         unitConfig.ConditionNeedsUpdate = [ "" ];
143         requiredBy = [ "sysinit-reactivation.target" ];
144         before = [ "sysinit-reactivation.target" ];
145         restartTriggers = [ "${config.environment.etc."sysusers.d".source}" ];
147         serviceConfig = {
148           # When we have an immutable /etc we cannot write the files directly
149           # to /etc so we write it to a different directory and symlink them
150           # into /etc.
151           #
152           # We need to explicitly list the config file, otherwise
153           # systemd-sysusers cannot find it when we also pass another flag.
154           ExecStart = lib.mkIf immutableEtc [
155             ""
156             "${config.systemd.package}/bin/systemd-sysusers --root ${builtins.dirOf immutablePasswordFilesLocation} /etc/sysusers.d/00-nixos.conf"
157           ];
159           # Make the source files writable before executing sysusers.
160           ExecStartPre = lib.mkIf (!userCfg.mutableUsers) (
161             lib.map (file: "-${pkgs.util-linux}/bin/umount ${passwordFilesLocation}/${file}") passwordFiles
162           );
163           # Make the source files read-only after sysusers has finished.
164           ExecStartPost = lib.mkIf (!userCfg.mutableUsers) (
165             lib.map (
166               file:
167               "${pkgs.util-linux}/bin/mount --bind -o ro ${passwordFilesLocation}/${file} ${passwordFilesLocation}/${file}"
168             ) passwordFiles
169           );
171           LoadCredential = lib.mapAttrsToList (
172             username: opts: "passwd.hashed-password.${username}:${opts.hashedPasswordFile}"
173           ) (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) systemUsers);
174           SetCredential =
175             (lib.mapAttrsToList (
176               username: opts: "passwd.hashed-password.${username}:${opts.initialHashedPassword}"
177             ) (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) systemUsers))
178             ++ (lib.mapAttrsToList (
179               username: opts: "passwd.plaintext-password.${username}:${opts.initialPassword}"
180             ) (lib.filterAttrs (_username: opts: opts.initialPassword != null) systemUsers));
181         };
182       };
184     };
186     environment.etc = lib.mkMerge [
187       ({
188         "sysusers.d".source = sysusersConfig;
189       })
191       # Statically create the symlinks to immutablePasswordFilesLocation when
192       # using an immutable /etc because we will not be able to do it at
193       # runtime!
194       (lib.mkIf immutableEtc (
195         lib.listToAttrs (
196           lib.map (
197             file:
198             lib.nameValuePair file {
199               source = "${immutablePasswordFilesLocation}/${file}";
200               mode = "direct-symlink";
201             }
202           ) passwordFiles
203         )
204       ))
205     ];
206   };
208   meta.maintainers = with lib.maintainers; [ nikstur ];