InfoGrab Docs

X.509 인증서로 커밋 및 태그 서명

요약

X.509는 공공 또는 사설 공개 키 인프라(PKI)에서 발급하는 공개 키 인증서의 표준 형식입니다. GitLab은 자체 인증서 저장소를 사용하므로 신뢰 체인을 정의합니다. 신뢰 체인의 루트 CA 또는 중간 인증서가 만료되어 갱신된 경우, 재확인하기 전까지 커밋이 일시적으로 "unverified"로 표시될 수 있습니다.

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년인 인증서 유효 기간 내에 있어야 합니다.
  • 서명 시간은 커밋 시간과 같거나 이후여야 합니다.
Note

신뢰 체인의 루트 CA 또는 중간 인증서가 만료되어 갱신된 경우, 재확인하기 전까지 커밋이 일시적으로 "unverified"로 표시될 수 있습니다.

커밋 상태가 이미 결정되어 데이터베이스에 저장된 경우, Rake 태스크를 사용하여 상태를 재확인합니다. GitLab은 백그라운드 워커를 사용하여 매일 인증서 해지 목록을 확인합니다.

알려진 이슈#

  • authorityKeyIdentifier, subjectKeyIdentifier, crlDistributionPoints가 없는 인증서는 Unverified로 표시됩니다. RFC 5280에 따른 PKI의 인증서를 사용하는 것을 권장합니다.

  • 사용자 지정 인증기관(CA) 업로드는 GitLab Self-Managed에서만 가능하므로 GitLab SaaS 제품에서는 Verified 배지가 표시되지 않습니다.

  • Digital Signature의 필수 키 사용(KU) 외에 인증서의 확장 키 사용(EKU) 섹션에 값을 설정하면 커밋이 Unverified로 표시될 수 있습니다. 이를 해결하려면 EKU 목록에 emailProtection을 추가합니다. RFC 5280이 이 제한 사항을 명시합니다.

    진단하려면 OpenSSL을 사용한 S/MIME 확인을 따릅니다.

  • 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 --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를 구성하려면:

  1. 다음 중 하나로 S/MIME Sign을 설치합니다:

    • 설치 프로그램 다운로드.
    • macOS에서 brew install smimesign 실행.
  2. smimesign --list-keys를 실행하여 인증서 ID를 가져옵니다.

  3. git config --global user.signingkey 를 실행하여 서명 키를 설정합니다. 를 인증서 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은 확인된 커밋의 상태를 데이터베이스에 저장합니다. 다음 후에 커밋을 재확인할 수 있습니다:

  • 루트 CA 또는 중간 인증서 갱신.
  • 인증서 저장소 변경.

커밋을 재확인하려면:

  1. 루트 CA와 모든 중간 인증서가 GitLab 인증서 저장소에 있는지 확인합니다.
  2. update_signatures Rake 태스크를 실행하여 이전에 확인된 커밋의 상태를 확인하고 업데이트합니다.

주요 확인 검사#

코드는 이러한 주요 검사를 수행하며, 모두 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
    
  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.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_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. 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을 사용하여 추가 조사를 수행할 수 있습니다.

추가 중간 인증서와 루트 인증서가 인증서 저장소에 추가되어 있는지 확인합니다. 웹 서버에서 인증서 체인이 구성되는 방식과의 일관성을 위해:

  • 커밋에 서명하는 Git 클라이언트는 인증서와 모든 중간 인증서를 서명에 포함해야 합니다.
  • GitLab 인증서 저장소는 루트만 포함해야 합니다.

만료 시 GitLab 신뢰 저장소에서 루트 인증서를 제거하면 해당 루트로 체인이 연결되는 커밋 서명이 unverified로 표시됩니다.

OpenSSL을 사용한 S/MIME 확인#

서명에 문제가 있거나 TLS 신뢰가 실패하는 경우 명령줄에서 OpenSSL을 사용하여 추가 디버깅을 수행할 수 있습니다.

Rails 콘솔에서 서명과 서명된 텍스트를 내보냅니다:

  1. 주요 확인 검사의 첫 두 단계가 필요하므로 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
    

이제 이 데이터를 OpenSSL을 사용하여 Linux 명령줄에서 조사할 수 있습니다:

  1. 서명이 포함된 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:
    
  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을 참조하세요:

        (키 사용) 두 확장 모두와 일치하는 목적이 없으면 인증서를 어떤 목적으로도 사용해서는 안 됩니다.

    • signer certificate not found, 다음 중 하나:

      • -nointern 인수를 추가했지만 -certfile을 제공하지 않았습니다.
      • 서명에 서명자의 인증서가 없습니다.

X.509 인증서로 커밋 및 태그 서명

