Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Managing SSH keys and repo cloning using multiple accounts on GitHub and BitBucket

Why Multiple SSH keys?

There are numerous reasons you may need to use multiple SSH keys for accessing GitHub and BitBucket

You may use the same computer for work and personal development and need to separate your work.

When acting as a consultant, it is common to have multiple GitHub and/or BitBucket accounts depending on which client you may be working for.

You may have different projects you're working on where you would like to segregate your access.

Whatever your reason may be, handling this in a standard Mac/linux PC setup is difficult.

Use the following guide to ensure you code is easy to manage, all changes correspond to the correct identity, and checking out new repositories is not a hassle.

This guide will work on Mac OS, most Linux distributions and on Windows when using Cygwin, GitBash, or Windows Subsystem for Linux with OpenSSH installed.

You must have Git 2.13 or above and OpenSSH installed to use the following guide.

1. Create keys

You will need one key for each different account you will use on either GitHub or BitBucket.

Whichever site you have more identities with determines how many keys you will need.

A single key can act both as a GitHub and BitBucket key but cannot be associated with more than one BitBucket or GitHub account.

If you already have created a key in ~/.ssh/id_rsa (the default location), you may use that in place of the ~/.ssh/msmith key in my examples or you can leave that key and add additional keys for the other identities.

Create the keys and ssh-add them (make sure to enter a secure password and do not just leave it blank)

$  ssh-keygen -t rsa -b 4096 -f ~/.ssh/key1_rsa -C "msmith@example.com" 
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): ************
Enter same passphrase again: 
Your identification has been saved in /Users/me/.ssh/key1_rsa.
Your public key has been saved in /Users/me/.ssh/key1_rsa.pub.
The key fingerprint is:
...
$  ssh-add ~/.ssh/key1_rsa


$  ssh-keygen -t rsa -b 4096 -f ~/.ssh/key2_rsa -C "jblige@example.com" 
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): ************
Enter same passphrase again: 
Your identification has been saved in /Users/me/.ssh/key2_rsa.
Your public key has been saved in /Users/me/.ssh/key2_rsa.pub.
The key fingerprint is:
...

$ ssh-add ~/.ssh/key2_rsa

2. Setup ~/.ssh/config

Create a file in ~/.ssh/config (if it does not already exist). You must make sure it is readable only by the owner and the group and public bits are set off.

touch ~/.ssh/config
chmod 600 ~/.ssh/config

We now need to add SSH configuration that specifies the github and bitbucket hostnames but with a suffix appended to qualify which key to use. We set the HostName to the correct github.com or bitbucket.org address.

Note: Linux users should either omit UseKeychain yes or add IgnoreUnknown UseKeychain (thanks soulofmischief)

~/.ssh/config
...

Host github.com-msmith
HostName github.com
UseKeychain yes
AddKeysToAgent yes
User git
IdentityFile ~/.ssh/msmith_rsa
IdentitiesOnly

Host bitbucket.org-msmith
HostName bitbucket.org
UseKeychain yes
AddKeysToAgent yes
User git
IdentityFile ~/.ssh/msmith_rsa
IdentitiesOnly

Host github.com-jblige
HostName github.com
UseKeychain yes
AddKeysToAgent yes
User git
IdentityFile ~/.ssh/jblige_rsa
IdentitiesOnly

Host bitbucket.org-jblige
HostName bitbucket.org
UseKeychain yes
AddKeysToAgent yes
User git
IdentityFile ~/.ssh/jblige_rsa
IdentitiesOnly

...

3. Add public keys to GitHub and BitBucket

Log into GitHub for each user and add the keys from ~/.ssh/xxxxx.pub to the respective users authorized SSH keys.

For more information on this see: https://confluence.atlassian.com/bitbucket/set-up-an-ssh-key-728138079.html

or

https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account

4. Create key specific .gitconfig

You will need a single directory where all code that corresponds to a given key will be checked out to.

I prefer to keep all those directories in one directory in my home ~/src and I name them according to the account name associated with the key

mkdir -p ~/src/msmith
mkdir -p ~/src/jblige

In each directory put a .gitconfig file.

~/src/msmith/.gitconfig
...
[user]
  email = msmith@example.com
    
[url "git@bitbucket.org-msmith"]
  insteadOf = git@bitbucket.org
  
[url "git@github.com-msmith"]
  insteadOf = git@github.com
~/src/jblige/.gitconfig
...
[user]
  email = jblige@example.com
  signingkey = ABCD1234
  
[url "git@bitbucket.org-jblige"]
  insteadOf = git@bitbucket.org
  
[url "git@github.com-jblige"]
  insteadOf = git@github.com
  
[commit]
  gpgsign = true

This way, I use the correct email address for both keys and have even set up automatic commit signing for jblige. I also rewrite all the hostnames for the original SSH connections to the correctly suffixed hostnames I created in the SSH config file.

For more information about GPG signing see: https://help.github.com/en/articles/signing-commits

or

https://confluence.atlassian.com/bitbucketserver/using-gpg-keys-913477014.html

5. Setup Git config includeif

