X.509 인증서로 커밋 및 태그 서명
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년인 인증서 유효 기간 내에 있어야 합니다.
- 서명 시간은 커밋 시간과 같거나 이후여야 합니다.
신뢰 체인의 루트 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 목록에 이메일이 두 개 이상 있는 경우, 첫 번째 이메일만 커밋 확인에 사용됩니다.
서명된 커밋 구성#
커밋, 태그 또는 둘 다 서명하려면 다음 단계를 수행해야 합니다:
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를 구성하려면:
-
다음 중 하나로 S/MIME Sign을 설치합니다:
- 설치 프로그램 다운로드.
- macOS에서
brew install smimesign실행.
-
smimesign --list-keys를 실행하여 인증서 ID를 가져옵니다. -
git config --global user.signingkey를 실행하여 서명 키를 설정합니다.를 인증서 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로 표시되는 이유를 조사하려면:
-
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.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
-
추가 중간 인증서와 루트 인증서가 인증서 저장소에 추가되어 있는지 확인합니다. 웹 서버에서 인증서 체인이 구성되는 방식과의 일관성을 위해:
- 커밋에 서명하는 Git 클라이언트는 인증서와 모든 중간 인증서를 서명에 포함해야 합니다.
- GitLab 인증서 저장소는 루트만 포함해야 합니다.
만료 시 GitLab 신뢰 저장소에서 루트 인증서를 제거하면
해당 루트로 체인이 연결되는 커밋 서명이 unverified로 표시됩니다.
OpenSSL을 사용한 S/MIME 확인#
서명에 문제가 있거나 TLS 신뢰가 실패하는 경우 명령줄에서 OpenSSL을 사용하여 추가 디버깅을 수행할 수 있습니다.
Rails 콘솔에서 서명과 서명된 텍스트를 내보냅니다:
-
주요 확인 검사의 첫 두 단계가 필요하므로
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
이제 이 데이터를 OpenSSL을 사용하여 Linux 명령줄에서 조사할 수 있습니다:
-
서명이 포함된 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: -
서명에서 인증서를 추출합니다:
/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을 참조하세요:(키 사용) 두 확장 모두와 일치하는 목적이 없으면 인증서를 어떤 목적으로도 사용해서는 안 됩니다.
-
-
signer certificate not found, 다음 중 하나:-nointern인수를 추가했지만-certfile을 제공하지 않았습니다.- 서명에 서명자의 인증서가 없습니다.
-
