Nixpkgs: fetchgit: support specifying location of public keys for ssh transport

Created on 8 Sep 2014  路  22Comments  路  Source: NixOS/nixpkgs

Follow up on #1923:

Currently using ssh urls for fetchgit results into:

Could not create directory '/var/empty/.ssh'.
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
Could not create directory '/var/empty/.ssh'.
Host key verification failed.

Maybe fetchgit should grow an extra parameters?

enhancement fetch

Most helpful comment

Okay, so I think this is basically solved by fetchgitPrivate which uses the SSH_AUTH_SOCK to reuse your SSH agent instead of passing in a specific private key. However, I'm also going to suggest two possible enhancements to the implementation of fetchgitPrivate.

First, using a variation on the gist that @zimbatm linked to, I can successfully build the repository by replacing fetchgit with fetchgitPrivate (and fixing the sha256 field):

# default.nix

{ pkgs ? import <nixpkgs> {} }:

pkgs.buildRubyGem rec {
  src = pkgs.fetchgitPrivate {
    url = "[email protected]:flori/json.git";
    rev = "80e8213ce940005a1c7314793d39a0c4cc786821";
    sha256 = "0ki6a4vnccb86rmssbc5x3d067zvkhyi6y973068hz81q0rjqf8a";
  };
  gemName = "json";
  version = "1.8.3";
  sha256 = "1nsby6ry8l9xg3yw4adlhk2pnc7i0h0rznvcss4vk3v74qg0k8lc";
}

... and supplying the appropriate SSH config file and SSH_AUTH_SOCK on the command line (since that is how fetchgitPrivate is configured to work):

nix-build default.nix -I ssh-config-file=/path/to/.ssh/config -I ssh-auth-sock=$SSH_AUTH_SOCK

So that alone is probably enough to mark this issue closed, but I'd like to also suggest two improvements to fetchgitPrivate to reduce the need to supply these parameters on the command line:

  • Use impureEnvVars to automatically inherit the SSH_AUTH_SOCK from the user's environment
  • Supply the configuration file directly as a nix argument

This would involve simplifying pkgs/build-support/fetchgit/private.nix to this:

{ fetchgit, writeScript, openssh, stdenv }:

sshConfigFile: fetchGitArgs:

derivation ((fetchgit fetchGitArgs).drvAttrs // {
  impureEnvVars = [ "SSH_AUTH_SOCK" ];

  GIT_SSH = writeScript "fetchgit-ssh" ''
    #! ${stdenv.shell}
    exec -a ssh ${openssh}/bin/ssh -F ${sshConfigFile} "$@"
  '';
})

... and then you'd just provide the ~/.ssh/config file in nix:

{ pkgs ? import <nixpkgs> {} }:

pkgs.buildRubyGem rec {
  src = pkgs.fetchgitPrivate /path/to/.ssh/config {
    url = "[email protected]:flori/json.git";
    rev = "80e8213ce940005a1c7314793d39a0c4cc786821";
    sha256 = "0ki6a4vnccb86rmssbc5x3d067zvkhyi6y973068hz81q0rjqf8a";
  };
  gemName = "json";
  version = "1.8.3";
  sha256 = "1nsby6ry8l9xg3yw4adlhk2pnc7i0h0rznvcss4vk3v74qg0k8lc";
}

... and now you can run the build command without any command line arguments:

$ nix-build default.nix

Would people be okay with that proposed change?

All 22 comments

(I guess the above output was from that short time period when git wrapped ssh? When using an ssh URL with fetchgit now I get error: cannot run ssh: No such file or directory.)

+1 for having something like an sshPrivateKey = "/path/to/id_rsa" argument to fetchgit. That would be nice supplement to fetchgitPrivate, which requires a ssh config file in NIX_PATH, which in turn points to the private key.

However, wouldn't this bring ssh as a hard dependency of fetchgit? Or can it be a "lazy" dependency, only pulled in if "sshPrivateKey" arg is provided?

Further, the private key must not be put in the nix store.

@domenkozar is this fixed?

No response, so close @zimbatm.

@Profpatsch this is still an issue. You can repro by running nix-build against https://gist.github.com/anonymous/fb9353cabf8dd9fecd28

