vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / profiles / macos-builder.nix
blob08b2fce40011bc5694f281b264af31617340e1ea
1 { config, lib, options, ... }:
3 let
4   keysDirectory = "/var/keys";
6   user = "builder";
8   keyType = "ed25519";
10   cfg = config.virtualisation.darwin-builder;
15   imports = [
16     ../virtualisation/qemu-vm.nix
18     # Avoid a dependency on stateVersion
19     {
20       disabledModules = [
21         ../virtualisation/nixos-containers.nix
22         ../services/x11/desktop-managers/xterm.nix
23       ];
24       # swraid's default depends on stateVersion
25       config.boot.swraid.enable = false;
26       options.boot.isContainer = lib.mkOption { default = false; internal = true; };
27     }
28   ];
30   options.virtualisation.darwin-builder = with lib; {
31     diskSize = mkOption {
32       default = 20 * 1024;
33       type = types.int;
34       example = 30720;
35       description = "The maximum disk space allocated to the runner in MB";
36     };
37     memorySize = mkOption {
38       default = 3 * 1024;
39       type = types.int;
40       example = 8192;
41       description = "The runner's memory in MB";
42     };
43     min-free = mkOption {
44       default = 1024 * 1024 * 1024;
45       type = types.int;
46       example = 1073741824;
47       description = ''
48         The threshold (in bytes) of free disk space left at which to
49         start garbage collection on the runner
50       '';
51     };
52     max-free = mkOption {
53       default = 3 * 1024 * 1024 * 1024;
54       type = types.int;
55       example = 3221225472;
56       description = ''
57         The threshold (in bytes) of free disk space left at which to
58         stop garbage collection on the runner
59       '';
60     };
61     workingDirectory = mkOption {
62        default = ".";
63        type = types.str;
64        example = "/var/lib/darwin-builder";
65        description = ''
66          The working directory to use to run the script. When running
67          as part of a flake will need to be set to a non read-only filesystem.
68        '';
69     };
70     hostPort = mkOption {
71       default = 31022;
72       type = types.int;
73       example = 22;
74       description = ''
75         The localhost host port to forward TCP to the guest port.
76       '';
77     };
78   };
80   config = {
81     # The builder is not intended to be used interactively
82     documentation.enable = false;
84     environment.etc = {
85       "ssh/ssh_host_ed25519_key" = {
86         mode = "0600";
88         source = ./keys/ssh_host_ed25519_key;
89       };
91       "ssh/ssh_host_ed25519_key.pub" = {
92         mode = "0644";
94         source = ./keys/ssh_host_ed25519_key.pub;
95       };
96     };
98     # DNS fails for QEMU user networking (SLiRP) on macOS.  See:
99     #
100     # https://github.com/utmapp/UTM/issues/2353
101     #
102     # This works around that by using a public DNS server other than the DNS
103     # server that QEMU provides (normally 10.0.2.3)
104     networking.nameservers = [ "8.8.8.8" ];
106     # The linux builder is a lightweight VM for remote building; not evaluation.
107     nix.channel.enable = false;
109     # Deployment is by image.
110     # TODO system.switch.enable = false;?
111     system.disableInstallerTools = true;
113     nix.settings = {
114       auto-optimise-store = true;
116       min-free = cfg.min-free;
118       max-free = cfg.max-free;
120       trusted-users = [ user ];
121     };
123     services = {
124       getty.autologinUser = user;
126       openssh = {
127         enable = true;
129         authorizedKeysFiles = [ "${keysDirectory}/%u_${keyType}.pub" ];
130       };
131     };
133     system.build.macos-builder-installer =
134       let
135         privateKey = "/etc/nix/${user}_${keyType}";
137         publicKey = "${privateKey}.pub";
139         # This installCredentials script is written so that it's as easy as
140         # possible for a user to audit before confirming the `sudo`
141         installCredentials = hostPkgs.writeShellScript "install-credentials" ''
142           set -euo pipefail
144           KEYS="''${1}"
145           INSTALL=${hostPkgs.coreutils}/bin/install
146           "''${INSTALL}" -g nixbld -m 600 "''${KEYS}/${user}_${keyType}" ${privateKey}
147           "''${INSTALL}" -g nixbld -m 644 "''${KEYS}/${user}_${keyType}.pub" ${publicKey}
148         '';
150         hostPkgs = config.virtualisation.host.pkgs;
152         script = hostPkgs.writeShellScriptBin "create-builder" (
153           ''
154             set -euo pipefail
155           '' +
156           # When running as non-interactively as part of a DarwinConfiguration the working directory
157           # must be set to a writeable directory.
158         (if cfg.workingDirectory != "." then ''
159           ${hostPkgs.coreutils}/bin/mkdir --parent "${cfg.workingDirectory}"
160           cd "${cfg.workingDirectory}"
161         '' else "") + ''
162           KEYS="''${KEYS:-./keys}"
163           ${hostPkgs.coreutils}/bin/mkdir --parent "''${KEYS}"
164           PRIVATE_KEY="''${KEYS}/${user}_${keyType}"
165           PUBLIC_KEY="''${PRIVATE_KEY}.pub"
166           if [ ! -e "''${PRIVATE_KEY}" ] || [ ! -e "''${PUBLIC_KEY}" ]; then
167               ${hostPkgs.coreutils}/bin/rm --force -- "''${PRIVATE_KEY}" "''${PUBLIC_KEY}"
168               ${hostPkgs.openssh}/bin/ssh-keygen -q -f "''${PRIVATE_KEY}" -t ${keyType} -N "" -C 'builder@localhost'
169           fi
170           if ! ${hostPkgs.diffutils}/bin/cmp "''${PUBLIC_KEY}" ${publicKey}; then
171             (set -x; sudo --reset-timestamp ${installCredentials} "''${KEYS}")
172           fi
173           KEYS="$(${hostPkgs.nix}/bin/nix-store --add "$KEYS")" ${lib.getExe config.system.build.vm}
174         '');
176       in
177       script.overrideAttrs (old: {
178         pos = __curPos; # sets meta.position to point here; see script binding above for package definition
179         meta = (old.meta or { }) // {
180           platforms = lib.platforms.darwin;
181         };
182         passthru = (old.passthru or { }) // {
183           # Let users in the repl inspect the config
184           nixosConfig = config;
185           nixosOptions = options;
186         };
187       });
189     system = {
190       # To prevent gratuitous rebuilds on each change to Nixpkgs
191       nixos.revision = null;
193       # to be updated by module maintainers, see nixpkgs#325610
194       stateVersion = "24.05";
195     };
197     users.users."${user}" = {
198       isNormalUser = true;
199     };
201     security.polkit.enable = true;
203     security.polkit.extraConfig = ''
204       polkit.addRule(function(action, subject) {
205         if (action.id === "org.freedesktop.login1.power-off" && subject.user === "${user}") {
206           return "yes";
207         } else {
208           return "no";
209         }
210       })
211     '';
213     virtualisation = {
214       diskSize = cfg.diskSize;
216       memorySize = cfg.memorySize;
218       forwardPorts = [
219         { from = "host"; guest.port = 22; host.port = cfg.hostPort; }
220       ];
222       # Disable graphics for the builder since users will likely want to run it
223       # non-interactively in the background.
224       graphics = false;
226       sharedDirectories.keys = {
227         source = "\"$KEYS\"";
228         target = keysDirectory;
229       };
231       # If we don't enable this option then the host will fail to delegate builds
232       # to the guest, because:
233       #
234       # - The host will lock the path to build
235       # - The host will delegate the build to the guest
236       # - The guest will attempt to lock the same path and fail because
237       #   the lockfile on the host is visible on the guest
238       #
239       # Snapshotting the host's /nix/store as an image isolates the guest VM's
240       # /nix/store from the host's /nix/store, preventing this problem.
241       useNixStoreImage = true;
243       # Obviously the /nix/store needs to be writable on the guest in order for it
244       # to perform builds.
245       writableStore = true;
247       # This ensures that anything built on the guest isn't lost when the guest is
248       # restarted.
249       writableStoreUseTmpfs = false;
251       # Pass certificates from host to the guest otherwise when custom CA certificates
252       # are required we can't use the cached builder.
253       useHostCerts = true;
254     };
255   };