Introduction

I wrote the original version of this blogpost almost a year ago when I got a new laptop.

The post offered a few options although in the end I only used the 3rd option.

Also, during 2022, git added support for SSH commit signing and so did GitHub which significantly simplifies the process of signing commits if you are already authenticating using SSH.

In this updated post, I want to summarize how to use this new functionality in the context of having multiple GitHub accounts on a single Linux/WSL machine.

Please read it carefully as there are some fixes and some updated gotchas! 😀

Git your configuration on

Git the right version number

Note that SSH commit signing is only supported from version 1.34 and higher of git. Unhelpfully, the Ubuntu package repositories in my WSL instance do not go any higher than 1.25 🤦‍♂️.

As such, I had to follow the instructions in this blogpost to get the most recent version of git installed for my WSL machine.

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.

If you want to sign using the same SSH key, note that you will need to upload it twice. Once as an authentication key and once as a signing key.

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 folder 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 = josh@acmesecurity.com #redacted email address
name = joshacmesecurity #redacted username
signingkey = ~/.ssh/joshacme_key.pub  #redacted path to public key
 
[github]
user = joshacmesecurity #redacted username
 
[gpg]
format = ssh #specifies that we want to use ssh signing

[commit]
gpgsign = true #should force signing on commits

[core]
sshCommand = ssh -i ~/.ssh/joshacme_key #redacted path to private key
# ~/Personal/.pers.gitconfig
 
[user]
email = jzzzzzzzzz@hotmail.com  #redacted email address
name = tghosth #username
signingkey = ~/.ssh/jzzzzzzzzz_key.pub #redacted path to public key
 
[github]
user = tghosth #username

[gpg]
format = ssh #specifies that we want to use ssh signing

[commit]
gpgsign = true #should force signing on commits

[core]
sshCommand = ssh -i ~/.ssh/jzzzzzzzzz_key #redacted path to private key

Git Gotchas

There were a couple of issues at this stage that had me scratching my head for a while

Browsing to the wrong path

Notice that I created softlinks at ~/Work and ~/Personal. Since this is WSL and I was mapping to my actual host, the actual links are something like /mnt/c/User/josh/Documents/……. etc etc. If you browse to the folder using that path, the config won’t work, it will only work if you browse via the softlink because that is how it is configured in the “includeIf” section.

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 🤦‍♂️.

SSH authentication and Signing

Making ssh work with git

I wasn’t previously familiar with using ssh authentication with GitHub so this caused me some challenges as well. During the course of the previous post I went through a few possible options but in the end I have only really used option 3 so I will focus on that version here.

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 during the clone operation.

As such, it is important to use the ssh link when cloning a repository which you want to authenticate to in this way. You can find the relevant link in the GitHub UI:

![](/assets/img/posts/2023/01/image.png?w=919)

Using ssh-agent to authenticate (and to sign)

I figured this option out whilst writing the previous 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. The same would go for each commit signing operation.

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)

You can also use the fast version:

josh@LAPTOP-ZZZZZZZZ:~/Personal/testclone$ eval `ssh-agent -s` && ssh-add ~/.ssh/jZZZZZZZZ6_key
Agent pid 841
Enter passphrase for /home/josh/.ssh/jZZZZZZZZ6_key:
Identity added: /home/josh/.ssh/jZZZZZZZZ6_key (jZZZZZZZZ6@hotmail.com)

I can then run git clone within 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 git@github.com: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 git@github.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.

Signing Commits

If you followed the instructions above, you should be basically done. No faffing around with GPG.

The configuration files tell git to force signing commits and which SSH key to use in each folder. You have already added the SSH key to ssh-agent so it is available for use so now it should happen seamlessly and if it is all working you should see the commits as verified in GitHub.

In closing

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 or on Mastodon @JoshCGrossman@Infosec.Exchange!

Updated: