|
35 | 35 | }; |
36 | 36 |
|
37 | 37 | userbornConfigJson = pkgs.writeText "userborn.json" (builtins.toJSON userbornConfig); |
| 38 | + userbornStaticFiles = |
| 39 | + pkgs.runCommand "static-userborn" { } |
| 40 | + "mkdir -p $out; ${lib.getExe cfg.package} ${userbornConfigJson} $out"; |
38 | 41 |
|
39 | 42 | immutableEtc = config.system.etc.overlay.enable && !config.system.etc.overlay.mutable; |
40 | 43 | # The filenames created by userborn. |
|
51 | 54 |
|
52 | 55 | enable = lib.mkEnableOption "userborn"; |
53 | 56 |
|
| 57 | + static = lib.mkOption { |
| 58 | + type = lib.types.bool; |
| 59 | + default = false; |
| 60 | + description = '' |
| 61 | + Whether to generate the password files at build time and store them directly |
| 62 | + in the system closure, without requiring any services at boot time. |
| 63 | +
|
| 64 | + This is STRICTLY intended for embedded appliance images that only have system |
| 65 | + users with manually managed static user IDs, and CANNOT be used with generation |
| 66 | + updates. |
| 67 | +
|
| 68 | + WARNING: In this mode, you MUST statically manage user IDs yourself, carefully. |
| 69 | + Beware, UID reuse is a serious security issue and it's your responsibility |
| 70 | + to avoid it over the entire lifetime of the system. |
| 71 | + ''; |
| 72 | + }; |
| 73 | + |
54 | 74 | package = lib.mkPackageOption pkgs "userborn" { }; |
55 | 75 |
|
56 | 76 | passwordFilesLocation = lib.mkOption { |
57 | 77 | type = lib.types.str; |
58 | | - default = if immutableEtc then "/var/lib/nixos" else "/etc"; |
59 | | - defaultText = lib.literalExpression ''if immutableEtc then "/var/lib/nixos" else "/etc"''; |
| 78 | + default = if immutableEtc && !cfg.static then "/var/lib/nixos" else "/etc"; |
| 79 | + defaultText = lib.literalExpression ''if immutableEtc && !config.services.userborn.static then "/var/lib/nixos" else "/etc"''; |
60 | 80 | description = '' |
61 | 81 | The location of the original password files. |
62 | 82 |
|
|
83 | 103 | message = "system.activationScripts.users has to be empty to use userborn"; |
84 | 104 | } |
85 | 105 | { |
86 | | - assertion = immutableEtc -> (cfg.passwordFilesLocation != "/etc"); |
87 | | - message = "When `system.etc.overlay.mutable = false`, `services.userborn.passwordFilesLocation` cannot be set to `/etc`"; |
| 106 | + assertion = (immutableEtc && !cfg.static) -> (cfg.passwordFilesLocation != "/etc"); |
| 107 | + message = "When `system.etc.overlay.mutable = false` and `services.userborn.static = false`, `services.userborn.passwordFilesLocation` cannot be set to `/etc`"; |
| 108 | + } |
| 109 | + { |
| 110 | + assertion = !(cfg.static && config.system.switch.enable); |
| 111 | + message = "You cannot use `services.userborn.static = true` with switchable configurations, it is ONLY indended for appliance images with fully static user IDs"; |
88 | 112 | } |
89 | 113 | ]; |
90 | 114 |
|
|
110 | 134 | ) userCfg.users |
111 | 135 | ); |
112 | 136 |
|
113 | | - services.userborn = { |
| 137 | + services.userborn = lib.mkIf (!cfg.static) { |
114 | 138 | wantedBy = [ "sysinit.target" ]; |
115 | 139 | requiredBy = [ "sysinit-reactivation.target" ]; |
116 | 140 | after = [ |
|
166 | 190 | }; |
167 | 191 | }; |
168 | 192 |
|
169 | | - # Statically create the symlinks to passwordFilesLocation when they're not |
170 | | - # inside /etc because we will not be able to do it at runtime in case of an |
171 | | - # immutable /etc! |
172 | | - environment.etc = lib.mkIf (cfg.passwordFilesLocation != "/etc") ( |
173 | | - lib.listToAttrs ( |
174 | | - lib.map ( |
175 | | - file: |
176 | | - lib.nameValuePair file { |
177 | | - source = "${cfg.passwordFilesLocation}/${file}"; |
178 | | - mode = "direct-symlink"; |
179 | | - } |
180 | | - ) passwordFiles |
181 | | - ) |
182 | | - ); |
| 193 | + environment.etc = |
| 194 | + if cfg.static then |
| 195 | + # In static mode, statically drop the files into an immutable /etc. |
| 196 | + lib.listToAttrs ( |
| 197 | + lib.map ( |
| 198 | + file: |
| 199 | + lib.nameValuePair file { |
| 200 | + source = "${userbornStaticFiles}/${file}"; |
| 201 | + mode = if file == "shadow" then "0000" else "0644"; |
| 202 | + } |
| 203 | + ) passwordFiles |
| 204 | + ) |
| 205 | + else if cfg.passwordFilesLocation != "/etc" then |
| 206 | + # Statically create the symlinks to passwordFilesLocation when they're not |
| 207 | + # inside /etc because we will not be able to do it at runtime in case of a |
| 208 | + # (non-static) immutable /etc! |
| 209 | + lib.listToAttrs ( |
| 210 | + lib.map ( |
| 211 | + file: |
| 212 | + lib.nameValuePair file { |
| 213 | + source = "${cfg.passwordFilesLocation}/${file}"; |
| 214 | + mode = "direct-symlink"; |
| 215 | + } |
| 216 | + ) passwordFiles |
| 217 | + ) |
| 218 | + else |
| 219 | + { }; |
183 | 220 | }; |
184 | 221 |
|
185 | 222 | meta.maintainers = with lib.maintainers; [ nikstur ]; |
|
0 commit comments