X.509 인증서로 커밋 및 태그에 서명하기
X.509는 공용 또는 사설 공개키 기반 기반 인프라(PKI)에 의해 발급된 공개 키 인증서의 표준 형식입니다. 개인 X.509 인증서는 S/MIME (Secure/Multipurpose Internet Mail Extensions)과 같은 인증 또는 서명 목적으로 사용됩니다. 그러나 Git은 GPG(GnuPG 또는 GNU Privacy Guard)와 유사한 방식으로 X.509 인증서로 커밋 및 태그에 서명하는 것을 지원합니다. 주요 차이점은 GitLab이 개발자 서명이 신뢰할 수 있는지 여부를 결정하는 방법입니다.
- X.509의 경우 루트 인증 기관이 GitLab 신뢰 저장소에 추가됩니다. (신뢰 저장소는 신뢰할 수 있는 보안 인증서의 저장소입니다.) 개발자 인증서가 신뢰된 루트 인증서로 연결되도록 필요한 중간 인증서와 결합되면 GitLab은 이를 신뢰할 수 있습니다.
- GPG의 경우 개발자가 계정에 GPG 키를 추가합니다.
GitLab은 고유의 인증서 저장소를 사용하며 따라서 신뢰 체인을 정의합니다. GitLab에서 검증되기 위해 커밋 또는 태그에 대한 다음 조건이 충족되어야 합니다: - 서명 인증서 이메일은 GitLab의 확인된 이메일 주소와 일치해야 합니다. - GitLab 인스턴스는 서명에 사용된 인증서에서 시작하여 GitLab 인증서 저장소의 신뢰할 수 있는 인증서에 이르는 완전한 신뢰 체인을 설정해야 합니다. 이 신뢰 체인에는 서명에서 제공된 중간 인증서를 포함할 수 있습니다. 인증서 관리자 루트 인증서와 같은 인증서를 GitLab 인증서 저장소에 추가해야 할 수 있습니다. - 서명 시간은 일반적으로 최대 세 년까지인 인증서 유효 기간 내에 있어야 합니다. - 서명 시간은 커밋 시간과 동일하거나 그 이후여야 합니다.
커밋 상태가 이미 결정되고 데이터베이스에 저장된 경우, Rake 작업을 사용하여 상태를 다시 확인할 수 있습니다. 문제 해결 섹션을 참조하세요. GitLab은 백그라운드 워커를 사용하여 매일 인증서 폐지 목록을 확인합니다.
제한 사항
-
authorityKeyIdentifier
,subjectKeyIdentifier
,crlDistributionPoints
가 없는 인증서는 Unverified로 표시됩니다. RFC 5280과 일치하는 PKI의 인증서를 사용하는 것을 권장합니다. - GitLab 16.2 및 이전 버전에서 서명 인증서의 Subject Alternative Name 목록에 이메일이 하나 이상 있는 경우, 첫 번째 이메일만 사용되어 커밋이 검증됩니다.
- GitLab 15.1 및 이전 버전에서 발급자 인증서의
X509v3 Subject Key Identifier
(SKI) 및 서명 인증서가 40자여야 합니다. SKI가 더 짧으면 GitLab에서 커밋이 검증되지 않으며, 짧은 주제 키 식별자는 또한 프로젝트에 액세스할 때 오류를 일으킬 수 있습니다. ‘커밋 서명 로드 중 오류 발생’ 및HTTP 422 Unprocessable Entity
오류 등.
서명된 커밋을 구성
커밋, 태그 또는 둘 다를 서명하려면 다음을 수행해야 합니다:
X.509 키 쌍 얻기
조직이 공개 키 기반 인프라 (PKI)를 가지고 있는 경우 해당 PKI에서 S/MIME 키를 제공합니다. PKI에서 S/MIME 키 쌍을 제공받지 못한 경우, 자체적으로 자체 서명 페어를 생성하거나 페어를 구매할 수 있습니다.
Git과 X.509 인증서 연결
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
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
커밋 서명과 확인
GitLab와 X.509 인증서를 연결한 후에 커밋에 서명할 수 있습니다:
-
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
태그 서명과 확인
GitLab와 X.509 인증서를 연결한 후 태그에 서명할 수 있습니다:
-
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
주요 확인 사항
코드는 다음의 주요 확인 사항들을 수행합니다 (https://gitlab.com/gitlab-org/gitlab/-/blob/v14.1.0-ee/lib/gitlab/x509/signature.rb#L33):
-
x509_certificate.nil?
이 false여야 합니다. -
x509_certificate.revoked?
이 false여야 합니다. -
verified_signature
이 true여야 합니다. -
user.nil?
이 false여야 합니다. -
user.verified_emails.include?(@email)
이 true여야 합니다. -
certificate_email == @email
이 true여야 합니다.
미확인
으로 표시되는 커밋의 원인을 조사하기 위해:
-
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
GitLab 16.2 및 이전 버전에서는 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
이면 이메일 주소가 사용자와 연결되지 않았으며 확인이 실패합니다. -
개발자 이메일 주소가 확인되었는지 확인하세요. 이 확인은
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
가 존재해야합니다. 이전 main verification checks의 signature
단계를 참조하세요.
-
확인을 위해 서명을 확인하세요(
issuer
확인 없이):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 인증서 저장소에 추가하세요.
-
추가한 후, (만약 이러한 문제 해결 단계를 통과한다면) 커밋 재확인 작업을 실행하세요.
-
문제를 해결하기 위해 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
-
OpenSSL을 사용하여 추가 조사를 수행할 수 있습니다.
-
추가 중간 인증서 및 루트 인증서를 인증서 저장소에 추가하세요. 웹 서버에서 인증서 체인이 구축되는 방식과 일관성을 유지하기 위해:
- 커밋을 서명하는 Git 클라이언트는 서명에 루트 및 모든 중간 인증서를 포함해야합니다.
- GitLab 인증서 저장소에는 루트만 포함해야합니다.
만약 GitLab의 신뢰 저장소에서 루트 인증서를 제거한다면, 예를 들어 만료되었을 때, 해당 루트로 연결된 커밋 서명이 unverified
로 표시됩니다.
OpenSSL을 사용한 S/MIME 검증
서명에 문제가 있거나, TLS 신뢰에 실패하는 경우 추가 디버깅을 위해 OpenSSL을 사용하여 명령줄에서 수행할 수 있습니다.
Rails 콘솔에서 서명 및 서명된 텍스트를 내보낼 수 있습니다:
-
이전 main verification checks의 초기 두 단계는 필요하므로
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
) - 검증은 제공된
-certfile
을 사용하여 진행되며 메시지에 포함된 인증서 대신 진행됩니다 (-nointern
)
-
메시지의 인증서를 사용하여 커밋을 부분적으로 확인하세요:
/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 신뢰 저장소를 사용합니다. 신뢰 저장소에서 루트 인증서가 누락되었거나 서명에 중간 인증서가 누락되어 신뢰할 수 있는 루트로의 체인을 구축할 수 없습니다.
- 서명에 포함되지 않으므로 중간 인증서를 신뢰 저장소에 넣을 수 있습니다.
-
인증서 추가 절차는
/etc/gitlab/trusted-certs
를 사용하여 패키지화된 GitLab을 위해 사용하세요.
- OpenSSL을 사용하여 추가 신뢰할 수 있는 인증서를 테스트하세요:
-CAfile /path/to/rootcertificate.pem
-
unsupported certificate purpose
:- 인증서는 반드시
Digital Signature
를 지정해야합니다. - 이것은 보통 서명자의 인증서의
X509v3 Key Usage
섹션에 있습니다. -
또한
X509v3 Extended Key Usage
섹션이 있습니다: 여기에 지정된 경우 반드시Digital Signature
를 포함해야합니다. 더 자세한 내용은 RFC 5280을 참조하세요:두 (Key Usage) 확장명 모두와 일치하는 용도가 없으면 인증서를 어떤 목적으로도 사용할 수 없습니다.
- 인증서는 반드시
-
signer certificate not found
, 아래 중 하나:-
-nointern
인자를 추가했지만-certfile
을 제공하지 않았습니다. - 서명에 서명자의 인증서가 누락되었습니다.
-
-