Skip to content

Latest commit

 

History

History
334 lines (243 loc) · 14 KB

File metadata and controls

334 lines (243 loc) · 14 KB

Git commit signing setup guide

Using GPG, SSH, or S/MIME, you can sign commits and tags locally. These commits and tags are marked as verified on GitHub so other people can be confident that the changes come from a trusted source.

The instructions on this page focus on GPG and SSH.

You should only set up one of these options - don't attempt to set up GPG and SSH commit signing!

See the full GitHub documentation here.

GPG commit signing

From Workstations

If you have already committed and need to retrospectively sign commits, follow the instructions below, then follow the retrospective commit signing instructions.

macOS

  1. Install gnupg & pinentry-mac with Brew:

    brew upgrade
    brew install gnupg pinentry-mac
    sed -i '' '/^export GPG_TTY/d' ~/.zshrc
    echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
    source ~/.zshrc
    PINENTRY_BIN=$(whereis -q pinentry-mac)
    mkdir -p ~/.gnupg
    touch ~/.gnupg/gpg-agent.conf
    sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf
    echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf
    gpgconf --kill gpg-agent
  2. Create a new GPG key:

    gpg --full-generate-key
    1. Pick RSA and RSA, or RSA (sign only) (there is no elliptic curve cryptography (ECC) support at the time of writing)

    2. keysize 4096 bits (the minimum accepted for GitHub)

    3. Select a key expiry time (personal choice)

    4. Real name Your GitHub handle

    5. Email address Your GitHub account email listed on your GitHub profile (you can use the privacy @users.noreply.github.com email address): Settings -> Emails -> Keep my email addresses private)

      If you go for the private email option, consider enabling Block command line pushes that expose my email.

    6. Avoid adding a comment (this may prevent git from auto-selecting a key - see Troubleshooting section below)

    7. Review your inputs and press enter O to confirm

    8. Define a passphrase for the key

  3. Test the key is visible and export the PGP PUBLIC KEY (to your clipboard):

    gpg -k # This should list the new key
    gpg --armor --export <my_email_address> | pbcopy

    Your PGP PUBLIC KEY is now in your clipboard!

  4. Add the public key to your GitHub account (Settings -> SSH and GPG keys -> New GPG key)

    Note the Key ID as you'll need this in the next step.

  5. Set your local git config to use GPG signing:

    git config --global user.email <my_email_address> # same one used during key generation
    git config --global user.name <github_handle>
    git config --global user.signingkey <key_id>
    git config --global commit.gpgsign true
    git config --global tag.gpgsign true
  6. Test it works:

    1. Create a temporary branch of your favourite repository.

    2. Make an inconsequential whitespace change.

    3. Commit the change.

      1. You will be prompted for your GPG key passphrase - optionally select to add it to the macOS Keychain.
    4. Check the latest commit shows a successful signing:

      $ git log --show-signature -1
      ...
      gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
      ...