Tier: Free, Premium, Ultimate
Offering: GitLab Self-Managed, GitLab Dedicated
원문 보기
요약

X.509는 공공 또는 사설 공개 키 인프라(PKI)에서 발급하는 공개 키 인증서의 표준 형식입니다. GitLab은 자체 인증서 저장소를 사용하므로 신뢰 체인을 정의합니다. 신뢰 체인의 루트 CA 또는 중간 인증서가 만료되어 갱신된 경우, 재확인하기 전까지 커밋이 일시적으로 "unverified"로 표시될 수 있습니다.

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년인 인증서 유효 기간 내에 있어야 합니다.
  • 서명 시간은 커밋 시간과 같거나 이후여야 합니다.
Note

신뢰 체인의 루트 CA 또는 중간 인증서가 만료되어 갱신된 경우, 재확인하기 전까지 커밋이 일시적으로 "unverified"로 표시될 수 있습니다.

커밋 상태가 이미 결정되어 데이터베이스에 저장된 경우, Rake 태스크를 사용하여 상태를 재확인합니다. GitLab은 백그라운드 워커를 사용하여 매일 인증서 해지 목록을 확인합니다.

알려진 이슈#

  • authorityKeyIdentifier, subjectKeyIdentifier, crlDistributionPoints가 없는 인증서는 Unverified로 표시됩니다. RFC 5280에 따른 PKI의 인증서를 사용하는 것을 권장합니다.

  • 사용자 지정 인증기관(CA) 업로드는 GitLab Self-Managed에서만 가능하므로 GitLab SaaS 제품에서는 Verified 배지가 표시되지 않습니다.

  • Digital Signature의 필수 키 사용(KU) 외에 인증서의 확장 키 사용(EKU) 섹션에 값을 설정하면 커밋이 Unverified로 표시될 수 있습니다. 이를 해결하려면 EKU 목록에 emailProtection을 추가합니다. RFC 5280이 이 제한 사항을 명시합니다.

    진단하려면 OpenSSL을 사용한 S/MIME 확인을 따릅니다.

  • 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 --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를 구성하려면:

  1. 다음 중 하나로 S/MIME Sign을 설치합니다:

    • 설치 프로그램 다운로드.
    • macOS에서 brew install smimesign 실행.
  2. smimesign --list-keys를 실행하여 인증서 ID를 가져옵니다.

  3. git config --global user.signingkey 를 실행하여 서명 키를 설정합니다. 를 인증서 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은 확인된 커밋의 상태를 데이터베이스에 저장합니다. 다음 후에 커밋을 재확인할 수 있습니다:

  • 루트 CA 또는 중간 인증서 갱신.
  • 인증서 저장소 변경.

커밋을 재확인하려면:

  1. 루트 CA와 모든 중간 인증서가 GitLab 인증서 저장소에 있는지 확인합니다.
  2. update_signatures Rake 태스크를 실행하여 이전에 확인된 커밋의 상태를 확인하고 업데이트합니다.

주요 확인 검사#

코드는 이러한 주요 검사를 수행하며, 모두 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
    
  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.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_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. 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을 사용하여 추가 조사를 수행할 수 있습니다.

추가 중간 인증서와 루트 인증서가 인증서 저장소에 추가되어 있는지 확인합니다. 웹 서버에서 인증서 체인이 구성되는 방식과의 일관성을 위해:

  • 커밋에 서명하는 Git 클라이언트는 인증서와 모든 중간 인증서를 서명에 포함해야 합니다.
  • GitLab 인증서 저장소는 루트만 포함해야 합니다.

만료 시 GitLab 신뢰 저장소에서 루트 인증서를 제거하면 해당 루트로 체인이 연결되는 커밋 서명이 unverified로 표시됩니다.

OpenSSL을 사용한 S/MIME 확인#

서명에 문제가 있거나 TLS 신뢰가 실패하는 경우 명령줄에서 OpenSSL을 사용하여 추가 디버깅을 수행할 수 있습니다.

Rails 콘솔에서 서명과 서명된 텍스트를 내보냅니다:

  1. 주요 확인 검사의 첫 두 단계가 필요하므로 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
    

이제 이 데이터를 OpenSSL을 사용하여 Linux 명령줄에서 조사할 수 있습니다:

  1. 서명이 포함된 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:
    
  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을 참조하세요:

        (키 사용) 두 확장 모두와 일치하는 목적이 없으면 인증서를 어떤 목적으로도 사용해서는 안 됩니다.

    • signer certificate not found, 다음 중 하나:

      • -nointern 인수를 추가했지만 -certfile을 제공하지 않았습니다.
      • 서명에 서명자의 인증서가 없습니다.