I recently had to set up a new laptop and one of the things I wanted was the ability to have both my work and personal GitHub accounts set up on one Linux environment, (more specifically WSL). I also wanted to ensure that at least my personal commits were signed using a GPG key
I discovered quite a few complications in this process so I wanted to include some documentation on how I achieved this. If you are an ssh or git expert then some of this might be obvious but otherwise hopefully it will be helpful!
Git your configuration on
The first step was to get my git configuration set up correctly.
Let me Google that for you
My primary resources for how to get the multiple users set up was a combination of the following two links which were really useful but I got caught in a few issues on the way, (not necessarily the fault of the posts through).
The first thing I liked from the GitGuardian link was having two separate paths for Work and Personal projects using two separate GitHub identities.
Based on their instructions, I created “Work” and “Personal” folders within my WSL home folders (actually soft-links to other locations), created the relevant ssh keys and then the relevant configuration files. Obviously I also had to copy my ssh public keys into the relevant page of the GitHub UI for each account.
Git configuration files
Here are the git configurations I ended up with. I will add some more information about parts them below but they are mostly based on the links above. Note that the heading within which each configuration line sits is important.
# ~/.gitconfig [includeIf "gitdir:~/Personal/"] path = ~/Personal/.gitconfig.pers [includeIf "gitdir:~/Work/"] path = ~/Work/.gitconfig.work [core] excludesfile = ~/.gitignore
# ~/Work/.work.gitconfig [user] email = email@example.com #redacted name = Josh Grossman [github] user = joshacmesecurity #redacted [core] sshCommand = ssh -i ~/.ssh/joshacme_key #redacted
# ~/Personal/.pers.gitconfig [user] email = jZZZZZZZ6@hotmail.com #redacted name = tghosth signingkey = 5517ZZZZZZZZZZZZ2A9 #redacted [github] user = tghosth [commit] gpgsign = true [core] sshCommand = ssh -i ~/.ssh/jZZZZZZZ6_key #redacted
There were a couple of issues at this stage that had me scratching my head for a while
Wrong file names
I had used dots in my file names for the personal and work configuration files whereas the main configuration file from the GitGuardian link used hyphens. This took me longer than I care to admit to figure out… This was certainly a PEBKAC issue.
Messed up double quotes
I was getting parsing errors for a very long time on my main configuration file. I tried all sorts of things including not using soft-links but using the full paths from the root instead. After much faffing, I realised that when I copied the same configuration files from the GitGuardian site, it had used “curly double quotes” instead of the regular double quotes and this was tripping up git 🤦♂️.
I wasn’t previously familiar with using ssh authentication with GitHub so this caused me some challenges as well. I will paste in an example of ssh git configuration file first and then walk through this aspect.
It is possible that there are other/better ways to do this so please free to tell me if you have ideas 😀.
SSH Configuration file
Here is the ssh configuration I ended up with. I will explain some of the key aspects further down.
Host ghpers HostName github.com AddKeysToAgent yes IdentityFile ~/.ssh/jZZZZZZZ6_key User jZZZZZZZ6@hotmail.com Host ghwork HostName github.com AddKeysToAgent yes IdentityFile ~/.ssh/joshacme_key User firstname.lastname@example.org
Making ssh work with git
I was used to using HTTPS for cloning repositories and personal access key authentication to push so this was also a bit of a learning curve. My main (eventual) discovery was that despite the use of the “sshCommand” parameter in the previous git configuration files, this is only used for “git fetch” and “git push” operations (not clone) and only when the repository’s origin is set using the SSH syntax rather than the HTTPS.
After some experimentation, I found a few possible ways to clone the repository in a way that would make this all work. In the examples below I have used my personal identity but I could also have used my work identity and cloned to the relevant Work directory.
Option 1 – Without explicitly choosing an account
It is possible to start by cloning the repository using the regular HTTPS clone mechanism within the “Personal” directory. I can copy the clone command straight out of the GitHub UI:
cd ~/Personal git clone https://github.com/tghosth/testclone
I now have the repository cloned locally but I now need to tell git to use the SSH mechanism instead of the HTTPS mechanism. I can do this as follows:
cd ~/Personal/testclone git remote set-url origin email@example.com:tghosth/testclone.git
Note that at no point did I need to specify the specific identity to use so maybe this could even be automated after a clone operation with some sort of hook…
Either way, if I now do a push, it asks me for the correct key passphrase and works successfully
josh@LAPTOP-ZZZZZZZZ:~/Personal/testclone$ git push -v Pushing to firstname.lastname@example.org:tghosth/testclone.git Enter passphrase for key '/home/josh/.ssh/jZZZZZZZZ6_key': To github.com:tghosth/testclone.git = [up to date] main -> main updating local tracking ref 'refs/remotes/origin/main' Everything up-to-date
My main concern about this approach is that I worry how effective it will be if there are multiple remotes or branches or something. The other disadvantage is that it is a two step process and also it will not work smoothly for private repositories.
Option 2 – Doing a clean ssh clone choosing the relevant account
The other option is a one step but I need to mess with the original clone command. When I copy the clone command for an ssh clone, it will look like this:
cd ~/Personal git clone email@example.com:tghosth/testclone.git
However, before I use it, I need to change it to tell the clone command which identity I want to use as otherwise it will return me errors. I can use the value in the Host field of the ssh configuration file above for this so the command will change to as follows:
cd ~/Personal git clone git@ghpers:tghosth/testclone.git
You can see above that “ghpers” was the Host I gave to my personal key in the configuration file.
I can then run this and git will know which SSH identity to use for the clone operation. Once I start doing fetches and pushes, it will be using the identity configured in the relevant git configuration file for this folder tree (.pers.gitconfig).
I like this method because it is a single command. Whilst I have to manually change the clone command rather than just copying it from the GitHub UI, I only have to do that once and then everything works. It will also work smoothly for private repositories.
Option 3 – Using ssh-agent
I actually figured this option out whilst writing this blog post 🙃.The freecodecamp link sort of alludes to this but not explicitly for easily cloning the repository in the first place.
The ssh-agent program temporarily keeps ssh private keys in memory and one advantage is that you only have enter the passphrase once per session rather than on every use individually. Without ssh-agent, for every git clone, git fetch and git push, I would need to enter the passphrase every single time.
However, another advantage for our use case is that when the key is held in ssh-agent and I do a git clone via ssh, the ssh operation will automatically use that key without needing to be told.
You can see this in the terminal fragment below. I start ssh-agent running in my current terminal, (see this explanation of why it needs to be done using eval). I then add the identity (my personal identity in this case) I want to use to the agent.
josh@LAPTOP-ZZZZZZZZ:~/Personal/testclone$ eval `ssh-agent -s` Agent pid 841 josh@LAPTOP-ZZZZZZZZ:~/Personal/testclone$ ssh-add -l The agent has no identities. josh@LAPTOP-ZZZZZZZZ:~/Personal/testclone$ ssh-add ~/.ssh/jZZZZZZZZ6_key Enter passphrase for /home/josh/.ssh/jZZZZZZZZ6_key: Identity added: /home/josh/.ssh/jZZZZZZZZ6_key (jZZZZZZZZ6@hotmail.com)
I can then run git clone in my Personal directory without changing the ssh path I copied from the GitHub UI. Note that I used a private repo in this example just to check it would work. It automatically uses my “personal” identity held in ssh-agent (as otherwise the clone would have failed).
josh@LAPTOP-ZZZZZZZZ:~/Personal$ git clone firstname.lastname@example.org:tghosth/testclonepriv.git Cloning into 'testclonepriv'... remote: Enumerating objects: 4, done. remote: Counting objects: 100% (4/4), done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 Receiving objects: 100% (4/4), 4.50 KiB | 1.50 MiB/s, done. josh@LAPTOP-ZZZZZZZZ:~/Personal$ cd testclonepriv/
I can then do a push operation and it will be using the identity configured in the relevant git config file for this folder tree (.pers.gitconfig). It doesn’t need a passphrase because the key is active in ssh-agent.
josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$ git push -v Pushing to email@example.com:tghosth/testclonepriv.git To github.com:tghosth/testclonepriv.git = [up to date] main -> main updating local tracking ref 'refs/remotes/origin/main' Everything up-to-date josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$
This option is nice because it also solves having to enter the passphrase every time. Obviously there are implications of using ssh-agent but for a single user local Linux machine it seems like a reasonable solution. If you are jumping between work and personal frequently, it might get fiddly but on the other hand it is most important for the initial clone operation.
GPG Signing Commits
This was more straightforward overall and GitHub has some good documentation for how to get it set up. At the time of writing, GitHub does not support commit signing using an SSH key so you have to set up a GPG key separately. You will notice that in my “.pers.gitconfig” file above I have user.signingkey and commit.gpgsign configured. (I am not currently using this for my work identity.)
Using the documentation, I was able to set this functionality up quite easily but once I had it set up, it kept failing with the following error:
josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$ git commit --allow-empty -m "test sign" error: gpg failed to sign the data fatal: failed to write commit object
After a painfully long time, I finally found a hint in a blog post somewhere that I needed to run the following command in my terminal first:
With that command run, the commit would pop up a GPG window in the terminal prompting me for my GPG passphrase (obviously different to my SSH passphrase) and would then create and sign the commit.
josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$ export GPG_TTY=$(tty) josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$ git commit --allow-empty -m "test sign" [main 169a1d7] test sign josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$
I can use “git log” to show the successful signature.
josh@LAPTOP-ZZZZZZZ:~/Personal/testclonepriv$ git log --show-signature commit 169a1d725d2ZZZZZZZZZZZZZZZZZZZZZc565ff3d5 (HEAD -> main) gpg: Signature made Wed Jan 26 09:42:20 2022 IST gpg: using RSA key 487BBZZZZZZZZZZZZZZZZZZZZZZZZZFB6E4682A9 gpg: Good signature from "Josh Grossman (tghosth) <jZZZZZZZZ6@hotmail.com>" [ultimate] Author: tghosth <jZZZZZZZZ6@hotmail.com> Date: Wed Jan 26 09:42:20 2022 +0200 test sign josh@LAPTOP-ZZZZZZZZ:~/Personal/testclonepriv$
Thanks for reading, I hope this is a useful summary and makes it easier for you to set up this functionality. If you have comments or feedback, the easiest option is to reach out to me on Twitter at @JoshCGrossman!