Windows/WSL

  1. Install (as administrator) Git for Windows (which includes Bash and GnuPG)

  2. Open Git Bash

  3. Create a new GPG key:

    gpg --full-generate-key
    1. Pick RSA and RSA, or RSA (sign only) (there is no elliptic curve cryptography (ECC) support at the time of writing)

    2. keysize = 4096 bits (the minimum accepted for GitHub)

    3. Select a key expiry time (personal choice)

    4. Real name = Your GitHub handle

    5. Email address = Your GitHub account email listed on your GitHub profile (you can use the privacy @users.noreply.github.com email address): Settings -> Emails -> Keep my email addresses private)

      If you go for the private email option, consider enabling Block command line pushes that expose my email.

    6. Avoid adding a comment (this may prevent git from auto-selecting a key - see Troubleshooting section below)

    7. Review your inputs and press enter O to confirm

    8. A new window called pinentry will appear prompting you to enter a passphrase.

  4. Test the key is visible and export the PGP PUBLIC KEY (to your clipboard):

    gpg -k # This should list the new key
    gpg --armor --export <my_email_address> | clip

    Your PGP PUBLIC KEY is now in your clipboard!

  5. Add the public key to your GitHub account (Settings -> SSH and GPG keys -> New GPG key)

    Note the Key ID as you'll need this in the next step.

  6. Set your local git config to use GPG signing:

    git config --global user.email <my_email_address> # same one used during key generation
    git config --global user.name <github_handle>
    git config --global user.signingkey <key_id>
    git config --global commit.gpgsign true
    git config --global tag.gpgsign true
  7. Now your key is created, make it available within Windows:

    1. Export the key:

      gpg --output <GitHub handle>.pgp --export-secret-key <my_email_address>
    2. Install (as administrator) Gpg4win (which includes GnuPG and Kleopatra)

      Ensure both GnuPG and Kleopatra are installed!

    3. Open Kleopatra -> Import -> Select the <GitHub handle>.pgp file created in the first step

    4. In cmd, test the key is visible and set your local git config to use GPG signing:

      gpg -k # This should list the new key
      git config --global user.email <my_email_address> # same one used during key generation
      git config --global user.name <github_handle>
      git config --global user.signingkey <key_id>
      git config --global commit.gpgsign true
      git config --global tag.gpgsign true
  8. Now make it available within WSL:

    1. Within Ubuntu:

      sudo ln -s /mnt/c/Program\ Files\ \(x86\)/GnuPG/bin/gpg.exe /usr/local/bin/gpg
      sudo ln -s gpg /usr/local/bin/gpg2
    2. Close and reopen your Ubuntu terminal

    3. Test the key is visible and set your local git config to use GPG signing:

      gpg -k # This should list the new key
      git config --global user.email <my_email_address> # same one used during key generation
      git config --global user.name <github_handle>
      git config --global user.signingkey <key_id>
      git config --global commit.gpgsign true
      git config --global tag.gpgsign true
  9. Test it works:

    1. Create a temporary branch of your favourite repository.

    2. Make an inconsequential whitespace change.

    3. Commit the change.

      1. You will be prompted for your GPG key passphrase.
    4. Check the latest commit shows a successful signing:

      $ git log --show-signature -1
      ...
      gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
      ...

From Pipelines

GitHub Actions

A GitHub Actions workflow will by default authenticate using a GITHUB_TOKEN which is generated automatically.

However, at the time of writing, to sign commits the workflow will need to use a dedicated GitHub identity (in which to register the GPG public key).

The workflow would then use a Personal Access Token, stored with the GPG private key in the repo secrets, like so:

steps:
  - name: Checkout
    uses: actions/checkout@v5
    with:
      token: ${{ secrets.BOT_PAT }}
      ref: main

Example run action script excerpt:

# Configure GPG Key for signing GitHub commits
if [ -n "${{ secrets.BOT_GPG_KEY }}" ]; then
  echo "GPG secret key found in repo secrets, enabling GitHub commmit signing"
  GITHUB_SIGNING_OPTION="-S"
  echo "${BOT_GPG_KEY}" | gpg --import
  # Highlight any expiry date
  gpg --list-secret-keys
fi
git add .
git config --global user.name "${GITHUB_USER_NAME}"
git config --global user.email "${GITHUB_USER_EMAIL}"
git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from GitHub Actions: ${WORKFLOW_URL}"
git push

AWS CodePipeline

The cryptographic libraries in the default Amazon Linux 2 distro are very old, and do not support elliptic curve cryptography. When using pre-existing solution elements updating the build container is not always an option. This restricts the GPG key algorithm to RSA. You should use RSA-4096, which is the required minimum for GitHub.

Furthermore, the Systems Manager Parameter Store will not accept a key that is generated for both signing and encrypting (which will contain a second key for the encryption). It will be too large to be pasted in as a valid parameter. So when generating the GPG key you must select type RSA (sign only) if you intend to use Parameter Store rather than AWS Secrets Manager.

Example AWS CodeBuild Buildspec excerpt:

