Security Advisory: `darwin.linux-builder` leaks its SSH private key into the Nix store and exposes its SSH port to the network

Summary

Two issues in the macOS Linux builder (darwin.linux-builder, darwin.linux-builder-x86_64, darwin.linux-builder-vz and nix-darwin’s nix.linux-builder) let other users gain trusted access to the builder VM and poison the build results that the host gets back:

  • SSH private key in the Nix store (GHSA-45qr-47fw-2p57): the run-builder script imports the whole keys directory into the Nix store with nix-store --add. That directory includes the SSH client private key builder_ed25519. The Nix store can be read and listed by every local user, so any local user can log in to the builder VM as builder, a trusted-users account of the builder’s Nix daemon.
  • SSH port reachable from the network (GHSA-w5j5-2jpq-3cv3): the QEMU builder forwards host TCP port virtualisation.darwin-builder.hostPort (default 31022) to the guest’s sshd without setting host.address, so QEMU’s user-mode network stack binds 0.0.0.0. Although the option is documented as the localhost port, the builder’s SSH service is reachable from every network the Mac is on. The macOS Application Firewall is off by default and does not block it.

Impact

The host imports remote-build outputs without checking signatures. Anyone who can log in to the builder can therefore:

  • Poison builds: put malicious content at output paths that the host’s daemon later imports as remote-build results. The content runs as any user of those packages, admins and root included. This gives cross-user privilege escalation on the host.
  • Read or modify build inputs and outputs that pass through the builder, including secrets used in builds.
  • Use the builder’s CPU, memory and disk, and hide data or tools in its store.

Through the first issue, any local user of the host can do this. Through the combination of both, anyone who gets the key from a backup, mirror or other store exposure can do the same remotely. Without a leaked key, the network exposure is limited to the extra attack surface of the guest’s sshd.

Am I affected?

You’re impacted by the key exposure if:

  • you run the Linux builder on macOS from a nixpkgs revision without the fix, either directly (nix run nixpkgs#darwin.linux-builder, darwin.linux-builder-x86_64, darwin.linux-builder-vz) or through nix-darwin (nix.linux-builder.enable = true)
  • the host has other local accounts, or its Nix store is backed up, shared over the network or mirrored

You can check whether the private key is currently in your store with:

ls -l /nix/store/*-keys/builder_ed25519 2>/dev/null

If the command lists a file, the key is exposed. If it lists nothing, the key may still have been exposed before a garbage collection. Consider the key compromised if the builder ever ran from an affected revision.

You’re impacted by the network exposure if:

  • you run the QEMU-based builder (darwin.linux-builder, darwin.linux-builder-x86_64, or nix-darwin’s nix.linux-builder with its default package) from a nixpkgs revision without the fix
  • you did not override virtualisation.forwardPorts to bind 127.0.0.1

You can confirm it while the builder is running (replace 31022 if you changed hostPort):

sudo lsof -nP -iTCP:31022 -sTCP:LISTEN

If the NAME column shows *:31022, the builder accepts connections from the network. If it shows 127.0.0.1:31022, it does not. This matters most on shared or untrusted networks. darwin.linux-builder-vz is not affected by this part.

NixOS machines, remote builders not created by this profile, and nix-darwin setups without nix.linux-builder are not affected by either issue.

What do I need to do?

1. Update

The fixes only put builder_ed25519.pub in the Nix store, and bind the forwarded SSH port to 127.0.0.1:

2. Rotate the builder key

Updating is not enough: you must also rotate the builder key, because it may already have been read:

  1. Stop the builder.
  2. Delete the old key pair from the working directory (./keys or $KEYS; /var/lib/linux-builder/keys for nix-darwin) and from /etc/nix/builder_ed25519{,.pub}.
  3. Remove the old store copies with nix-store --delete /nix/store/*-keys (check the matches first), and look for copies in backups and mirrors.
  4. Run the fixed create-builder (or rebuild nix-darwin). It generates and installs a new key pair.
  5. Consider the builder’s store untrusted: delete the builder disk image so the guest starts with a clean store.

Until you can update

  • Stop the builder, or run nix-collect-garbage after each start on a host with no other local users.

  • For the QEMU builder, either enable the macOS Application Firewall and block incoming connections to QEMU, or override the port forwarding rule in the builder config (nix-darwin: nix.linux-builder.config):

    virtualisation.forwardPorts = lib.mkForce [
      { from = "host"; host.address = "127.0.0.1"; host.port = 31022; guest.port = 22; }
    ];
    

Acknowledgement

Thank you to @booxter for reporting these issues to the nixpkgs security team and working on the fixes.

References

6 Likes