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
SSH Configuration
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:
- HOST
- 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.
- AddKeysToAgent
- Indicates whether the key and passphrase should be automatically added to the
ssh-agent
to minimize the number of times a password is required.
- HostName
- 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).
- IdentityFile
- The file path to your SSH private key
- IdentitiesOnly
- Only use the provided identity file, even if there might be other identities available
- User
- 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.
Git
The .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 = [email protected]
If a Git clone uses a URL that starts with [email protected]
(for example [email protected]: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 protected]: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:
Environment | URL |
---|---|
EMU | [email protected]:ken-emu/internal.git |
Non-EMU | [email protected]:ken-notemu/public.git |
We might configure this as follows:
[url "ghec:ken-notemu"]
insteadOf = [email protected]:ken-notemu
[url "emu:ken-emu"]
insteadOf = [email protected]: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 url
and insteadOf
fields.
The first URL [email protected]: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.
Automation
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 protected]
2git config --global url.ghec:ken-notemu.insteadOf [email protected]:ken-notemu
3git config --global url.emu:ken-emu.insteadOf [email protected]:ken-emu
Next Steps
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, MATCH
).
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.
- The
.gitconfig
configuration modifies theorigin
for 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!