Skip to content

Commit ec9f81c

Browse files
authored
Revise GPG commit signing guide for macOS & Windows (#361)
Updated instructions for GPG key generation and configuration on macOS and Windows. Also updated GitHub Actions checkout action version.
1 parent 0f3caec commit ec9f81c

File tree

1 file changed

+181
-75
lines changed

1 file changed

+181
-75
lines changed

practices/guides/commit-signing.md

Lines changed: 181 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,200 @@
11
# Git commit signing setup guide
22

3-
- [Git commit signing setup guide](#git-commit-signing-setup-guide)
4-
- [From Workstations](#from-workstations)
5-
- [macOS](#macos)
6-
- [Windows](#windows)
7-
- [From Pipelines](#from-pipelines)
8-
- [GitHub Actions](#github-actions)
9-
- [AWS CodePipeline](#aws-codepipeline)
10-
- [Troubleshooting](#troubleshooting)
3+
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)).
114

12-
## From Workstations
5+
> You should only set up **one** of these options - **don't attempt to set up GPG and SSH commit signing**!
136
14-
### macOS
7+
The instructions on this page focus on the recommended method - GPG.
158

16-
- Install the [Brew package manager](https://brew.sh)
9+
## GPG commit signing
1710

18-
```bash
19-
brew upgrade
20-
brew install gnupg pinentry-mac
21-
gpg --full-generate-key
22-
```
11+
### From Workstations
2312

24-
- Accept the defaults, Curve 25519 etc.
25-
- Enter your GitHub account name as the Real Name
26-
- Enter your GitHub account email as the Email Address
27-
- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
28-
- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email*
29-
- Define a passphrase for the key and keep it in your password manager
13+
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).
3014

31-
```bash
32-
gpg --armor --export ${my_email_address} | pbcopy
33-
```
15+
#### macOS
3416

35-
- 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*
36-
- Paste it in
17+
1. Install `gnupg` & `pinentry-mac` with [Brew](https://brew.sh):
3718

38-
```bash
39-
git config --global user.email ${my_email_address} # same one used during key generation
40-
git config --global user.name ${my_username}
41-
git config --global commit.gpgsign true
42-
sed -i '' '/^export GPG_TTY/d' ~/.zshrc
43-
echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
44-
source ~/.zshrc
45-
PINENTRY_BIN=$(whereis -q pinentry-mac)
46-
sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf
47-
echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf
48-
gpgconf --kill gpg-agent
49-
```
19+
```bash
20+
brew upgrade
21+
brew install gnupg pinentry-mac
22+
sed -i '' '/^export GPG_TTY/d' ~/.zshrc
23+
echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
24+
source ~/.zshrc
25+
PINENTRY_BIN=$(whereis -q pinentry-mac)
26+
mkdir -p ~/.gnupg
27+
touch ~/.gnupg/gpg-agent.conf
28+
sed -i '' '/^pinentry-program/d' ~/.gnupg/gpg-agent.conf
29+
echo "pinentry-program ${PINENTRY_BIN}" >> ~/.gnupg/gpg-agent.conf
30+
gpgconf --kill gpg-agent
31+
```
5032

51-
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.
33+
1. Create a new GPG key:
5234

53-
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*.
35+
```bash
36+
gpg --full-generate-key
37+
```
5438

55-
### Windows
39+
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)
40+
1. Select a key expiry time (personal choice)
41+
1. `Real name` = Your GitHub handle
42+
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
5643

57-
- Install [Git for Windows](https://git-scm.com/download/win), which includes Bash and GnuPG
58-
- Right-click on the Desktop > *Git Bash Here*
44+
> If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`.
5945

60-
```bash
61-
gpg --full-generate-key
62-
```
46+
1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
47+
1. Review your inputs and press enter `O` to confirm
48+
1. Define a passphrase for the key
6349

64-
- Pick *RSA and RSA*, or *RSA (sign only)* - there is no elliptic curve cryptography (ECC) support at the time of writing
65-
- Set key size to 4096 bit, the minimum accepted for GitHub
66-
- Enter your GitHub account name as the Real Name
67-
- Enter your GitHub account email as the Email Address
68-
- Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
69-
- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email*
70-
- Define a passphrase for the key and keep it in your password manager
50+
1. Test the key is visible and export the PGP public key (to your clipboard):
7151

72-
```bash
73-
gpg --armor --export ${my_email_address} | clip
74-
```
52+
```bash
53+
gpg -k # This should list the new key
54+
gpg --armor --export <my_email_address> | pbcopy
55+
```
7556

76-
- 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*
77-
- Paste it in
57+
> Your PGP public key is now in your clipboard!
7858

79-
```bash
80-
git config --global user.email ${my_email_address} # same one used during key generation
81-
git config --global user.name ${my_username}
82-
git config --global commit.gpgsign true
83-
```
59+
1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`)
60+
61+
> Note the `Key ID` as you'll need this in the next step.
62+
63+
1. Set your local git config to use GPG signing:
64+
65+
```bash
66+
git config --global user.email <my_email_address> # same one used during key generation
67+
git config --global user.name <github_handle>
68+
git config --global user.signingkey <key_id>
69+
git config --global commit.gpgsign true
70+
git config --global tag.gpgsign true
71+
```
72+
73+
1. Test it works:
74+
75+
1. Create a temporary branch of your favourite repository.
76+
1. Make an inconsequential whitespace change.
77+
1. Commit the change.
78+
1. You will be prompted for your GPG key passphrase - optionally select to add it to the macOS Keychain.
79+
1. Check the latest commit shows a successful signing:
80+
81+
```bash
82+
$ git log --show-signature -1
83+
...
84+
gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
85+
Author: <github_handle> <<my_email_address>>
86+
...
87+
```
88+
89+
#### Windows/WSL
90+
91+
1. Install (as administrator) [Git for Windows](https://git-scm.com/download/win) (which includes Bash and GnuPG)
92+
1. Open `Git Bash`
93+
1. Create a new GPG key:
94+
95+
```bash
96+
gpg --full-generate-key
97+
```
98+
99+
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)
100+
1. Select a key expiry time (personal choice)
101+
1. `Real name` = Your GitHub handle
102+
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
103+
104+
> If instead you opt for the private *@users.noreply.github.com* email address, consider enabling `Block command line pushes that expose my email`.
105+
106+
1. Avoid adding a comment (this *may* prevent git from auto-selecting a key - see Troubleshooting section below)
107+
1. Review your inputs and press enter `O` to confirm
108+
1. A new window called pinentry will appear prompting you to enter a passphrase.
84109
85-
When you commit you will be prompted to enter the GPG key passphrase into a Pinentry window.
110+
1. Test the key is visible and export the PGP public key (to your clipboard):
86111
87-
## From Pipelines
112+
```bash
113+
gpg -k # This should list the new key
114+
gpg --armor --export <my_email_address> | clip
115+
```
88116
89-
### GitHub Actions
117+
> Your PGP public key is now in your clipboard!
118+
119+
1. [Add the public key to your GitHub account](https://github.com/settings/gpg/new) (`Settings` -> `SSH and GPG keys` -> `New GPG key`)
120+
121+
> Note the `Key ID` as you'll need this in the next step.
122+
123+
1. Set your local git config to use GPG signing:
124+
125+
```bash
126+
git config --global user.email <my_email_address> # same one used during key generation
127+
git config --global user.name <github_handle>
128+
git config --global user.signingkey <key_id>
129+
git config --global commit.gpgsign true
130+
git config --global tag.gpgsign true
131+
```
132+
133+
1. Now your key is created, make it available within Windows:
134+
135+
1. Export the key:
136+
137+
```bash
138+
gpg --output <GitHub handle>.pgp --export-secret-key <my_email_address>
139+
```
140+
141+
1. Install (as administrator) [Gpg4win](https://www.gpg4win.org/) (which includes GnuPG and Kleopatra)
142+
143+
> **Ensure both `GnuPG` and `Kleopatra` are installed!**
144+
145+
1. Open Kleopatra -> `Import` -> Select the `<GitHub handle>.pgp` file created in the first step
146+
1. In `cmd`, test the key is visible and set your local git config to use GPG signing:
147+
148+
```bash
149+
gpg -k # This should list the new key
150+
git config --global user.email <my_email_address> # same one used during key generation
151+
git config --global user.name <github_handle>
152+
git config --global user.signingkey <key_id>
153+
git config --global commit.gpgsign true
154+
git config --global tag.gpgsign true
155+
```
156+
157+
1. Now make it available within WSL:
158+
159+
1. Within Ubuntu:
160+
161+
```bash
162+
sudo ln -s /mnt/c/Program\ Files\ \(x86\)/GnuPG/bin/gpg.exe /usr/local/bin/gpg
163+
sudo ln -s gpg /usr/local/bin/gpg2
164+
```
165+
166+
1. Close and reopen your Ubuntu terminal
167+
168+
1. Test the key is visible and set your local git config to use GPG signing:
169+
170+
```bash
171+
gpg -k # This should list the new key
172+
git config --global user.email <my_email_address> # same one used during key generation
173+
git config --global user.name <github_handle>
174+
git config --global user.signingkey <key_id>
175+
git config --global commit.gpgsign true
176+
git config --global tag.gpgsign true
177+
```
178+
179+
1. Test it works:
180+
181+
1. Create a temporary branch of your favourite repository.
182+
1. Make an inconsequential whitespace change.
183+
1. Commit the change.
184+
1. You will be prompted for your GPG key passphrase.
185+
1. Check the latest commit shows a successful signing:
186+
187+
```bash
188+
$ git log --show-signature -1
189+
...
190+
gpg: Good signature from "<github_handle> <<my_email_address>>" [ultimate]
191+
Author: <github_handle> <<my_email_address>>
192+
...
193+
```
194+
195+
### From Pipelines
196+
197+
#### GitHub Actions
90198
91199
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.
92200
@@ -97,7 +205,7 @@ The workflow would then use a Personal Access Token, stored with the GPG private
97205
```yaml
98206
steps:
99207
- name: Checkout
100-
uses: actions/checkout@v3
208+
uses: actions/checkout@v5
101209
with:
102210
token: ${{ secrets.BOT_PAT }}
103211
ref: main
@@ -121,7 +229,7 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from GitHub Actions: $
121229
git push
122230
```
123231
124-
### AWS CodePipeline
232+
#### AWS CodePipeline
125233
126234
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.
127235
@@ -138,7 +246,7 @@ if [[ ${BOT_SSH_KEY} != "None" ]]; then
138246
echo "StrictHostKeyChecking yes" >> ~/.ssh/config
139247
echo "UserKnownHostsFile=~/.ssh/known_hosts" >> ~/.ssh/config
140248
echo "${BOT_SSH_KEY}" > ~/.ssh/ssh_key
141-
echo -e "\n\n" >> ~/.ssh/ssh_key
249+
echo -e "\n\n" >> ~/.ssh/ssh_key
142250
chmod 600 ~/.ssh/ssh_key
143251
eval "$(ssh-agent -s)"
144252
ssh-add ~/.ssh/ssh_key
@@ -174,10 +282,8 @@ git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from ${SCRIPT_URL}"
174282
git push
175283
```
176284
177-
## Troubleshooting
178-
179-
Re-run your git command prefixed with GIT_TRACE=1
285+
### Troubleshooting
180286
181-
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](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary.
287+
Re-run your git command prefixed with `GIT_TRACE=1`.
182288
183-
If you have already committed and need to retrospectively sign this commit [please follow the instructions here](./retrospective-commit-signing.md).
289+
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](https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key), though this should not be necessary.

0 commit comments

Comments
 (0)