正式なドキュメントは英語版であり、この日本語訳はAI支援翻訳により作成された参考用のものです。日本語訳の一部の内容は人間によるレビューがまだ行われていないため、翻訳のタイミングにより英語版との間に差異が生じることがあります。最新かつ正確な情報については、英語版をご参照ください。

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は、バックグラウンドワーカーを使用して、証明書失効リストを毎日確認します。

既知の問題

  • authorityKeyIdentifiersubjectKeyIdentifier、およびcrlDistributionPointsがない証明書は、未検証と表示されます。RFC 5280に準拠したPKIからの証明書を使用することをお勧めします。

  • 検証済みバッジは、カスタム認証局のアップロードがGitLab Self-Managedでのみ可能なため、GitLab SaaSオファリングには表示されません。

  • 証明書のExtended Key Usage(EKU)セクションに、必須のKey Usage(KU)Digital Signatureに加えて値を設定すると、コミットが未検証と表示される可能性があります。これを解決するには、EKUリストにemailProtectionを追加します。RFC 5280に、この制限事項が規定されています。

    診断するには、OpenSSLを使用したS/MIME検証済みに従ってください。この変更でイシューが解決しない場合は、イシュー440189でフィードバックをお寄せください。

  • GitLab 16.2以前のバージョンでは、署名証明書のSubject Alternative Nameリストに複数のメールがある場合、最初のメールのみがコミットの検証済みに使用されます

署名されたコミットの設定

コミット、タグ、またはその両方に署名するには、次の手順を実行する必要があります:

  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の設定に進むことができます。

署名にキーを使用するように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を設定するには:

  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は、チェックされたコミットのステータスをデータベースに保存します。次の後、コミットを再検証済みできます:

  • ルート認証局または中間証明書を更新します。
  • 証明書ストアに変更を加えます。

コミットを再検証済みするには:

  1. ルート認証局とすべての中間証明書がGitLab証明書ストアにあることを確認します。
  2. 以前に検証済みのコミットのステータスを確認および更新するには、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未検証と表示される理由を調査するには:

  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, commit.project)

    チェックを実行して特定されたイシューに対処するために変更を加えた場合は、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

    GitLab 16.2以前では、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. デベロッパーのメールアドレスが検証済みであることを確認します。このチェックでは、trueが返されるはずです:

    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ステップを参照してください。

  1. 発行者を確認せずに署名を確認すると、trueが返されます:

    signature.__send__ :valid_signature?
  2. 署名時刻と日付を確認します。このチェックではtrueが返される必要があります:

    signature.__send__ :valid_signing_time?
    • コードは、コード署名証明書の有効期限が切れることを許可します。

    • コミットは、証明書の有効期間中、およびコミットのデータスタンプ以降に署名する必要があります。not_beforenot_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. 問題を解決するかどうかを確認するために、Railsコンソールで追加の証明書を動的に追加できます。

      1. 変更可能なトラストストアcert_storeを使用して、署名を再テストします。それでもfalseで失敗するはずです:

        cert_store = signature.__send__ :cert_store
        signature.__send__(:p7).verify([], cert_store, signature.__send__(:signed_text))
      2. 追加の証明書を追加して、再テストします:

        cert_store.add_file("/etc/ssl/certs/my_new_root_ca.pem")
        signature.__send__(:p7).verify([], cert_store, signature.__send__(:signed_text))
    4. 署名に含まれる証明書を表示します:

      pp signature.__send__(:p7).certificates ; nil
    5. OpenSSLを使用してコマンドラインでさらに調査を実行できます

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

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

期限切れなどの理由で、GitLabトラストストアからルート証明書を削除すると、そのルートにチェーンバックするコミット署名がunverified未検証と表示されます。

OpenSSLを使用したS/MIME検証済み

署名にイシューがある場合、またはTLSトラストが失敗した場合、コマンドラインでOpenSSLを使用してさらにデバッグを実行できます。

Railsコンソールから署名と署名付きテキストをエクスポートします:

  1. 主な検証済みチェックの最初の2つのステップは、signatureが設定されている必要があるため、必須です。

  2. OpenSSLでは、PKCS7 PEM形式のデータがBEGIN PKCS7END PKCS7で区切られている必要があるため、通常、これを修正する必要があります:

    pkcs7_text = signature.signature_text.sub('-----BEGIN SIGNED MESSAGE-----', '-----BEGIN PKCS7-----')
    pkcs7_text = pkcs7_text.sub('-----END SIGNED MESSAGE-----', '-----END PKCS7-----')
  3. 署名と署名付きテキストを書き出します:

    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を使用してこのデータを調査できます:

  1. 署名を含む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:
  2. 署名から証明書を抽出します:

    /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を使用してテストを実行できます。
  3. 抽出された証明書を使用して、コミットを部分的に検証済みにします:

    /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を使用して行われます
  4. メッセージ内の証明書を使用して、コミットを部分的に検証済みにします:

    /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が含まれます。

  5. コミットを完全に検証済みにします:

    /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を提供していません。
      • 署名に署名者の証明書がありません。