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
Configure GPG agent
The first step is to ensure GPG is configured with the most secure options available.
✏️ NOTE
The examples provided here useGNUPGHOMEso 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
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
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
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}')
)
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
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}'
)"
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
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}"
You can follow those steps here for GitHub and here for GitLab.
Top comments (0)