DEV Community

Jesse P. Johnson
Jesse P. Johnson

Posted on • Edited on

Commit Signing - GnuPG

Overview

DevSecOps as a practice is intended to introduce security early within the development process. One of simplest and with the highest impact on security capabilities is commit signing.

Because I work multiple projects that have varying capabilities and setup I decided to put together a bunch of useful primers on this subject. This document covers how to securely setup commit / tag signing.

Others can be found here:

Why commit signing is important?

Commit signing is a safeguard against:

  • tampering
  • impersonation
  • protects against account takeover
  • strengthens the software supply chain

Setup GPG and Create keys

Install gnupg along with pinentry-mac to provide input to GnuPG.

brew install gnupg pinentry-mac
Enter fullscreen mode Exit fullscreen mode

Configure GPG agent

The first step is to ensure GPG is configured with the most secure options available.

✏️ NOTE
The examples provided here use GNUPGHOME so that existing GPG keys won't be effected. Just set it accordingly to fit your needs:

export GNUPGHOME="${HOME}/.gnupg"

or

export GNUPGHOME="$(mktemp -d)"
cat > "${GNUPGHOME}/gpg.conf" <<EOF
# Strong algorithms
default-new-key-algo ed25519/cv25519
personal-digest-preferences SHA512 SHA384 SHA256
cipher-algo AES256

# Verification
require-valid-signatures
verify-options show-uid-validity
with-fingerprint

# Key expiration prompt
ask-cert-expire

# Quantum readiness (future versions)
# require-pqc-encryption
EOF
Enter fullscreen mode Exit fullscreen mode

Understanding GnuPG key management

Next we should consider key management. There are two distinct approaches that can be taken using GPG keys.

One approach would be to set an expiration and rotate keys as they expire. This is solid practice as the shorter the validation period the less impact of a key compromise. Unfortunately, this approach requires the use of a Time Stamp Authority (TSA) so that commits continue to be valid after rotation. Currently, neither GitHub or GitLab support this though.

The second would be to create a GPG hierarchy where the universal key never expires but subkeys do. The benefit of this approach is that it separates the identity from key use.

This is a solid practice but unfortunately comes with an additional requirement that neither GitHub nor GitLab support.

Now that we have a starting config we'll move onto creating a GPG key hierarchy.

We need to define our identity so that we can reuse it.

name="${NAME:-First N. Last}"
email="${EMAIL:-first.n.last@example.com}"
echo -n 'Enter password:' && read -r -s passphrase
Enter fullscreen mode Exit fullscreen mode

Create GnuPG universal key

Here we create a universal key that will act as the root of our GPG key hierarchy. This will be used to generate subkeys from.

gpg --batch --full-generate-key <<EOF
Key-Type: eddsa
Key-Curve: Ed25519
Key-Usage: sign
Name-Real: ${name}
Name-Email: ${email}
Passphrase: ${passphrase}
Expire-Date: 0
EOF
Enter fullscreen mode Exit fullscreen mode

Setup GnuPG subkey

To create our subkey we need the fingerprint of our universal key.

gpg_fp=$(
  gpg --list-options show-only-fpr-mbox --list-secret-keys \
  | awk '{print $1}')
)
Enter fullscreen mode Exit fullscreen mode

We can then create a signing subkey that will be used for commit / tag signing.

gpg --batch --quick-add-key "${gpg_fp}" ed25519 sign 1y
Enter fullscreen mode Exit fullscreen mode

Setup git for commit signing

To setup git so that it can perform commit signing we will need to get the key id for our subkey. Here we'll retrieve the key id of last subkey created.

signingkey="$(
  gpg --list-secret-keys "${email}" --keyid-format LONG \
  | awk '/^ssb/{print $2}' \
  | awk -F'/' 'END{print $2}'
)"
Enter fullscreen mode Exit fullscreen mode

Commit signing can then be configured directly through the git CLI.

# Set your user identity (must match info of the GPG key)
git config --global user.name "${name}"
git config --global user.email "${email}"

# Set your signing key
git config --global user.signingkey "${signingkey}"

# Ensure commits are signed by default
git config --global commit.gpgsign true

# Ensure tags are signed by default
git config --global tag.gpgsign true
Enter fullscreen mode Exit fullscreen mode

Configure git account with GPG signature

To begin we first need to export the public key.

gpg_asc="${email//[@.]/_/}.asc"
gpg --output "${HOME}/${gpg_asc}" \
  --armor \
  --export "${email}"
Enter fullscreen mode Exit fullscreen mode

You can follow those steps here for GitHub and here for GitLab.

Top comments (0)