Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

darwin.builder: init #206951

Merged
merged 3 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/builders/special.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 60 additions & 0 deletions doc/builders/special/darwin-builder.section.md
Original file line number Diff line number Diff line change
@@ -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).
Comment on lines +7 to +8
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to change this via the root's ssh_config


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 changes: 7 additions & 0 deletions nixos/modules/profiles/keys/ssh_host_ed25519_key
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmwAAAJASuMMnErjD
JwAAAAtzc2gtZWQyNTUxOQAAACCQVnMW/wZWqrdWrjrRPhfEFFq1KLYguagSflLhFnVQmw
AAAEDIN2VWFyggtoSPXcAFy8dtG1uAig8sCuyE21eMDt2GgJBWcxb/Blaqt1auOtE+F8QU
WrUotiC5qBJ+UuEWdVCbAAAACnJvb3RAbml4b3MBAgM=
-----END OPENSSH PRIVATE KEY-----
Comment on lines +1 to +7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not in favor of hardcoding a private key. What alternatives do we have?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of a good alternative here. However, in this case I think it's not an issue because this is only use as the SSH host key and not the client's key (which is still handled securely)

1 change: 1 addition & 0 deletions nixos/modules/profiles/keys/ssh_host_ed25519_key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJBWcxb/Blaqt1auOtE+F8QUWrUotiC5qBJ+UuEWdVCb root@nixos
134 changes: 134 additions & 0 deletions nixos/modules/profiles/macos-builder.nix
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have turned this off since a while to reduce io load but didn't do benchmarking. Is the difference noticeable? Not sure if we should turn it on by default or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually am not too attached to this setting. I don't remember why I added it


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}
Comment on lines +65 to +69
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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}
installCredentials = let
install = ${hostPkgs.coreutils}/bin/install;
in pkgs.writeShellScript "install-credentials" ''
"${install}" -g nixbld -m 600 "''${1}/${user}_${keyType}" ${privateKey}
"${install}" -g nixbld -m 644 "''${1}/${user}_${keyType}.pub" ${publicKey}

If the goal is to make the script as easy as possible then this is in the end as easy as it gets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I deliberately did not do it that way because I wanted the script to be easier to read. In particular, I wanted the script to fit within 80 columns

Copy link
Member

@SuperSandro2000 SuperSandro2000 Dec 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something does not get easier to read if it fits within 80 columns but has two extra lines.

'';

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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be taken from nixpkgs instead of being hardcoded here or set once on initialization.


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;
};
}
19 changes: 19 additions & 0 deletions pkgs/top-level/darwin-packages.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Gabriella439 marked this conversation as resolved.
Show resolved Hide resolved
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;
})