Managing different SSH Keys for different Hosts is well-understood.
Different keys for the same host (e.g., github.com
), based on which GitHub Organization we’re working with, that’s… a bit trickier.
But, we have the technology!
We’re going to configure our SSH and Git clients to seamlessly switch SSH keys between our personal GitHub Account and our GitHub Enterprise Cloud, EMU Account.
🔐 Typical Multi-Key Setup
Most guides and information we’ll find on the Internet about managing multiple SSH keys focus on multiple Host
values.
As in, use key A
for example.com
and key B
for stevenharman.net
.
A simplified ~/.ssh/config
for that might look something like:
Host example.com
IdentityFile ~/.ssh/id_a_ed25519
Host stevenharman.net
IdentityFile ~/.ssh/id_b_ed25519
🤹 Different HostName
s, Different Keys
But what happens when the HostName
, i.e., the real host name that will be used for the SSH connection, for both is the same?
For example, when we have a personal GitHub Account (for Open Source work, some private repositories, and maybe even some repos belonging to other GitHub Organizations), and second GitHub Enterprise Cloud Account provisioned by our employer.
For brevity, let’s say our personal GitHub Account is fry
.
And our work for Planet Express, Inc., has a GitHub Enterprise Cloud instance, and provisioned us a fry_plnx
Enterprise Managed Users Account.
In both cases, the SSH HostName
is github.com
.
We might try a configuration like this:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_fry_ed25519
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_fry_plnx_ed25519
This configuration will work for git
talking to GitHub for the fry
Account, because it is matched first.
But when working with a repository for the fry_plnx
Account, the same first Host github.com
entry matches, and uses the id_fry_ed25519
SSH Key.
But the fry_plnx
Account doesn’t know about that key, and so… we see errors.
$ git clone git@github.com:planet-express/delivery_service.git
ERROR: Repository not found.
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
🤡 Use Different Host
values
At this point, most articles on the Internet will tell us to override the SSH command used to connect, or (IMO, slightly better) use different Host
values for each.
The later would look something like this:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_fry_ed25519
Host github-plnx
HostName github.com
User git
IdentityFile ~/.ssh/id_fry_plnx_ed25519
A big downside to this technique is that any time we’re cloning a new repository for our fry_plnx
Account, we’ll need to adjust the URL used.
Meaning we can no longer copy/pasta the command from the GitHub UI.
Nor rely on muscle memory we’ve built up over years of using our personal Account.
# Instead of the actual URL
$ git clone git@github.com:planet-express/delivery_service.git
# Substitue in our custom Host value for the `github.com` part
$ git clone git@github-plnx:planet-express/delivery_service.git
And this will work! But we have to remember to manually substitute those values for each repository we clone. It’s toil. And I detest toil.
🤖 Automate Substituting the Host
I happened across an article using Git’s url.<base>.insteadOf
variable to swap URLs.
We can use that to teach Git about our Host github-plnx
, and have it auto-magically swap github.com
back in when talking over SSH to GitHub.
We’ll add this to our ~/.gitconfig
:
# See custom `Host github-plnx` in ~/.ssh/config
[url "github-plnx:planet-express"]
insteadOf = git@github.com:planet-express
By including the planet-express
GitHub Organization name in the match for the url
variable, Git will alter the URL git@github.com:planet-express/delivery_service.git
by matching github.com:planet-express
and swapping in github-plnx:planet-express
.
Then the SSH config matches the host github-plnx
, and using that matched config, replaces github-plnx
with the actual user (git
) and host name (github.com
).
At which point we’re back to the original URL.
🎉 NEAT!
With that in place, Git commands for repositories under github.com/planet-express
will use the id_fry_plnx_ed25519
SSH key.
Any other repositories will fail to match the github-plnx
host, thus using the regular Host github.com
SSH Config, and the id_fry_ed25519
key.
NOTE: We’d need to add a similar entry for other Organizations in the GitHub Enterprise Cloud instance that we need to work with.
🧹 Keeping .gitconfig
Clean
If you’ve got your dotfiles in Git, you might want to keep employer and/or machine-specific configuration out of your ~/.gitconfig
.
For example, our work machine needs to know about the custom url "github-plnx:planet-express"
Git config, but our personal MacBook likely doesn’t!
Or maybe we don’t, or cannot, expose every GitHub Enterprise Cloud Organizations we’re a part of.
Luckily, we can leverage Git’s ability to include
other config files to keep such customizations out of Git.
We can adjust our ~/.gitconfig
, which is in our dotfiles Git repo, to include another config file which is NOT in Git.
Add the following the bottom of our ~/.gitconfig
:
[include]
path = ~/.gitconfig_custom
If this file isn’t present, Git just ignores it. As is the case on my personal MacBook. 😅 Phew!
When it is present, Git will load the file, allowing us to override or add additional configuration.
Now we move our [url …]
configuration from ~/.gitconfig
into the new ~/.gitconfig_custom
file:
[url "github-plnx:planet-express"]
insteadOf = git@github.com:planet-express
And voila!
We are switching SSH keys automatically, and our ~/.gitconfig
is clean of any knowledge about the specific GitHub Organizations, etc…