Note: if you are too impatient to read the whole thing, feel free to jump to the TL;DR Summary

Why do we need this?

As much as we love Github, its approach to managing access to repositories can sometimes be quite frustrating. The core problem is that, for some mysterious reason, Github has decided that using the same SSH public key with multiple users or repositories (via Deploy Keys) is a bad idea. I have no idea why they decided so, since there’s no apparent security implication that I can think of, but since they have, we now have to deal with it.

To be fair, Github has implemented many convenient ways of managing access to repos: https://help.github.com/articles/managing-deploy-keys and one in particular: Deploy Keys is an extremely smart way for scripting deployments on a remote server with that server’s SSH Key. Actually, I stand corrected: it would have been very convenient if there were no donot-reuse-the-keys rule. It is very likely that you will need to work with multiple repos on the same server (duh!) and since you cannot associate the same key with multiple repos, Deploy Keys become quite limited.

The solution Github kindof suggests in such cases is to create and use “Machine Users”. I personally really hate this approach: creation of pseudo users shouldn’t be a solution for system’s shortcomings. Machines are not users and don’t have e-mails. That’s silly.

Since Github doesn’t allow us to reuse an SSH Key, the only sane solution is to jump through some hoops and generate + use multiple keys on the server itself. Let’s look at some effective approaches of doing that.

Let’s assume we have two repos on Github:

  • git@github.com:inadarei/foo.git and
  • git@github.com:inadarei/bar.git

which we need to script deployments of, on an imaginary myshinyserver.com Linux server.

Creation of Multiple SSH Keys

First things first. Let’s create SSH keys for each of our repos:

$ ssh-keygen -f ~/.ssh/id_rsa.github.inadarei.foo

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/irakli/.ssh/id_rsa.github.inadarei.foo.
Your public key has been saved in /home/irakli/.ssh/id_rsa.github.inadarei.foo.pub.

$ ssh-keygen -f ~/.ssh/id_rsa.github.inadarei.bar

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/irakli/.ssh/id_rsa.github.inadarei.bar.
Your public key has been saved in /home/irakli/.ssh/id_rsa.github.inadarei.bar.pub.

Go on Github.com to the corresponding repositories and create Deploy Keys for each repo, where you will copy/paste the corresponding public keys (e.g. ~/.ssh/id_rsa.github.inadarei.foo.pub)

Now that we have an SSH key per repo, there’re multiple ways for how we can actually use them. Let’s discuss some of [my] top choices.

Option 1: Custom Hosts in an SSH Config

This is probably the most straightforward approach exploiting SSH’s capability of aliasing hostnames and allowing you to use distinct configuration per host (or aliased host).

Create a file ~/.ssh/config if you do not already have it and place the following configuration in it:

# account for the foo repo
Host github.com-foo
    HostName github.com
    User inadarei
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa.github.inadarei.foo

# account for the bar repo
Host github.com-bar
    HostName github.com
    User inadarei
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa.github.inadarei.bar

Basically you are telling SSH (and correspondingly Git, when it uses SSH for repo access) to create multiple host aliases for the same hostname of github.com and that each alias should use distinct configurations. Please note the “IdentitiesOnly yes” directive. This config line is very important to make sure that SSH does actually use the identity file you indicate. Otherwise your configuration may be overridden by an SSH agent or other things on the system.

IMPORTANT: When you check out a repository with this approach you cannot use the actual Git URL such as: git@github.com:inadarei/foo.git. Instead, you have to edit that URL to use the aliased hostname. For instance, the repo URL for the foo repository will be: git@github.com-foo:inadarei/foo.git. Please note the github.com-foo used as the hostname.

If you have an existing local checkout you should alter the URL of the remote by either:

  • issuing a git command such as:

      git remote set-url origin git@github.com-foo:inadarei/foo.git
    
  • or by editing the url parameter of the [remote "origin"] section in your local .git/config file of the checkout.

Option 2: Using SSH-Add

ssh-add utility on a Unix system allows you to quickly and affectively indicate which SSH key you want to use. This is a quicker, but probably less flexible approach.

To see the SSH Keys currently configured:

$ ssh-add -l
2048 ...REDACTED... /home/irakli/.ssh/id_rsa (RSA)
2048 ...REDACTED... /home/irakli/.ssh/id_rsa.foo (RSA)
2048 ...REDACTED... /home/irakli/.ssh/id_rsa.bar (RSA)

In this example my SSH knows about three keys. This actually won’t work since git/Github will only pay attention to the first one and fail.

To delete all known keys:

$ ssh-add -D
All identities removed.

To add a new key:

$ ssh-add ~/.ssh/id_rsa.github.inadarei.foo
Identity added: /home/irakli/.ssh/id_rsa.github.inadarei.foo (/home/irakli/.ssh/id_rsa.github.inadarei.foo)

$ ssh-add -l
2048 ...REDACTED... /home/irakli/.ssh/id_rsa.foo (RSA)

IMPORTANT: Do not forget to run ssh-add -D after you are done messing with Github in this approach, to go back to your default SSH private key.

If you have password-protected SSH private keys, you may want to use ssh-agent so it only asks the password once + saves it and doesn’t annoy you with asking the password all the time.

Also, if you want to ssh-add a key only temporarily (say, in a deployment script) you can do something like the following:

$ ssh-agent bash -c 'ssh-add ~/.ssh/id_rsa.github.inadarei.foo; git clone git@github.com:inadarei/foo.git'

This way ssh-agent will fork the process and ssh-add will only be effective for the duration of the execution of the git clone command (or whatever else git command you need to execute and end-up scripting).

TL;DR Summary

$ ssh-keygen -f ~/.ssh/id_rsa.github.inadarei.foo

In the ~/.ssh/config:

# account for the foo repo
Host github.com-foo
    HostName github.com
    User inadarei
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_rsa.github.inadarei.foo

Make sure to check-out the repo with the aliased hostname (github.com-foo) or if you already have a checkout run something like:

$ git remote set-url origin git@github.com-foo:inadarei/foo.git

Post Scriptum

I hope that at some point Github will either remove the “donot reuse SSH keys” constraint or at least clearly explain to us why they are putting us through all the trouble.

Meanwhile, I hope this blog post is helpful to somebody.