X.509証明書によるコミットとタグの署名
GitLab 12.8で導入されました。
X.509は、公開または非公開の秘密鍵基盤(PKI) によって発行される公開鍵証明書の標準フォーマットです。個人のX.509証明書は、S/MIME(Secure/Multipurpose Internet Mail Extensions)のような認証や署名の目的で使われます。しかし、GitもGPG(GnuPG、GNU Privacy Guard)と同様にX.509証明書によるコミットやタグの署名をサポートしています。主な違いは、開発者の署名が信頼できるかどうかをGitLabが判断する方法です:
- X.509の場合、ルート認証局がGitLabトラストストアに追加されます。(X.509の場合、ルート認証局がGitLabトラストストアに追加されます(トラストストアとは、信頼できるセキュリティ証明書のリポジトリです)。署名に必要な中間証明書と組み合わせることで、開発者の証明書は信頼できるルート証明書にチェーンバックすることができます。
- GPG については、開発者は自分のアカウントにGPG 鍵を追加します。
GitLabは独自の証明書ストアを使用するため、トラストチェーンを定義します。コミットやタグがGitLabによって検証されるには:
- 署名証明書のEメールアドレスがGitLabで認証されたEメールアドレスと一致する必要があります。
- GitLabインスタンスは、署名の証明書からGitLab証明書ストアの信頼できる証明書までの完全な信頼チェーンを確立できなければなりません。このチェーンには、署名で提供された中間証明書を含めることができます。作成者ルート証明書などの証明書をGitLab証明書ストアに追加する必要があるかもしれません。
- 署名時間は証明書の有効期間内である必要があり、通常は最長3年です。
- 署名時刻はコミット時刻と等しいか、それよりも遅い。
コミットのステータスが既に決定されデータベースに保存されている場合は、Rakeタスクを使用してステータスを再確認してください。トラブルシューティングのセクションを参照してください。GitLabはバックグラウンドワーカーで証明書の失効リストを毎日チェックします。
制限事項
-
authorityKeyIdentifier
、subjectKeyIdentifier
、crlDistributionPoints
を使用しない自己署名証明書には対応していません。RFC 5280に準拠したPKIからの証明書を使用することをお勧めします。 - 署名証明書の Subject Alternative Name リストに複数の電子メールがある場合、コミットの検証には最初のものだけが使用されます。
- 発行者証明書と署名証明書の
X509v3 Subject Key Identifier
(SKI) は40 文字でなければなりません。SKI が短いと GitLab でコミットが検証済みとして表示されません。また、短い Subject Key Identifier はプロジェクトにアクセスする際に‘An error occurred while loading commit signatures’ やHTTP 422 Unprocessable Entity
エラーを引き起こす可能性があります。
署名付きコミットの設定
コミット、タグ、またはその両方に署名するには、以下の設定が必要です:
X.509キー・ペアの取得
あなたの組織が公開鍵基盤(PKI)を持っている場合、その PKI は S/MIME 鍵を提供します。PKI から S/MIME 鍵ペアが提供されていない場合は、自分で自己署名ペアを作成するか、ペアを購入します。
X.509証明書をGitに関連付けます。
X.509署名を利用するには、Git 2.19.0以降が必要です。Gitのバージョンは、git --version
.
正しいバージョンであれば、Gitの設定に進むことができます。
Linux
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
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 はチェックしたコミットのステータスをデータベースに保存します。Rake タスクを使って、以前にチェックしたコミットのステータスをチェックすることができます。
変更したら、このコマンドを実行します:
sudo gitlab-rake gitlab:x509:update_signatures
主な検証チェック
このコードでは以下のキー・チェックを行い、すべてverified
を返す必要があります:
-
x509_certificate.nil?
は偽であるべきです。 -
x509_certificate.revoked?
は偽であるべきです。 -
verified_signature
は真であるべきです。 -
user.nil?
は偽であるべきです。 -
user.verified_emails.include?(@email)
は真であるべきです。 -
certificate_email == @email
は真であるべきです。
コミットがなぜ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)
チェックの結果判明したイシューに対処するために変更を加えた場合は、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
既知のイシューがあります:
Subject Alternative Name
リストのSubject Alternative Name
最初のメールだけがSubject Alternative Name
比較されます。Subject Alternative Name
リストを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
を返した場合、メールアドレスはユーザーに関連付けられておらず、チェックは失敗します。 -
開発者のメールアドレスが検証済みであることを確認します。このチェックは真を返さなければなりません:
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
する必要があります。前の主な検証チェックのステップを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タスクを実行してコミットを再確認します。
-
署名を含む証明書を表示します:
pp signature.__send__(:p7).certificates ; nil
-
追加の中間証明書とルート証明書が証明書ストアに追加されていることを確認します。ウェブサーバでの証明書チェーンの構築方法との一貫性を保つため:
- コミットに署名する Git クライアントは、証明書とすべての中間証明書を署名に含めるようにしましょう。
- GitLab証明書ストアにはルート証明書のみを含める必要があります。
GitLabトラストストアからルート証明書を削除した場合、例えばそれが期限切れになった場合、そのルートにチェーンバックするコミット署名はunverified
と表示されます。