# Create SSH identity for connecting to GitHub
BOT_SSH_KEY=$(aws ssm get-parameter --name "/keys/ssh-key" --query "Parameter.Value" --output text --with-decryption 2> /dev/null || echo "None")
if [[ ${BOT_SSH_KEY} != "None" ]]; then
  mkdir -p ~/.ssh
  echo "Host *" >> ~/.ssh/config
  echo "StrictHostKeyChecking yes" >> ~/.ssh/config
  echo "UserKnownHostsFile=~/.ssh/known_hosts" >> ~/.ssh/config
  echo "${BOT_SSH_KEY}" > ~/.ssh/ssh_key
  echo -e "\n\n" >> ~/.ssh/ssh_key
  chmod 600 ~/.ssh/ssh_key
  eval "$(ssh-agent -s)"
  ssh-add ~/.ssh/ssh_key
fi

gpg --version
echo
BOT_GPG_KEY=$(aws ssm get-parameter --name "/keys/gpg-key" --query "Parameter.Value" --output text --with-decryption 2> /dev/null || echo "None")
if [ "${BOT_GPG_KEY}" != "None" ]; then
  echo "Encrypted GPG secret key found in the Parameter Store, enabling GitHub commmit signing"
  GITHUB_SIGNING_OPTION="-S"
  echo "${BOT_GPG_KEY}" | gpg --import
  # Highlight any expiry date
  gpg --list-secret-keys
  gpg-agent --daemon
  echo
fi

# GitHub publishes its public key fingerprints here:
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
# The known_hosts entry below was obtained by validating the fingerprint on a separate computer
echo "github.com ssh-ed25519 ${GITHUB_FINGERPRINT}" >> ~/.ssh/known_hosts

git config --global advice.detachedHead false
git config --global user.name "${GITHUB_USER_NAME}"
git config --global user.email "${GITHUB_USER_EMAIL}" # same one used during key generation
git clone git@github.com:${GITHUB_ORG_NAME}/${GITHUB_REPO_NAME}.git

# Make git repository source code changes here

git add .
git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from ${SCRIPT_URL}"
git push

Troubleshooting

Re-run your git command prefixed with GIT_TRACE=1.

A failure to sign a commit is usually because the name or email does not quite match those which were used to generate the GPG key, so git cannot auto-select a key. Ensure that these are indeed consistent. (If you added a comment when creating your GPG key, this may cause a mismatch: the comment will be visible when listing your GPG keys, e.g. RealName (Comment) <EmailAddress>.) You are able to force a choice of signing key, though this should not be necessary.

SSH commit signing

  1. If you do not already have SSH key access set up on your GitHub account, first generate a new SSH key. To create a new SSH key, you need to run the following command. This will generate a new SSH key of the type ed25519 and associate it with your email address (replace <my_email_address> with your actual email address):

    ssh-keygen -t ed25519 -C "<my_email_address>" -f "~/.ssh/github-signing-key"

    When you run this command, it will ask you to enter a passphrase. Choose a strong passphrase and make sure to remember it, as you will need to provide it when your key is loaded by the SSH agent.

  2. Signing commits with an SSH key is not the default method, so you need to configure Git accordingly:

    1. Run the following command to instruct Git to use the SSH signing key format, instead of the default GPG:

      git config --global gpg.format ssh
    2. Next, specify the private key for Git to use:

      git config --global user.signingkey ~/.ssh/github-signing-key
    3. Lastly, instruct Git to sign all of your commits:

      git config --global commit.gpgsign true
  3. Add the SSH public key to your GitHub account (Settings -> SSH and GPG keys -> New SSH key)

    1. Key type = Signing Key

    2. Copy the contents of your public key file and paste it into the Key field.

      cat ~/.ssh/github-signing-key.pub
    3. Add SSH key

  4. To ensure your configuration works as expected, make a commit to a branch locally and push it to GitHub. When you view the commit history of the branch on GitHub, your latest commit should now display a Verified tag, which indicates successful signing with your GPG or SSH key.