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

SSH Public Key authentication #282

Closed
wants to merge 2 commits into from
Closed

SSH Public Key authentication #282

wants to merge 2 commits into from

Conversation

bunjiboys
Copy link

This pull request will add support for public key authentication with SSH transports. It adds three new optional arguments to clone_repository, ssh_pubkey, ssh_privkey and ssh_passphrase.

To use public key authentication you would need to supply both the public and the private key, while the passphrase is optional, as passphrases are not required for key pairs.

Example:

git_clone('ssh://github.com/bunjiboys/pygit2.git', 
          pubkey='/home/user/.ssh/id_rsa.pub', privkey='/home/user/.ssh/id_rsa')

Note: there is a bug in the current master branch of libgit2 (has been fixed in development), that prevents the shorter repo urls to be used. In the above example this would cause an "Early EOF" error: 'github.com:bunjiboys/pygit2.git'. See libgit2/libgit2#1839 for more information.

Also note, that with the current master release of libgit2, if you try to clone using SSH and don't provide valid credentials, you can trigger a segfault (fixed by libgit2/libgit2@219f318)

@carlosmn
Copy link
Member

This hard-codes a particular usage of ssh in a fairly awkward way. Public key with ssh isn't so special that it deserves its own arguments in clone. Any transport may accept any number of authentication methods. E.g. the default ssh transport also accepts a username/password credential.

This btw only works as-is because the ssh transport on 0.19 will simply set the username to "git" if you haven't given it, without giving the user any chance to change it.

@bunjiboys
Copy link
Author

I would be happy to do this in other ways if there are any suggestions, but I would imagine most people that require ssh with authentication would use public key authentication for their git repos.

With this patch you can still set the user, by supplying it in the repo URL, user@server:path/repo.git or ssh://user@server:path/repo.git, this patch does not change that behavior.

To avoid adding the key pairs directly to the call, we would have to add an option for what type of authentication you want (user/pass, pubkey etc) and then infer that the user always wants to use ~/.ssh/id_rsa or ~/.ssh/id_dsa but I don't like making the assumption on what key pair to use, since I'm sure many like me have separate key pairs for Github, we would need some way for the user to specify which key pair to use. We could of course have it look for the standard ones, if the user didnt supply a key pair as a sane default, but they should be overridable

@carlosmn
Copy link
Member

carlosmn commented Nov 2, 2013

I would be happy to do this in other ways if there are any suggestions, but I would imagine most people that require ssh with authentication would use public key authentication for their git repos.

This doesn't mean that ssh keypair should be elevated to have it's own three parameters in the clone function, the same way user/pass for HTTP or SSH should't, the same way kerberos tokens shouldn't.

Take a look at the API you're wrapping. Authentication information has several parts which must go together. It can be a username/password combination, username/keypair or a username with some answer to a challenge (ssh omits the username because it's buggy).

Some of these combinations can be known in advance, in which case it's ok to pass an optional parameter to clone with the authentication information that gets transformed into what libgit2 is expecting, but it has to be one object with whatever information is appropriate for the type of authentication.

With this patch you can still set the user, by supplying it in the repo URL, user@server:path/repo.git or ssh://user@server:path/repo.git, this patch does not change that behavior.

The example in the PR summary only works because it's taking into account the wrong username choice made by the transport.

To avoid adding the key pairs directly to the call, we would have to add an option for what type of authentication you want (user/pass, pubkey etc) and then infer that the user always wants to use ~/.ssh/id_rsa or ~/.ssh/id_dsa but I don't like

No. You must never assume anything of the sort. The program using the library must tell you each time exactly what authentication information it wants to use. What we can't have is a combinatorial explosion of arguments for every authentication mechanism we come up with.

@jdavid
Copy link
Member

jdavid commented Nov 23, 2013

Have not yet checked the code, but does it change something now we have moved to libgit2 0.20?

@carlosmn
Copy link
Member

0.20 changed a bit in this by unifying all the remote callbacks into the one struct and adding the missing username to the git_cred struct. It also changed the credential names to something sensible instead of what libssh2 calls them.

@jdavid
Copy link
Member

jdavid commented Nov 23, 2013

What about something like:

from pygit2 import clone_repository, Keypair

credentials = Keypair('foobar', '/home/user/.ssh/id_rsa.pub', '/home/user/.ssh/id_rsa')
clone_repository('ssh://github.com/bunjiboys/pygit2.git', credentials=credentials)

Where Keypair would be a callable helper:

class Keypair(object):
    def __init__(self, username, public_key, private_key, passphrase=None):
        self.username = username
        self.public_key = public_key
        self.private_key = private_key
        self.passphrase = passphrase

   def __call__(self):
       return cred_ssh_key_new(self.username, self.public_key, self.private_key, self.passphrase)

Or something like that.

@carlosmn
Copy link
Member

This is a lot like what rugged has done. You either pass in a lambda that will return the credentials or the credentials directly if you already know them, which should work quite nicely.

We'd also need to let the user return the object created by this cred_ssh_key_new() directly.

@lowlander
Copy link

Hey all, any news on this topic?

@lowlander
Copy link

Hey all, I tried the patch and it seems to work for a clone. The problem is after the repo is cloned and you try to open it with repo = Repository("bla/.git") and than try to do a fetch on "origin" it will want the SSH credentials again. So it seems that just passing credentials to clone is not enough. Any ideas on how to make ssh remotes work? It really is (at least for me) the one missing feature that makes the whole library unusable :-/ (can't do much with it when you can't clone your repos)

@carlosmn
Copy link
Member

Storing credentials in a repository's configuration is outside of what the library should do. It's the application that must decide how to handle credentials, and storing in the repo is generally a bad option, as other users are often allowed to read those files.

If you want to read the ssh key from some place, it's your tool's responsibility to do it.

@carlosmn
Copy link
Member

This should be fixed via #354

@carlosmn carlosmn closed this Mar 31, 2014
@remusavram
Copy link

Where can I found the most simple example of cloning a remote repository using SSH credentials?

@paqogomez
Copy link

@remusavram, quite old, but this page is listed pretty high on google for this problem.
https://www.pygit2.org/recipes/git-clone-ssh.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants