GitLab 차트 구성 요소 사이에 TLS 사용하기

GitLab 차트는 다양한 구성 요소 사이에 전송 계층 보안(TLS)을 사용할 수 있습니다. 이를 위해서는 서비스에 대한 인증서를 제공하고, 그 인증서와 그들을 서명한 인증 기관(CA)을 사용하도록 서비스를 구성해야 합니다.

준비 작업

각 차트에는 해당 서비스에 대한 TLS를 활성화하는 방법 및 적절한 구성을 보장하기 위해 필요한 다양한 설정에 대한 설명서가 있습니다.

내부 사용을 위한 인증서 생성

참고: GitLab은 고급 PKI 인프라 또는 인증 기관을 제공하지 않습니다.

이 문서의 목적을 위해, Cloudflare의 CFSSL을 사용하는 콘셉트 증명 스크립트를 아래에 제공합니다. 이 스크립트는 자체 서명된 인증 기관 및 모든 서비스에 사용할 수 있는 와일드카드 인증서를 생성합니다.

이 스크립트는 다음을 수행합니다:

  • CA 키 쌍 생성
  • GitLab 구성 요소 서비스 엔드포인트에 대한 서비스를 위한 서명된 인증서 생성
  • 두 가지 Kubernetes Secret 객체 생성:
    • 서버 인증서 및 키 쌍이 있는 kuberetes.io/tls 유형의 비밀
    • 오직 NGINX Ingress에 필요한 CA의 공개 인증서로서 ca.crt를 포함하는 Opaque 유형의 비밀

전제 조건:

  • 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

#############
## K8s에 인증서 로드
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

참고: 이 스크립트는 CA의 개인 키를 유지하지 않습니다. 이는 개념 증명을 보조하기 위한 것일 뿐이며, 실제 운영에는 사용하지 않아야 합니다.

스크립트는 두 개의 환경 변수가 설정되어 있는 것을 예상합니다:

  1. NAMESPACE: 나중에 GitLab을 설치할 Kubernetes 네임스페이스입니다. 기본값은 kubectl과 동일하게 default입니다.
  2. 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 및 SAN

다양한 GitLab 구성 요소는 서로의 서비스의 DNS 이름을 통해 통신합니다. GitLab 차트에서 생성된 Ingress 객체는 tls.verify: true(기본)인 경우 NGINX에 검증할 이름을 제공해야 합니다. 따라서 각 GitLab 구성 요소는 서비스의 이름이 포함된 SAN을 갖거나 Kubernetes 서비스의 DNS 항목에서 허용된 와일드카드를 포함하는 인증서를 받아야 합니다.

  • service-name.namespace.svc
  • *.namespace.svc

인증서 내의 이러한 SAN을 보장하지 않을 경우, 기능하지 않는 인스턴스 및 “연결 실패” 또는 “SSL 검증 실패”라는 암호화된 로그가 발생할 수 있습니다.

필요한 경우 helm template을 사용하여 모든 서비스 객체 이름의 전체 목록을 검색할 수 있습니다. TLS 없이 GitLab을 배포한 경우 Kubernetes에서 이러한 이름을 조회할 수 있습니다:

kubectl -n ${NAMESPACE} get service -lrelease=${RELEASE}

구성

예제 구성은 examples/internal-tls에서 찾을 수 있습니다.

이 문서의 목적을 위해 shared-cert-values.yaml를 제공했으며, 이는 내부 사용을 위한 인증서 생성에서 생성된 인증서를 GitLab 구성 요소가 사용하도록 구성합니다.

구성해야 할 주요 사항:

  1. 전역 사용자 정의 인증 기관.
  2. 서비스 수신기를 위한 각 구성 요소별 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 구성 요소 간의 모든 통신은 NGINX Ingress에서 각 GitLab 구성 요소로의 연결을 포함하여 네트워크를 통해 TLS 보안으로 전송됩니다.

NGINX Ingress는 모든 입방향 TLS를 종료하고 트래픽을 전달할 적절한 서비스를 결정한 다음 GitLab 구성 요소로 새로운 TLS 연결을 형성합니다. 여기에 표시된대로 구성된 경우, GitLab 구성 요소에서 제공하는 인증서도 CA에 대해 _검증_합니다.

이는 Toolbox pod에 연결하여 확인하고 다양한 구성 요소 서비스를 쿼리함으로써 확인할 수 있습니다. NGINX Ingress가 사용하는 Webservice Pod의 주 서비스 포트에 연결하는 한 예는 다음과 같습니다.

$ 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 Ingress가 GitLab 구성 요소의 인증서를 확인하는데 문제가 있을 수 있습니다.

이를 해결하기 위해 임시로 gitlab.webservice.workhorse.tls.verifyfalse로 설정할 수 있습니다.

NGINX Ingress 컨트롤러에 연결하여 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이 제대로 제공되지 않았거나 Namespace 내에서 존재하지 않을 수 있음.