使用 X.509 证书签名提交和标签
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed, GitLab Dedicated
X.509 是一种由公共或私有公钥基础设施 (PKI) 颁发的公钥证书的标准格式。 个人 X.509 证书用于身份验证或签名目的,例如 S/MIME(安全/多用途互联网邮件扩展)。 然而,Git 也支持使用 X.509 证书对提交和标签进行签名,方式类似于使用 GPG (GnuPG,或 GNU Privacy Guard)。 主要区别在于 GitLab 确定开发者签名是否可信的方式:
- 对于 X.509,将根证书颁发机构添加到 GitLab 信任存储中。 (信任存储是受信任的安全证书的存储库。)结合签名中所需的任何中间证书,开发者的证书可以链接回受信任的根证书。
- 对于 GPG,开发者将他们的 GPG 密钥 添加到他们的账户中。
GitLab 使用自己的证书存储,因此定义了 信任链。 要让 GitLab 验证提交或标签:
- 签名证书的电子邮件必须与 GitLab 中已验证的电子邮件地址匹配。
- GitLab 实例需要从签名中的证书到 GitLab 证书存储中的受信任证书的完整信任链。 此链可能包括签名中提供的中间证书。你可能需要添加证书,如证书颁发机构根证书, 到 GitLab 证书存储。
- 签名时间必须在 证书有效期 内,通常最多三年。
- 签名时间等于或晚于提交时间。
如果信任链中的根 CA 或中间证书过期并续期,提交可能会暂时显示为"未验证",直到你 重新验证它们。
如果提交的状态已经确定并存储在数据库中, 请使用 Rake 任务 重新检查状态。 参考 故障排除部分。 GitLab 通过后台作业每天检查证书吊销列表。
已知问题
-
没有
authorityKeyIdentifier、subjectKeyIdentifier和crlDistributionPoints的证书显示为 未验证。我们 建议使用符合 RFC 5280 的 PKI 提供的证书。 -
已验证 徽章不会显示在 GitLab SaaS 服务中, 因为 上传自定义证书颁发机构 (CA) 仅适用于 GitLab 自托管版本。
-
在证书的扩展密钥用法 (EKU) 部分设置值,除了
Digital Signature所需的密钥用法 (KU) 外, 很可能导致你的提交显示为 未验证。 要解决此问题,请将emailProtection添加到你的 EKU 列表中。 RFC 5280 指定了此限制。要诊断它,请遵循 使用 OpenSSL 进行 S/MIME 验证。 如果此更改不能解决问题, 请在 issue 440189 中提供反馈。
-
在 GitLab 16.2 及更早版本中,如果你的签名证书的主题备用名称列表中有多个电子邮件地址, 只有第一个用于验证提交。
配置已签名的提交
要对你的提交、标签或两者进行签名,你必须:
获取 X.509 密钥对
如果你的组织有公钥基础设施 (PKI),该 PKI 提供 S/MIME 密钥。如果你没有来自 PKI 的 S/MIME 密钥对,请创建你自己的自签名对或购买一对。
将你的 X.509 证书与 Git 关联
要利用 X.509 签名,你需要 Git 2.19.0 或更高版本。你可以
使用命令 git --version 检查你的 Git 版本。
如果你有正确的版本,可以继续配置 Git。
配置 Git 使用你的密钥进行签名:
signingkey=$( gpgsm --list-secret-keys | egrep '(key usage|ID)' | grep -B 1 digitalSignature | awk '/ID/ {print $2}' )
git config --global user.signingkey $signingkey
git config --global gpg.format x509要配置 Windows 或 macOS:
-
通过以下方式安装 S/MIME Sign:
- 下载安装程序。
- 在 macOS 上运行
brew install smimesign。
-
通过运行
smimesign --list-keys获取你的证书 ID。 -
通过运行
git config --global user.signingkey <ID>设置你的签名密钥,将<ID>替换为证书 ID。 -
使用以下命令配置 X.509:
git config --global gpg.x509.program smimesign git config --global gpg.format x509
签名和验证提交
在 将你的 X.509 证书与 Git 关联 后, 你可以对你的提交进行签名:
-
创建 Git 提交时,添加
-S标志:git commit -S -m "feat: x509 signed commits" -
推送到 GitLab,并使用
--show-signature标志检查你的提交是否已验证:git log --show-signature -
如果你不想每次提交时都输入
-S标志, 运行此命令 让 Git 每次都对你的提交进行签名:git config --global commit.gpgsign true
签名和验证标签
在 将你的 X.509 证书与 Git 关联 后, 你可以开始对你的标签进行签名:
-
创建 Git 标签时,添加
-s标志:git tag -s v1.1.1 -m "My signed tag" -
推送到 GitLab 并使用以下命令验证你的标签已签名:
git tag --verify v1.1.1 -
如果你不想每次标签时都输入
-s标志, 运行此命令 让 Git 每次都对你的标签进行签名:git config --global tag.gpgsign true
相关主题
故障排除
对于没有管理员访问权限的提交者,查看 已签名的提交的验证问题 列表以寻找可能的修复方法。本页面的其他故障排除建议需要管理员访问权限。
重新验证提交
GitLab 将已检查提交的状态存储在数据库中。 你可以在以下情况后重新验证提交:
- 续期根 CA 或中间证书。
- 对证书存储进行更改。
要重新验证提交:
- 确保根 CA 和任何中间证书都在 GitLab 证书存储中。
- 运行
update_signaturesRake 任务 来检查和更新先前已验证提交的状态。
主要验证检查
代码执行
这些关键检查,
所有检查都必须返回 verified:
x509_certificate.nil?应为 false。x509_certificate.revoked?应为 false。verified_signature应为 true。user.nil?应为 false。user.verified_emails.include?(@email)应为 true。certificate_email == @email应为 true。
要调查为什么提交显示为 未验证:
-
sudo gitlab-rails console -
识别你要调查的项目(通过路径或 ID)和完整的提交 SHA。 使用此信息创建
signature以运行其他检查:project = Project.find_by_full_path('group/subgroup/project') project = Project.find_by_id('121') commit = project.repository.commit_by(oid: '87fdbd0f9382781442053b0b76da729344e37653') signedcommit=Gitlab::X509::Commit.new(commit) signature=Gitlab::X509::Signature.new(signedcommit.signature_text, signedcommit.signed_text, commit.committer_email, commit.created_at)如果你在运行检查时发现的问题进行了更改,请重启 Rails 控制台并从头开始再次运行检查。
-
检查提交上的证书:
signature.x509_certificate.nil? signature.x509_certificate.revoked?两个检查都应返回
false:> signature.x509_certificate.nil? => false > signature.x509_certificate.revoked? => false一个 已知问题 导致 这些检查因
Validation failed: Subject key identifier is invalid而失败。 -
对签名进行加密检查。代码必须返回
true:signature.verified_signature如果它返回
false,则 进一步调查此检查。 -
确认提交和签名上的电子邮件地址匹配:
- Rails 控制台显示正在比较的电子邮件地址。
- 最后一个命令必须返回
true:
sigemail=signature.__send__:certificate_email commitemail=commit.committer_email sigemail == commitemail在 GitLab 16.2 及更早版本中,只有第一个电子邮件 在
Subject Alternative Name列表中被比较。要显示Subject Alternative Name列表,运行:signature.__send__ :get_certificate_extension,'subjectAltName'如果开发者的电子邮件地址不是列表中的第一个,此检查 失败,提交被标记为
unverified。 -
提交上的电子邮件地址必须与 GitLab 中的账户关联。 此检查应返回
false:signature.user.nil? -
检查电子邮件地址是否与 GitLab 中的用户关联。此检查应 返回一个用户,例如
#<User id:1234 @user_handle>:User.find_by_any_email(commit.committer_email)如果它返回
nil,则电子邮件地址未与用户关联,检查失败。 -
确认开发者的电子邮件地址已验证。此检查必须返回 true:
signature.user.verified_emails.include?(commit.committer_email)如果前一个检查返回
nil,此命令将显示错误:NoMethodError (undefined method `verified_emails' for nil:NilClass) -
验证状态存储在数据库中。要显示数据库记录:
pp CommitSignatures::X509CommitSignature.by_commit_sha(commit.sha);nil如果所有前面的检查都返回了正确的值:
-
verification_status: "unverified"表示数据库记录需要 更新。使用 Rake 任务。 -
[]表示数据库还没有记录。在 GitLab 中查找提交 以检查签名并存储结果。
-
加密验证检查
如果 GitLab 确定 verified_signature 为 false,在 Rails 控制台中调查原因。
这些检查需要 signature 存在。参考前面 主要验证检查 的 signature 步骤。
-
检查签名,不检查颁发者,应返回
true:signature.__send__ :valid_signature? -
检查签名时间和日期。此检查必须返回
true:signature.__send__ :valid_signing_time?-
代码允许代码签名证书过期。
-
提交必须在证书的有效期内签名, 并且在或晚于提交的时间戳。显示提交时间和 证书详细信息,包括
not_before、not_after:commit.created_at pp signature.__send__ :cert; nil
-
-
检查签名,包括可以建立 TLS 信任。此检查必须返回
true:signature.__send__(:p7).verify([], signature.__send__(:cert_store), signature.__send__(:signed_text))-
如果此失败,添加建立信任所需的缺失证书 到 GitLab 证书存储。
-
添加更多证书后,(如果这些故障排除步骤通过) 运行 Rake 任务 重新验证提交。
-
你可以在 Rails 控制台中动态添加额外证书来检查 是否解决了问题。
-
使用可修改的信任存储
cert_store重新测试签名。 它应该仍然失败,返回false:cert_store = signature.__send__ :cert_store signature.__send__(:p7).verify([], cert_store, signature.__send__(:signed_text)) -
添加额外证书,并重新测试:
cert_store.add_file("/etc/ssl/certs/my_new_root_ca.pem") signature.__send__(:p7).verify([], cert_store, signature.__send__(:signed_text))
-
-
显示签名中包含的证书:
pp signature.__send__(:p7).certificates ; nil
-
确保任何额外的中间证书和根证书都已添加 到证书存储中。为了与 Web 服务器上构建证书链的方式保持一致:
- 签名提交的 Git 客户端应在签名中包含证书 和所有中间证书。
- GitLab 证书存储应仅包含根证书。
如果你从 GitLab
信任存储中删除根证书,例如当它过期时,链接回该根的提交签名将显示为 unverified。
使用 OpenSSL 进行 S/MIME 验证
如果签名有问题,或者 TLS 信任失败,可以使用 OpenSSL 在命令行上进行进一步调试。
从 Rails 控制台 导出签名和已签名文本:
-
主要验证检查 的前两个步骤是必需的,这样
signature就已设置。 -
OpenSSL 要求 PKCS7 PEM 格式数据以
BEGIN PKCS7和END PKCS7为界,所以这通常需要修复:pkcs7_text = signature.signature_text.sub('-----BEGIN SIGNED MESSAGE-----', '-----BEGIN PKCS7-----') pkcs7_text = pkcs7_text.sub('-----END SIGNED MESSAGE-----', '-----END PKCS7-----') -
写出签名和已签名文本:
f1=File.new('/tmp/signature_text.pk7.pem','w') f1 << pkcs7_text f1.close f2=File.new('/tmp/signed_text.txt','w') f2 << signature.signed_text f2.close
现在可以使用 OpenSSL 在 Linux 命令行上调查这些数据:
-
可以查询包含签名的 PKCS #7 文件:
/opt/gitlab/embedded/bin/openssl pkcs7 -inform pem -print_certs \ -in /tmp/signature_text.pk7.pem -print -noout它应该在输出中包含至少一个
cert部分;签名者的证书。输出中有大量低级别的详细信息。以下是应该存在的一些结构和标题的示例:
PKCS7: d.sign: cert: cert_info: issuer: validity: notBefore: notAfter: subject:如果开发者的代码签名证书由中间证书颁发机构颁发, 应该有额外的证书详细信息:
PKCS7: d.sign: cert: cert_info: cert: cert_info: -
从签名中提取证书:
/opt/gitlab/embedded/bin/openssl pkcs7 -inform pem -print_certs \ -in /tmp/signature_text.pk7.pem -out /tmp/signature_cert.pem如果此步骤失败,签名可能缺少签名者的证书。
- 在 Git 客户端上修复此问题。
- 下一步将失败,但如果你将签名者的证书复制到
GitLab 服务器,你可以使用
-nointern -certfile signerscertificate.pem进行一些测试。
-
使用提取的证书部分验证提交:
/opt/gitlab/embedded/bin/openssl smime -verify -binary -inform pem \ -in /tmp/signature_text.pk7.pem -content /tmp/signed_text.txt \ -noverify -certfile /tmp/signature_cert.pem -nointern输出通常包括:
- 父提交
- 提交中的名称、电子邮件和时间戳
- 提交文本
Verification successful(或类似内容)
此检查与 GitLab 执行的检查不同,因为:
- 它不验证签名者的证书 (
-noverify) - 验证是使用提供的
-certfile而不是消息中的证书 (-nointern) 进行的
-
使用消息中的证书部分验证提交:
/opt/gitlab/embedded/bin/openssl smime -verify -binary -inform pem \ -in /tmp/signature_text.pk7.pem -content /tmp/signed_text.txt \ -noverify这应该与上一步使用提取的证书获得相同的结果。
如果消息中缺少证书,错误包括
signer certificate not found。 -
完全验证提交:
/opt/gitlab/embedded/bin/openssl smime -verify -binary -inform pem \ -in /tmp/signature_text.pk7.pem -content /tmp/signed_text.txt如果此步骤失败,GitLab 中的验证也会失败。
解决任何错误,例如:
certificate verify error .. unable to get local issuer certificate:- 无法建立信任链。
- 此 OpenSSL 二进制文件使用 GitLab 信任存储。要么信任存储中缺少根证书,
要么签名缺少中间证书,无法构建到受信任根的链。
- 如果不可能将中间证书包含在签名中,可以将它们放入信任存储。
- 将证书添加到信任存储
的过程适用于打包的 GitLab - 使用
/etc/gitlab/trusted-certs。
- 使用以下命令使用 OpenSSL 测试额外的受信任证书:
-CAfile /path/to/rootcertificate.pem
unsupported certificate purpose:-
证书必须在签名者证书的
X509v3 Key Usage部分指定Digital Signature。 -
如果指定了
X509v3 Extended Key Usage(EKU) 部分,它必须包含emailProtection。 有关更多详细信息,请参阅 RFC 5280:如果没有与 (Key Usage) 扩展一致的目的,则该证书不得用于任何目的。
如果向 EKU 列表的此添加不能解决问题, 请在 issue 440189 中提供反馈。
-
signer certificate not found,要么:- 你添加了
-nointern参数,但没有提供-certfile。 - 签名缺少签名者的证书。
- 你添加了