Using SSH with multiple GitLab/GitHub accounts

If you have multiple accounts on GitLab, GitHub and undoubtedly others, you have probably noticed you can add your SSH-key to only one account. Well, I ran into this problem too, and found this excellent blog explaining how to work around that. To keep this information closer, I decided to repeat it here in my own words, but that's where most of the credit goes.

I skipped the Windows explanations here, because I don't know anything about that at all and just repeating it here would be useless. The original blog has some pointers for you, though.

Create a separate key pair per account

It probably sounds obvious – you can't use a single key pair for all your accounts, so you create one per account. If you don't know how, GitLab and GitHub can help you out, or you can just follow this here example:

  ssh-keygen -t ed25519 -b 2048 -C "<comment>"

For <comment> I like to use Service AccountName (eg: GitLab Punksmurf) but it doesn't matter what it actually says so it's up to you. Make sure you save the key in an appropriate file (like .ssh/id_ed25519_service_accountname) and not overwrite your default one. You can now add the public key to your account (instructions on GitLab, GitHub).

You can test if the service correctly recognises you, just by connecting over ssh:

    ssh -i ~/.ssh/id_ed25519_gitlab_accountname -F /dev/null -o IdentitiesOnly=yes git@gitlab.com
    ssh -i ~/.ssh/id_ed25519_github_accountname -F /dev/null -o IdentitiesOnly=yes git@github.com

Both of these will respond with a welcome text including your account name, and close the connection.

The -i option tells it to use the specified identity file, and the -F option tells it to use /dev/null as the ssh config file, so it won't default to any options you might have set in ~/.ssh/config (for example a default key). Lastly, the -o IdentitiesOnly=yes instructs ssh to only use the specified identity files and not your default one. It may work without this (it has for me), but also it may not. See the "Trouble in paradise" section below.

Adding the new key to your ssh agent

If you're using a passphrase you might need (want?) to tell the ssh-agent about your new key, and have it keep the passphrase for you so you don't need to enter it all the time. This differs per OS. GitHub) explains this better than I can, but the tl;dr should be as follows:

On Linux and macOS without a passphrase:

    ssh-add ~/.ssh/id_ed25519_service_accountname

On macOS with a passphrase (pre-Monterey):

    ssh-add -K ~/.ssh/id_ed25519_service_accountname

On macOS with a passphrase (Monterey and presumably later):

    ssh-add --apple-use-keychain ~/.ssh/id_ed25519_service_accountname

Getting git to use the correct key pair

I find it easiest to have one account use the default key pair I have on the machine – so on my work machine, I use the default key pair for my work-related account and if I want to work on a personal project I use a separate key for that account, so I don't have to configure an exception for all repositories I have checked out.

For each repository where you want to use a different key from your default one, you tell git to use a different ssh command like the one we tested above:

    git config core.sshCommand "ssh -i ~/.ssh/id_ed25519_service_accountname -F /dev/null -o IdentitiesOnly=yes"

From now on git will use that ssh command for that repository, instead of the default.

Cloning a repo

If you need to clone a repo, you cannot configure it yet. However you cannot clone it, until you configure it. Right?

Luckily there is a solution too, you can use the GIT_SSH_COMMAND environment variable:

    GIT_SSH_COMMAND='ssh -i .ssh/id_ed25519_service_accountname -F /dev/null -o IdentitiesOnly=yes' git clone git@gitlab.com:user/repo.git

Trouble in paradise

I am not the most knowledgeable person where it comes to ssh, it's agents and whatnot, but at some point this stopped working for me and I had to figure out why gitlab started identifying me again as the account matching the default key. I am currently back to the original configuration described here, so I don't exactly know what is, or was, going on. I hope that if this happens to you, you might find this information helpful.

A first useful tool is to use the -v flag with ssh, making it verbose and spit out a lot of information about the connection it is setting up and most importantly the identity file it is using:

    ssh -i ~/.ssh/id_ed25519_gitlab_punksmurf -F /dev/null -v git@gitlab.com

In the output, you will see which files it tries and in what order. Ideally, you'd want it to only use the one you specified. Here is some example output:

    debug1: Will attempt key: /Users/punksmurf/.ssh/id_ed25519_gitlab_punksmurf <snip>
    debug1: Will attempt key: /Users/punksmurf/.ssh/id_ed25519 <snip>
    ...
    debug1: Offering public key: /Users/punksmurf/.ssh/id_ed25519_gitlab_punksmurf <snip>
    debug1: Server accepts key: /Users/punksmurf/.ssh/id_ed25519_gitlab_punksmurf <snip>
    debug1: Authentication succeeded (publickey).
    Authenticated to gitlab.com ([172.65.251.78]:22).
    ...
    Welcome to GitLab, @Punksmurf!
    ...
    Connection to gitlab.com closed.

So here you see it's still considering my default id_ed25519 key. Since it offers the more specific one first I am in the clear, at least until it for some reason decides to swap these two around again. I do not know why it will still attempt the default one (and you can find a lot of questions on Stack Overflow and related sites about it, so at least that helps me not to feel too stupid).

I fixed it by adding the option -o IdentitiesOnly=yes, which will make ssh only consider the identities you specify Note though: this will include any identities you have specified in your ssh config, for example if you have a piece of config like this:

    Host *
        IdentityFile ~/.ssh/id_ed25519

Since we're using -F /dev/null to ignore this config that should not be an issue in our case.

Conclusion

All in all it's not too complicated to get working, even though it is a little annoying to have to do. Happy coding!