Using SSH with multiple identities / certificates

A while back,I decided to start using SSH for all my GIT repos. However, being a consultant, I end up being involved in a lot of different repos, for a lot of different customers. And on top of that, I sometimes write code on my spare time as well. This causes a bit of a problem when it comes to SSH. So I decided to write a a post about how you solve it. It might be obvious to a lot of people, but it might also help someone with the same problem. If nothing else, it will help me when I forget in the future…

The problem

The problem is that, by default, the SSH agent will use a single certificate for all authentication. This is fine, if you only have one identity. For example, if you only have one GitHub account, you are good to go with just one certificate. Just give GitHub the public key, and it knows that whenever it sees someone using that key, it is you. And whenever you try to use SSH with GitHub, the SSH agent will use that certificate, and all is fine.

However, if you need to have multiple GitHub accounts, or multiple identities in general, it becomes a bit more problematic. You can only register a public key with a single account, which is kind of expected as the key is supposed to identify that you are you. However, if you have 2 accounts, you have 2 different identities, and you are actually 2 different “yous”. This requires us to use 2 different keys.

So how do we solve that?

The solution

Well, the first step is to create a new set of keys to use with our second identity. This is fairly easy to do by running

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_myOtherAccount

This will generate a new 4096 bit, RSA-based set of keys with the private key in the file id_rsa_myOtherAccount, and the public in id_rsa_myOtherAccount.pub.

Note: If you are using PowerShell on Windows, ~/ doesn’t actually work. Instead you have to use the full path, which is c:\Users\<USERNAME>\.ssh. So, for the above example it would be c:\Users\<USERNAME>\.ssh\id_rsa_myOtherAccount. However, if you write ~/.ssh and press TAB, PowerShell will add the path automatically for you.

Note: You can place your keys wherever you want, but ~/.ssh is the standard location.

The ssh-keygen command will ask for an “optional” passphrase. And I place optional in quotes here, because even if it is technically optional, I don’t think it should ever be left empty. You should definitely add a passphrase, and make it a good one! This is used for authentication, it needs to be secure!

Once you have input your passphrase twice, you will see something like this

The key fingerprint is:
SHA256:A5rYe8Sjz92GZ893FDwbpLUs9gAv9BYn8qqLSoYSV28 <USERNAME>@<COMPUTER_NAME>
The keys randomart image is:
+---[RSA 4096]----+
|                 |
|           + o + |
|     ..   . * X .|
|   o.+..   . X B |
| ...+ +ES   = + =|
|  o .+.. . .   o.|
| . .oo.  ..    . |
|  . o+ .oo+.  . .|
|     .+..=o.o. . |
+----[SHA256]-----+

Ok, so now we have 2 sets of keys that we can use. The default id_rsa and the newly generated id_rsa_myOtherAccount, as well as the corresponding public keys.

With the new key pair in place, we can go ahead and add the newly created public key to our second GitHub account (or wherever you need your second identity).

Once your service of choice knows your public key, we need to add the corresponding private key to the the SSH agent. And then we need to tell the agent, that it should use that new key in some scenarios. Let’s do it one step at a time

To add the new key to the SSH agent, we can run

ssh-add ~/.ssh/id_rsa_myOtherAccount

Note: Once again, PowerShell does not support ~/ and needs the full path…

This will register the new key with the SSH agent. However, it will still default to using the default one when asked to provide a key. So we need to configure it to use the new key in some cases. This is done by creating a file called config at ~/.ssh/. On Windows, this could be done by running

notepad c:\Users\<USERNAME>\.ssh\config

In this file, we can tell the SSH agent how to behave when it sees certain hosts. So for example, I could add

Host git-myOtherAccount
  HostName git.com
  User git
  IdentityFile c:/Users/<USERNAME>/.ssh/id_rsa_myOtherAccount
  IdentitiesOnly yes

This tells the SSH agent that whenever I try to talk to the host git-myOtherAccount, it should open a connection to git.com, using the username git and the key from c:/Users/<USERNAME>/.ssh/id_rsa_myOtherAccount.

Wait, what? The host git-myOtherAccount? Yes, the host entry in this configuration is basically any string that you want. It will then be replaced by whatever is set in the HostName field whenever you try to open an SSH connection to that host.

The next thing we need to do, is to make sure that we replace the host for SSH connection we want to make. In the case of GitHub, that means changing the address for any repo that we want to clone using this new key.

For example, say that you have a repo with an address of git@github.com:ChrisKlug/K8S4DEVS.git. If we use this address, the SSH agent will look at the ~/.ssh/config file to see if there is any Host with the value github.com. If doesn’t find one, it will just use the default key (id_rsa).

However, if it finds an entry with the same value, it will replace the host name with the value in the HostName field, and use the key configured for this host instead.

