X.509証明書を使用してコミットとタグに署名する
- プラン: Free、Premium、Ultimate
- 提供形態: GitLab Self-Managed、GitLab Dedicated
X.509は、公開または非公開の公開鍵基盤(PKI)によって発行される公開キー証明書の標準形式です。個人のX.509証明書は、S/MIME(Secure/Multipurpose Internet Mail Extensions)などの認証または署名目的で使用されます。ただし、GitもGPG(GnuPGまたはGNU Privacy Guard)と同様の方法で、X.509証明書を使用したコミットとタグの署名もサポートしています。主な違いは、GitLabがデベロッパーの署名が信頼できるかどうかを判断する方法です:
- X.509の場合、ルート認証局がGitLabトラストストアに追加されます。(トラストストアは、信頼できるセキュリティ証明書のリポジトリです)。署名に必要な中間証明書と組み合わせることで、デベロッパーの証明書を信頼できるルート証明書にチェーンバックすることができます。
- GPGの場合、デベロッパーは自分のGPGキーをアカウントに追加します。
GitLabは独自の証明書ストアを使用しているため、トラストチェーンを定義します。コミットまたはタグがGitLabによって検証されるには:
- 署名証明書のメールが、GitLabで検証済みのメールアドレスと一致する必要があります。
- GitLabインスタンスには、署名内の証明書からGitLab証明書ストア内の信頼できる証明書への完全なトラストチェーンが必要です。このチェーンには、署名に含まれる中間証明書が含まれる場合があります。認証局ルート証明書などの証明書をGitLab証明書ストアに追加する必要がある場合があります。
- 署名時刻は、証明書の有効期間内である必要があります。通常、最大3年です。
- 署名時刻は、コミット時刻と同じか、それ以降である必要があります。
信頼チェーン内のルート認証局または中間証明書が期限切れとなり更新された場合、コミットは再検証されるまで一時的に「未検証」と表示される可能性があります。
コミットのステータスがすでに決定され、データベースに保存されている場合は、Rakeタスクを使用してステータスを再確認します。GitLabは、バックグラウンドワーカーを使用して、証明書失効リストを毎日確認します。
既知の問題
authorityKeyIdentifier、subjectKeyIdentifier、crlDistributionPointsがない証明書は、未検証と表示されます。RFC 5280に準拠したPKIからの証明書の使用をお勧めします。GitLab.comの提供では、検証済みバッジは表示されません。これは、カスタム認証局(CA)をアップロードする機能がGitLab Self-Managedでのみ利用可能であるためです。
必須のKey Usage(KU)
Digital Signatureに加えて、証明書のExtended Key Usage(EKU)セクションに値を設定すると、コミットが未検証と表示される可能性があります。これを解決するには、EKUリストにemailProtectionを追加します。RFC 5280に、この制限事項が規定されています。診断するには、OpenSSLを使用したS/MIME検証に従ってください。
GitLab 16.2以前では、署名証明書のSubject Alternative Nameリストに複数のメールがある場合、最初のメールのみがコミットの検証に使用されます。
署名付きコミットを設定する
コミット、タグ、またはその両方に署名するには、次の手順を実行する必要があります:
X.509キーペアを取得する
組織に公開鍵基盤(PKI)がある場合、そのPKIがS/MIMEキーを提供します。PKIからのS/MIMEキーペアがない場合は、独自の自己署名キーペアを作成するか、キーペアを購入してください。
X.509証明書をGitに関連付ける
X.509署名を利用するには、Git 2.19.0以降が必要です。Gitのバージョンは、git --versionコマンドで確認できます。
正しいバージョンがある場合は、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 x509Windowsまたは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である必要があります。
コミットがUnverifiedと表示される理由を調査するには:
Railsコンソールを起動します:
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, commit.project)チェックを実行して特定された問題に対処するために変更を加えた場合は、Railsコンソールを再起動し、最初からもう一度チェックを実行します。
コミットの証明書を確認します:
signature.x509_certificate.nil? signature.x509_certificate.revoked?両方のチェックで
falseが返される必要があります:> signature.x509_certificate.nil? => false > signature.x509_certificate.revoked? => false署名の暗号学的チェックを実行します。コードは
trueを返す必要があります:signature.verified_signaturefalseを返す場合は、このチェックをさらに調査してください。コミットと署名のメールアドレスが一致することを確認します:
- Railsコンソールには、比較されるメールアドレスが表示されます。
- 最後のコマンドは
trueを返す必要があります:
sigemail=signature.__send__:certificate_email commitemail=commit.committer_email sigemail == commitemailGitLab 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コンソールから署名と署名付きテキストをエクスポートします:
主な検証チェックの最初の2つの手順を実行して、
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
これで、LinuxコマンドラインでOpenSSLを使用してこのデータを調査できます:
署名を含むPKCS #7ファイルをクエリできます:
/opt/gitlab/embedded/bin/openssl pkcs7 -inform pem -print_certs \ -in /tmp/signature_text.pk7.pem -print -noout出力には、少なくとも1つの
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) - メッセージ内のもの(
-nointern)ではなく、指定された-certfileを使用して検証が行われる
メッセージ内の証明書を使用してコミットを部分的に検証します:
/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)拡張と一致する目的がない場合、証明書はいかなる目的にも使用できません。
signer certificate not found、次のいずれか:- 引数
-nointernを追加したが、-certfileを指定していない。 - 署名に署名者の証明書がない。
- 引数