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はバックグラウンドワーカーで証明書の失効リストを毎日チェックします。

制限事項

署名付きコミットの設定

コミット、タグ、またはその両方に署名するには、以下の設定が必要です:

  1. X.509キーペアを取得します。
  2. X.509証明書をGitに関連付けます
  3. コミットの署名と検証
  4. タグの署名と検証

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を設定します:

  1. S/MIME Signをインストールします:
    • インストーラをダウンロードします。
    • MacOS上でbrew install smimesign
  2. smimesign --list-keys を実行して証明書の ID を取得します。
  3. git config --global user.signingkey <ID> を実行し、<ID> を証明書の ID に置き換えて、署名鍵を設定します。
  4. このコマンドでX.509を設定します:

    git config --global gpg.x509.program smimesign
    git config --global gpg.format x509
    

コミットの署名と検証

X.509 証明書を Git に関連付けたら、コミットに署名することができます:

  1. Git のコミットを作成する際に、-S フラグを追加します:

    git commit -S -m "feat: x509 signed commits"
    
  2. GitLab にプッシュし、--show-signature フラグでコミットが検証されていることを確認します:

    git log --show-signature
    
  3. コミットするたびに-S フラグを入力したくない場合は、 このコマンドを実行すると Git が毎回署名してくれます:

    git config --global commit.gpgsign true
    

タグへの署名と確認

X.509証明書をGitに関連づけたら、タグに署名を始めましょう:

  1. Git タグを作成する際に、-s フラグを追加します:

    git tag -s v1.1.1 -m "My signed tag"
    
  2. GitLab にプッシュし、このコマンドでタグが署名されていることを確認してください:

    git tag --verify v1.1.1
    
  3. タグをつけるたびに-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 と表示されるのかを調べるには :

  1. Railsコンソールを起動します:

    sudo gitlab-rails console
    
  2. 調査対象のプロジェクト(パスまたは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コンソールを再起動してチェックを最初からやり直してください。

  3. コミットの証明書を確認します:

    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 で失敗します。

  4. 署名の暗号チェックを実行します。コードはtrue を返す必要があります:

    signature.verified_signature
    

    もしfalse を返したら、このチェックをさらに調査してください。

  5. コミットと署名のメールアドレスが一致していることを確認してください:

    • 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 とマークされます。

  6. コミットのメールアドレスは GitLab のアカウントに関連づけられていなければなりません。このチェックはfalse を返すはずです:

    signature.user.nil?
    
  7. メールアドレスがGitLabのユーザーに関連付けられているかチェックしてください。このチェックは#<User id:1234 @user_handle> のようなユーザーを返すはずです:

    User.find_by_any_email(commit.committer_email)
    

    nil を返した場合、メールアドレスはユーザーに関連付けられておらず、チェックは失敗します。

  8. 開発者のメールアドレスが検証済みであることを確認します。このチェックは真を返さなければなりません:

    signature.user.verified_emails.include?(commit.committer_email)
    

    前のチェックがnilを返した場合、このコマンドはエラーを表示します:

    NoMethodError (undefined method `verified_emails' for nil:NilClass)
    
  9. 検証ステータスはデータベースに保存されています。データベースのレコードを表示するには

    pp CommitSignatures::X509CommitSignature.by_commit_sha(commit.sha);nil
    

    前のチェックですべて正しい値が返された場合:

    • verification_status: "unverified" はデータベースレコードを更新する必要があることを示します。Rakeタスクを使用してください。

    • [] はデータベースにまだレコードがないことを示しています。GitLab でコミットを探して署名をチェックし、結果を保存します。

暗号化検証チェック

GitLabがverified_signaturefalse と判断した場合は、Railsコンソールでその理由を調べてください。これらのチェックがsignature 存在 signatureする必要があります。前の主な検証チェックのステップをsignature 参照して signatureください。

  1. 発行者をチェックせずに署名をチェックすると、true を返します:

    signature.__send__ :valid_signature?
    
  2. 署名日時をチェックします。このチェックはtrueを返さなければなりません:

    signature.__send__ :valid_signing_time?
    
    • コードは、コード署名証明書の有効期限を許可します。
    • コミットは証明書の有効期間中で、コミットの日付スタンプ以降に署名されなければなりません。not_before,not_after を含むコミット時刻と証明書の詳細を表示します:

       commit.created_at
       pp signature.__send__ :cert; nil
      
  3. TLSの信頼が確立できることを含め、署名をチェックします。このチェックはtrue を返す必要があります:

    signature.__send__(:p7).verify([], signature.__send__(:cert_store), signature.__send__(:signed_text))
    
    1. これに失敗した場合は、信頼を確立するために必要な証明書が足りない場合は、GitLab証明書ストアに追加してください。

    2. 証明書を追加した後、(このトラブルシューティングの手順が成功したら)Rakeタスクを実行してコミットを再確認します。

    3. 署名を含む証明書を表示します:

      pp signature.__send__(:p7).certificates ; nil
      

追加の中間証明書とルート証明書が証明書ストアに追加されていることを確認します。ウェブサーバでの証明書チェーンの構築方法との一貫性を保つため:

  • コミットに署名する Git クライアントは、証明書とすべての中間証明書を署名に含めるようにしましょう。
  • GitLab証明書ストアにはルート証明書のみを含める必要があります。

GitLabトラストストアからルート証明書を削除した場合、例えばそれが期限切れになった場合、そのルートにチェーンバックするコミット署名はunverifiedと表示されます。