Armed with this bit of knowledge, we can update the GitHub address we got to the repo, and turn it into git@git-myOtherAccount:ChrisKlug/K8S4DEVS.git. As you can see, I just replaced github.com with git-myOtherAccount. The SSH agent will now find an entry in the config that corresponds to that. It will then automatically replace the host with github.com (as configured), and use the configured key instead. E.g. git@git-myOtherAccount:ChrisKlug/K8S4DEVS.git is turned back into git@github.com:ChrisKlug/K8S4DEVS.git, but the id_rsa_myOtherAccount key is used instead of the default one.

Note: You can use any host you want to. As mentioned before, it is just a string that is used to find the correct configuration.

Some extra comments

I had some extra thoughts and comments that might be useful, so I thought I would throw them in here as well…

SSH on Windows

WIndows 10 comes with an SSH agent built in. So there is no need to run third party tools like PuTTy anymore. Instead, you just need to turn on the built in agent. This is done by doing the following.

First, open the “Manage Optional Features” window and search for “OpenSSH Client”.

optional features dialog

If you can’t find it, it needs to be installed by clicking on “Add a feature”. In the window that opens, search for “OpenSSH Client” and install it.

Next, open the “Services” window and look for the “OpenSSH Authentication Agent” service. Make sure that it is set to start up automatically, and that it is running.

OpenSSH Authentication Agent Properties

Once we know that the agent is running, we need to make sure that it is the default agent being used by the system. Git for Windows includes a bunch of extras, including an SSH set up that used to make it easier to work with on previous versions of Windows. Today, this isn’t needed as Windows 10 has pretty much all of it built in.

So, to make sure that we are actually using the Windows 10 built-in agent, open up a terminal window (cmd.exe for example) and run

where ssh

This should tell you that the SSH executable is located at C:\Windows\System32\OpenSSH\ssh.exe.

Note: If it isn’t, have a look at your PATH variable to find out what path is causing it to end up at the wrong place. Once you have found it, either remove that path, or make sure you move it down so that the C:\Windows\System32 is placed before the one that causes the problem.

Once we know that the right SSH executable is being used, we should also also tell Git to use that same executable instead. This can be done by either creating a new environment variable called GIT_SSH with the value C:\Windows\System32\OpenSSH\ssh.exe. Or by setting up a Git configuration variable called core.sshCommand. Like this

git config --global core.sshCommand "'C:\Windows\System32\OpenSSH\ssh.exe'"

That should be it! Your computer should now have a running SSH agent for you to use. You just need to add your keys and make sure you use the SSH endpoint for your Git repos!

Updating the Git information

If you are using multiple identities for your repos, it is also very likely that you want to use different user information when interacting with them as well.

By default, Git uses the globally configured name and e-mail address. However, if you have for example a private account and a work account, you probably want your commits to reflect which one you are actually using. The SSH part will only do the authentication, the commits are still created and tagged by Git.

So to update your user information for a single repo, just run

git config user.name "<YOUR NAME>"
git config user.email  "<YOUR EMAIL ADDRESS>"

This will make a local change for this particular repo only, which makes sure that any commits you add to this repo are added with the correct information.

Updating existing Git repos

If you already have existing Git repos and you want to change to SSH, you just need to update the URL for the origin. This is done by executing

git remote set-url origin git@<HOST>:<username>/<repo>

Note: Remember that the HOST can be either a real host, or a made up one from the SSH configuration

Managing SSH keys

Once in a while, you might want to see what keys are currently registered with the SSH agent. This is easily done by running

ssh-add -l

And if you want to remove a key, just run

ssh-add -d <PATH TO KEY>

And finally, a tiny little warning…

A little config warning

If you happen to create a config that looks something like this

Host GitHubPrivate
  HostName github.com
  User git
  IdentityFile c:/Users/chris/.ssh/id_rsa_private
  IdentitiesOnly yes

Host github.com
  HostName github.com
  User git
  IdentityFile c:/Users/chris/.ssh/id_rsa_github
  IdentitiesOnly yes

and then try to clone a repo using for example git@GitHubPrivate:ChrisKlug/K8S4DEVS.git. The SSH agent will use id_rsa_github instead of the (probably) intended id_rsa_private.

Why? Well, here is why!

First the agent will find the GitHubPrivate configuration, and replace the host name with github.com and use the id_rsa_private key. However, after changing the host to github.com, it will keep going through the configuration and find another entry for the github.com host. This new entry says that it should use id_rsa_github. So the agent changes the key to be id_rsa_github. So in the end, you end up with github.com and id_rsa_github, which is probably not the intended result.

To solve it, make sure you don’t override changed hosts further down the list of configurations.

That’s it for now I think. Being able to use multiple keys/identities is really useful in a lot of scenarios. But for me, it is always a pain to remember exactly how to configure it. Thus this post. I hope the information in it was useful for you as well!

zerokoll

Chris

Developer-Badass-as-a-Service at your service