Multiple Git Identities & SSH

Having multiple Git configurations is inevitable for the busy developer.  It is likely a good thing (you are using version control in multiple settings).  However, setting your development workstation for multiple git accounts can be difficult.  Also, if you want to use Git with ssh, you need to be able to differentiate multiple ssh accounts from each other.  Furthermore, you might desire to use multiple Git platforms at once (public SaaS GitHub, GitHub Enterprise, Git-o-Lite, or Bit Bucket) with each one having both personal and professional repos and accounts.  There are several concepts you need to understand before getting started with using multiple Git identities with ssh:
  • Git protocols
  • Ssh passwordless logins
  • Git remotes
Let’s talk about each of these with respect to what you need to know to get working.

Git Protocols:
Git can use multiple protocols for data transfer communications.  According to the official Git-scm documentation, there are 4:  local, HTTP, SSH, and GIT.  Local is only used, as the name implies, for local Git operations on local disk or even NFS or CIFS mounts.  So, our options for over the network/internet communications are the other 3.  Since the bare Git protocol does not offer much in terms of security (no authentication, no encryption), the optimal choices are HTTPS or SSH.  As the Git documentation mentions, there are some drawbacks to doing HTTPS (server setup, credentials caching and storage, etc), but it is certainly widely used because services such as GitHub make the server side part simple, leaving only the workstation side setup to deal with of which, for OS X users, the keychain makes things nice.  Finally, SSH is ubiquitous and provides good security.  When combined with password-less logins, SSH becomes very convenient as well.

SSH Passwordless Logins:
SSH provides great transport security as well as authentication.  Due to it’s use of public/private key pairs and a configuration file, you can create multiple SSH identities each of which can use their own public/private key pair.  By exchanging your public key with remote servers you connect to, combined with a tracking mechanism and file called known hosts which tracks machine network addresses for added security, SSH allows you to safely login without passwords.  The public/private key pair encryption with the remote host is superior security to text passwords.

Git Remotes:
Git uses the concept of a remote to track the location of the central repository per project.  While the beauty of Git is that the project repos are distributed to all contributors on a project, the best practice is the use a central server for which everyone pushes their changes back for easier tracking.  This central source server in Git is called “origin” by convention.
When you clone a repo on a remote Git server, there is a git dotfile directory created with a config file inside.  This config file tracks the remote origin URL used to locate the Git server.  This configuration file can be edited to provide the functionality you need even after you clone a repo.

Configuring Multiple Git Identities with SSH
Assumptions:
  • You have already created SSH keys and organized them in a way you like.
    • Note:  I prefer to have different SSH keys for personal versus professional.  I also find using a naming scheme other than the default helpful in identifying which keypairs are for which use.  I realize this might be considered overkill by some but I prefer this and it also helps make the example more illustrative by showing different keys with different accounts.
  • You have already setup GitHub public, GitHub Enterprise, BitBucket, Stash, Git-o-lite, or GitLab accounts and added your desired SSH public keys to them.
  • You are using OS X or Linux.
    • No offense to my Windows buddies here, but things aren’t the same config wise on Windows and I wanted to keep this post simple.
  • For purposes of this example, I will use multiple accounts in GitHub public, GitHub Enterprise, BitBucket, and Git-o-lite.
    • Note:  this SSH config file is abridged to just show the Git relevant portions.
Steps:
1) Create a SSH Config file for configuring multiple SSH identities.
cd ~/  
touch .ssh/config
2) Open the new config file and edit it as follows (customizing for your accounts/keys/etc):
     
vim ~/.ssh/config
#######################################################
# CUSTOM .SSH CONFIG FILE FOR MULTIPLE SSH IDENTITIES #
#######################################################

#SSH Github Personal Public Account
#Maps to a per repo .git/config email setting to work
Host github.com-myuserid-personal
HostName github.com
User git
IdentityFile ~/.ssh/id_myuserid-personal@example.com
IdentitiesOnly yes
#LogLevel DEBUG3

#SSH Github Personal Public Account GISTS
#Maps to a per repo .git/config email setting to work
Host github.com-myuserid-personal
HostName gists.github.com
User git
IdentityFile ~/.ssh/id_myuserid-personal@example.com
IdentitiesOnly yes

#SSH Github Professional Public Account
#Maps to a per repo .git/config email setting to work
Host github.com-myuserid-professional
HostName github.com
User git
IdentityFile ~/.ssh/id_myuserid-professional@example.com
IdentitiesOnly yes
#LogLevel DEBUG3

#SSH Github Personal Public Account GISTS
#Maps to a per repo .git/config email setting to work
Host github.com-myuserid-professionall
HostName gists.github.com
User git
IdentityFile ~/.ssh/id_myuserid-professional@example.com
IdentitiesOnly yes

