给 GitHub commit 配置 SSH 签名
git 的「作者」字段是完全自由填写的——我可以把 user.email 设成 linus@kernel.org,commit 出来的记录就显示是 Linus Torvalds 写的。GitHub 也不会拦:它只看 email,commit 推上去后会显示对应账号的头像。
Commit signature 就是用密码学手段证明「这个 commit 真的是我写的」。用一把私钥签名,GitHub 用对应的公钥验证,通过后页面上会显示一个绿色 Verified 徽章。
这事在多人协作的仓库里很重要——任何人都能伪装成 maintainer 推送看似人畜无害的代码。即使是个人项目,签名也是账号「被 push 历史」的可信度证明:哪天有人黑进你的 GitHub 账号偷塞了一个未签名的 commit,验证记录能让旁人一眼看出真伪。
配置一次就一劳永逸,下面记录完整步骤。
选 SSH 不选 GPG
历史上 git 用 GPG 签名,配置麻烦、工具难用、密钥管理一团糟。2022 年起 git 支持用 SSH key 签名,强烈推荐这条路。理由:
- 复用已有 key —— 如果已经能
git push,那把 SSH key 同时拿来做签名就行 - 不用装新工具 —— OpenSSH 系统里已经有了
- 配置简单 —— 三条 git 命令搞定
配置步骤
1. 生成 SSH key
ssh-keygen -t ed25519 -C "<id>+<username>@users.noreply.github.com"
-t ed25519 选 Ed25519 算法。比传统 RSA 短得多(公钥就一行)、生成快、安全等级更高。系统的 OpenSSH < 6.5(2014 年前的老机器)才需要回退用 -t rsa -b 4096。
-C 是 comment,纯标识用途,不参与加密——填什么都行,但用 GitHub 给的 noreply 邮箱(<id>+<username>@users.noreply.github.com)有几个好处:
- 公钥会上传到 GitHub,访问
https://github.com/<username>.keys任何人都能看到 comment 里的内容。用 noreply 邮箱不暴露真实地址 - 跟 git config、allowed_signers 用同一个 email,三处一致不会乱
实际的 noreply 邮箱在 GitHub 网页的 Settings → Emails 里能看到,形如 12345678+yourname@users.noreply.github.com,下面所有地方出现的占位符都换成你自己的那个。
一路回车走完默认(保存到 ~/.ssh/id_ed25519)。
2. 告诉 git 用这把 key 签名
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
三条命令分别做的事:
- 用 SSH 模式签名(不用 GPG)
- 指定公钥文件路径,GitHub 端验证用
- 默认所有 commit 都签名(不写就要每次手动加
-S)
3. 配置本地的 allowed_signers
git 在本地验证签名时需要一个 allowed_signers 文件,告诉它「这把 public key 对应的是谁」:
mkdir -p ~/.config/git
echo "<id>+<username>@users.noreply.github.com $(cat ~/.ssh/id_ed25519.pub)" > ~/.config/git/allowed_signers
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
这一步严格说不必要——少了它 commit 还是能签名、GitHub 还是能验证。它只影响本地的 git log --show-signature 命令能不能 verify 自己的签名。配上未来不会困惑。
4. 把公钥加到 GitHub(作为 Signing Key)
IMPORTANT
GitHub 区分两种 key:Authentication Key(用于 push/pull 认证)和 Signing Key(用于 commit 签名验证)。同一把公钥要分别添加两次,作为两种用途。
操作:
cat ~/.ssh/id_ed25519.pub
复制输出的公钥内容,浏览器打开 GitHub → Settings → SSH and GPG keys → New SSH key,Key type 下拉选 Signing Key(默认是 Authentication Key——别选错),粘贴公钥,保存。
如果之前已经把这把 key 加过 Authentication 用途,Signing 这块需要单独再加一次。
5. 验证
随便找一个 git 仓库测一下:
echo "test" >> README.md
git commit -am "test signed commit"
git log --show-signature -1
看到类似这行就说明本地验证通过:
Good "git" signature for <id>+<username>@users.noreply.github.com with ED25519 key SHA256:...
push 上去后 GitHub 上这条 commit 会显示 Verified 绿章。
邮箱一致性(最容易踩的坑)
GitHub 验证 commit 签名时检查三件事必须一致:
- commit 的 author email(
git config user.email) - GitHub 账号绑定并验证过的 email(
Settings → Emails) - allowed_signers 里写的 email
如果启用了 GitHub 的 email privacy(隐藏真实邮箱),就用代理邮箱 <id>+<username>@users.noreply.github.com 贯穿三处。先确认 git config --global user.email 设的是 noreply 邮箱:
git config --global user.email
# 输出应该是 <id>+<username>@users.noreply.github.com
# 如果不是,改:
git config --global user.email "<id>+<username>@users.noreply.github.com"
三处不一致的话,commit 在 GitHub 上会显示 Unverified——绿章变成黄色感叹号。
排错
配错了的时候按以下顺序排查,绝大部分问题都能定位。
症状一:commit 在 GitHub 上显示 Unverified
最常见。git log --show-signature -1 看本地:
- 如果本地 verify 通过、GitHub 显示 Unverified——是邮箱不一致。检查上面三处。
- 如果本地报「No signature found」——
commit.gpgsign没开。git config --global commit.gpgsign true重新配,然后git commit --amend --no-edit给最近一个 commit 补签名。
症状二:commit 时报「error: gpg failed to sign the data」
git 用错了签名工具。检查 git config --global gpg.format,输出必须是 ssh。如果是空的或 openpgp,重设:
git config --global gpg.format ssh
症状三:git log --show-signature 报「key is not in allowed_signers file」
allowed_signers 文件没建好或 git 找不到它。检查:
cat ~/.config/git/allowed_signers # 看内容是不是 "<email> ssh-ed25519 AAAA..."
git config --global gpg.ssh.allowedSignersFile # 看 git 是否指向这个文件
allowed_signers 文件的格式是「邮箱 + 空格 + 完整公钥」,缺一不可。注意公钥要带 ssh-ed25519 算法前缀。
症状四:仓库里有些 commit 显示 Verified、有些 Unverified
混合状态正常——Unverified 通常是配置签名之前的老 commit、或临时 --no-gpg-sign 跳过签名的 commit、或别人 push 进来的 commit。不用管历史,关注新 commit 即可。
换电脑了怎么办
签名 key 是绑机器的(私钥不应该跨机器复制)。换电脑后:
- 新机器上重跑上面的步骤 1-3,生成新 key + 配 git
- 把新公钥作为 Signing Key 添加到 GitHub(顺便也加 Authentication Key,省得 push 不了)
- 老电脑那把 key 不用删——GitHub 一个账号可以挂多把 signing key,互不冲突
一些细节
commit 时要不要输密码?
如果 SSH key 设了 passphrase,每次 commit 要输入。可以用 ssh-agent 缓存解锁状态——ssh-add ~/.ssh/id_ed25519 一次,之后整个登录会话不用再输。如果 key 没设 passphrase,就完全无感。
历史 commit 怎么办?
不会回溯签名(除非 rebase 重写历史,不推荐)。新 commit 开始签名就行,老的就老的。
在 GitHub 网页上改文件、合并 PR 也算签名吗?
算。GitHub 用自己的 GPG key(commit 历史里看到的 author 是 web-flow)替你签——网页上点 Edit 改文件、合 PR、squash merge 之类的操作产生的 commit 都自动签名。所以不需要为「网页编辑会破坏签名链」担心。