I came across this issue when automating infrastructure provisioning. I needed a way to pull the repository code for my app in the provisioning scripts. I didn’t want to use the ssh keys I have setup for the entire Github account due to security. I discovered that github has the ability for you to add per repository SSH keys, called Deploy keys. The docs totally left me in the dark. I had no idea how to do any of this so I had to spend days researching. I decided to write this article to save everyone else hours of time scouring the internet trying to figure out how the hell to do this.
Why use Deploy keys?
Why would you want to use Deploy keys? When automating infrastructure provisioning you don’t want to expose your personal SSH keys. These deploy keys are going to be used only for cloning a repo, you may be able to use them for other things I didn’t research that not my problem. LOL.
SSH keys when setup correctly, allow a higher level of security than user name and password. Many people are automating by scripting a user name and password, that is BAD. Also if you don’t set a passphrase for the SSH key it won’t prompt for it in the shell terminal. Normally you want your ssh key secured with a pass phrase, but for infrastructure automation we need no pass.
I won’t cover how to automate the infrastructure that would be a series of articles. What I want to cover is how in specific to use multiple SSH keys.
The syntax is wacky as it gets. First off when you are using GIT to pull/push/clone etc. from Github, git is using SSH underneath. So in order to use multiple SSH keys you actually configure SSH not GIT, but git reads the command you type and interacts with SSH on your behalf. Totally confusing. My first few hours were filled with a lot of WTF?
First off the SSH config is stored in your users .ssh directory. On most Linux distributions that is in the user you are logged in as home directory. Basically /home/username/.ssh/ this directory will hold your SSH certs, known_hosts file, config file and others. The ssh config file is always named config and goes in the .ssh directory. If you are logged in as root it will be /root/.ssh/config. Many times when provisioning a server automatically the only user you have at first is root.
Example ssh config file should look like this
Host hostAlias User git Hostname github.com IdentityFile=/root/.ssh/id_rsa
Yes it goes in a file exactly like that, no equals, no semicolons no quotes, just 1980’s YAML LOLOL. The most confusing setting above, which gets more confusing if you read the docs, is Host. Just think of it as Alias. I have no idea why it is even called Host instead of Alias. That threw me and so many others off. I kept putting the same value I had for Hostname. Hostname is the exact name of the host where your repo is, github.com for this example. Identity file is the private key file location.
Another thing to look at is git for the User. You might be able to use other names, but next I’ll show how the name part ties in.
To use the above setting to clone a repo for example you would type the following at the command line.
git clone git@hostAlias:repo-owner-name/repo-name.git .
See the User git and the Host hostAlias. This looks so similar to the regular clone command. For example here is another one of my Github repositories a public one so you can play with this.
This is the default to clone a repo. This has a default name for User of git and a default value for Host of github.com. I haven’t experimented enough yet but I am guessing you can change the name in the configs to anything you want such as Billy and use a command like :
So back to the question how do you use multiple SSH/Deploy keys with Git and Github?
Host hostAlias User git Hostname github.com IdentityFile=/root/.ssh/id_rsa Host otherAlias User git Hostname github.com IdentityFile=/root/.ssh/id_rsa_2 Host billy User git Hostname github.com IdentityFile=/root/.ssh/id_rsa_3
Each IdentityFile must be a unique ssh or deploy key, they are the same thing, both are ssh keys.
Then to clone from each for example you would use the following for example.
git clone git@hostAlias:repo-owner-name/repo-name.git .
git clone git@otherAlias:repo-owner-name/repo-name.git .
git clone git@billy:repo-owner-name/repo-name.git .
The format is User@Host:repo-owner-name/actual-repo.git
The dot . I am putting at the end of the clone IS AWESOME.
It tells Git to clone into the current directory and don’t use the name of the repo as the directory name. Basically just clone the damn repo into this damn folder. Without the dot it includes the repo name too. I often just want /opt/app-directory < code in that folder.
AND DON’T FORGET THE SECRET SAUCE
Now that you have multiple SSH keys you must do some special magic to let SSH know about the keys. For each key you have to tell the ssh-agent it’s name. Basically when SSH does it’s thing your SSH client has to give a list of keys to the SSH agent on the server you are contacting. GIT uses SSH so you must tell SSH where the keys are for your github accounts.
To do this on linux you start the ssh-agent then you add the keys. It is a bit of a pain. First you must start the agent, then you add the key.
#start the agent on linux like so
The value you give to ssh-add command should be the ones you used for your IdentityFile definitions. You will need to add each private key to the agent before it will work.
To test that your setup is working you can do the following and read the output. If there was a problem it will tell you, like it couldn’t find the key.
Running those commands will let you know if everything is configured properly.
BUT IT GETS FUNNER GUYS
All of the 999 things above are still not enough if you want to automate this process. If you do all of the above and try automating the process, github will prompt you for a passphrase for an ssh key. It won’t be the deploy key either, NO why do that, that would be logical and make sense. What it wants is the passhprhase to the entire account, not the deploy key.
How to fix this?
And there is still, still more, you must chmod the .ssh directory to 600 such as
chmod -R 600 /home/user/.ssh
or where ever your ssh files are stored.
You may also need to do the following.
Create a dummy instance. On this instance issue the git clone command. When it asks for the passphrase enter the passphrase for the account that OWNS THE REPO, not the deploy key passphrase which should be empty.
This will add github to known_hosts file. Now use cat to output that info and copy it. You can’t use xclip like I mention in another article, no that’s not allowed for some no brain reason. Once you copy the code from known_hosts create another file on your system called known_hosts. You will need to upload this file along with the ssh deploy keys so that you are not prompted during automated clones.
If there is some sort of openssh setting or a way to do this automatically, I haven’t found it yet.
If you would like more information on how to create the ssh deploy keys themselves, read this article I wrote.
If you want more information about ssh checkout my list of resources here
A really good book I found really handy is
SSH Mastery: OpenSSH, PuTTY, Tunnels and Keys (IT Mastery Book 12)