SSH with Multiple GitHub/GitLab Accounts

Posted Thursday, July 28, 2022 by Sri.Tagged MEMO
EDIT STATUS:new

Pushing Git Changes without Typing Passwords

I'm lazy and use SSH public keys with my GitHub/GitLab repositories so I don't have to type a long password every time I push to one of my repos. This is a poor security practice, but let's document how it works anyway!

The short version: you upload a public key to your account settings on GitHub/GitLab. After that, these services will authenticate using that public key to check that you can prove you have the corresponding private key. If you do, then your git commands will run...no passwords typed!

The tricky part is determining why your SSH operations fail to work. It boils down to specifying WHICH KEYS ARE OFFERED and knowing how to SPECIFY THE CORRECT HOST URL, which is not as straightforward as you would think. There are two main concepts behind making it work:

  • The git remote is an alias for the URL of the repository you are tracking. This is set using the git remote add <alias> <url> command. For SSH the URL is the same format as a SSH login (e.g. git@gitlab.com:/path/to/repo/reponame.git).Note that the usernane should be git for both GitLab and GitHub, not your login name KEY: The gitlab.com in the connection string can refer to a Host entry in SSH config.

  • Your public/private SSH key pairs (aka "identities") are typically stored in your user directory at ~/.sshThe files with .pub extensions are your public keys, the ones without any extension are private keys. Don't share your private key! Don't forget to back them up when reinstalling the operating system! A program called ssh-agent is loaded to "offer" a list of identities to the remote host, which will then try them one-after-the-other until it gets a response that PROVES you are indeed a valid user of the repository you're accessing. KEY: The Host entry in SSH config can be be told to offer a different identities even to the same domain (e.g. github.com)

1. Choosing what identity keys are offered

The available key pairs ("identities") are stored in ~/.ssh and referred to in the ~/.ssh/config settings file. However, it is the helper program ssh-agent that does the actual communication with a remote server to authenticate you before your git commands are allowed to run. This helper program usually runs automatically on computer startup.

When using the command line, ssh-agent is initialized with a set of keys using the ssh-add ~/.ssh/keyname command. This temporarily adds the identities you want to authenticate with. You also use this command to remove all identities with ssh-add -D to start fresh. Finally, you can use ssh-add -l to list all the currently loaded identities. To connect to different remote hosts with SSH, you use these commands to load/unload the proper identities for each.

Remember, though, that I'm choosing convenience over security. There are several shortcuts you can add by adding Host entries to the ~/.ssh/config file.

2. Setting the correct Host options

The Host entries in ~/.ssh./config can be used as an address in an SSH connection string. This is super important, because it allows you to specify a particular IdentityFile to go with a particular HostName. Consider these two examples defined in the same config file:

# address one
Host github.com 
  HostName github.com
  User git
  PreferredAuthentications publickey
  IdentityFile /Users/dsri/.ssh/id_rsa

# address two
Host github.com-extra  
  HostName github.com
  User git
  PreferredAuthentications publickey
  IdentityFile /Users/dsri/.ssh/github-ed25519

Note: At the time of this writing, the SourceTree git client adds its own entries to ~/.ssh/config so you should review them, particularly when setting a git remotes for the first time with a new repo.

It is through the use of Host and the IdentifyFile that you create an alias for a particular connection. Note how both Host entries use the same user and HostName, but have different IdentityFile settings.

You can use the command ssh -Tvvv <Host> to debug the connection authentication and see which identities are being offered and tested; if you don't see the one you expect, your config might be goofed up. Remember, <Host> refers to one of the Host entries in ~/.ssh/config!

  ssh -Tvvv github.com
  ssh -Tvvv github-extra.com

For further convenience, you can add the IdentitiesOnly, UseKeychain, and AddKeysToAgent settings (the last tow are MacOS only):

Host github.com-second
  HostName github.com
  User git
  PreferredAuthentications publickey
  IdentityFile /Users/dsri/.ssh/id_rsa
  IdentitiesOnly yes    # use only IdentityFiles in this entry
  UseKeychain yes       # macos: store any passwords in apple keychain
  AddKeysToAgent yes    # macos: load this key to ssh-agent on startup

The MacOS settings are conveniences that would be handled in Linux or Windows differently.

3. Setting Git Repository Remotes

If you are using an SSH Host alias that is NOT just "github.com" or "gitlab.com", you MUST use change your remote URL accordinglycough SourceTree . For example, say you create a new repo on GitHub and want to add it as a remote to a local repo. GitHub provides instructions after you create the enw repo that look like this:

$ git remote add origin git@github.com:dsriseah/myrepo.git
$ git push -u origin main
$ git remote -v

Note: 'origin' is the name of the remote, and is an arbitrary convention much like the use of master are used to refer to the "main branch" of a repo.

For remote add origin line, the host here does not refer to the example config just above it. If my github.com-second host alias is not the one I used with the github account, authentication will fail! The remote add has to be changed to:

$ git remote add origin git@github.com-second:dsriseah/myrepo.git

Command Cheat Sheet

Using ssh-agent and ssh-add

eval ssh-agent          # run the ssh agent if it's not already running 

ssh-add ~/.ssh/keyFile  # load identity for 'keyFile'
ssh-add -l              # list loaded identities
ssh-add -D              # unload all identities

Troubleshoot SSH connection

ssh -T domain.com       # test if can connect with current config
ssh -Tvvvv domain.com   # add a ton of debug output