Support SSH Git URLs for authenticated connections to private repositories
jnicholls opened this issue ยท 64 comments
Right now there is no way for cargo to connect to a private repository, whether or HTTPS or SSH. I believe if the git
Cargo.toml dependency option would support a non-URL string such as git@github.com:user/repo
then the Git client in cargo would likely successfully connect.
This is a major hole in cargo right now; I'm forced to submodule everything which is tedious.
Thanks in advance.
This is actually supported, Cargo just doesn't prompt for a password (see #1306). You can use git credentials to store information (which Cargo reads).
I've tried this before only a few weeks ago and it was not working as
advertised, despite that issue having been closed and me running the latest
cargo. I'll try again.
On Tuesday, July 28, 2015, Alex Crichton notifications@github.com wrote:
This is actually supported, Cargo just doesn't prompt for a password (see
#1306 #1306). You can use git
credentials http://git-scm.com/docs/gitcredentials to store information
(which Cargo reads).โ
Reply to this email directly or view it on GitHub
#1851 (comment).
Sent from Gmail Mobile
Ok, but if you run into any problems feel free to open an issue! It's a difficult code path to test and it's not exercised that much, so there may be a bug or two lurking.
This is in fact an issue. I have my osxkeychain git credential helper setup, and my credentials are cached. All fresh terminals authenticate seamlessly to HTTPS GitHub URLs of private repositories. However, cargo continues to report Unable to update https://github.com/blah/blah
both on stable and nightly cargo. The credential helper is setup in the global config (~/.gitconfig)
[credential]
helper = osxkeychain
Please advise. Thanks in advance!
EDIT: I have also tried a file store helper, which also does not work.
[credential]
helper = store
Do I need to setup credentials in some special way other than using a credential helper?
Sounds like some investigation is warranted!
As stated in the OP, SSH URLs in the form of git@server.com:user/repo
are not supported by Cargo. When parsing the .toml
file, it does not understand that as being a legitimate URL (because it isn't).
This is a suggestion. Apparently one can use ssh://user@server.com/repo
and Cargo is happy with that, since it is in fact a legitimate URL and libgit2 understands it just fine.
Specifically, it sounds like you had a submodule which used a URL of the form git@example.com:user/repo
and when Cargo tried to check that out it failed because it wasn't able to parse the URL and set the right credentials?
That's correct! Whether it is specified directly as the git
argument of a [dependency]
entry, or if it's present as a submodule url in .gitmodules
, it will not work.
I actually thought this was working until I switched locations.
I set up a project with a git ssh://Si@blah.org:/repo/blah.git
dependency whilst on OSX, and thought it was picking up my .ssh config just fine - certainly my id_rsa
, but perhaps also a custom ssh port number.
Now that I'm on Linux, the same project ignores all .ssh config for git dependencies, though it does work when including the password, port number, and what I had for breakfast in the URL.
Are you running through vagrant or something? In either case, is ssh-agent
running?
I've had no problems on OS X or Linux on vagrant with ssh-agent enabled in
my Vagrantfile.
On Saturday, December 5, 2015, Si notifications@github.com wrote:
I actually thought this was working until I switched locations.
I set up a project with a git ssh://Si@blah.org:/repo/blah.git dependency
whilst on OSX, and thought it was picking up my .ssh config just fine -
certainly my id_rsa, but perhaps also a custom ssh port number.Now that I'm on Linux, the same project ignores all .ssh config for git
dependencies, though it does work when including the password, port number,
and what I had for breakfast in the URL.โ
Reply to this email directly or view it on GitHub
#1851 (comment).
Sent from Gmail Mobile
Physical machines, unfortunately in different countries.
My best guess is that ssh-agent was running on the Mac without my realising it. As soon as I use ssh-agent here on Linux, it works as expected. I still need to be explicit about the port, but that may also have been the case on the Mac. The repo is just a personal one, so I was relying on ssh auth negotiation beforehand.
@simon-nicholls you may also be running into #2078, right now Cargo doesn't take a look at .ssh/config
Thanks. That clears things up for me.
I would like to bump this issue. I am trying to connect to a remote, private ssh repository on Gitlab. I change the scp syntax git@gitlab.com:project/supercool.git
into ssh://gitlab.com/project/supercool.git
. When running cargo update
it gives me an error saying it cannot authenticate. I do have the key inside my ~/.ssh, and it is registered using ssh-add. It is also part of my GNOME Keyring, which my git credential helper is set to.
I can clone the repository manually, as well as ssh into the server.
@Binero change your URL to ssh://git@gitlab.com/project/supercool.git
(notice the git@
user addition) and try again?
@jnicholls Gold. Worked nicely.
I'm having a similar issue:
reponame = { git = "ssh://git@gitlab.com:22/mkollaro/reponame.git" }
but cargo fails with:
$ cargo test --verbose
Updating git repository `ssh://git@gitlab.com:22/mkollaro/reponame.git`
error: Unable to update ssh://git@gitlab.com:22/mkollaro/reponame.git
Caused by:
failed to fetch into /home/mkollaro/.cargo/git/db/reponame-e51b056051cf84
Caused by:
failed to authenticate when downloading repository
attempted ssh-agent authentication, but none of the usernames `git` succeeded
Caused by:
[23/-1] error authenticating: no auth sock variable
Doing a git clone
with the very same URL works fine. I'm using cargo 0.12.0-nightly (6b98d1f 2016-07-04)
.
Btw, I had to add an explicit port number because it's complaining about an invalid port number without it - maybe another bug?
@mkollaro that actually looks like it's a different error perhaps?
I believe git clone
will transitively invoke ssh
itself which will read ~/.ssh/config
for keys and such. Cargo, however, which uses libgit2 which uses libssh2, will not read ~/.ssh/config
yet (not implemented). The only authentication method method supported by Cargo right now is connecting to the ssh-agent. Do you have the agent running with your keys added?
On Mac OS X (Sierra) I had to create a .ssh/config
file like this:
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_rsa
with the (private) rsa file pointed to, and then issue the command:
ssh-add -K ~/.ssh/id_rsa
which (finally!) allowed an entry like:
git = "ssh://git@github.com/skipjaq/loda.git"
to work perfectly. (Now I only have to cure the coding bugs.)
I do not know how often I will have to repeat the ssh-add
command, but it appears this ought to hold at least until the next reboot, which (on MAC at least) is pretty rare.
This anomaly is apparently a feature of ssh-agent
on Mac OS X Sierra.
@Zteve I am seeing quite weird behavior when https://github.com/username/XXXX.git rejects to work, while switching prefix to ssh://
makes it work.
@pronebird Yes, the scheme https:
fails for me also. I can only get it to work if I use the ssh:
scheme with the git@
prefix on the host part.
Have you tried it with https://git@github.com/username/XXXX.git
? I haven't.
@Zteve tried with https://git@...
but unfortunately it doesn't work. Only ssh://
works for private repos.
@pronebird using the https
URL requires password authentication which Cargo doesn't implement. Using the ssh
URL uses ssh-agent authentication which is implemented.
@pronebird OK. Thanks for trying. Incidentally, I've just rebooted (for a macOS update) and didn't have to re-issue the ssh-add
command. This had been necessary after previous reboots.
Thanks for the clarification @alexcrichton .
Thank you for showing how to make this work!
Here are some rambling thoughts on use cases...
My employer already has a heavy investment in Ruby and Node.js, and we're rapidly expanding our use of Rust. This issue and #3917, unfortunately, mean that the Rust workflow for private repositories is clunkier than for Ruby or Node, and this will require some modifications throughout our development, CI and deployment systems.
In a Ruby Gemfile
, we can write:
gem 'my_gem', :git => 'git@github.com:<company>/<repo>.git'
In a Node package.json
file, we can write:
"package-name": "git+ssh://git@github.com:<company>/<repo>.git"
Alternatively, we can run private package server or subscribe to a hosted one, such as the one provided by npm
. (The relevant Cargo issue for that is #3917.) Normally, we lean towards a private package server and we only use private git URLs when working with unreleased branches.
From a security perspective, we're very careful about long-lived credentials on developer machines. We use 2FA heavily and issue temporary credentials for as many services as possible. We do assume that developers have GitHub SSH keys, but I don't know if we assume ssh-agent
is set up correctly.
It looks like our only real choice here is to make ssh-agent
work for everybody, including the CI system (ugh). It would be very nice if Cargo could read ~/.ssh/config
, which would avoid the need for booting up an ssh-agent
inside a Docker container on the CI server. Alternatively, it would be marvelous to have the ability to host our own crates.io server for private packages.
But right now, these limitations of cargo
seems like a major speedbumps for Rust adoption inside companies, at least for anything that extends beyond a single private repository.
Thanks for the comment @emk! Some points of note:
-
It looks like node's and Ruby's support for SSH urls is exactly the same as Cargo's, modulo a few details. A repository fetched over SSH just needs to use a URL like
ssh://github.com/company/repo.git
. Cargo doesn't currently supportgit@github.com:...
style urls (that's what this issue is). -
The authentication is then the next phase, and Cargo primarily supports ssh-agent which works through libssh2. What doesn't work is that Cargo doesn't read
.ssh/config
. My guess is that npm and rubygems literally shell out tossh
, whereas Cargo uses a bundled libssh2 library. -
Private registries are definitely desired! That's the purpose of #3917.
OK, here are my notes on using private Git repos with cargo
and our CI system.
So far, this is mostly a list of things which don't work, in case somebody else needs to make private crates work with a CI system.
Attempt 1: ssh-agent
authentication
- Figure out who has the 2FA tokens for GitHub deployment account with read-only access.
- Create new SSH keys and add them to the GitHub deployment account.
- Store SSH keys in our secret management service.
- Try to fetch secrets from secret management service at build time and pass to
cargo
viassh-agent
insidedocker build
. FAILED because passing secrets todocker build
is actually pretty terrible. We have some workarounds for this with other package managers, but it assumes that we have a working private package repository available over HTTP (details elided).
Attempt 2: cargo vendor
- Run
cargo vendor
in hopes of selectively vendoring just the private sub-module. FAILED becausecargo vendor
vendors all the publicly available packages from crates.io, but not anything from GitHub. So it's basically the opposite of what we need.
OK, for attempt 3, I'm going to read about [source.*]
overrides and see if I can make them work with GitHub.
@alexcrichton As far as I know, both bundler
and npm
allow https
URLs with embedded passwords, which makes it pretty easy to use GitHub deployment tokens. These aren't ideal from a security perspective, but they're a lot easier than getting ssh-agent
running inside a docker build
command on a CI system.
There relevant section of our Dockerfile
looks like:
WORKDIR /app
ADD ./ /app
RUN cargo build --release
Trying to get ssh-agent
running in this context is moderately annoying, but not impossible.
I think the ideal outcome here would be:
- Implement #3917 (but that's a bigger, longer-term project).
- Support the same kinds of URLs and configuration
npm
andbundler
. Specifically, read.ssh/config
and support HTTPS with username + password. This would allow existing workarounds for Ruby or Node to be re-used withcargo
with less fuss.
Oh, and:
Attempt 3: Try to manually vendor the private crate
After reading the docs, it looks like this really only works for crates from crates.io. I don't want to replace crates.io, I just want to override a single crate.
At this point, I think I may need to write a helper utility for my CI system which manually checks out private repositories and provides an override. The ergonomics for this are fairly painful, and it seems like "private Rust crates" would be a common use-case for companies adopting Rust.
Anyway, thank you for your advice! I'll try to figure something out.
(EDIT: I'm going to write this up as either a separate issue or a blog post, but the TL;DR is that private crate dependencies are surprisingly hard to get working with a build system, and that most of the cargo
features that look like they might help have surprising limitations, such as only working for crates published to crates.io.)
I tried to use my_crate = { git = 'ssh://git@foo.com:my-repo' }
but it said invalid port number.
Then I tried my_crate = { git = 'ssh://git@foo.com/my-repo' }
but it said authentication failure, even though it's NOT an auth failure.
Turns out I had to write my_crate = { git = 'ssh://git@foo.com/~/my-repo' }
which is not obvious. It should give a more helpful error with /
, not auth failure.
Btw, why doesn't my_crate = { git = 'https://github.com/user/my-repo' }
work for private repos anymore?
OK, just a quick follow up: We do finally have private git repositories working with a credential helper. In practice, it's a significant nuisance to set up across developer machines and CI, but once it's automated, it actually works fairly well.
Thank you to the cargo
team for explaining this!
The Cargo team discussed this issue briefly in our meeting today, and we'd be happy to take a PR for it!
Cargo doesn't currently support git@github.com:... style urls (that's what this issue is).
This is an issue for repositories that include submodules. A .gitmodules
written by git submodule add
will look like this: url = git@github.com:akshayknarayan/test-submodule
.
Then, attempting to build a crate with the following dependency:
[dependencies]
cargo-submodules-test = { git = "ssh://git@github.com/akshayknarayan/cargo-submodules-test" }
will give the following error message:
โ cargo b
Updating git repository `ssh://git@github.com/akshayknarayan/cargo-submodules-test`
error: failed to load source for a dependency on `cargo-submodules-test`
Caused by:
Unable to update ssh://git@github.com/akshayknarayan/cargo-submodules-test
Caused by:
failed to update submodule `test-submodule`
Caused by:
invalid url `git@github.com:akshayknarayan/test-submodule`: relative URL without a base
ssh-agent method seems not working on windows, there is my steps. Similar method works fine on Mac and Linux.
- install git for windows
$ git --version
git version 2.20.1.windows.1
- use default git bash, generate ssh key
- empty config for ~/.gitconfig
- have the following for ~/.ssh/config
Host *
UseKeychain yes
AddKeysToAgent yes
IdentityFile ~/.ssh/id_rsa
- lauch ssh agent
eval `ssh-agent -s`
ssh-add
ssh-add -L showing key was added
- cargo build with error
...
Caused by:
Unable to update ssh://git@bitbucket.org/xxx/xxx.git#964db79b
Caused by:
failed to fetch into C:\Users\xxx\.cargo\git\db\substrate-79104a8293a5645e
Caused by:
failed to authenticate when downloading repository
attempted ssh-agent authentication, but none of the usernames `git` succeeded
Caused by:
error authenticating: failed connecting agent; class=Ssh (23)
Any suggestion? Thanks for your help!
able to get this working on windows10 by update ~/.cargo/config
[net]
git-fetch-with-cli = true
Here's what worked for me on MacOS Mojave.
- URL in
Cargo.toml
should be in form ofssh://git@github.server.com/my-user-id/repo
- You must configure your Enterprise GitHub for SSH access and upload the right keys
- Get
ssh-agent
running - Add your private key (corresponding to the uploaded SSH public key) via
ssh-add ~/.ssh/my-private-key
note: flag-K
isno longer acceptedbyssh-add
. Correction: MacOS-includedssh-add
does support-K
, but the mainstreamssh-add
installed by Macports - does not.
Then Cargo was able to pull the crate from there.
It worked with and without the following addition to the ~/.cargo/config
:
[net]
git-fetch-with-cli = true
but I put it in, so it will probably stay there, unless I find a reason to remote it.
I also had
[credential "https://github.server.com"]
username = my-user-id
helper = store
added to ~/.gitconfig
, but don't know how relevant it was.
@akshayknarayan 's issue above is #7202, which is fixed by #7238 . Not sure how to figure out which release that fix will be in though? Seems at least 0.40:
$ git merge-base 130e11c --is-ancestor master && echo yes || echo no
yes
$ git merge-base 130e11c --is-ancestor 0.39.0 && echo yes || echo no
no
So I had an issue with using a forwarded ssh agent (through pythons paramiko SSH implementation) with Cargo.
The git-fetch-with-cli = true
fixed it for me but I do not really understand why. Doesn't the git cli also use libgit2? Do the rust bindings somehow mess up environment variables?
Any hint on how to debug this would be appreciated. There's some more info on my problem here: paramiko/paramiko#1626
The git cli does not use libgit2.
I could not fix the issue. So I cloned repository and used direct path in config package = { path = ... }
. It is useful if you just need to install dependency once.
Related to "invalid port" error, I have urls of this style (thanks to azure devops):
git@ssh.dev.azure.com:v3/organization/project/repository
If I use that format, it fails with "relative URL without a base". If I prepend "ssh://" it fails with "invalid port" (:v3).
Of course git clon
ing works, so the url should be valid.
Struggled with this, turns out that I needed to add my ssh keys to the ssh-agent with ssh-add
(ssh-add -A
worked for me).
The reason I don't need to do this for the git
CLI is that it uses OpenSSH, which parses ~/.ssh/config
and picks up the IdentityFile
lines I have there. libssh2 doesn't, so they need to be added manually.
You can check whether you have non-default ssh keys by running:
$ grep IdentityFile ~/.ssh/config | sort -u
IdentityFile ~/.ssh/gibfahn_id_ed25519
IdentityFile ~/.ssh/gibfahn_id_rsa
and then add them with:
$ ssh-add ~/.ssh/gibfahn_id_ed25519 ~/.ssh/gibfahn_id_rsa
able to get this working on windows10 by update ~/.cargo/config
[net] git-fetch-with-cli = true
That's the one. Thanks!
If I prepend "ssh://" it fails with "invalid port" (:v3).
Try to replace ":" with "/". It works for gitlab.
I ended up writing down all the different ways to auth and the gotchas here: https://fahn.co/posts/cargo-auth-for-private-git-repos.html
It got a bit long ๐ . Not sure if any of it is worth adding to the Cargo docs.
It seems git changed the behavior of URL, all of upon solutions doesn't work anymore.
$ cargo install --git 'ssh://git.lab.com/rust/example-module.git'
Updating git repository `ssh://git.lab.com/rust/example-module.git`
Enter passphrase for key '/home/xqkuang/.ssh/id_rsa':
error: failed to fetch into: /home/xqkuang/.cargo/git/db/trpc-rust-ec60571bf845afd2
Caused by:
process didn't exit successfully: `git fetch --force --update-head-ok 'ssh://git.lab.com/rust/example-module.git' 'refs/heads/master:refs/remotes/origin/master' 'HEAD:refs/remotes/origin/HEAD'` (exit code: 128)
--- stderr
fatal: remote error: Git:Project error, please check URL.
$ git fetch --force --update-head-ok 'ssh://git.lab.com/rust/example-module.git'
'refs/heads/master:refs/remotes/origin/master' 'HEAD:refs/remotes/origin/HEAD'
Enter passphrase for key '/home/xqkuang/.ssh/id_rsa':
fatal: remote error: Git:Project error, please check url.
But changing the url argument of git fetch
to the correct URL git@git.lab.com:rust/example-module.git
is working.
$ git fetch --force --update-head-ok 'git@git.lab.com:rust/example-module.git'
'refs/heads/master:refs/remotes/origin/master' 'HEAD:refs/remotes/origin/HEAD'
Enter passphrase for key '/home/xqkuang/.ssh/id_rsa':
remote: Finding sources: 100% (3124/3124)
remote: Total 3124 (delta 1718), reused 3055 (delta 1718)
Receiving objects: 100% (3124/3124), 567.77 KiB | 19.58 MiB/s, done.
Resolving deltas: 100% (1718/1718), done.
From git.lab.com:rust/example-module
* [new branch] master -> origin/master
* [new ref] -> origin/HEAD
Is it possible for the cargo to translate the URL to the correct one?
I think reporting to Git developers that something changed would be better here. Can you, perhaps, bisect where GIt changed this behavior?
I think reporting to Git developers that something changed would be better here. Can you, perhaps, bisect where GIt changed this behavior?
The problem is git fetch supports the URL schema like git@git.lab.com:rust/example-module.git
, but not the cargo schema ssh://git.lab.com/rust/example-module.git
I just think cargo should adapt the URL resolve mechanism of git, and solve the problem.
Git supports ssh://
just fine; I use it all the time. Something else is up here.
I think I see the issue. You have ssh://host/
vs. user@host:
. Try ssh://user@host/
instead.
Note to all other hipster fish
users.
it doesn't seem cargo install --git ssh://git@github.com/org/crate
command work on fish
. You need to switch to bash
, add your ssh key and then you'll be able to download the crate
It seems very odd that the shell would affect something like that. Are there any other symptoms?
It seems very odd that the shell would affect something like that. Are there any other symptoms?
There isn't much except the error output:
โฏ cargo install --git ssh://git@github.com/xxx/xxxxx --branch main
Updating git repository `ssh://git@github.com/xxx/xxxxx`
error: failed to fetch into: /Users/onat.mercan/.cargo/git/db/xxxxx-db26b348fdd70ca7
Caused by:
failed to authenticate when downloading repository
* attempted ssh-agent authentication, but no usernames succeeded: `git`
if the git CLI succeeds then `net.git-fetch-with-cli` may help here
https://doc.rust-lang.org/cargo/reference/config.html#netgit-fetch-with-cli
Caused by:
no authentication available
It seems like something is wrong with the libgit2
used internally. I'm not sure what it thinks using the remote url's username has to do with contacting the local ssh-agent
.
The only thing fish could be doing is some magic on that argument, but it looks like it got through successfully. I would recommend some strace
to see what is happening under the hood and whether any strings are being manip'd improperly.
Your bash config is probably setting up an ssh-agent and your fish config is not.
Your bash config is probably setting up an ssh-agent and your fish config is not.
It's the other way around. My bash config is not setting up ssh-agent but fish does.
The only thing fish could be doing is some magic on that argument, but it looks like it got through successfully. I would recommend some
strace
to see what is happening under the hood and whether any strings are being manip'd improperly.
I am on Mac and I'll try to figure out if I could get any meaningful trace with dtruss
. I tried verbose
display option to get more info from cargo
but the output is the same. I'll also try to debug a local cargo version.
If one shell has an ssh-agent and the other doesn't, that is the difference. It is not related to fish or bash specifically.
just curious to know is this issue technically resolved per?:
#1851 (comment)
I'm getting this error not locally but in github actions, has anybody found any workaround for that case?
I now appear to have an https:// style git url failing to install because cargo attempts to use the SSH version of the repo's url. Not sure how this happens though, or if the SSH version of the url has been cached or stored somewhere without my knowledge.
I now appear to have an https:// style git url failing to install because cargo attempts to use the SSH version of the repo's url.
Check for insteadOf
configurations in Git that can rewrite URLs.