How to sign your commits to GitHub using Visual Studio Code on Windows 10 and WSL2

27 August 2020

I have recently been spending more and more time doing web development on Windows 10 using WSL2 and Windows Terminal. I haven't completely moved away from MacOS but the experience is so enjoyable that I will likely switch to a Surface Book when it comes time to upgrade my MacBook Pro.

The one (minor) gripe holding me back was the inability to use signed commits from within Visual Studio Code when code, git and the gpg agent are running inside of WSL2. The issue? The passphrase prompt would not show up.

A pinentry to enter my passphrase for signed commits is displayed inside Windows Terminal if I set it up to display via TTY and do all of my git commands from within the terminal. But I couldn't find a way to display a pinentry within Windows 10 when using either the Visual Studio Code git interface or the built-in terminal. I don't mind using a separate terminal to run my git commands, but it's just so convenient to do it within my editor.

Thankfully, I have now solved it!

A brief intro about signed commits

Commits in git will be attributed to whatever values you have set against the name and email fields in your gitconfig. In theory, this would allow anybody to impersonate someone else when committing code by using the email address associated with the other person's GitHub account.

It may be worth noting that this only allows someone to change the author of commits. It won't grant access to any private repositories or elevate rights on GitHub.

Thankfully we can use GnuPG to sign our commits which in turn allows Github to verify that the changes were authored by us and not some imposter.

Verified commits on GitHub

A handy bonus of signing our commits is that GitHub will now also show a verified tag allowing everybody to see we're trustworthy.

Update Windows 10

The complete setup worked on a fresh install of the current stable version of Windows 10 (2004 Build 19041.450). I cannot confirm if this will work on an older version so you may be required to update.

You can check your version of Windows by hitting Win+R and running "winver"

Install GPG

Start by installing GPG4Win in Windows 10. GnuPG core is the only requirement but I usually also install Kleopatra - everything else can be left unchecked.

Despite most of the setup being inside WSL2 we still need a graphical pinentry app to be installed in Windows 10. Visual Studio Code is installed on the Windows 10 side and as such we need that graphical pinentry to enter a passphrase when committing code from the editor.

The rest of the commands in this tutorial will be run inside WSL2.

Open a Terminal into WSL2 and install the required packages. These packages were already installed for me and this is only here for completeness sake.

sudo apt-get install gpg gnupg gpg-agent

I use Ubuntu so your command may differ if your distribution of choice uses another package manager.

Update configuration

Inside WSL2, edit ~/.gnupg/gpg-agent.conf (if this file does not exist, create it).

~/.gnupg/gpg-agent.conf
default-cache-ttl 34560000
max-cache-ttl 34560000
pinentry-program "/mnt/c/Program Files (x86)/GnuPG/bin/pinentry-basic.exe"

The default-cache-ttl and max-cache-ttl is set to a really high value - 400 days to be precise. GnuPG will now cache our passphrase for that length of time or until we next restart our laptop. This saves us from being prompted for the passphrase every single commit given the default cache time is (if I recall) 10 minutes.

The pinentry-program configuration explicitly tells GnuPG to use the pin entry app installed in Windows 10 to prompt the user to enter the passphrase set when generating the GPG key pair (see next section).

It is recommended to explicitly restart the gpg agent to force these changes. Alternatively, restart Windows at this point.

gpgconf --kill gpg-agent

Generate GPG Key Pair

The generated GPG key pair consists of a:

  • private key - As the name suggests it should not be shared. This key is used to sign our commits.
  • public key - It is safe to distribute this key and we'll upload to GitHub later. GitHub uses the public key to verify the signature in our commits to correctly identify us as the author.

Run the following command to generate the actual key pair.

gpg --full-generate-key

You will be prompted to confirm certain values:

  • Key Type: Select RSA and RSA
  • Key Size: 4096
  • Expiry: Select a timeframe that is suitable to your requirements.

You will then be asked to enter your name and email address. It is important that you enter the same email address that you use with your GitHub account. Ensure that this email address is also set in your local git config (~/.gitconfig).

Create the passphrase for your GPG key pair

