1.2.2.2.6.3 NGINX Ingress Controller에서 TLS termination(feats. Let’s Encrypt)
Ingress Controller에서 외부 수신을 SSL로 하기 위한 설정을 확인합니다.
NGINX Ingress Controller 설치
-
kubectl 사용이 가능한 Cloud Shell 또는 작업환경에 접속합니다.
-
nginx ingress controller 설치할 파일 deploy.yaml을 다운로드 받습니다.
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/cloud/deploy.yaml
-
ingress-nginx-controller의 Load Balancer Service 유형에 대한 설정을 annotation으로 추가합니다.
-
oci.oraclecloud.com/load-balancer-type: "lb"
을 추가하여 OCI Load Balancer를 사용하고 관련 설정을 추가합니다. -
service.beta.kubernetes.io/oci-load-balancer-backend-protocol: "TCP"
Ingress 레벨에서 tls를 설정할 것이 때문에 여기는 HTTP가 아닌 TCP 로 설정합니다.... --- apiVersion: v1 kind: Service metadata: labels: ... name: ingress-nginx-controller namespace: ingress-nginx annotations: oci.oraclecloud.com/load-balancer-type: "lb" service.beta.kubernetes.io/oci-load-balancer-shape: "flexible" service.beta.kubernetes.io/oci-load-balancer-shape-flex-min: "10" service.beta.kubernetes.io/oci-load-balancer-shape-flex-max: "10" service.beta.kubernetes.io/oci-load-balancer-backend-protocol: "TCP" spec: ... type: LoadBalancer ...
-
-
다음 명령으로 NGINX Ingress Controller를 설치합니다.
kubectl apply -f deploy.yaml
생성된 OCI Load Balancer 확인
-
OCI 콘솔에 로그인합니다.
-
좌측 상단 햄버거 메뉴에서 Networking > Load Balancers > Load Balancer로 이동합니다.
-
동일한 Public IP로 생성된 Load Balancer를 클릭하여, Listener를 확인합니다. 보면 그림과 같이 HTTP 프로토콜로 80, 443 포트로 수신하고 있습니다.
DNS 서비스 설정
이미 구입한 Domain Name이 있다는 전제하에 설정하는 과정입니다. 테스트를 위해 따로 구입한 Domain Name(thekoguryo.xyz)을 사용하였습니다. 앞선 실습에서 등록하지 않은 경우 아래와 같이 등록합니다.
-
도메인 구입처 또는 OCI DNS 서비스에서 위임하여 서비스하는 경우 OCI DNS 서비스에서 사용한 host를 등록합니다.
-
추가할 레코드를 입력하고 제출합니다.
-
Record Type: A - IPv4 Address
-
Name: *.ingress
- 와일드 카드 형식으로 ingress controller가 사용할 서브 Domain Name을 입력합니다.
-
Address: 매핑할 IP, 여기서는 앞서 만든 OCI Native Ingress Controller의 Load Balancer의 IP 입력
-
예, GoDaddy DNS 관리화면
-
DNS 테스트
-
nslookup 툴로 등록한 DNS를 테스트 해봅니다. 잘 등록된 것을 알 수 있습니다.
$ nslookup *.ingress.thekoguryo.xyz Server: 127.0.0.11 Address: 127.0.0.11#53 Non-authoritative answer: Name: *.ingress.thekoguryo.xyz Address: 158.180.xx.xxx
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=ghcr.io/thekoguryo/nginx-hello:blue kubectl expose deployment nginx-blue --name nginx-blue-svc --port 80 kubectl create deployment nginx-green --image=ghcr.io/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
-
작성한
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.170,152.69.xxx.xx 80, 443 43s
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.13.3/cert-manager.yaml
-
설치 확인
cert-manager namespace에 자원들이 정상 실행중인 확인합니다.
$ kubectl get all -n cert-manager NAME READY STATUS RESTARTS AGE pod/cert-manager-77c645c9cd-w5vk4 1/1 Running 0 30s pod/cert-manager-cainjector-6678d4cbcd-z58zf 1/1 Running 0 31s pod/cert-manager-webhook-996c79df8-bd64t 1/1 Running 0 30s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/cert-manager ClusterIP 10.96.44.207 <none> 9402/TCP 33s service/cert-manager-webhook ClusterIP 10.96.138.173 <none> 443/TCP 33s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/cert-manager 1/1 1 1 31s deployment.apps/cert-manager-cainjector 1/1 1 1 32s deployment.apps/cert-manager-webhook 1/1 1 1 31s NAME DESIRED CURRENT READY AGE replicaset.apps/cert-manager-77c645c9cd 1 1 1 31s replicaset.apps/cert-manager-cainjector-6678d4cbcd 1 1 1 32s replicaset.apps/cert-manager-webhook-996c79df8 1 1 1 31s
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 부분만 본인 것으로 수정하여 반영합니다.
wget https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml
kind
: 네임스페이스에만 사용되는 Issuer가 아닌 전체 쿠버네티스 클러스터에 사용하기 위해ClusterIssuer
유형으로 변경합니다.spec.acme.email
: 본인 이메일로 변경
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 apply -f staging-issuer.yaml
-
결과를 확인합니다.
$ kubectl get ClusterIssuer NAME READY AGE letsencrypt-staging True 23s
TLS Ingress 자원 배포
-
테스트 앱은 이전 그대로 사용합니다.
-
ingress 설정 YAML(
tls-termination-cert-manager.yaml
)을 작성합니다.- 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 delete -f tls-termination.yaml ingress.networking.k8s.io "ingress-tls-termination" deleted $ kubectl apply -f tls-termination-cert-manager.yaml ingress.networking.k8s.io/ingress-tls-termination-cert-manager created $ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-tls-termination-cert-manager nginx green.ingress.thekoguryo.xyz,blue.ingress.thekoguryo.xyz 152.69.xxx.xxx 80, 443 36s
-
인증서 발급 확인
지정한 spec.tls.secretName으로 secret이 만들어지고, certificate 상태(READY)가 True가 되면 정상 발급되었습니다.
$ kubectl get secret NAME TYPE DATA AGE ingress-thekoguryo-xyz-tls kubernetes.io/tls 2 34s $ 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는 해당 문제가 발생하지 않습니다. 필요한 경우 cert manager issuer를 production issuer를 사용하겠습니다. 대신 production issuer는 생성을 반복할 경우 limit에 걸릴 수 있습니다.
-
해당 에러가 발생하는 경우 변경된 새 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 앱도 인증에러 없이 접속됩니다.
이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.