ASCII Thoughts

SSH authentication on Github for multiple users

I have 2 Github accounts: a personal one, and a professional one. If you have more than one account on Github, you've probably experienced this before:

ERROR: Permission to user2/repo.git denied to user1.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Why does this happen?

To better understand what's going on, let's assume we have 2 users:

First, let's get some debugging info. In ~/.ssh/config, add:

Host github.com
  LogLevel DEBUG

Then try again a git push in a repository owned by user2:

debug1: Connecting to github.com [192.30.252.131] port 22.
debug1: Connection established.
[...]
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: ~/.ssh/id_rsa
[...]
debug1: Authentication succeeded (publickey).

The key used for authentication is id_rsa attached to user1. This is the problem. SSH tries keys in a certain order, and id_rsa occurs first. The authentication succeeds at the SSH level (there IS a user attached to this key), but fails at the git level because user1 is not a member of the repository we are trying to push to, owned by user2.

Now that we understand what the problem is, let's try to fix it.

First attempt

Since SSH allows for custom configuration per host, the first idea that comes to mind is to create a special host with a preferred key. In ~/.ssh/config, let's add a section for user2:

Host github-user2
  User git
  Hostname github.com
  IdentityFile ~/.ssh/id_github_rsa

We will also need to update our github repository. We need to change the current origin from its default [email protected] to github-user2:

cd ~/projects/user2/project
git remote set-url origin github-user2:user2/project.git

Great! Let's try to push again:

ERROR: Permission to user2/project.git denied to user1.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

Mmmm.. Looks like that didn't do it. Let's increase our log level to the maximum:

Host github-user2
  LogLevel DEBUG3
  User git
  Hostname github.com
  IdentityFile ~/.ssh/id_github_rsa

And push again:

debug2: ssh_connect: needpriv 0
debug1: Connecting to github.com [192.30.252.128] port 22.
debug1: Connection established.
[...]
debug2: key: ~/.ssh/id_rsa (0x700100200300),
debug2: key: ~/.ssh/id_github_rsa (0x700200300400), explicit
[...]
debug1: Next authentication method: publickey
debug1: Offering RSA public key: ~/.ssh/id_rsa
[...]
debug1: Authentication succeeded (publickey).

And so here is where weirdness happens: id_github_rsa key seems to be recognized from our git config (marked as explicit), but somehow id_rsa is still used first. Why?

It took me a minute to figure this one out. I spent some time searching and eventually found this blog post by Drew Crawford. The problem comes from the way IdentityFile works. From the man:

Specifies a file from which the user's DSA, ECDSA or RSA authentication identity is read. The default is ~/.ssh/identity for protocol version 1, and ~/.ssh/id_dsa, ~/.ssh/id_ecdsa and ~/.ssh/id_rsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. [...]

So despite specifying a preferred key for authentication, SSH will first try keys already loaded in the agent. Since id_rsa is our main key and we use it everywhere, chances are it's loaded in the agent first, therefore overriding our configuration.

With this new information in hand, let's update our SSH config.

Second attempt

So we need to tell SSH to ignore any keys already loaded in the agent, and just use the one we specify. Thankfully, there is an option for that, IdentitiesOnly:

Specifies that ssh(1) should only use the authentication identity files configured in the ssh_config files, even if ssh-agent(1) or a PKCS11Provider offers more identities. The argument to this keyword must be yes or no. This option is intended for situations where ssh-agent offers many different identities. The default is no.

Let's update our ~/.ssh/config:

Host github-user2
  User git
  Hostname github.com
  IdentitiesOnly yes
  IdentityFile ~/.ssh/id_github_rsa

And try one last push:

$ cd ~/projects/user2/project
$ git push
Everything up-to-date

Hooray \o/

Recap

So to successfully authenticate on Github with a secondary user, you need to:

  1. Create a dedicated SSH config with IdentityFile and IdentitiesOnly
  2. Update the origin URL of every repo to use the SSH config: github-user:user/repo

Not that complicated, but not easy to figure out either. It certainly took me a minute, especially since my keys were not always loaded in the same order in the SSH agent, which confused things a bit. So I hope this will avoid you some headaches :)

That's it for today, cheers!