Finally, you will be asked for a passphrase. The passphrase will be used to encrypt the private key for added security. You will then be prompted to enter this passphrase when making a signed commit to decrypt the key.

Retrieve the unique signing key id

Now that our key pair has been created we need to retrieve our unique signing key id.

gpg --list-secret-keys --keyid-format LONG

Here is an example of the output you should see with fabricated ids.

-----------------------------------
sec   rsa4096/00EF4D3F22885E4B 2019-11-20 [SC]
      1234567890ABCDEF1234567890ABCDEF12345678
uid                 [ultimate] Rick Deckard <deckard@example.com>
ssb   rsa4096/ABC123D7FAA52318 2019-11-20 [E]

The key id is the 16-character sequence following rsa4096/ in the sec (secret) row at the top. In our example above, our signing key id would be 00EF4D3F22885E4B.

Alternatively, you could run gpg --list-signatures to find the same signing key id next to a sig tag.

Make note of this signing key as we will use it in the sections below.

Git Settings

If you haven't already done so, make sure to add the same email used in GitHub and your GPG key pair to your gitconfig.

git config --global user.email "deckard@example.com"

And if not yet done, be sure to add your name too.

git config --global user.name "Rick Deckard"

Configure git to use your GPG signing key.

git config --global user.signingkey 00EF4D3F22885E4B

You can conveniently set git to sign every commit by default. If you don't then you will need to include the -s flag in the command line for every commit you wish to sign.

git config --global commit.gpgsign true

There is also the option to automatically sign tags.

git config --global tag.gpgsign true

If you don't want to set these globally, you can also set it per repository using a --local parameter when in a git controlled project directory.

Add the GPG public key to GitHub

Firstly, retrieve and copy your GPG public key using your signing key. You will add this to your GitHub account so that it can verify every signed commit as actually coming from you.

gpg --armor --export 00EF4D3F22885E4B

Open a browser and navigate to your GitHub SSH and GPG Keys settings.

Click on the New GPG Key button and paste in your previously copied public key. This should be all the text including the start -----BEGIN PGP PUBLIC KEY BLOCK----- and end -----END PGP PUBLIC KEY BLOCK----- tags.

Enable commit signing in Visual Studio Code

We now need to set Visual Studio Code to append the -s flag to every commit that it runs from the built-in git user interface.

Thankfully, this is really easy to accomplish. Open the settings and search for "gpg" and check the box for Enables commit signing with GPG.

Alternatively, you can add this setting directly to your settings.json with the following line.

"git.enableCommitSigning": true

Make a few signed commits

I recommend running a test commit from both the command-line as well as via the Visual Studio Code interface.

Regardless of which method you decide to test first you will see a window pop up on your first commit now that GPG signing is setup. This is the pinentry and it is asking for your passphrase that you set when creating the key pair. The passphrase is used to decrypt your private key before git will complete the commit.

Enter your GPG passphrase

At this stage the passphrase will be cached after the first successful entry as per our ~/.gnupg/gpg-agent.conf settings.

Verify signed commits

Locally, you can verify that the commits were signed with your signature.

git log --show-signature -1

If you now push any of these changes to your remote in GitHub you will see a verified status next to each signed commit.

Remote Containers

As an added bonus I can also confirm the above setup worked when running commits from within the Microsoft pre-built VS Code Development Container Node image. This is helpful if you want to use Remote Containers for development and make signed commits from the Visual Studio Code integrated terminal.

The only extra requirement when using Remote Containers is to install socat on your WSL2 instance otherwise the git commits won't work.

sudo apt-get install socat

Keep in mind that the version of git that comes with the Microsoft development containers are a little behind so you might want to add an upgrade step to the Dockerfile. Or bring your own Dockerfile - you're the master of your own destiny.

Conclusion

And that’s it! Enjoy your newly verified status on GitHub.

Share this article
Was this post helpful?
Buy me a coffeeBuy me a coffee
About
I am a London based web developer currently focused on JavaScript, Node and React. Get in touch if you wish to compare eslint configs or debate the deadliest enemy in videogame history (spoiler alert: it was the first goomba).
© 39digits 2020