MonkeyTech Software for code monkeys

Managing SSH keys using keychain

As a developer you probably work on projects that live on GitHub or some other repository. If you are anything like me you have several accounts and each requires its own SSH identity. Or maybe you’ve just noticed that each time you login you have to:

  1. Start a new ssh-agent process
  2. Add SSH identities to the agent
  3. Enter you passphrase

Naturally doing this every time you start a new session is tiresome and lame. In this post I demonstrate how to use keychain to automate this at login. I’ll also share how to configure SSH in such a way that the correct SSH identity is used when dealing with multiple accounts.

Install packages

We’ll be using the OpenSSH package which contains utilities to facilitate ssh communication as well as generate ssh keys. We are going to delegate management of these ssh keys (identities) to the Keychain. Keychain has the added benefit of allowing one long-running ssh-agent process per system, rather than per login. No more starting the ssh-agent everytime we login. Yay!

If you already have OpenSSH and Keychain installed these installed skip to Configuring Keychain.

Installing OpenSSH

First things first, make sure you have OpenSSH installed on your system. You may already have this installed by default depending on what system you are running. There are a variety of ways to check this. I suggest using your system’s native package manager (pacman, dpkg, etc) to query for its existence. Alternatively, look for a running ssh daemon (ssh or sshd) or service (systemctl).

Arch

$ pacman -Qi openssh

Debian (Ubuntu)

$ dpkg -l | grep openssh-server

systemd

$ systemctl status ssh

ssh daemon

$ ssh -V

Install the OpenSSH package:

Arch

$ sudo pacman -S openssh

Debian (Ubuntu)

$ sudo apt-get install openssh-server
Note: If you are on a system running systemd it may be necessary to enable and start the ssh service. $ sudo systemctl enable ssh $ sudo systemctl start ssh

Generate new SSH key

If you are starting from a fresh SSH installation you won’t have any ssh keys at this point. So let’s create one. Make sure to substitute [your_email@goeshere.com] with your email address.

$ cd ~/.ssh
$ ssh-keygen -t ed25519 -C "[your_email@goeshere.com]"

Generating public/private rsa key pair.
Note: If the home directory `~/.ssh` doesn't exist go ahead and make one. Though this is not a requirement, it is the default location for ssh keys.

Installing Keychain

Arch

$ sudo pacman -S keychain

Debian (Ubuntu)

$ sudo apt-get install keychain

Configuring Keychain

Keychain drives both ssh-agent and ssh-add so we don’t have to. The best part is Keychain also maintains a single ssh-agent process for us. That’s a very good thing because if you created your ssh key using a passphrase you’ll only need to provide it once across multiple login sessions.

When a new shell session is created (aka we log in) we want the keychain to provide the necessary environment variables for an SSH client to be able to find our agent. We let it do the work of:

  1. Adding the keys to the agent
  2. Managing the agent between sessions.

Add the following to ~/.bash_profile or .zshrc:

eval $(keychain --eval --quiet id_ed25519 id_rsa)
Note: If you are running Arch Linux make this change to ~/.bashrc rather than ~/.bash_profile. ~/.bashrc is sourced by both login and non-login shells and will allow Keychain to function across graphical and non-graphical environments.

In the above example we are using some bash to:

  • Execute the output of our keychain command.
  • Use the --eval switch which causes the keychain to output the lines needed to set the above mentioned environment variables.
  • Use the --quiet switch so keychain will only display warnings and errors when this file is sourced.
  • Lastly, we provide the keychain with a space delimited list of keys we want added to the ssh agent.
Note: By default keychain will look for keys in the ~/.ssh directory. If you want to add a key outside of this directory just provide the full path.

If you use a single ssh key that you are managing, with GitHub for example, then you are done. If you have two accounts with a single host or multiple hosts, we are almost done.

Configure SSH for multiple hosts or accounts

We have added our SSH keys to the keychain. All that is left to do is tell the SSH agent which key to use for a given host (GitHub, Google, AWS, whatever). We do that using a configuration file stored in the same location as our keys.

  1. Create ~/.ssh/config if one does not exist.
  2. Edit ~/.ssh/config adding a Host entry for each host and/or each account.

Example:

# work account
Host github.com-work
  HostName github.com
	User git
	IdentityFile ~/.ssh/id_rsa_work

# Personal account
Host github.com
	HostName github.com
	User git
	IdentityFile ~/.ssh/id_rsa

# GCP account
Host google.com
  HostName source.developers.google.com
	User git
	IdentityFile ~/.ssh/id_ed25519_google

  • Host - This is just a label or alias.
  • HostName - The real host name that you will log in to (e.g. Github.com).
  • User - The username to use to log in (e.g. “git”, “tsmith”, etc).
  • IdentityFile - The ssh private key to use.

In this example, my first entry has a Host entry called, “github.com-work”. That’s not a real host URI (not that I know of anyway). This is just a label or alias. The SSH agent will substitute this value with the value of HostName. This allows me to use 2 different ssh keys with a single host. This allows me to have multiple ssh keys associated with a single host.

For example:

# ssh will use ssh key '~/.ssh/id_rsa_work'
$ ssh github.com-work
# ssh will use ssh key '~/.ssh/id_rsa'
$ ssh github.com

Using the same example configuration, lets say my work repository lives here: git@github.com:SuperRocketCo/ignition-module.git. I can clone this repository using the Host value associated with my work ssh key (IdentityFile):

# notice the use of github.com-work rather than github.com
$ git clone git@github.com-work:SuperRocketCo/ignition-module.git

...

$ git remote -v

origin git@github.com-work:SuperRocketCo/ignition-module.git (fetch)
origin git@github.com-work:SuperRocketCo/ignition-module.git (push)

Cool?