By admin , 29 April 2026

What you will achieve

You will configure Git to sign commits and tags using your existing SSH key, set up the allowed-signers file for verification, and integrate with GitHub or GitLab so signatures display as verified.

Why SSH signing

SSH signing landed in Git 2.34 (November 2021). It reuses the SSH key you already use for pushes - no separate GPG key management, no gpg-agent headaches. Modern Git hosts (GitHub, GitLab, Gitea, Forgejo) verify SSH signatures natively.

Step 1: confirm Git version

git --version
# git version 2.34.0 or later

Step 2: confirm an SSH key exists

ls -la ~/.ssh/id_ed25519*
# If missing:
ssh-keygen -t ed25519 -C "[email protected]"

Step 3: configure Git to sign with SSH

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

Now every commit and tag is signed automatically.

Step 4: test

cd /tmp/sign-test
git init
echo "test" > file.txt
git add . && git commit -m "Test signed commit"
git log --show-signature -1

Output includes:

Good "git" signature with ED25519 key SHA256:...

Step 5: configure allowed signers

To verify others' signatures locally, build an allowed_signers file:

mkdir -p ~/.config/git
echo "[email protected] $(cat ~/.ssh/id_ed25519.pub)" \
  >> ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers

Add lines for each collaborator whose signatures you want to verify:

[email protected] ssh-ed25519 AAAA...
[email protected] ssh-ed25519 AAAA...

Step 6: verify a commit

git verify-commit HEAD
git log --show-signature
git verify-tag v1.0.0

Step 7: upload key to your Git host

For GitHub: Settings → SSH and GPG keys → New SSH key → paste your public key, mark it as a "Signing key" (separate from authentication keys, optional but cleaner).

For GitLab: User Settings → SSH Keys → set the usage type to "Signing".

After upload, your commits show "Verified" badges in the host's UI.

Step 8: sign older commits

To retroactively sign an unsigned series:

# Sign the last commit
git commit --amend --no-edit -S

# Sign a range via rebase
git rebase -i <base> --exec 'git commit --amend --no-edit -S'

SHAs change; coordinate before pushing.

Step 9: enforce signed commits

On GitHub or GitLab, branch protection can require signed commits before merge. Combine with required reviews to raise the bar against credential theft.

gh api -X PUT repos/owner/repo/branches/main/protection \
  -F required_signatures.enabled=true

Step 10: handle multiple machines

Each laptop typically has its own key. Three approaches:

  • Per-machine key, all uploaded to your host as signing keys. Simplest.
  • One master key, copied between machines. Convenient, riskier.
  • Hardware key (YubiKey, Solo) used across machines. Highest security.

Hardware-backed signing

Generate an SSH key on a YubiKey:

ssh-keygen -t ed25519-sk -O resident -C "[email protected]"

Use the resulting ~/.ssh/id_ed25519_sk.pub as your signing key. Each signature requires a touch of the hardware - a meaningful security upgrade.

Step 11: signing tags

git tag -s v1.0.0 -m "Release 1.0.0"
git tag -v v1.0.0

Signed tags are the foundation of release attestation; tools like cosign and in-toto build supply chains atop them.

Troubleshooting

  • "gpg: skipped: No secret key" - means GPG is still configured. Run git config --global gpg.format ssh.
  • "Bad signature" on someone else's commit - their key is not in your allowed_signers.
  • Host shows "Unverified" despite local verification - public key not uploaded as a signing key on the host.

The result

Every commit and tag you create is cryptographically tied to your SSH key. Your host displays the verified badge. Branch protection can require signatures, raising the cost of stolen credentials. The setup is one afternoon; the security benefit is permanent.