X.509 인증서로 커밋 및 태그에 서명하기
X.509는 공개 또는 사설 공개 키 기반 구조(PKI)에서 발급된 공개 키 인증서의 표준 형식입니다. 개인용 X.509 인증서는 S/MIME(보안/다목적 인터넷 메일 확장)와 같은 인증이나 서명 목적으로 사용됩니다. 그러나 Git은 GPG(GnuPG 또는 GNU Privacy Guard)와 유사한 방식으로 X.509 인증서로 커밋 및 태그에 서명하는 것을 지원합니다. 주된 차이점은 GitLab이 개발자의 서명이 신뢰할 수 있는지 여부를 확인하는 방법입니다:
- X.509의 경우 루트 인증 기관이 GitLab의 신뢰 리포지터리에 추가됩니다. (신뢰 리포지터리는 신뢰할 수 있는 보안 인증서의 리포지터리입니다.) 서명에 필요한 중간 인증서와 결합하여, 개발자 인증서는 신뢰할 수 있는 루트 인증서까지 체인될 수 있습니다.
- GPG의 경우, 개발자는 GPG 키를 계정에 추가합니다.
GitLab은 자체 인증서 리포지터리를 사용하며, 그러므로 신뢰 체인을 정의합니다. GitLab이 확인하는 커밋 또는 태그에 대해:
- 서명 인증서의 이메일은 GitLab의 검증된 이메일 주소와 일치해야 합니다.
- GitLab 인스턴스는 서명에 있는 인증서에서 신뢰할 수 있는 GitLab 인증서 리포지터리에 있는 인증서까지 전체적인 신뢰 체인을 확립할 수 있어야 합니다. 이 신뢰 체인에는 서명에 제공된 중간 인증서들이 포함될 수 있습니다. 사용자가 GitLab 인증서 리포지터리에 인증서(예: 인증 기관 루트 인증서)를 추가해야 할 수도 있습니다.
- 서명 시간은 일반적으로 3년까지입니다. 인증서 유효 기간 내에 있어야 합니다.
- 서명 시간은 커밋 시간과 동일하거나 그 이후여야 합니다.
커밋 상태가 이미 확인되어 데이터베이스에 저장되어 있는 경우, 상태를 다시 확인하기 위해 Rake 작업을 사용하세요. 문제 해결 섹션을 참조하세요. GitLab은 백그라운드 작업자를 사용하여 매일 인증서 폐지 디렉터리을 확인합니다.
제한 사항
-
authorityKeyIdentifier
,subjectKeyIdentifier
, 및crlDistributionPoints
이 없는 인증서는 확인되지 않음(Unverified)으로 나타납니다. RFC 5280과 일치하는 PKI에서 인증서를 사용하는 것을 권장합니다. - GitLab 16.2 및 이전 버전에서 서명 인증서의 대체 이름 디렉터리에 둘 이상의 이메일이 있는 경우, 첫 번째 이메일만 사용하여 커밋을 확인합니다.
- GitLab 15.1 및 이전 버전에서 발급자 인증서의
X509v3 Subject Key Identifier
(SKI)와 서명 인증서는 40자여야 합니다. SKI가 더 짧은 경우, 커밋은 GitLab에서 확인된 것으로 표시되지 않으며 짧은 주제 키 식별자는 또한 프로젝트에 접근할 때 오류(예: ‘커밋 서명 로딩 중 오류 발생’,HTTP 422 Unprocessable Entity
오류 등)를 발생시킬 수 있습니다.
서명된 커밋 구성
커밋, 태그, 또는 둘 다를 서명하려면 다음을 수행해야 합니다:
- X.509 키 쌍을 획득하세요.
- X.509 인증서를 Git에 연결하세요.
- 커밋 서명 및 확인하세요.
- 태그 서명 및 확인하세요.
X.509 키 쌍 획득
조직에서 공개 키 기반 구조(PKI)를 사용하는 경우, 해당 PKI에서 S/MIME 키를 제공합니다. PKI가 제공하지 않는 경우, 자체적으로 자가 서명 키 쌍을 생성하거나 페어를 구입할 수 있습니다.
X.509 인증서를 Git에 연결
X.509 서명을 활용하려면 Git 2.19.0 이상이 필요합니다. 명령어 git --version
으로 Git 버전을 확인할 수 있습니다.
올바른 버전이 있다면, 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를 구성하려면:
-
S/MIME Sign을 설치하세요.
- 설치 프로그램을 다운로드하거나
- macOS의 경우
brew install smimesign
명령을 실행하세요.
- 인증서의 ID를
smimesign --list-keys
명령으로 가져오세요. -
아래 명령을 사용하여 서명 키를 설정하세요.
<ID>
를 인증서 ID로 바꿔 넣으세요.git config --global user.signingkey <ID> 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 "내 서명된 태그"
-
GitLab에 푸시한 후 다음 명령으로 태그가 서명되었는지 확인합니다:
git tag --verify v1.1.1
-
태그할 때마다
-s
플래그를 입력하고 싶지 않다면, 다음 명령을 사용하여 Git이 태그마다 서명하도록 설정합니다:git config --global tag.gpgsign true
관련 주제
문제 해결
관리자 액세스 권한이 없는 커미터의 경우, 서명된 커밋의 확인 문제 디렉터리을 검토하여 문제를 해결할 수 있습니다. 이 페이지의 다른 문제 해결 제안은 관리자 액세스 권한이 필요합니다.
커밋 재확인
GitLab은 데이터베이스에 확인된 커밋의 상태를 저장합니다. 무엇인가 변경한 후에 다음 명령을 사용하십시오:
sudo gitlab-rake gitlab:x509:update_signatures
주요 확인 사항
코드는 다음의 키 확인을 수행하며, 모두 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)
수정사항을 반영해야 하는 경우, 수정사항을 반영하여 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
가 존재하여야 합니다. 이전의 주요 확인 사항에서 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을 사용하여 추가 디버깅을 수행할 수 있습니다.
레일스 콘솔에서 서명 및 서명된 텍스트를 내보냅니다:
-
주요 검증 체크의 초기 두 단계가 필요하여
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
이제 이 데이터를 Linux 명령줄에서 OpenSSL을 사용하여 조사할 수 있습니다:
-
서명을 포함한 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 신뢰 리포지터리를 사용합니다. 신뢰 리포지터리에 루트 인증서가 없거나
서명에 중간 인증서가 누락되어 신뢰할 수 있는 루트로 연결되는 체인을 구축할 수 없습니다.
- 중간 인증서를 서명에 포함시키기 어려운 경우 신뢰 리포지터리에 넣을 수 있습니다.
- 패키지된 GitLab에 대한 신뢰 리포지터리에 인증서를 추가하는 절차는 여기를 참조하세요:
/etc/gitlab/trusted-certs
를 사용하여 인증서 추가 절차를 확인합니다.
- OpenSSL을 사용하여 추가 신뢰할 수 있는 인증서를 테스트합니다:
-CAfile /path/to/rootcertificate.pem
-
unsupported certificate purpose
:- 인증서는
Key Usage
에서Digital Signature
를 지정해야 합니다. - 이것은 보통 서명자의 인증서의
X509v3 Key Usage
섹션에 있습니다. -
X509v3 Extended Key Usage
섹션도 있습니다. 여기에 명시된 경우Digital Signature
가 포함되어야 합니다. 자세한 내용은 RFC 5280을 참조하세요:Key Usage와 일치하는 목적이 없으면 해당 인증서를 어떤 목적으로도 사용할 수 없습니다.
- 인증서는
-
signer certificate not found
:-
-nointern
인수를 추가했지만-certfile
을 제공하지 않았습니다. - 서명자의 인증서가 누락되었습니다.
-
-