I just ran into this issue. For other people who have this same issue, you can work around it by using HTTP auth (i.e. https://${username}:${password}@github.com/your-name/your-project.git). The following link explains how to deal with two-factor authentication:

Needless to say, do not actually save your username and password in the Nix expression. Provide them on the command line via --arg or something similar and use them to template the URL.

Okay, so I think this is basically solved by fetchgitPrivate which uses the SSH_AUTH_SOCK to reuse your SSH agent instead of passing in a specific private key. However, I'm also going to suggest two possible enhancements to the implementation of fetchgitPrivate.

First, using a variation on the gist that @zimbatm linked to, I can successfully build the repository by replacing fetchgit with fetchgitPrivate (and fixing the sha256 field):

# default.nix

{ pkgs ? import <nixpkgs> {} }:

pkgs.buildRubyGem rec {
  src = pkgs.fetchgitPrivate {
    url = "[email protected]:flori/json.git";
    rev = "80e8213ce940005a1c7314793d39a0c4cc786821";
    sha256 = "0ki6a4vnccb86rmssbc5x3d067zvkhyi6y973068hz81q0rjqf8a";
  };
  gemName = "json";
  version = "1.8.3";
  sha256 = "1nsby6ry8l9xg3yw4adlhk2pnc7i0h0rznvcss4vk3v74qg0k8lc";
}

... and supplying the appropriate SSH config file and SSH_AUTH_SOCK on the command line (since that is how fetchgitPrivate is configured to work):

nix-build default.nix -I ssh-config-file=/path/to/.ssh/config -I ssh-auth-sock=$SSH_AUTH_SOCK

So that alone is probably enough to mark this issue closed, but I'd like to also suggest two improvements to fetchgitPrivate to reduce the need to supply these parameters on the command line:

  • Use impureEnvVars to automatically inherit the SSH_AUTH_SOCK from the user's environment
  • Supply the configuration file directly as a nix argument

This would involve simplifying pkgs/build-support/fetchgit/private.nix to this:

{ fetchgit, writeScript, openssh, stdenv }:

sshConfigFile: fetchGitArgs:

derivation ((fetchgit fetchGitArgs).drvAttrs // {
  impureEnvVars = [ "SSH_AUTH_SOCK" ];

  GIT_SSH = writeScript "fetchgit-ssh" ''
    #! ${stdenv.shell}
    exec -a ssh ${openssh}/bin/ssh -F ${sshConfigFile} "$@"
  '';
})

... and then you'd just provide the ~/.ssh/config file in nix:

{ pkgs ? import <nixpkgs> {} }:

pkgs.buildRubyGem rec {
  src = pkgs.fetchgitPrivate /path/to/.ssh/config {
    url = "[email protected]:flori/json.git";
    rev = "80e8213ce940005a1c7314793d39a0c4cc786821";
    sha256 = "0ki6a4vnccb86rmssbc5x3d067zvkhyi6y973068hz81q0rjqf8a";
  };
  gemName = "json";
  version = "1.8.3";
  sha256 = "1nsby6ry8l9xg3yw4adlhk2pnc7i0h0rznvcss4vk3v74qg0k8lc";
}

... and now you can run the build command without any command line arguments:

$ nix-build default.nix

Would people be okay with that proposed change?

