Skip to content

Commit ace3d12

Browse files
Add detail on code repository commit signing and OIDC federated authN (#274)
* Add reasons for signing commits, and setup guide * Create commit-signing.md * Update security-repository.md * Update security-repository.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update README.md * Update README.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update security-repository.md * Update security-repository.md * Update commit-signing.md * Update README.md * Update README.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Markdown compliance updates * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Update commit-signing.md * Markdown validation tweaks * Markdown validation tweaks
1 parent 398d53e commit ace3d12

File tree

3 files changed

+224
-4
lines changed

3 files changed

+224
-4
lines changed

practices/guides/commit-signing.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# Git Commit Signing Setup Guide
2+
3+
- [From Workstations](#from-workstations):
4+
- [macOS](#macos)
5+
- [Windows](#windows)
6+
- [From Pipelines](#from-pipelines):
7+
- [GitHub Actions](#github-actions)
8+
- [AWS CodePipeline](#aws-codepipeline)
9+
- [Troubleshooting](#troubleshooting)
10+
11+
<br>
12+
13+
## From Workstations
14+
15+
### macOS
16+
17+
- Install the [Brew package manager](https://brew.sh)
18+
19+
```bash
20+
brew upgrade
21+
brew install gnupg pinentry-mac
22+
gpg --full-generate-key
23+
```
24+
25+
- Accept the defaults, Curve 25519 etc.
26+
- Enter your GitHub account name as the Real Name
27+
- Enter your GitHub account email as the Email Address
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
30+
31+
```bash
32+
gpg --armor --export ${my_email_address} | pbcopy
33+
```
34+
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
37+
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+
echo export GPG_TTY=\$\(tty\) >> ~/.zshrc
43+
source ~/.zshrc
44+
echo "pinentry-program /opt/homebrew/bin/pinentry-mac" >> ~/.gnupg/gpg-agent.conf
45+
gpgconf --kill gpg-agent
46+
```
47+
48+
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.
49+
50+
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*.
51+
52+
<br>
53+
54+
### Windows
55+
56+
- Install [Git for Windows](https://git-scm.com/download/win), which includes Bash and GnuPG
57+
- Right-click on the Desktop > *Git Bash Here*
58+
59+
```bash
60+
gpg --full-generate-key
61+
```
62+
63+
- Pick *RSA and RSA*, or *RSA (sign only)* - there is no elliptic curve cryptography (ECC) support at the time of writing
64+
- Set key size to 4096 bit, the minimum accepted for GitHub
65+
- Enter your GitHub account name as the Real Name
66+
- Enter your GitHub account email as the Email Address
67+
- You can use the privacy *@users.noreply.github.com* email address listed in the GitHub profile: *Settings > Email*
68+
- Define a passphrase for the key and keep it in your password manager
69+
70+
```bash
71+
gpg --armor --export ${my_email_address} | clip
72+
```
73+
74+
- 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*
75+
- Paste it in
76+
77+
```bash
78+
git config --global user.email ${my_email_address} # same one used during key generation
79+
git config --global user.name ${my_username}
80+
git config --global commit.gpgsign true
81+
```
82+
83+
When you commit you will be prompted to enter the GPG key passphrase into a Pinentry window.
84+
85+
<br>
86+
87+
## From Pipelines
88+
89+
### GitHub Actions
90+
91+
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.
92+
93+
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).
94+
95+
The workflow would then use a Personal Access Token, stored with the GPG private key in the repo secrets, like so:
96+
97+
```yaml
98+
steps:
99+
- name: Checkout
100+
uses: actions/checkout@v3
101+
with:
102+
token: ${{ secrets.BOT_PAT }}
103+
ref: main
104+
```
105+
106+
Example run action script excerpt:
107+
108+
```bash
109+
# Configure GPG Key for signing GitHub commits
110+
if [ -n "${{ secrets.BOT_GPG_KEY }}" ]; then
111+
echo "GPG secret key found in repo secrets, enabling GitHub commmit signing"
112+
GITHUB_SIGNING_OPTION="-S"
113+
echo "${BOT_GPG_KEY}" | gpg --import
114+
# Highlight any expiry date
115+
gpg --list-secret-keys
116+
fi
117+
git add .
118+
git config --global user.name "${GITHUB_USER_NAME}"
119+
git config --global user.email "${GITHUB_USER_EMAIL}"
120+
git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from GitHub Actions: ${WORKFLOW_URL}"
121+
git push
122+
```
123+
124+
<br>
125+
126+
### AWS CodePipeline
127+
128+
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.
129+
130+
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.
131+
132+
Example AWS CodeBuild Buildspec excerpt:
133+
134+
```bash
135+
# Create SSH identity for connecting to GitHub
136+
BOT_SSH_KEY=$(aws ssm get-parameter --name "/keys/ssh-key" --query "Parameter.Value" --output text --with-decryption 2> /dev/null || echo "None")
137+
if [[ ${BOT_SSH_KEY} != "None" ]]; then
138+
mkdir -p ~/.ssh
139+
echo "Host *" >> ~/.ssh/config
140+
echo "StrictHostKeyChecking yes" >> ~/.ssh/config
141+
echo "UserKnownHostsFile=~/.ssh/known_hosts" >> ~/.ssh/config
142+
echo "${BOT_SSH_KEY}" > ~/.ssh/ssh_key
143+
echo -e "\n\n" >> ~/.ssh/ssh_key
144+
chmod 600 ~/.ssh/ssh_key
145+
eval "$(ssh-agent -s)"
146+
ssh-add ~/.ssh/ssh_key
147+
fi
148+
149+
gpg --version
150+
echo
151+
BOT_GPG_KEY=$(aws ssm get-parameter --name "/keys/gpg-key" --query "Parameter.Value" --output text --with-decryption 2> /dev/null || echo "None")
152+
if [ "${BOT_GPG_KEY}" != "None" ]; then
153+
echo "Encrypted GPG secret key found in the Parameter Store, enabling GitHub commmit signing"
154+
GITHUB_SIGNING_OPTION="-S"
155+
echo "${BOT_GPG_KEY}" | gpg --import
156+
# Highlight any expiry date
157+
gpg --list-secret-keys
158+
gpg-agent --daemon
159+
echo
160+
fi
161+
162+
# GitHub publishes its public key fingerprints here:
163+
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints
164+
# The known_hosts entry below was obtained by validating the fingerprint on a separate computer
165+
echo "github.com ssh-ed25519 ${GITHUB_FINGERPRINT}" >> ~/.ssh/known_hosts
166+
167+
git config --global advice.detachedHead false
168+
git config --global user.name "${GITHUB_USER_NAME}"
169+
git config --global user.email "${GITHUB_USER_EMAIL}" # same one used during key generation
170+
git clone [email protected]:${GITHUB_ORG_NAME}/${GITHUB_REPO_NAME}.git
171+
172+
# Make git repository source code changes here
173+
174+
git add .
175+
git commit ${GITHUB_SIGNING_OPTION} -am "Automated commit from ${SCRIPT_URL}"
176+
git push
177+
```
178+
179+
<br>
180+
181+
## Troubleshooting
182+
183+
Re-run your git command prefixed with GIT_TRACE=1
184+
185+
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. 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.

practices/security-repository.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ Depending on your use case, you may want to create additional teams (e.g. a read
5353
### Branch protection
5454

5555
- Require <!-- markdown-link-check-disable -->[pull request code reviews](https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#require-pull-request-reviews-before-merging)<!-- markdown-link-check-enable -->, by at least one code owner, to merge a branch.
56-
- Require <!-- markdown-link-check-disable -->[signed commits](https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#require-signed-commits)<!-- markdown-link-check-enable -->, and, accordingly, check that commits are verified before merging.
56+
- Require <!-- markdown-link-check-disable -->[signed commits](https://docs.github.com/en/github/administering-a-repository/defining-the-mergeability-of-pull-requests/about-protected-branches#require-signed-commits)<!-- markdown-link-check-enable -->, and, accordingly, check that commits are verified before merging. Git treats authentication and identity separately - any authenticated user can impersonate another developer when committing code. This means that even if a junior account is compromised it could have significant consequences, for example impersonating the lead developer in the hope of an easy merge. Only by requiring signing can identity truly be verified. [Setup Guides](guides/commit-signing.md) for macOS, Windows, GitHub Actions, and AWS CodePipeline.
5757
- Invalidate existing reviews when new commits are pushed (`fresh-commits-invalidate-existing-reviews` option).
5858
- Require adequate automated status checks prior to merging. This should always include checking that branches are up to date.

tools/nhsd-git-secrets/README.md

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,52 @@
11
# Git-Secrets examples
2+
23
This folder comprises examples for implementing AWSLabs Git-Secrets, which is our default implementation for [secrets scanning](../../quality-checks.md). As with any default, we expect teams to resolve any caveats as they best see fit, and of course to contribute to these examples.
34

4-
# Why secrets scanning
5+
## Why secrets scanning
6+
57
Although we might be re-stating the obvious here, there's two main goals to consistent secrets scanning:
8+
69
1. Remove any secrets that may have been checked into the codebase in the past.
710
2. Prevent any new secrets from making it into the codebase.
811

912
Essentially, we want to avoid the NHS facing [potentially dire consequences](https://www.zdnet.com/article/data-of-243-million-brazilians-exposed-online-via-website-source-code/) due to exposure of secrets.
1013

11-
# How to get started
14+
## How to get started
15+
1216
If your team isn't doing secrets scanning at all yet, the fundamental first step is to understand the current state of the art. Use the [Macbook](README-mac-workstation.md) or Windows (coming soon...) guides to set up and run Git-Secrets for a nominated team member. Run the tooling, and ascertain whether there's any immediate actions to be taken.
1317

14-
# Getting to green
18+
## Getting to green
19+
1520
Once you've verified there's no urgent actions on your code, the next steps towards getting to green are:
21+
1622
1. Ensure every team member is doing local scans. Stopping secrets before code has been committed is cheap, removing them from git history is expensive.
1723
2. Run these same scripts as part of your deployment pipelines as a second line of defence.
24+
25+
## Consider using OIDC instead of secrets
26+
27+
OpenID Connect allows federated authentication from pipeline workflows to AWS and Azure without storing credentials in repository secrets at all, so no expiry to manage. GitHub documentation for achieving this for:
28+
29+
- [AWS](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services)
30+
- [Azure](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure)
31+
- [Google](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform)
32+
33+
Configuration for OIDC is light. See the below example GitHub Actions workflow excerpt which connects to both AWS and Azure:
34+
35+
```yaml
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v3
39+
40+
- name: Configure AWS STS credentials via OIDC
41+
uses: aws-actions/configure-aws-credentials@v1
42+
with:
43+
role-to-assume: ${{ secrets.AWS_ROLE_ID }}
44+
aws-region: eu-west-2
45+
46+
- name: Configure Azure identity token via OIDC
47+
uses: azure/login@v1
48+
with:
49+
client-id: ${{ secrets.AZ_CLIENT_ID }}
50+
tenant-id: ${{ secrets.AZ_TENANT_ID }}
51+
allow-no-subscriptions: true
52+
```

0 commit comments

Comments
 (0)