From 1983db2eb9f5685feb720d25bac8f692890a00c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Est=C3=A9fano=20Vechietti?= <311154804+estefano-vechietti-azion@users.noreply.github.com> Date: Tue, 3 Nov 2026 11:36:45 -0300 Subject: [PATCH] docs: add the guide to signing commits with an SSH key Protected branches in the organization will require verified commit signatures. The guide covers creating the key, configuring git, adding the key to GitHub as a Signing Key, verifying the result, remote and 1Password setups, re-signing a branch, automations that commit, and the common failures by verification reason. SSH only: every signing key registered in the organization today is an SSH key. CONTRIBUTING.md links it; README.md lists it as not inherited. Co-Authored-By: Claude Opus 5.5 --- CONTRIBUTING.md | 2 + README.md | 1 + docs/commit-signing.md | 151 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 docs/commit-signing.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ba7ddc..64f8700 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,6 +21,8 @@ does not publish its own `CONTRIBUTING.md`. 4. Add or update tests for the behavior you change, and make sure CI is green. 5. Fill in the pull request template, including the version bump (`#major`, `#minor`, `#patch` or `#none`). 6. Do not commit secrets, credentials or internal hostnames. Secret scanning blocks pushes that contain them. +7. Sign your commits. Protected branches require verified signatures: see + [Signing commits with an SSH key](https://github.com/aziontech/.github/blob/main/docs/commit-signing.md). Pull requests are merged after review by the code owners of the changed files. diff --git a/README.md b/README.md index c743a0b..26845e8 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A file in a repository always takes precedence over the default here. | [`CONTRIBUTING.md`](CONTRIBUTING.md) | Branches, Conventional Commits, pull requests, versioning | | [`.github/ISSUE_TEMPLATE/`](.github/ISSUE_TEMPLATE/) | Bug report and feature request forms; blank issues disabled | | [`.github/pull_request_template.md`](.github/pull_request_template.md) | Pull request description | +| [`docs/commit-signing.md`](docs/commit-signing.md) | Signing commits with an SSH key (not inherited; linked from `CONTRIBUTING.md`) | `CODEOWNERS` is **not** inherited by other repositories: each repository keeps its own. The `CODEOWNERS` here only covers this repository. diff --git a/docs/commit-signing.md b/docs/commit-signing.md new file mode 100644 index 0000000..143e3f9 --- /dev/null +++ b/docs/commit-signing.md @@ -0,0 +1,151 @@ +# Signing commits with an SSH key + +The `aziontech` organization requires signed commits on protected branches. Once the rule is enforced, a push or +merge that carries a commit without a verified signature is rejected. The rule is announced before it is enforced. + +Setup takes about five minutes and can reuse the SSH key you already push with. + +- Commits made in the GitHub web interface (web edits, squash merges) are signed by GitHub. They need nothing. +- SSH signing needs git 2.34 or later (`git --version`). + +## 1. Have an SSH key + +The key you already push with works. To check whether one exists: + +```bash +ls ~/.ssh/id_ed25519.pub ~/.ssh/id_rsa.pub 2>/dev/null +``` + +If a file is listed, go to step 2. Otherwise, create an ed25519 key with a passphrase: + +```bash +ssh-keygen -t ed25519 -C "you@azion.com" +``` + +Accept the default path (`~/.ssh/id_ed25519`) and set a passphrase. On macOS, store the passphrase in the Keychain so +you are not asked for it on every commit: + +```bash +ssh-add --apple-use-keychain ~/.ssh/id_ed25519 +``` + +To keep a separate key for signing only, create one with `-f ~/.ssh/id_ed25519_signing` and use that path in the +steps below. + +## 2. Configure git + +Run once per machine: + +```bash +git config --global gpg.format ssh +git config --global user.signingkey ~/.ssh/id_ed25519.pub +git config --global commit.gpgsign true +git config --global tag.gpgsign true +``` + +Check that git's email is a verified email on your GitHub account. Without that, the commit shows as Unverified even +with a valid signature. + +```bash +git config --global user.email # must be an email verified on your GitHub account +``` + +Optional, so that `git log --show-signature` can verify your signatures locally: + +```bash +echo "$(git config --global user.email) $(cat ~/.ssh/id_ed25519.pub)" >> ~/.ssh/allowed_signers +git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers +``` + +## 3. Add the key to GitHub as a Signing Key + +This is the step most often missed. The key you push with is registered as an Authentication Key, which does not count +for signatures. The same key has to be added again, as a Signing Key. + +1. Copy the public key: `pbcopy < ~/.ssh/id_ed25519.pub` (on Linux, `cat` it and copy). +2. Open [github.com/settings/ssh/new](https://github.com/settings/ssh/new). +3. Under **Key type**, choose **Signing Key**. +4. Give it a title (for example `MacBook – signing`), paste the key and save. + +The same with the GitHub CLI: + +```bash +gh ssh-key add ~/.ssh/id_ed25519.pub --type signing --title "MacBook - signing" +``` + +If `gh` asks for permission, run `gh auth refresh -s admin:ssh_signing_key` first. + +## 4. Verify + +Make a commit and check it locally: + +```bash +git commit --allow-empty -m "test: signed commit" +git log --show-signature -1 +``` + +With `allowed_signers` configured, the output shows `Good "git" signature for you@azion.com`. Without it, it shows +`gpg.ssh.allowedSignersFile needs to be configured`, and the commit is signed all the same. + +After pushing to a test branch, the commit shows the **Verified** badge in the pull request or the repository's commit +list. From the command line: + +```bash +gh api repos/aziontech//commits/ --jq '.commit.verification | {verified, reason}' +``` + +Expect `verified: true` and `reason: valid`. Remember to drop the test commit. + +## Special cases + +**Several machines.** Repeat steps 2 and 3 on each machine, with one Signing Key per machine. An account can hold as +many as needed. + +**Coder, devcontainers and remote SSH.** On the remote machine, let git sign through the forwarded ssh-agent instead of +copying the private key there. On the remote machine, run: + +```bash +git config --global gpg.format ssh +git config --global user.signingkey "key::$(ssh-add -L | head -1)" +git config --global commit.gpgsign true +``` + +This needs a forwarded agent (`ssh -A`, or `ForwardAgent yes` in `~/.ssh/config`). VS Code Remote and devcontainers +forward it on their own. + +**1Password.** If the key lives in 1Password, use its signing program: + +```bash +git config --global gpg.ssh.program "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" +``` + +The rest is the same, with `user.signingkey` set to the public key copied from 1Password. + +**IDEs.** VS Code, JetBrains IDEs and GitHub Desktop read git's global configuration. Nothing to set in them. + +**Unsigned commits already on your branch.** Sign them again and force-push. Only on your own branches, never on +shared ones. + +```bash +git rebase --exec 'git commit --amend --no-edit -S' origin/ +git push --force-with-lease +``` + +**Automations that commit.** CI jobs that run `git commit` and `git push` (version bumps, changelogs, generated code) +are rejected on protected branches. Either commit through the GitHub API with `GITHUB_TOKEN`, which GitHub signs, or +give the bot a signing key of its own. + +## Common problems + +The `reason` field from the verification command (step 4) usually names the cause. + +| Symptom | Cause | Fix | +|---|---|---| +| Unverified, `reason: unknown_key` | Key registered only as an Authentication Key, or not at all | Add it as a Signing Key (step 3) | +| Unverified, `reason: unverified_email` or `bad_email` | git's `user.email` is not a verified email on the account | Fix `user.email`, or verify the email on GitHub | +| Commit with no badge at all | `commit.gpgsign` is off in that repository or on that machine | Run step 2; check with `git config commit.gpgsign` | +| `unsupported value for gpg.format: ssh` | git older than 2.34 | Upgrade git (`brew upgrade git`) | +| `agent refused operation`, or a passphrase prompt on every commit | Key not loaded in the ssh-agent | `ssh-add --apple-use-keychain ~/.ssh/id_ed25519` | +| Push rejected with `Commits must have verified signatures` | An unsigned commit on the branch | Sign the commits again (Special cases) | + +Azion engineers with questions not answered here: ask `@aziontech/team-delivery-engineering`.