To activate the .gitconfig files in ~/src/*, edit the .gitconfig file in your home directory and add an includeif statement for each of the .gitconfig files referencing the directory they are in

~/.gitconfig
...

[includeif "gitdir:~/src/msmith/"]
	path = ~/src/msmith/.gitconfig
	
[includeif "gitdir:~/src/jblige/"]
	path = ~/src/jblige/.gitconfig
	

Do not forget the trailing slash in the [includeif "gitdir:... statement. (thanks loizoskounios)

6. Cloning the repositories

You then clone the code using the SSH clone address (i.e. git@bitbucket.org... or git@github.com..., not https://bitbucket.org... nor https://github.com...) into the directory that corresponds to the key you want to use for that clone.

$  cd ~/src/msmith
$  git clone git@github.com:someuser/somerepo.git
...

Because of the rewriting, git will actually attempt to clone using the suffixed address corresponding to the configuration in the SSH config file but because of the SSH configuration it will use the original hostname when actually connecting to the host ensuring you use the right key.

All commits/pulls/pushes to/from those repositories will use the corresponding config/key/account.

Credits

This work was adapted from the following

@loizoskounios

This comment has been minimized.

Copy link

commented Apr 8, 2019

Very clean, thank you.

Note that the trailing slash in [includeif "gitdir:~/src/msmith/"] is important as it is replaced by the glob pattern /**.

@soulofmischief

This comment has been minimized.

Copy link

commented Apr 11, 2019

Great writeup. includeif was new to me... opens up some cool possibilities. I ended up integrating this technique into an ansible role to automatically manage creating directories, configs, keys, etc. for a few systems like this with different git accounts.

I would recommend making a note about UseKeychain. I believe this command is only for macOS; git will fail on other boxes.
Linux/BSD/etc users shouldn't include this line, or should use IgnoreUnknown UseKeychain.

@theychx

This comment has been minimized.

Copy link

commented May 22, 2019

Thank you for making this guide.
The IdentitiesOnly entries in your ~/.ssh/config example are missing the yes argument.

@seed-of-apricot

This comment has been minimized.

Copy link

commented Jun 11, 2019

Nice article. But I have one question.

When I install git LFS, the pre-push will execute run-command to push the current commit.
However, it won't read the added keys (in step 1) so that it cannot upload the lfs forever.
Is it possible to deal with this issue?

Note: I have "C:\Windows\System32\OpenSSH\ssh.exe" as GIT_SSH user varibale and can push the repo without entering the passphrase when lfs is not installed.

C:\Users\pc_user\src\git_user\repo>git push origin master
21:29:12.542304 exec-cmd.c:238          trace: resolved executable dir: C:/Program Files/Git/mingw64/bin
21:29:12.548300 git.c:439               trace: built-in: git push origin master
21:29:12.553303 run-command.c:663       trace: run_command: unset GIT_PREFIX; 'C:\Windows\System32\OpenSSH\ssh.exe' git@git_user 'git-receive-pack '\''github_user/repo.git'\'''
warning: agent returned different signature type ssh-rsa (expected rsa-sha2-512)
21:29:13.659145 run-command.c:663       trace: run_command: .git/hooks/pre-push origin git@git_user:github_user/repo.git
21:29:13.738152 exec-cmd.c:238          trace: resolved executable dir: C:/Program Files/Git/mingw64/libexec/git-core
21:29:13.742152 git.c:704               trace: exec: git-lfs pre-push origin git@git_user:github_user/repo.git
21:29:13.742152 run-command.c:663       trace: run_command: git-lfs pre-push origin git@git_user:github_user/repo.git21:29:13.787155 trace git-lfs: exec: git 'version'
21:29:13.845159 trace git-lfs: exec: git '-c' 'filter.lfs.smudge=' '-c' 'filter.lfs.clean=' '-c' 'filter.lfs.process=' '-c' 'filter.lfs.required=false' 'rev-parse' 'HEAD' '--symbolic-full-name' 'HEAD'
21:29:13.951170 trace git-lfs: exec: git 'config' '-l'21:29:13.981171 trace git-lfs: pre-push: refs/heads/master 2389bc61f841d2839edbff2f27edcb0053d533b0 refs/heads/master 0000000000000000000000000000000000000000
21:29:14.763232 trace git-lfs: run_command: C:\Windows\System32\OpenSSH\ssh.exe -- git@git_user git-lfs-authenticate github_user/repo.git upload
// won't finish
@yinzara

This comment has been minimized.

Copy link
Owner Author

commented Jun 11, 2019

Since you're on Windows you need to use the Windows SSH agent.

https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement

@seed-of-apricot

This comment has been minimized.

Copy link

commented Jun 11, 2019

Yes I use the ssh agent on Win10 and I have my keys registered already.
The problem is that when run_command: C:\Windows\System32\OpenSSH\ssh.exe is called it executes a new instance of the ssh-agent where the key is not stored.

C:\Users\pc_user\src\git_user\repo>ssh-add -l
4096 SHA256:brahbrah user1_email (RSA)
4096 SHA256:hogehoge user2_email (RSA)
@seed-of-apricot

This comment has been minimized.

Copy link

commented Jun 20, 2019

Here's a related issue.
PowerShell/Win32-OpenSSH#1377

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.