src = pkgs.fetchgitPrivate /path/to/.ssh/config {

You mean it鈥檚 okay if that鈥檚 an argument, since fetchgitPrivate will only be used from private nix files?
I guess that鈥檚 nice.

let
   myGit = fetchgitPrivate "${myuser.home}/.ssh";
   myGithub = set: fetchFromGitHubPrivate "${myuser.home}/.ssh" ({ owner = "me"; } // set);
in

fetchFromGitHubPrivate should exist, too. And maybe all other flavors of fetchFoo.

Yeah, the .ssh/config file can be made a private file if it contains any secrets, although it shouldn't need to. The impure SSH_AUTH_SOCK environment variable forwarded from your environment actually does the authentication for you using your ambient ssh agent. For example, in my case the .ssh/config file was just empty so I could pass writeText "ssh-config" "" if I wanted to.

Actually, I've come to the conclusion that the current behavior of fetchgitPrivate is the ideal behavior and requires no modification

The thing that convinced me that the current behavior is the way to go was getting a package to work with hydra. The only way to securely provision a secret to a hydra build is via the Nix search path.

Also, the SSH_AUTH_SOCK argument is not necessary (and won't even work in some cases like remote builds). Another approach is to provide an ssh-config-file with an IdentityFile line, such as this:

StrictHostKeyChecking No
UserKnownHostsFile /dev/null
IdentityFile /path/to/.ssh/id_rsa

... and then supply the path to that on the Nix path like this:

$ nix-build example.nix -I ssh-config-file=path/to/above/config

So the conclusion I came to is that this issue is solved by fetchgitPrivate but could perhaps use better documentation on how to correctly use it in various circumstances (like local vs remote builds, hydra, nixops, etc.).

The only issue with fetchgitPrivate is that tools like nix-prefetch-git don't make a distinction between private and public repos and then the packaging tools like npm2nix, bundix, ... also don't make the distinction. Is there a heuristic that could be used to make that distinction possible?

Actually, there seems to be an issue with this. If nix.useSandbox (formerly known as nix.useChroot) is set, ssh-config-file won't be reachable from the build process, and building by will fail with something like Can't open user config file /path/to/ssh-config-file: No such file or directory. As soon as nix.useSandbox is disabled, everything work as @Gabriel439 described.

To make this work, I think that fetchgitPrivate needs to take ssh-config-file and ssh-auth-sock as input parameters for overriding the current behavior. I'll try this now and submit a pull-request afterwards if it works.

@k0001 There is one reason that you might want to still preserve the old ability to provide ssh-config-file via the NIX_PATH which is that it's the only way that seems to reliably work with Hydra (since Hydra seems to only let you configure the NIX_PATH when customizing an expression to build)

@Gabriel439 just so that we are on the same page: Are you using NixOps to deploy your Hydra instance? If so, how do you manage the deployment and permissions of the ssh-config-file file specified through Hydra, as well as the deployment and permissions of the associated IdentityFile?

@k0001 I don't use nixops to deploy the Hydra instance. I just use nixos directly

I may open another issue for this- but I am having a similar problem with fetchcvs. Basically, ssh refuses to authenticate anoncvs without /var/empty/.ssh/known_hosts. I wonder if anyone has any work arounds for this? (Currently all of the CVS stuff is fetched through pserver but that can be hit or miss).

This is resolved now (there is builtins.fetchGit, fetch-git, and fetchgitPrivate that all support this).

@matthewbauer: I've opened #41814.

@yegortimoshenko actually it is still not clear to me how one could use ssh-config-file in a sandbox ("sandbox = true" in nix.conf).

Could @k0001, @Gabriel439 or anyone else comment if they have a working solution?

@jokogr You could pass it via NIX_PATH:

nix-build -I ssh-config-file=/path/to/ssh/config

Then you can refer to it in Nix expression as <ssh-config-file>.

@yegortimoshenko: as @k0001 mentioned, /path/to/ssh/config is inaccessible in a sandbox, i.e. I get an error like "No such file". Are you using a special path to override this?

You can specify additional paths available in sandbox via extra-sandbox-paths in /etc/nix/nix.conf or nix.sandboxPaths NixOS option.

This is resolved now (there is builtins.fetchGit, fetch-git, and fetchgitPrivate that all support this).

@yegortimoshenko Did you mean that fetchGit supports using the same ssh-config-file and ssh-auth-sock parameters on NIX_PATH that is supported by fetchgitPrivate? If so, what's the point of using fetchgitPrivate then?

For posterity, I had no luck trying to pass 'ssh-config-file' to the Hydra jobset. Instead I moved the config file itself to its standard location for Hydra (/var/lib/hydra/.ssh/config) and it got caught up fine.

Tip: you can more interactively test the evaluation by executing hydra-eval-jobset proj jobsetname as the hydra user. You can directly observe what happens over git/ssh then.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

langston-barrett picture langston-barrett  路  3Comments

ghost picture ghost  路  3Comments

yawnt picture yawnt  路  3Comments

copumpkin picture copumpkin  路  3Comments

domenkozar picture domenkozar  路  3Comments