I'm having a problem using two Github SSH keys on my macos where it gets stuck on the first account used after a reboot.
Background:
My mac is set up with two Github accounts - one for a personal
repository and one for a business
repo. The business repo is private. The personal repo is public, but only my personal account has access to push.
System setup:
My system and global configs are empty
~/.ssh/config:
# business
Host business
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa
UseKeychain yes
AddKeysToAgent yes
#personal
Host personal
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_personal
UseKeychain yes
AddKeysToAgent yes
personalreporoot/.git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
name = personalgithubaccount
email = [email protected]
[remote "origin"]
url = git@personal:mygithubuser/personalrepo
fetch = refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
businessreporoot/.git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
name = businessgithubaccount
email = [email protected]
[remote "origin"]
url = git@business:businessname/businessrepo
fetch = refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
It all worked as intended until I restarted my computer. I noticed the problem when I was trying to push to my personal repository. It errored saying business account didn't have permission to push to the personal repo.
I spent a number of hours trying through Stack Overflow and internet articles, most of which recommended deleting my macos Keychain git
entries and removing osxkeychain from the config files, but no no avail. Finally, resorting to troubleshooting 101, I rebooted and viola, I could push to the personal account and it used my personal credential now instead of my business credential.
BUT -- then I went to git pull
on the business repo and it said it was unable to find the repository. Figuring I had the reciprocal problem and it was now stuck on my personal account, I rebooted, pulled the business repo and it worked, but now I couldn't push to my personal repository because git was trying to use my business repo credential again.
I found a workaround in This SO article that linked to these instructions. It seems that running $ ssh-add -D
after a reboot will make Git respect each repo's SSH key. But a subsequent macos restart brings the problem back.
So...the questions are, why does Git get pinned to the first SSH credential used after a reboot, why does clearing SSH identities with $ ssh-add -D
fix the problem, and how do I improve my setup so I don't have to do the workaround after restarts?
CodePudding user response:
TL;DR
Use IdentitiesOnly yes
and IdentityFile
to carefully claim to be just one person. That is, update your sections a bit:
Host business
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa
IdentitiesOnly yes
Then make sure you use the URL ssh://business/path/to/work-repo.git
for work repositories (or business:path/to/repo.git
if you like the short version), and ssh://personal/path/to/personal-repo.git
for the personal ones.
Long
So...the questions are, why does Git get pinned to the first SSH credential used after a reboot ...
It doesn't, really—and this isn't really a macOS issue, it's general across any ssh setup (though the details will vary depending on whether and how you use ssh-agent; macOS sets you up so that all new Terminal windows share one agent by default, which is normally what you want anyway). The trick to understanding this is to understand how ssh keys work: how ssh presents keys to the sites to which you wish to authenticate, and how GitHub uses these ssh keys.
First, let's define an ssh key. It is:
- a string of bytes you use with certain encryption algorithms;
- built from public and private keys.
In general, you keep the private key private to yourself and present the public key, or something encrypted with the private key, or something encrypted with both keys, or whatever: the details aren't terribly important. What we know (and care about) is that the public key gets given out, and the private key doesn't, and we can just pretend that the "key" you present to some other web site (such as github.com
) is the public key.
Now, just offering the key itself does not prove that you are you. Someone could have sneakily copied your public key during some earlier transaction. So GitHub (or whoever you are connecting to) will send you some encrypted, randomized data that you can only decrypt if you have your private key. You decrypt it and thereby prove to GitHub that not only do you have the public key you sent them, but also you're the holder of the corresponding private key.
GitHub, then, take these two things into account and now know that you are who you claimed to be. But who did you claim to be? Why, whoever owns that public key, that's who.
So, suppose you have two keys, both of which work. Today, you present public key A first. They send you a challenge to test whether you really are the holder of the private half of A and you respond correctly. Well, that settles it: you're A.
Tomorrow, you call them up and present public key B first. They send you a challenge, you respond correctly, and that settles it. You're B.
You are who you offered the first time. So the person you are, as far as GitHub is concerned, is the person you claimed to be the first time.
But suppose that on Saturday (if tomorrow is Friday) you offer public key A but fail to send the right response, then offer public key B and do send the right response. GitHub will now think that you're B.
So GitHub really believe that you're whoever you successfully claim to be. That means that even if you always offer A first, revoking your private key for A will suffice to make you become B. That's more or less what your ssh-add -D
does. (Your ssh can be smart and not even offer the public key if it doesn't have the right private key, since there's no point in claiming to be A if you're planning to fail the challenge. Either way works, of course, but overall it's better not to rattle the door too much with a million key attempts: people, or properly programmed computers, will get suspicious.)
In the end, this means you must make sure that Git doesn't offer the wrong key first. When using the agent, you can add as many keys as you like and ssh will be able to "see" all of them. It will offer all of the working key-pairs, in some order, unless you use IdentitiesOnly yes
. If you do use this line, it will offer only the keys for which there are IdentityFile
lines and/or -i
options. (Note that you can list more than one file, and ssh will still try multiple keys, in whatever order.)