diff --git a/doc/builders/special.xml b/doc/builders/special.xml index 8902ce5c81329..525eb71abfe7e 100644 --- a/doc/builders/special.xml +++ b/doc/builders/special.xml @@ -7,4 +7,5 @@ + diff --git a/doc/builders/special/darwin-builder.section.md b/doc/builders/special/darwin-builder.section.md new file mode 100644 index 0000000000000..96b1fcb507f25 --- /dev/null +++ b/doc/builders/special/darwin-builder.section.md @@ -0,0 +1,60 @@ +# darwin.builder {#sec-darwin-builder} + +`darwin.builder` provides a way to bootstrap a Linux builder on a macOS machine. + +This requires macOS version 12.4 or later. + +This also requires that port 22 on your machine is free (since Nix does not +permit specifying a non-default SSH port for builders). + +You will also need to be a trusted user for your Nix installation. In other +words, your `/etc/nix/nix.conf` should have something like: + +``` +extra-trusted-users = +``` + +To launch the builder, run the following flake: + +```ShellSession +$ nix run nixpkgs#darwin.builder +``` + +That will prompt you to enter your `sudo` password: + +``` ++ sudo --reset-timestamp /nix/store/…-install-credentials.sh ./keys +Password: +``` + +… so that it can install a private key used to `ssh` into the build server. +After that the script will launch the virtual machine: + +``` +<<< Welcome to NixOS 22.11.20220901.1bd8d11 (aarch64) - ttyAMA0 >>> + +Run 'nixos-help' for the NixOS manual. + +nixos login: +``` + +> Note: When you need to stop the VM, type `Ctrl`-`a` + `c` to open the `qemu` +> prompt and then type `quit` followed by `Enter` + +To delegate builds to the remote builder, add the following options to your +`nix.conf` file: + +``` +# - Replace ${ARCH} with either aarch64 or x86_64 to match your host machine +# - Replace ${MAX_JOBS} with the maximum number of builds (pick 4 if you're not sure) +builders = ssh-ng://builder@localhost ${ARCH}-linux /etc/nix/builder_ed25519 ${MAX_JOBS} - - - c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUpCV2N4Yi9CbGFxdDFhdU90RStGOFFVV3JVb3RpQzVxQkorVXVFV2RWQ2Igcm9vdEBuaXhvcwo=' + +# Not strictly necessary, but this will reduce your disk utilization +builders-use-substitutes = true +``` + +… and then restart your Nix daemon to apply the change: + +```ShellSession +$ sudo launchctl kickstart -k system/org.nixos.nix-daemon +``` diff --git a/nixos/modules/profiles/keys/ssh_host_ed25519_key b/nixos/modules/profiles/keys/ssh_host_ed25519_key new file mode 100644 index 0000000000000..b18489795369e --- /dev/null +++ b/nixos/modules/profiles/keys/ssh_host_ed25519_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmwAAAJASuMMnErjD +JwAAAAtzc2gtZWQyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmw +AAAEDIN2VWFyggtoSPXcAFy8dtG1uAig8sCuyE21eMDt2GgJBWcxb/Blaqt1auOtE+F8QU +WrUotiC5qBJ+UuEWdVCbAAAACnJvb3RAbml4b3MBAgM= +-----END OPENSSH PRIVATE KEY----- diff --git a/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub b/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub new file mode 100644 index 0000000000000..2c45826715fc5 --- /dev/null +++ b/nixos/modules/profiles/keys/ssh_host_ed25519_key.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJBWcxb/Blaqt1auOtE+F8QUWrUotiC5qBJ+UuEWdVCb root@nixos diff --git a/nixos/modules/profiles/macos-builder.nix b/nixos/modules/profiles/macos-builder.nix new file mode 100644 index 0000000000000..895dd04cb4852 --- /dev/null +++ b/nixos/modules/profiles/macos-builder.nix @@ -0,0 +1,134 @@ +{ config, pkgs, ... }: + +let + keysDirectory = "/var/keys"; + + user = "builder"; + + keyType = "ed25519"; + +in + +{ imports = [ + ../virtualisation/qemu-vm.nix + ]; + + # The builder is not intended to be used interactively + documentation.enable = false; + + environment.etc = { + "ssh/ssh_host_ed25519_key" = { + mode = "0600"; + + source = ./keys/ssh_host_ed25519_key; + }; + + "ssh/ssh_host_ed25519_key.pub" = { + mode = "0644"; + + source = ./keys/ssh_host_ed25519_key.pub; + }; + }; + + # DNS fails for QEMU user networking (SLiRP) on macOS. See: + # + # https://github.com/utmapp/UTM/issues/2353 + # + # This works around that by using a public DNS server other than the DNS + # server that QEMU provides (normally 10.0.2.3) + networking.nameservers = [ "8.8.8.8" ]; + + nix.settings = { + auto-optimise-store = true; + + min-free = 1024 * 1024 * 1024; + + max-free = 3 * 1024 * 1024 * 1024; + + trusted-users = [ "root" user ]; + }; + + services.openssh = { + enable = true; + + authorizedKeysFiles = [ "${keysDirectory}/%u_${keyType}.pub" ]; + }; + + system.build.macos-builder-installer = + let + privateKey = "/etc/nix/${user}_${keyType}"; + + publicKey = "${privateKey}.pub"; + + # This installCredentials script is written so that it's as easy as + # possible for a user to audit before confirming the `sudo` + installCredentials = pkgs.writeShellScript "install-credentials" '' + KEYS="''${1}" + INSTALL=${hostPkgs.coreutils}/bin/install + "''${INSTALL}" -g nixbld -m 600 "''${KEYS}/${user}_${keyType}" ${privateKey} + "''${INSTALL}" -g nixbld -m 644 "''${KEYS}/${user}_${keyType}.pub" ${publicKey} + ''; + + hostPkgs = config.virtualisation.host.pkgs; + + in + hostPkgs.writeShellScriptBin "create-builder" '' + KEYS="''${KEYS:-./keys}" + ${hostPkgs.coreutils}/bin/mkdir --parent "''${KEYS}" + PRIVATE_KEY="''${KEYS}/${user}_${keyType}" + PUBLIC_KEY="''${PRIVATE_KEY}.pub" + if [ ! -e "''${PRIVATE_KEY}" ] || [ ! -e "''${PUBLIC_KEY}" ]; then + ${hostPkgs.coreutils}/bin/rm --force -- "''${PRIVATE_KEY}" "''${PUBLIC_KEY}" + ${hostPkgs.openssh}/bin/ssh-keygen -q -f "''${PRIVATE_KEY}" -t ${keyType} -N "" -C 'builder@localhost' + fi + if ! ${hostPkgs.diffutils}/bin/cmp "''${PUBLIC_KEY}" ${publicKey}; then + (set -x; sudo --reset-timestamp ${installCredentials} "''${KEYS}") + fi + KEYS="$(nix-store --add "$KEYS")" ${config.system.build.vm}/bin/run-nixos-vm + ''; + + system.stateVersion = "22.05"; + + users.users."${user}"= { + isNormalUser = true; + }; + + virtualisation = { + diskSize = 20 * 1024; + + memorySize = 3 * 1024; + + forwardPorts = [ + { from = "host"; guest.port = 22; host.port = 22; } + ]; + + # Disable graphics for the builder since users will likely want to run it + # non-interactively in the background. + graphics = false; + + sharedDirectories.keys = { + source = "\"$KEYS\""; + target = keysDirectory; + }; + + # If we don't enable this option then the host will fail to delegate builds + # to the guest, because: + # + # - The host will lock the path to build + # - The host will delegate the build to the guest + # - The guest will attempt to lock the same path and fail because + # the lockfile on the host is visible on the guest + # + # Snapshotting the host's /nix/store as an image isolates the guest VM's + # /nix/store from the host's /nix/store, preventing this problem. + useNixStoreImage = true; + + # Obviously the /nix/store needs to be writable on the guest in order for it + # to perform builds. + writableStore = true; + + # This ensures that anything built on the guest isn't lost when the guest is + # restarted. + writableStoreUseTmpfs = false; + }; +} diff --git a/pkgs/top-level/darwin-packages.nix b/pkgs/top-level/darwin-packages.nix index 8c1e259525044..c270dd2220ddf 100644 --- a/pkgs/top-level/darwin-packages.nix +++ b/pkgs/top-level/darwin-packages.nix @@ -205,4 +205,23 @@ impure-cmds // appleSourcePackages // chooseLibs // { discrete-scroll = callPackage ../os-specific/darwin/discrete-scroll { }; + # See doc/builders/special/darwin-builder.section.md + builder = + let + toGuest = builtins.replaceStrings [ "darwin" ] [ "linux" ]; + + nixos = import ../../nixos { + configuration = { + imports = [ + ../../nixos/modules/profiles/macos-builder.nix + ]; + + virtualisation.host = { inherit pkgs; }; + }; + + system = toGuest stdenv.hostPlatform.system; + }; + + in + nixos.config.system.build.macos-builder-installer; })