Unverified Commit bcc8d11f authored by Domen Kožar's avatar Domen Kožar Committed by GitHub
Browse files

Merge pull request #206951 from MercuryTechnologies/gabriella/macos-builder

darwin.builder: init
parents e9b218c3 13bd3271
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7,4 +7,5 @@
 </para>
 <xi:include href="special/fhs-environments.section.xml" />
 <xi:include href="special/mkshell.section.xml" />
 <xi:include href="special/darwin-builder.section.xml" />
</chapter>
+60 −0
Original line number Diff line number Diff line
# 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 = <your username goes here>
```

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
```
+7 −0
Original line number Diff line number Diff line
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmwAAAJASuMMnErjD
JwAAAAtzc2gtZWQyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmw
AAAEDIN2VWFyggtoSPXcAFy8dtG1uAig8sCuyE21eMDt2GgJBWcxb/Blaqt1auOtE+F8QU
WrUotiC5qBJ+UuEWdVCbAAAACnJvb3RAbml4b3MBAgM=
-----END OPENSSH PRIVATE KEY-----
+1 −0
Original line number Diff line number Diff line
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJBWcxb/Blaqt1auOtE+F8QUWrUotiC5qBJ+UuEWdVCb root@nixos
+134 −0
Original line number Diff line number Diff line
{ 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;
  };
}
Loading