GitLab 차트 컴포넌트간의 TLS 사용

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

준비사항

각 차트에는 해당 서비스를 위한 TLS를 활성화하고 적절한 구성을 보장하기 위한 설명서가 있습니다.

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

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

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

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

  • CA 키 쌍 생성
  • GitLab 컴포넌트 서비스 엔드포인트에 사용될 인증서 서명
  • 두 개의 쿠버네티스 시크릿 객체 생성
    • 서버 인증서 및 키 페어를 포함하는 kubernetes.io/tls 유형의 시크릿
    • NGINX Ingress에서 필요한 ca.crt로서 public CA 인증서만을 포함하는 Opaque 유형의 시크릿

필수 요구 사항:

  • Bash 또는 호환 가능한 쉘
  • cfssl이 쉘에서 사용 가능하고 PATH 내에 있는지 확인
  • kubectl이 사용 가능하고, 나중에 GitLab을 설치할 쿠버네티스 클러스터를 가리키도록 구성
    • 스크립트를 실행하기 전에 인증서를 설치할 원하는 네임스페이스를 만들었는지 확인하세요.

이 스크립트의 콘텐츠를 복사하여 컴퓨터에 저장하고, 결과 파일을 실행 가능하도록 만드는 것을 권장합니다. poc-gitlab-internal-tls.sh로 저장하는 것을 제안합니다.

#!/bin/bash
set -e
#############
## 작업 디렉터리 만들고 이동
pushd $(mktemp -d)

#############
## 환경 설정
NAMESPACE=${NAMESPACE:-default}
RELEASE=${RELEASE:-gitlab}
## 이후의 변수가 설정되지 않으면 스크립트 중단
set -u
## SAN(SANitized)의 알려진 기대 패턴
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

#############
## 쿠버네티스에 인증서 로드
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
note
이 스크립트는 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 및 SANs

다양한 GitLab 컴포넌트는 각자의 서비스의 DNS 이름을 통해 서로 통신합니다. GitLab 차트에서 생성된 Ingress 객체는 NGINX가 tls.verify: true으로 설정될 때, 트래픽을 전달해야 하는 이름을 NGINX에 제공합니다. 따라서 각 GitLab 컴포넌트는 서비스의 이름 또는 Kubernetes 서비스 DNS 항목에서 허용되는 와일드카드를 포함하는 SAN을 갖는 인증서를 받아야 합니다.

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

인증서 내에서 이러한 SAN을 보장하지 않으면 기능하지 않는 인스턴스 및 “연결 실패” 또는 “SSL 검증 실패”와 같이 굉장히 난해한 로그가 나타납니다.

필요하다면, 모든 서비스 객체 이름의 전체 디렉터리을 가져오기 위해 helm template을 사용할 수 있습니다. TLS 없이 GitLab을 배포한 경우, 해당 이름들을 조회할 수 있습니다.

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 컴포넌트 간의 모든 통신은 TLS 보안을 거쳐 네트워크를 통해 이루어집니다. 이는 NGINX Ingress가 각 GitLab 컴포넌트로부터의 연결에서 수신 TLS를 해제하고, 트래픽을 전달하고, 그런 다음 GitLab 컴포넌트로의 새로운 TLS 연결을 형성합니다. 여기에서 보여진대로 구성할 경우, NGINX Ingress는 또한 GitLab 컴포넌트로부터 제공되는 인증서를 _검증_합니다.

Toolbox pod에 연결하여 다양한 컴포넌트 서비스에 쿼리를 보내어 이를 확인할 수 있습니다. 여기에는 Webservice pod의 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"

출력은 다음과 같아야 합니다.

*   10.60.0.237:8181로 시도 중...
* 10.60.0.237의 gitlab-webservice-default.testing.svc에 연결되었습니다 (10.60.0.237) port 8181 (#0)
* ALPN, h2를 제공함
* ALPN, http/1.1을 제공함
* 인증서 확인 위치를 성공적으로 설정하였습니다:
*  CA file: /etc/ssl/certs/ca-certificates.crt
*  CA path: /etc/ssl/certs
* TLSv1.3 (OUT), TLS 핸드셰이크, 클라이언트 hello (1):
* TLSv1.3 (IN), TLS 핸드셰이크, 서버 hello (2):
* TLSv1.3 (IN), TLS 핸드셰이크, 암호화된 확장 (8):
* TLSv1.3 (IN), TLS 핸드셰이크, 인증서 (11):
* TLSv1.3 (IN), TLS 핸드셰이크, CERT verify (15):
* TLSv1.3 (IN), TLS 핸드셰이크, Finished (20):
* TLSv1.3 (OUT), TLS 암호 변경, Change cipher spec (1):
* TLSv1.3 (OUT), TLS 핸드셰이크, Finished (20):
* TLSv1.3를 사용하는 SSL 연결 / TLS_AES_128_GCM_SHA256
* ALPN, 서버가 프로토콜에 동의하지 않음
* 서버 인증서:
*  subject: CN=gitlab.testing.internal
*  시작일: Jul 18 19:15:00 2022 GMT
*  만료일: Jul 18 19:15:00 2023 GMT
*  subjectAltName: "*.testing.svc"에 매치되는 인증서의 "gitlab-webservice-default.testing.svc" 호스트
* 발행자: CN=gitlab.testing.internal.ca
* SSL 인증서 확인 완료.
> 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에 접근할 수 없는 경우의 예시 내용입니다:

# 위치 거부됨. 이유: "인증서 획득 오류: 로컬 SSL 인증서 testing/gitlab-internal-tls-ca를 찾을 수 없음"
return 503;

이로 인해 발생할 수 있는 일반적인 문제들:

  • CA 인증서가 Secret 내의 ca.crt이라는 키에 없는 경우
  • Secret이 제대로 제공되지 않았거나 네임스페이스 내에 존재하지 않을 수 있음