|
| 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. |
0 commit comments