Avenue Code Snippets

Safely Working with Multiple Git Providers

Written by Rafael Romão | 10/14/20 5:00 PM

Developers who work with more than one Git provider need to be aware of this common pitfall and know how to avoid it.

The Problem

Most developers who use Git on a daily basis are used to the commands below:


git config --global user.name "Your Name" git config --global user.email "youremail@yourdomain.com"

What they do is to write the user name and email that must be used on your commits to a configuration file, ~/.gitconfig.

After the commands above, the file will look like this:

[user]
name = Your Name
email = youremail@yourdomain.com

And when you issue the command below, your commit will have the name and email configured in this file in the author property:

git commit -m “my commit message”

You can explicitly set the author in a git commit like this, though:

git commit -m “my commit message” --author=” Your Name 
<youremail@yourdomain.com>”

And Git will ask you for this information the first time you try to commit without having this .gitconfig file configured:

*** Please tell me who you are.

Run:

git config --global user.email "you@example.com"
git config --global user.name "Your Name"
to set your account's default identity.
Omit --global to set the identity only in this repository.
fatal: no email was given and auto-detection is disabled

Now suppose that, like me, you work as a contractor consultant responsible for pushing changes to private repositories from client organizations. In this case, you will have to use an email provided by this client organization, which will not be the same one you use in your public GitHub account, for example, and most of the time these organizations will have a confidentiality contract with you or your employer that requests you to not disclose any information about what you are working on or who you are working for.

That said, what would happen if you first configure your .gitconfig like this:

[user]
name = Your Name
email = youremail@yourclient.com

And then, in your free time, you use the same computer to work on a public GitHub project and push some changes there?

Yes, I’ve heard of that happening. The Git commit worked without any errors or warnings, and the client email ended up in a public GitHub repository. And it turns out the client had a scan policy that found it there and sent an alert to the person who pushed the commit with the wrong email.

But why did that happen? The reason that the wrong email was accepted without any authentication error is that, in this case, the authentication happened through an SSH key, so a username and password is not requested to push. This is the best way to authenticate to Git, but if the same SSH key is used by multiple Git providers, or if multiple keys are configured but the mapping between the SSH key and Git provider is not done properly, this kind of mistake can happen.

How Can We Avoid This Problem?

First, configure multiple SSH keys, one for each Git provider:

GENERATING AND REGISTERING THE SSH KEYS

In summary, follow the instructions given on this page and give different file names for each key.

Here I will demonstrate how to do it for two GitHub providers, the public one and the enterprise one, as an example.

❯ cd ~/.ssh
❯ ssh-keygen -t rsa -b 4096 -C "myemail@gmail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/rromao/.ssh/id_rsa): 
id_rsa_gh
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_gh.
Your public key has been saved in id_rsa_gh.pub.
The key fingerprint is:
SHA256:4t6aVVnyuXKtKLiFLc4slxblBEKeA/iirns9bX2gFUA myemail@gmail.com
The key's randomart image is:
+---[RSA 4096]----+
| .....E          |
|.  o..o          |
| .  +. o  . .    |
|. .  .  +  = .   |
|..    .+S.o o    |
|.    ..++.   o   |
|.  . .+B+.. o .  |
| .. +=O*o .+ .   |
|+o   *Oo.o. .    |
+----[SHA256]-----+

Copy the SSH public key to the clipboard:

❯ pbcopy < id_rsa_gh.pub

Create a new SSH Key on GitHub and paste the pub key there:

Now I repeat the process with the GitHub enterprise account provided by my client.

❯ ssh-keygen -t rsa -b 4096 -C "myemail@myclient.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/rromao/.ssh/id_rsa):
id_rsa_client
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_client.
Your public key has been saved in id_rsa_client.pub.
The key fingerprint is:
SHA256:XHCX83jUxtpWohwnDmiKQfPUDl1uTxY/F+k9NPviouk 
myemail@myclient.com
The key's randomart image is:
+---[RSA 4096]----+
|   .o .o.oo o. +.|
|    .+. =+..=o++*|
|     o.=  ++oX==*|
|    . ...o += +*+|
|        S   .. .o|
|              . .|
|             . . |
|           .. .  |
|         .E. .   |
+----[SHA256]-----+

Copy the SSH public key to the clipboard:

❯ pbcopy < id_rsa_client.pub

The address to register a new SSH Key in the GitHub enterprise is the same, but with a different base URL.

Mapping the Keys to the Git Providers

Mapping each key to a different Git provider is how we tell Git which key to use to authenticate on each Git provider. This will allow us to git push without being prompted for an authentication, but this will not avoid the wrong email problem. But we will get there soon.

To map different keys to different Git providers, we need to edit the file ~/.ssh/config with entries like these:

# Client
Host clientdomain.com
HostName clientdomain.com
User git
IdentityFile ~/.ssh/id_rsa_client
AddKeysToAgent yes
UseKeychain yes
# Public
>Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_rsa_gh
AddKeysToAgent yes
UseKeychain yes

At this point, if we do a git config --global user.email "myemail@myclient.com" and leave it this way, we are doomed. All our commits to our public GitHub will be pushed with our client’s email.

The Solution

The solution to this problem is actually quite simple. We just need to create different .gitconfig files, register our user information in there, and assign each one to a different folder hierarchy.

In my case, I have a folder named ~/Projects where I keep all my Git projects. In there, I have the folders Client, Company, and Personal. Client folder contains all my, well, client projects, while in the Company folder I keep my Avenue Code internal projects, and in the Personal folder I keep the ones you can find in my public GitHub.

An important detail here is that I named the Client folder as Client and not with the name of my client. This way, I have another guarantee that I will not accidentally leak the name of my client in case I want to push my Git configurations to a public Git repository, for example.

Now, in each of these three folders, I will create a file named .gitconfig with contents like the following:

❯ code ~/Projects/Personal/.gitconfig
[user]
email = myemail@gmail.com
❯ code ~/Projects/Company/.gitconfig
[user]
email = myemail@avenuecode.com
❯ code ~/Projects/Client/.gitconfig
[user]
email = myemail@myclient.com 

But that is not all. There is one final step. I need to inform Git about these files, and I can do that by editing my ~/.gitconfig file like this:

[includeIf "gitdir:~/Projects/Personal/"]
path = ~/Projects/Personal/.gitconfig
[includeIf "gitdir:~/Projects/Company/"]
path = ~/Projects/Company/.gitconfig
[includeIf "gitdir:~/Projects/Client/"]
path = ~/Projects/Client/.gitconfig
[user]
name = Rafael Romão
useConfigOnly = true
[pull]
rebase = true 

This will tell Git to get the missing configs, in this case the user.email, from the referenced files. Also, since there is no email informed here, if we try to git commit anything in a folder hierarchy that is not mapped, Git will present that message informing us that it does not know which user email to use. It is important that we do not inform a global user.email after that, since we might end up having to clone a Git repository outside these mapped folders, and if there is no global user.email, we will not accidentally use it.

That is what I have for today. If you work with more than one Git provider, I strongly recommend that you review your Git configs and ensure you will not have this kind of problem. Sadly, when this happens, we usually do not notice until a few months have passed.

If you have any questions or other recommendations on this topic, leave us a comment below.