GitHub Codespaces is an instant, cloud-based development environment that uses a container to provide you with common languages, tools, and utilities for development.

When you commit a change with Git, it accepts as an author whatever value you want: you could claim to be whoever you want when you create a commit. Thus, when you see an unsigned commit, you have no guarantee that the author is really the person whose name is on the log and that the change you see is really what the author wrote. By signing your Git commits, you have the ability to prove that you were the author of a specific code change, and you can ensure that no one can modify your commit or its metadata. Attacks on the software supply chain are getting more common, and their potential consequences have become more dangerous.

You can allow GitHub to automatically use GPG to sign commits you make in your codespaces, so other people can be confident that the changes come from a trusted source.

However, while you can configure GitHub to sign your commits, it looks like it is not possible (at least now) to sign tags.

To test signing, I created a test repository, opened it in a codespace, and created a test commit:

Creating a signed commit in a codespace

git log confirms that the commit is signed, although it cannot verify the signature because of the missing key (the “E” letter).

If we look at the commit log in the GitHub UI, we will see that the commit was indeed signed — with GitHub’s verified signature. This makes sense: GitHub doesn’t have access to my private signing keys, and that is why it cannot sign the commit with my key.

Now let’s imagine we want to create a release. To do that, we create a tag and sign it: git tag -s 1.0.0 -m 1.0.0. And this fails.

Tag signing fails

Setting the GIT_TRACE environment variable does not help much:

$ GIT_TRACE=1 git tag -s 1.0.0 -m 1.0.0
17:38:12.073051 git.c:439               trace: built-in: git tag -s 1.0.0 -m 1.0.0
17:38:12.073795 run-command.c:663       trace: run_command: /.codespaces/bin/gh-gpgsign --status-fd=2 -bsau 'GitHub <[email protected]>'
error: gpg failed to sign the data
error: unable to sign the tag

This is a bit strange because when you sign a commit, git invokes the same command:

$ GIT_TRACE=1 git commit -a -S -m "Update test.txt"
17:40:02.995489 git.c:439               trace: built-in: git commit -a -S -m 'Update test.txt'
17:40:02.998058 run-command.c:663       trace: run_command: /.codespaces/bin/gh-gpgsign --status-fd=2 -bsau 'GitHub <[email protected]>'
17:40:03.302086 run-command.c:663       trace: run_command: git gc --auto
17:40:03.303635 git.c:439               trace: built-in: git gc --auto
[master 91f54b3] Update test.txt
 Author: Volodymyr Kolesnykov <[email protected]>
 1 file changed, 1 insertion(+)

Unfortunately, the Troubleshooting GPG verification for GitHub Codespaces article does not help. However, I still need to be able to sign tags. What should I do?

The scenario I have come up with is not perfect, but it works; it is probably possible to automate it with the help of dotfiles.

First, I created a secret for codespaces here (more about encrypted secrets for codespaces), put my private GPG key into it, and made it available for my test repository. Then I restarted the codespace. Next, I imported the GPG key from the secret:

echo "${GPG_KEY}" > ~/key.asc
gpg --batch --import ~/key.asc
rm -f ~/key.asc

Then I had to set the environment variables to the values gpg expects:

export GPG_TTY=$(tty)
export GIT_COMMITTER_NAME="$(git config --get"
export GIT_COMMITTER_EMAIL="$(git config --get"

By default, GPG_COMMITTER_EMAIL is set to [email protected]; because I don’t have a signing key matching that email, I need to set it to my email address.

Finally, I had to replace the gpg.program in Git and set it to /usr/bin/gpg:

git config --global gpg.program /usr/bin/gpg

And after that, I was able to sign tags in Git:

Tag signing finally works

Things to do:

  1. Remove the private key from secrets after it has been imported. Codespace secrets live in /workspaces/.codespaces/shared/.env-secrets as secret_name=base64(secret_value) pairs. However, the key still lives in ~/.gnupg/private-keys-v1.d/, so this probably won’t add more security.
  2. If you close and open the terminal, the GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL environment variables will be reset to their initial values. This probably happens because both are stored as secrets, and secrets got restored by the /etc/profile.d/ script.
  3. Try to automate the entire process with dotfiles.

How to Sign Tags in GitHub Codespaces
Tagged on:         

Leave a Reply

Your email address will not be published. Required fields are marked *