#SSH Bitbucket Personal Public Account
#Maps to a per repo .git/config email setting to work
Host bitbucket.org
HostName bitbucket.org
User git
IdentityFile ~/.ssh/id_myuserid-personal@example.com
IdentitiesOnly yes
#LogLevel DEBUG3

#SSH Github Enterprise Account
#Maps to a per repo .git/config email setting to work
Host github.example.com
HostName github.example.com
User myldapuserid
IdentityFile ~/.ssh/id_myuserid-professional@example.com
IdentitiesOnly yes
#LogLevel DEBUG3

#SSH Private Git-o-lite
#Maps to a per repo .git/config email setting to work
Host gitolite.example.com
HostName gitolite.example.com
User gitolite
IdentityFile ~/.ssh/id_myuserid-professional@example.com
IdentitiesOnly yes


3) Finally, you will need to modify the .git/config file in each repo you have cloned and will clone to match the name you specified in the Host section of the .ssh/config file for the URL in the remote “origin” section.
This is because Git is using the Hostname in the URL to match the Git hostname for the SSH key.  Think of this as kind of a DNS lookup by Git (probably a poor analogy) to locate the SSH hostname.
Here are some examples to map to the SSH config file above.  This is especially helpful when you have multiple user accounts on the same Git provider platform (such as GitHub) and need to differentiate user accounts (and associated SSH keys) when the hostname is the same (github.com, etc).
Examples:
cat ~/git-repos/github-public/personal/project1/.git/config

--- snipped ---

[remote "origin"]
    url = git@github.com-myuserid-personal:myuserid-personal/project1.git
    fetch = +refs/heads/*:refs/remotes/origin/*

--- snipped ---


cat ~/git-repos/github-public/professional/project2/.git/config

--- snipped ---

[remote "origin"]
    url = git@github.com-myuserid-professional:myuserid-professional/project2.git
    fetch = +refs/heads/*:refs/remotes/origin/*

--- snipped ---


cat ~/git-repos/bitbucket/personal/project3/.git/config

--- snipped ---

[remote "origin"]
    url = git@bitbucket.org:myuserid-personal/project3.git
    fetch = +refs/heads/*:refs/remotes/origin/*

--- snipped ---


cat ~/git-repos/github-enterprise/professional/project4/.git/config

--- snipped ---

[remote "origin"]
    url = git@github.example.com:myuserid-professional/project4.git
    fetch = +refs/heads/*:refs/remotes/origin/*

--- snipped ---


cat ~/git-repos/git-o-lite/professional/project5/.git/config

--- snipped ---

[remote "origin"]
    url = git@git-o-lite.example.com:myuserid-professional/project5.git
    fetch = +refs/heads/*:refs/remotes/origin/*

--- snipped ---

Closing thoughts:
The need to edit your .git/config files on new or cloned repos when the remote origin path needs modification to match your associated SSH config is a little annoying.
I am sure there is a way to automate this to match the paths setup in the .ssh/config file, but I have not embarked to do this just yet.
The problem is you would need to define what SSH identity you want to use by name and then do a post Git Clone “hook” of sorts.  Git clone post hooks don’t exist and so you are left with using some form of git template combined with a script which updates the remote origin URL with the SSH identity Host identity path you desire.  I suppose you could do a sed operation via  bash script executed from some git init customizations combined with a git clone —template, but remembering all these setup steps steps made me just opt for using the simple git command line manually after I clone.
(Note: for all existing cloned git repos you have, you’ll need to go into each one and update the remote origin URL in the .git/config file to take advantage of any custom SSH host name path you are using).
For now, you can use the following command to update the per repo .git/config remote origin URL path:
git remote -v

git remote set-url origin git@github.com-myuserid-personal:myuserid-personal/project1.git

git remote -v

Troubleshooting:
Note that in the SSH config file examples I provided, there is a line saying #LogLevel DEBUG3.  This is a great way to see live SSH authentication debugging information as you try to authenticate to each remote GIT server.  I left them remarked out just so I could turn them on if I ever needed to troubleshoot a connection.  As you learn how to use these SSH identity approaches to multiple GIT identities, you’ll find this debug command super helpful.

Sources:
https://git-scm.com/book/ch4-1.html
http://ricardianambivalence.com/2013/09/22/github-for-work-and-play-multiple-accounts/
http://nerderati.com/2011/03/17/simplify-your-life-with-an-ssh-config-file/
http://stackoverflow.com/questions/10228065/git-hooks-is-there-a-clone-hook
http://stackoverflow.com/questions/2141492/git-clone-and-post-checkout-hook/2141577#2141577
http://stackoverflow.com/questions/16363460/set-up-a-default-directory-structure-on-git-init
http://monkeypatch.me/blog/mixing-professional-and-personal-git-configurations.html



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s