GitLab 차트 간 구성 요소 간 TLS 사용
GitLab 차트는 다양한 구성 요소 간에 전송 계층 보안(TLS)을 사용할 수 있습니다.
이를 통해 보안 인증서를 제공해야 하며, 해당 서비스가 이러한 인증서와 이를 서명한 인증 기관(CA)을 사용하도록 구성해야 합니다.
준비
각 차트는 해당 서비스에 대한 TLS 활성화 및 적절한 구성을 보장하기 위해 필요한 다양한 설정에 관한 문서를 가지고 있습니다.
내부 사용을 위한 인증서 생성
이 문서의 목적을 위해, 아래에 Proof of Concept 스크립트를 제공하며, 이는 Cloudflare의 CFSSL을 사용하여 자체 서명된 인증 기관과 모든 서비스에 사용할 수 있는 와일드카드 인증서를 생성합니다.
이 스크립트는 다음을 수행합니다:
- CA 키 쌍을 생성합니다.
- 모든 GitLab 구성 요소 서비스 엔드포인트에 서비스를 제공하기 위한 인증서를 서명합니다.
- 두 개의 Kubernetes 비밀 객체를 생성합니다:
- 서버 인증서와 키 쌍이 있는
kubernetes.io/tls
유형의 비밀입니다. - NGINX Ingress에서 필요로 하는 CA의 공개 인증서만 포함된
Opaque
유형의 비밀로ca.crt
입니다.
- 서버 인증서와 키 쌍이 있는
사전 요구 사항:
- Bash 또는 호환 셸.
-
cfssl
이 셸에서 사용 가능하고PATH
에 포함되어 있습니다. -
kubectl
이 사용 가능하며 나중에 GitLab이 설치될 Kubernetes 클러스터를 가리키도록 구성되어 있습니다.- 이 스크립트를 실행하기 전에 이러한 인증서를 설치할 네임스페이스를 생성했는지 확인하세요.
이 스크립트의 내용을 복사하여 귀하의 컴퓨터에 저장하고 결과 파일을 실행 가능하게 만들 수 있습니다. poc-gitlab-internal-tls.sh
를 제안합니다.
#!/bin/bash
set -e
#############
## 작업 디렉토리 생성 및 이동
pushd $(mktemp -d)
#############
## 환경 설정
NAMESPACE=${NAMESPACE:-default}
RELEASE=${RELEASE:-gitlab}
## 이 시점 이후 변수에 값이 없으면 중단
set -u
## SAN에 대한 예상 패턴
CERT_SANS="*.${NAMESPACE}.svc,${RELEASE}-metrics.${NAMESPACE}.svc,*.${RELEASE}-gitaly.${NAMESPACE}.svc"
#############
## 기본 CA 구성 생성
cfssl print-defaults config > ca-config.json
## CA 생성
echo '{"CN":"'${RELEASE}.${NAMESPACE}.internal.ca'","key":{"algo":"ecdsa","size":256}}' | \
cfssl gencert -initca - | \
cfssljson -bare ca -
## 인증서 생성
echo '{"CN":"'${RELEASE}.${NAMESPACE}.internal'","key":{"algo":"ecdsa","size":256}}' | \
cfssl gencert -config=ca-config.json -ca=ca.pem -ca-key=ca-key.pem -profile www -hostname="${CERT_SANS}" - |\
cfssljson -bare ${RELEASE}-services
#############
## Kubernetes에 인증서 로드
kubectl -n ${NAMESPACE} create secret tls ${RELEASE}-internal-tls \
--cert=${RELEASE}-services.pem \
--key=${RELEASE}-services-key.pem
kubectl -n ${NAMESPACE} create secret generic ${RELEASE}-internal-tls-ca \
--from-file=ca.crt=ca.pem
이 스크립트는 두 개의 환경 변수 설정을 기대합니다:
-
NAMESPACE
: 나중에 GitLab을 설치할 Kubernetes 네임스페이스. 기본값은default
입니다. -
RELEASE
: 나중에 GitLab을 설치하는 데 사용할 Helm 리리스 이름. 기본값은gitlab
입니다.
이 스크립트를 실행하려면 두 변수를 export
하거나 스크립트 이름 앞에 값을 추가할 수 있습니다.
export NAMESPACE=testing
export RELEASE=gitlab
./poc-gitlab-internal-tls.sh
스크립트가 실행된 후, 두 개의 비밀이 생성되고 임시 작업 디렉토리에는 모든 인증서와 그 키가 포함되어 있습니다.
$ pwd
/tmp/tmp.swyMgf9mDs
$ kubectl -n ${NAMESPACE} get secret | grep internal-tls
testing-internal-tls kubernetes.io/tls 2 11s
testing-internal-tls-ca Opaque 1 10s
$ ls -1
ca-config.json
ca.csr
ca-key.pem
ca.pem
testing-services.csr
testing-services-key.pem
testing-services.pem
필요한 인증서 CN 및 SANs
다양한 GitLab 구성 요소들이 각자의 서비스 DNS 이름을 통해 서로 통신합니다.
GitLab 차트에서 생성된 Ingress 객체는 tls.verify: true
(기본값)일 때 확인할 이름을 NGINX에 제공해야 합니다. 따라서 각 GitLab 구성 요소는 자신의 서비스 이름이거나 Kubernetes 서비스 DNS 항목에 허용되는 와일드카드를 포함한 SAN이 있는 인증서를 받아야 합니다.
service-name.namespace.svc
*.namespace.svc
인증서 내 SAN을 보장하지 않을 경우 비기능적인 인스턴스가 발생하며, “연결 실패” 또는 “SSL 검증 실패”라는 다소 암호화된 로그가 출력됩니다.
필요한 경우, helm template
를 사용하여 모든 서비스 객체 이름의 전체 목록을 검색할 수 있습니다. GitLab이 TLS 없이 배포된 경우 Kubernetes에 대해 이러한 이름을 쿼리할 수 있습니다:
kubectl -n ${NAMESPACE} get service -lrelease=${RELEASE}
구성
예제 구성은 examples/internal-tls에서 찾을 수 있습니다.
이 문서의 목적을 위해 shared-cert-values.yaml
을 제공하였으며, 이는 위의 스크립트로 생성된 인증서를 사용하도록 GitLab 구성 요소를 구성합니다. 내부 사용을 위한 인증서 생성.
구성해야 할 주요 항목:
- 전역 사용자 지정 인증 기관.
- 서비스 리스너를 위한 구성 요소별 TLS. (각 차트의 문서를 참조하십시오, charts/)
이 과정은 YAML의 고유한 앵커 기능을 활용하여 크게 단순화됩니다. shared-cert-values.yaml
의 잘린 코드 조각은 다음과 같습니다:
.internal-ca: &internal-ca gitlab-internal-tls-ca
.internal-tls: &internal-tls gitlab-internal-tls
global:
certificates:
customCAs:
- secret: *internal-ca
workhorse:
tls:
enabled: true
gitlab:
webservice:
tls:
secretName: *internal-tls
workhorse:
tls:
verify: true # 기본값
secretName: *internal-tls
caSecretName: *internal-ca
결과
모든 구성 요소가 서비스 리스너에서 TLS를 제공하도록 구성되면, GitLab 구성 요소 간의 모든 통신은 TLS 보안으로 네트워크를 통과합니다. 여기에는 NGINX Ingress에서 각 GitLab 구성 요소로의 연결도 포함됩니다.
NGINX Ingress는 모든 수신 TLS를 종료하고, 트래픽을 전달할 적절한 서비스를 결정한 다음, GitLab 구성 요소에 대한 새 TLS 연결을 형성합니다. 여기와 같이 구성할 경우, GitLab 구성 요소가 제공하는 인증서를 CA에 대해 _검증_합니다.
이것은 Toolbox 포드에 연결하고 다양한 구성 요소 서비스에 대해 쿼리함으로써 검증할 수 있습니다. NGINX Ingress가 사용하는 웹 서비스 포드의 기본 서비스 포드에 연결하는 예는 다음과 같습니다:
$ kubectl -n ${NAMESPACE} get pod -lapp=toolbox,release=${RELEASE}
NAME READY STATUS RESTARTS AGE
gitlab-toolbox-5c447bfdb4-pfmpc 1/1 Running 0 65m
$ kubectl exec -ti gitlab-toolbox-5c447bfdb4-pfmpc -c toolbox -- \
curl -Iv "https://gitlab-webservice-default.testing.svc:8181"
출력은 다음 예제와 유사해야 합니다:
* Trying 10.60.0.237:8181...
* Connected to gitlab-webservice-default.testing.svc (10.60.0.237) port 8181 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: CN=gitlab.testing.internal
* start date: Jul 18 19:15:00 2022 GMT
* expire date: Jul 18 19:15:00 2023 GMT
* subjectAltName: host "gitlab-webservice-default.testing.svc" matched cert's "*.testing.svc"
* issuer: CN=gitlab.testing.internal.ca
* SSL certificate verify ok.
> HEAD / HTTP/1.1
> Host: gitlab-webservice-default.testing.svc:8181
문제 해결
브라우저에서 GitLab 인스턴스에 접근할 수 없는 경우, HTTP 503 오류가 발생하며, NGINX 인그레스가 GitLab 구성 요소의 인증서를 확인하는 데 문제가 있을 가능성이 높습니다.
이 문제를 해결하기 위해 gitlab.webservice.workhorse.tls.verify
를
일시적으로 false
로 설정할 수 있습니다.
NGINX 인그레스 컨트롤러에 연결할 수 있으며,
인증서 확인 문제와 관련하여 nginx.conf
에 메시지가 기록됩니다.
예시 내용, Secret에 접근할 수 없는 경우:
# Location denied. Reason: "error obtaining certificate: local SSL certificate
testing/gitlab-internal-tls-ca was not found"
return 503;
이 문제를 유발하는 일반적인 원인:
- CA 인증서가 Secret 내에
ca.crt
라는 이름의 키에 포함되어 있지 않음. - Secret이 제대로 제공되지 않았거나, 네임스페이스 내에 존재하지 않음.