diff --git a/practices/guides/commit-signing.md b/practices/guides/commit-signing.md index 9021bab9..939ebafa 100644 --- a/practices/guides/commit-signing.md +++ b/practices/guides/commit-signing.md @@ -1,92 +1,200 @@ # Git commit signing setup guide -- [Git commit signing setup guide](#git-commit-signing-setup-guide) - - [From Workstations](#from-workstations) - - [macOS](#macos) - - [Windows](#windows) - - [From Pipelines](#from-pipelines) - - [GitHub Actions](#github-actions) - - [AWS CodePipeline](#aws-codepipeline) - - [Troubleshooting](#troubleshooting) +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 (see the full GitHub documentation [here](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)). -## From Workstations +> You should only set up **one** of these options - **don't attempt to set up GPG and SSH commit signing**! -### macOS +The instructions on this page focus on the recommended method - GPG. -- Install the [Brew package manager](https://brew.sh) +## GPG commit signing -```bash -brew upgrade -brew install gnupg pinentry-mac -gpg --full-generate-key -``` +### From Workstations -- Accept the defaults, Curve 25519 etc. -- Enter your GitHub account name as the Real Name -- Enter your GitHub account email as the Email Address -- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below) -- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email* -- Define a passphrase for the key and keep it in your password manager +If you have already committed and need to retrospectively sign commits, follow the instructions below, then follow the [retrospective commit signing instructions](./retrospective-commit-signing.md). -```bash -gpg --armor --export ${my_email_address} | pbcopy -``` +#### macOS -- Public key is now in your clipboard - in your GitHub account add it to your profile via *Settings > SSH and GPG Keys> Add New GPG Key* -- Paste it in +1. Install `gnupg` & `pinentry-mac` with [Brew](https://brew.sh): -```bash -git config --global user.email ${my_email_address} # same one used during key generation -git config --global user.name ${my_username} -git config --global commit.gpgsign true -sed -i '' '/^export GPG_TTY/d' ~/.zshrc -echo export GPG_TTY=\$\(tty\) >> ~/.zshrc -source ~/.zshrc -PINENTRY_BIN=$(whereis -q pinentry-mac) -sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf -echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf -gpgconf --kill gpg-agent -``` + ```bash + 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 + ``` -The first time you commit you will be prompted to add the GPG key passphrase to the macOS Keychain. Thereafter signing will happen seamlessly without prompts. +1. Create a new GPG key: -Most of the published solutions for this don't work because *brew* seems to have moved the default folder for binaries, plus many guides contain obsolete settings for *gpg-agent*. + ```bash + gpg --full-generate-key + ``` -### Windows + 1. Pick `ECC (sign and encrypt)` then `Curve 25519` ([Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) offers the strongest encryption at time of writing) + 1. Select a key expiry time (personal choice) + 1. `Real name` = Your GitHub handle + 1. `Email address` = An email address [registered against your GitHub account](https://github.com/settings/emails) - to enable [Smart Commits](https://nhsd-confluence.digital.nhs.uk/x/SZNYRg#UsingtheGitHubintegrationinJira-SmartCommits) ([Jira/GitHub integration](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/)), use your `@nhs.net` address -- Install [Git for Windows](https://git-scm.com/download/win), which includes Bash and GnuPG -- Right-click on the Desktop > *Git Bash Here* + > If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`. -```bash -gpg --full-generate-key -``` + 1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below) + 1. Review your inputs and press enter `O` to confirm + 1. Define a passphrase for the key -- Pick *RSA and RSA*, or *RSA (sign only)* - there is no elliptic curve cryptography (ECC) support at the time of writing -- Set key size to 4096 bit, the minimum accepted for GitHub -- Enter your GitHub account name as the Real Name -- Enter your GitHub account email as the Email Address -- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below) -- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email* -- Define a passphrase for the key and keep it in your password manager +1. Test the key is visible and export the PGP public key (to your clipboard): -```bash -gpg --armor --export ${my_email_address} | clip -``` + ```bash + gpg -k # This should list the new key + gpg --armor --export | pbcopy + ``` -- Public key is now in your clipboard - in your GitHub account add it to your profile via *Settings > SSH and GPG Keys> Add New GPG Key* -- Paste it in + > Your PGP public key is now in your clipboard! -```bash -git config --global user.email ${my_email_address} # same one used during key generation -git config --global user.name ${my_username} -git config --global commit.gpgsign true -``` +1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`) + + > Note the `Key ID` as you'll need this in the next step. + +1. Set your local git config to use GPG signing: + + ```bash + git config --global user.email # same one used during key generation + git config --global user.name + git config --global user.signingkey + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +1. Test it works: + + 1. Create a temporary branch of your favourite repository. + 1. Make an inconsequential whitespace change. + 1. Commit the change. + 1. You will be prompted for your GPG key passphrase - optionally select to add it to the macOS Keychain. + 1. Check the latest commit shows a successful signing: + + ```bash + $ git log --show-signature -1 + ... + gpg: Good signature from " <>" [ultimate] + Author: <> + ... + ``` + +#### Windows/WSL + +1. Install (as administrator) [Git for Windows](https://git-scm.com/download/win) (which includes Bash and GnuPG) +1. Open `Git Bash` +1. Create a new GPG key: + + ```bash + gpg --full-generate-key + ``` + + 1. Pick `ECC (sign and encrypt)` then `Curve 25519` ([Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519) offers the strongest encryption at time of writing) + 1. Select a key expiry time (personal choice) + 1. `Real name` = Your GitHub handle + 1. `Email address` = An email address [registered against your GitHub account](https://github.com/settings/emails) - to enable [Smart Commits](https://nhsd-confluence.digital.nhs.uk/x/SZNYRg#UsingtheGitHubintegrationinJira-SmartCommits) ([Jira/GitHub integration](https://support.atlassian.com/jira-software-cloud/docs/process-issues-with-smart-commits/)), use your `@nhs.net` address + + > If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`. + + 1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below) + 1. Review your inputs and press enter `O` to confirm + 1. A new window called pinentry will appear prompting you to enter a passphrase. -When you commit you will be prompted to enter the GPG key passphrase into a Pinentry window. +1. Test the key is visible and export the PGP public key (to your clipboard): -## From Pipelines + ```bash + gpg -k # This should list the new key + gpg --armor --export | clip + ``` -### GitHub Actions + > Your PGP public key is now in your clipboard! + +1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`) + + > Note the `Key ID` as you'll need this in the next step. + +1. Set your local git config to use GPG signing: + + ```bash + git config --global user.email # same one used during key generation + git config --global user.name + git config --global user.signingkey + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +1. Now your key is created, make it available within Windows: + + 1. Export the key: + + ```bash + gpg --output .pgp --export-secret-key + ``` + + 1. Install (as administrator) [Gpg4win](https://www.gpg4win.org/) (which includes GnuPG and Kleopatra) + + > **Ensure both `GnuPG` and `Kleopatra` are installed!** + + 1. Open Kleopatra -> `Import` -> Select the `.pgp` file created in the first step + 1. In `cmd`, test the key is visible and set your local git config to use GPG signing: + + ```bash + gpg -k # This should list the new key + git config --global user.email # same one used during key generation + git config --global user.name + git config --global user.signingkey + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +1. Now make it available within WSL: + + 1. Within Ubuntu: + + ```bash + sudo ln -s /mnt/c/Program\ Files\ \(x86\)/GnuPG/bin/gpg.exe /usr/local/bin/gpg + sudo ln -s gpg /usr/local/bin/gpg2 + ``` + + 1. Close and reopen your Ubuntu terminal + + 1. Test the key is visible and set your local git config to use GPG signing: + + ```bash + gpg -k # This should list the new key + git config --global user.email # same one used during key generation + git config --global user.name + git config --global user.signingkey + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +1. Test it works: + + 1. Create a temporary branch of your favourite repository. + 1. Make an inconsequential whitespace change. + 1. Commit the change. + 1. You will be prompted for your GPG key passphrase. + 1. Check the latest commit shows a successful signing: + + ```bash + $ git log --show-signature -1 + ... + gpg: Good signature from " <>" [ultimate] + Author: <> + ... + ``` + +### From Pipelines + +#### GitHub Actions A GitHub Actions workflow will by default authenticate using a [GITHUB_TOKEN](https://docs.github.com/en/actions/security-guides/automatic-token-authentication) which is generated automatically. @@ -97,7 +205,7 @@ The workflow would then use a Personal Access Token, stored with the GPG private ```yaml steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v5 with: token: ${{ secrets.BOT_PAT }} ref: main @@ -121,7 +229,7 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from GitHub Actions: $ git push ``` -### AWS CodePipeline +#### 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. @@ -138,7 +246,7 @@ if [[ ${BOT_SSH_KEY} != "None" ]]; then 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 + echo -e "\n\n" >> ~/.ssh/ssh_key chmod 600 ~/.ssh/ssh_key eval "$(ssh-agent -s)" ssh-add ~/.ssh/ssh_key @@ -174,10 +282,8 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from ${SCRIPT_URL}" git push ``` -## Troubleshooting - -Re-run your git command prefixed with GIT_TRACE=1 +### Troubleshooting -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) `.) You are able to [force a choice of signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary. +Re-run your git command prefixed with `GIT_TRACE=1`. -If you have already committed and need to retrospectively sign this commit [please follow the instructions here](./retrospective-commit-signing.md). +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) `.) You are able to [force a choice of signing key](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary.