Ssh-config IdentityFile: "%u" expanding to invoking user, not build user, during fetchgitPrivate

I’m trying to access code in a private repository when building on NixOS.

To do this, I created a directory /etc/nix/ssh with a config file therein, and am using -I ssh-config-file=/etc/nix/ssh/config on the nix-build command line. Similarly, /etc/nixos/configuration.nix has had nix.sandboxPaths = [ "/etc/nix/ssh" ]; added, to ensure that the paths are accessible.

Contents of /etc/nix/ssh/config are as follows:

StrictHostKeyChecking no # MITM not much use here: We don't trust the code we check out; it has to match our hashes
IdentityFile /etc/nix/ssh/keys/%u.key

…whereas /etc/nix/ssh/keys contains a nixbld1.key, nixbld2.key, etc; all with the same contents (a private key granted read-only access to the relevant repositories), but chown’d to the named user. There’s also a chaduffy.key, mapping to my own username and owned by my own user.


The way I understand this to work is that when nix-build is run, whichever builder gets the job will run this as its own user, and thus access its own key. However, that’s not actually what happens:

Failed to add the host to the list of known hosts (/home/chaduffy/.ssh/known_hosts).
Load key "/etc/nix/ssh/keys/chaduffy.key": Permission denied
git@github.threatbuild.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Notably, the SSH client is referring to my home directory, rather than an empty/dummy nixbld home directory – and yet it’s unable to access a file which I do have access to (less /etc/nix/ssh/keys/chaduffy.key works fine), and fails with a “permission denied”, not a “file not found”, indicating that (1) the config file was correctly read (to direct to the key), and (2) we aren’t being disrupted by chroot/sandbox behavior.

Any guidance as to how one might hunt this down?

Kind of a random guess: Is sandboxing actually enabled?

$ nixos-option nix.useSandbox
Value:
true
...
$ fgrep sandbox /etc/nix/nix.conf
sandbox = true

If I use sysdig to trace execution, I see open() calls made to nix.sandboxPaths-listed locations as going directly to the real filesystem, whereas others go to /nix/store/<hash>.drv.chroot/.

But anyhow – if the sandbox were disrupting the ability to reach the files, it’d be “not found”, not “permission denied”… right?

Are you aware of builtins.fetchGit? It runs git as the user invoking the build and outside the sandbox, allowing the build to remain sandboxed and pure while also reusing the user’s normal git config. It seems like a good fit for what you’re trying to do.

1 Like

Now I’m embarrassed – it really was that easy! :slight_smile:

I read “fetchgitPrivate” as being appropriate for “private repositories”, and the warning in its code when no ssh-config-file is set implies as much as well.

What’s the actual use case?

The fetchGit builtin is a fairly recent addition, having been introduced with nix 2.0. The fetchGitPrivate hack was necessary with nix 1.x.

2 Likes

I’m not sure why but builtins.fetchGit hangs for me forever on NixOS (unstable-channel). I’m using GnuPG with SSH support but have tried OpenSSH as well without luck. It works fine on a MacOS though.

Could that be due to a passphrase-protected key or something like that?