4.1.4.2 NGINX Ingress Controller에서 TLS termination(feats. Let’s Encrypt)
Ingress Controller에서 외부 수신을 SSL로 하기 위한 설정을 확인합니다.
Self-Signed 인증서 사용하기
테스트 목적으로 Self-Signed 인증서를 만들어 사용하는 방법을 확인해 봅니다. 실제 환경에서는 공인 인증기관에서 발급받은 인증서를 사용합니다. Self-Signed 인증서 발급 절차만 대체되어 TLS Secret 등록과정부터는 동일하게 수행됩니다.
참고 문서
인증서 만들기
Cloud Shell 또는 작업환경에서 다음 명령으로 인증서를 생성합니다. 공인 인증기관에서 발급받은 인증서 사용시 하지 않아도 됩니다.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
TLS Secret을 만듭니다.
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
실행결과
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc" Generating a 2048 bit RSA private key *************************************************************************************************************************************************************+++++ ****************************************************************************************************************************+++++ writing new private key to 'tls.key' ----- $ kubectl create secret tls tls-secret --key tls.key --cert tls.crt secret/tls-secret created
TLS Ingress 자원 배포
테스트를 위한 샘플 앱을 배포합니다. PATH 기반 라우팅 때 사용한 앱을 그대로 사용합니다.
kubectl create deployment nginx-blue --image=thekoguryo/nginx-hello:blue kubectl expose deployment nginx-blue --name nginx-blue-svc --port 80 kubectl create deployment nginx-green --image=thekoguryo/nginx-hello:green kubectl expose deployment nginx-green --name nginx-green-svc --port 80
ingress 설정 YAML(
tls-termination.yaml
)을 작성합니다.- spec.tls.secretName으로 앞서 생성한 Self-Signed 인증서 이름을 사용합니다.
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-tls-termination spec: ingressClassName: nginx tls: - secretName: tls-secret rules: - host: blue.ingress.thekoguryo.xyz http: paths: - path: / pathType: Prefix backend: service: name: nginx-blue-svc port: number: 80 - host: green.ingress.thekoguryo.xyz http: paths: - path: / pathType: Prefix backend: service: name: nginx-green-svc port: number: 80
기존 테스트 ingress는 삭제하고, 작성한
tls-termination.yaml
을 배포합니다.$ kubectl apply -f tls-termination.yaml ingress.networking.k8s.io/ingress-tls-termination created $ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-tls-termination nginx blue.ingress.thekoguryo.xyz,green.ingress.thekoguryo.xyz 10.0.20.182,152.xx.xxx.xxx 80, 443 41s
TLS 적용 결과 검증
ingress rule에서 적용한 host 명으로 각각 접속하여 결과를 확인합니다.
아래와 같이 https로 접속되고 Self-Signed 인증서로 인한 경고 메시지가 뜹니다.
고급을 클릭하고 해당 페이지로 이동을 선택합니다.
브라우저 주소창 메뉴를 통해 인증서 정보를 확인합니다. Self-Signed 인증서로 루트 인증서가 신뢰할 수 없다는 경고를 확인할 수 있습니다.
Let’s Encrypt & Cert Manager 사용하기
Let’s Encrypt는 무료 인증서 발급 사이트로 TLS에 사용할 인증서를 발급 받을 수 있습니다. 대신 90일 동안만 유효하며 만료전에 갱신해야 합니다. Kubernetes에서는 Cert Manager를 통해 자동으로 갱신할 수 있습니다.
설치 참고 문서
Cert Manager 배포
Cloud Shell 또는 작업환경에서 Cert Manager를 배포합니다.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml
설치 확인
cert-manager namespace에 자원들이 정상 실행중인 확인합니다.
$ kubectl get all -n cert-manager NAME READY STATUS RESTARTS AGE pod/cert-manager-5f68c9c6dd-ck55h 1/1 Running 0 37s pod/cert-manager-cainjector-57d6fc9f7d-f298w 1/1 Running 0 37s pod/cert-manager-webhook-5b7ffbdc98-wldq6 1/1 Running 0 36s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/cert-manager ClusterIP 10.96.239.66 <none> 9402/TCP 40s service/cert-manager-webhook ClusterIP 10.96.170.209 <none> 443/TCP 39s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/cert-manager 1/1 1 1 38s deployment.apps/cert-manager-cainjector 1/1 1 1 38s deployment.apps/cert-manager-webhook 1/1 1 1 37s NAME DESIRED CURRENT READY AGE replicaset.apps/cert-manager-5f68c9c6dd 1 1 1 38s replicaset.apps/cert-manager-cainjector-57d6fc9f7d 1 1 1 38s replicaset.apps/cert-manager-webhook-5b7ffbdc98 1 1 1 37s
Let’s Encrypt Issuer 구성
본 예제에서는 Let’s Encrypt에서 제공하는 Staging Issuer, Production Issuer을 사용할 수 있습니다. 여기서는 테스트용도로 Staging Issuer를 사용하겠습니다.
Let’s Encrypt Staging Issuer 설정
https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml 파일에서 email 부분만 본인 것으로 수정하여 반영합니다.
네임스페이스에만 사용되는 Issuer가 아닌 전체 쿠버네티스 클러스터에 사용하기 위해 ClusterIssuer 유형을 사용합니다.
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: # The ACME server URL server: https://acme-staging-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: user@example.com # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-staging # Enable the HTTP-01 challenge provider solvers: - http01: ingress: ingressClassName: nginx
Let’s Encrypt Production Issuer
Production Issuer도 https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/production-issuer.yaml 파일을 이용해 동일한 방식으로 설치할 수 있습니다. 다만 사용 limit로 인해 삭제, 생성을 반복할 경우 Rate Limit에 걸릴 수 있습니다.
설정 적용
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml
적용 예시
$ kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml # 편집화면에서 ClusterIssuer 유형으로 변경합니다. # 편집화면에서 email 주소를 변경합니다. issuer.cert-manager.io/letsencrypt-staging created
결과를 확인합니다.
$ kubectl get ClusterIssuer NAME READY AGE letsencrypt-staging True 23s
TLS Ingress 자원 배포
테스트 앱은 이전 그대로 사용합니다.
ingress 설정 YAML(
tls-termination-cert-manager.yaml
)을 작성합니다.- 문서 작성일 기준 (STAGING) Doctored Durian Root CA의 만료로 인해 staging issuer 사용시에도 유효하지 않은 인증서라고 나올 수 있습니다. cert manager issuer는 production issuer를 사용하겠습니다. 대신 production issuer는 생성을 반복할 경우 limit에 걸릴 수 있습니다.
- cert-manager.io/cluster-issuer: 방금 생성한 letsencrypt-staging 설정합니다. issuer가 아닌 cluster-issuer 유형을 사용합니다.
- spec.tls 하위에 tls 저장할 저장소 이름 및 발급받아 사용할 도메인 이름을 지정합니다
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-tls-termination-cert-manager annotations: cert-manager.io/cluster-issuer: "letsencrypt-staging" spec: ingressClassName: nginx tls: - hosts: - green.ingress.thekoguryo.xyz - blue.ingress.thekoguryo.xyz secretName: ingress-thekoguryo-xyz-tls rules: - host: green.ingress.thekoguryo.xyz http: paths: - path: / pathType: Prefix backend: service: name: nginx-green-svc port: number: 80 - host: blue.ingress.thekoguryo.xyz http: paths: - path: / pathType: Prefix backend: service: name: nginx-blue-svc port: number: 80
기존 테스트 ingress는 삭제하고, 작성한
tls-termination-cert-manager.yaml
을 배포합니다.$ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-tls-termination-cert-manager nginx green.ingress.thekoguryo.xyz,blue.ingress.thekoguryo.xyz 10.0.20.182,152.69.234.225 80, 443 56s
인증서 발급 확인
지정한 spec.tls.secretName으로 secret이 만들어지고, certificate 상태(READY)가 True가 되면 정상 발급되었습니다.
$ kubectl get secret NAME TYPE DATA AGE ingress-thekoguryo-xyz-tls kubernetes.io/tls 2 34s letsencrypt-staging Opaque 1 32m $ kubectl get certificate NAME READY SECRET AGE ingress-thekoguryo-xyz-tls True ingress-thekoguryo-xyz-tls 80s
Let’s Encrypt Root CA 변경으로 인해 인증오류 해결
Staging Issuer를 사용할 경우, (STAGING) Doctored Durian Root CA X3 만료로 인해 웹브라우저 접속했을 때 인증 오류가 발생하는 경우가 있습니다. Production Issuer는 해당 문제가 발생하지 않습니다.
해당 에러가 발생하는 경우 변경된 새 Root CA를 let’s encrypt 사이트에서 다운 받아 브라우저에 등록해 줘야 합니다.
(STAGING) 루트 인증서 파일 다운로드
https://letsencrypt.org/certs/staging/letsencrypt-stg-root-x1.pem
Windows 크롬 브라우저 기준: (STAGING) 루트 인증서 추가
크롬 브라우저 > 설정 > 개인정보 및 보안 > 인증서 관리 로 이동
인증서 가져오기 클릭
다운받은 파일 선택
인증서 설치
인증서 등록후 확인
신뢰할 수 있는 루트 인증 기관에 방금 등록한 (STAGING) 인증서가 보임
인증서가 등록후 다시 앱의 웹페이지를 접속하면 인증오류가 발생하지 않고, 인증 경로가 아래와 같이 보이게 됩니다.
Mac 기준: (STAGING) 루트 인증서 추가
맥 키체인 접근을 실행합니다.
시스템 키체인 잠금 해제합니다
새로운 키체인 항목 생성 아이콘을 클릭합니다.
다운받은 (STAGING) 인증서 파일 선택
추가된 인증서를 더블클릭합니다.
SSL을 항상 신뢰로 변경합니다. 창을 닫으면, 관리자 암호 입력후 저장할 수 있습니다.
인증서 설정이 완료되었습니다.
인증서가 등록후 다시 앱의 웹페이지를 접속하면 인증오류가 발생하지 않고, 인증 경로가 아래와 같이 보이게 됩니다. Root CA가 (STAGING) Doctored Durian Root CA X3에서 등록한 (STAGING) Pretend Pear X1으로 변경되었습니다.
TLS 적용 결과 검증
ingress rule에서 적용한 host 명으로 각각 접속하여 결과를 확인합니다.
아래와 같이 https로 접속되고 Self-Signed 인증서와 달리 경고 없이 유효한 인증서로 표시됩니다.
DNS 대체 주소에 요청한 host가 모두 등록되어, blue 앱도 인증에러 없이 접속됩니다.
이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.