An interesting challenge awaits teams that have unique security or service needs with their source control. If you have a single source control system or a single GitHub environment, then everything just works. You have one credential to work with, so it is generally seamless. When you have multiple environments, then it becomes more complex. For example, what if you are using GitHub Enterprise Cloud (GHEC) for your public contributions, Enterprise Managed User (GHEC-EMU) for private company activities, and GHAE or Enterprise Server for sensitive projects? This is where we can take advantage of configuration settings with SSH and Git.
To be clear, this is just one way to make this work. There are actually several approaches that can work well
First, you’ll want to create an alias in SSH (
~/.ssh/config) for each environment that requires a distinct credential. The credential will be configured with a
HOST value that you will use to identify the credentials to be used. Think of this value as an alias that will reference the login credentials and the actual server hostname. For example, I might configure four environments this way:
HOST ghec AddKeysToAgent yes HostName github.com IdentityFile ~/.ssh/ghec_rsa IdentitiesOnly yes User git HOST ghae AddKeysToAgent yes HostName myco.ghe.com IdentityFile ~/.ssh/ghae_rsa IdentitiesOnly yes User git HOST ghes AddKeysToAgent yes HostName ghes.myco.corp IdentityFile ~/.ssh/ghes_rsa IdentitiesOnly yes User git HOST emu AddKeysToAgent yes HostName github.com IdentityFile ~/.ssh/emu_rsa IdentitiesOnly yes User git
Let’s quickly explore these settings:
- This configures an alias that we’ll use to represent each environment that requires a unique credential. It creates a mapping based on the provided host name. When this name is referenced, the associated configuration will be used.
- Indicates whether the key and passphrase should be automatically added to the
ssh-agentto minimize the number of times a password is required.
- The fully-qualified domain name for the Git host. You may have multiple entries that have the same value (for example, using github.com for both EMU and non-EMU instances).
- The file path to your SSH private key
- Only use the provided identity file, even if there might be other identities available
- The user name for authentication. With GitHub, this will always be
git, so we can hardcode that value.
With that configured, we can now move on to configuring Git.
.gitconfig file allows us to bind things together. We take advantage of the
url..insteadOf command to create our mappings. This works by matching the start of the connection URL and replacing it with something else. For example, we might use this for the GHAE configuration:
[url "ghae"] insteadOf = firstname.lastname@example.org
If a Git clone uses a URL that starts with
email@example.com (for example
firstname.lastname@example.org:some-org/my-repo.git), this entry will instruct Git to replace that string with the
url value —
ghae. The resulting URL,
ghae:some-org/my-repo.git, will be stored as the
origin for the repo. Next, this modified URL will be processed to create an SSH connection. Since the new host name appears to be
ghae, it will match the second entry we created in the SSH
config. This entry specifies that the hostname should be
myco.ghe.com and the user should be
git. This effectively causes it to create an SSH connection to retrieve
email@example.com:some-org/my-repo.git. This approach also works with GitHub Enterprise Server (GHES).
If we only have a single GitHub.com enterprise, we can use the same approach. If we have both an EMU and a non-EMU enterprise, we need to do something extra. Because both of those environments have the same host name, that’s not enough to guarantee the match. In this case, we might also include the organization name as part of the matching configuration. For example, if we have this configuration:
We might configure this as follows:
[url "ghec:ken-notemu"] insteadOf = firstname.lastname@example.org:ken-notemu [url "emu:ken-emu"] insteadOf = email@example.com:ken-emu
By including the organization name in the match, we can properly select the credential we want to use. In order for this to work, it’s important to make sure that the organization name is included in both the
The first URL
firstname.lastname@example.org:ken-emu/internal.git is altered by the Git config to be
ghec:ken-emu/internal.git. The SSH config matches the host,
ghec, and finds the appropriate configuration. It replaces
ghec with the actual user and host name, essentially recreating the original URL. It will then use the specified identity key to establish the connection. The second URL has the host specified as
emu, so it will map to a different identity and the same host name, github.com.
It is possible to alter the
.gitconfig from the Git command line. The command is simply:
git config --global url.NEW_URL.insteadOf ORIGINAL_URL. For example, the configurations above are:
1git config --global url.ghae.insteadOf email@example.com 2git config --global url.ghec:ken-notemu.insteadOf firstname.lastname@example.org:ken-notemu 3git config --global url.emu:ken-emu.insteadOf email@example.com:ken-emu
There’s a lot more you can do with these files to configure your environments, and a lot of ways that they can be used to select an SSH identity for connection. There are other features which can support more complex needs. For example, if multiple
insteadOf strings match the URL, the longest match is used. This could enable you to create catch-all patterns for common cases. Similarly, the SSH config has more complex matching options available (for example,
It’s also important to consider that each approach has tradeoffs. Each approach will have strengths and weaknesses. In this approach, there are two important considerations:
- If you’re using both EMU and non-EMU instances of GitHub.com (or needing two different identities for the same host name), you will need a mapping that includes more of the URL (such as the organization name) to ensure the right credential is selected.
.gitconfigconfiguration modifies the
originfor each matching cloned repo, changing the original URL that was provided.
With that in mind, try a few different approaches to see what works best for you!