유효한 SSL/TLS 인증서는 현대 애플리케이션 환경의 핵심 요구 사항입니다. 안타깝게도 애플리케이션을 배포할 때 인증서(또는 인증서) 갱신을 관리하는 일은 종종 뒷전으로 밀려납니다. 인증서의 수명은 제한되어 있으며 DigiCert 인증서의 경우 약 13개월 이고 Let's Encrypt 인증서의 경우 90일 입니다. 보안된 액세스를 유지하려면 이러한 인증서가 만료되기 전에 갱신/재발급되어야 합니다. 대부분 운영팀의 업무량이 많기 때문에 인증서 갱신이 때때로 미뤄지기도 하는데, 인증서가 만료 날짜에 가까워지거나 더 나쁜 경우 만료되면 혼란이 발생합니다.
이럴 필요는 없습니다. 약간의 계획과 준비를 통해 인증서 관리를 자동화하고 간소화할 수 있습니다. 여기서는 세 가지 기술을 사용하여 Kubernetes에 대한 솔루션을 살펴보겠습니다.
이 블로그에서는 엔드포인트에 고유하고 자동으로 갱신되고 업데이트된 인증서를 제공하여 인증서 관리를 간소화하는 방법을 알아봅니다.
기술적인 세부 사항을 살펴보기에 앞서 몇 가지 용어를 정의해야 합니다. "TLS 인증서"라는 용어는 Ingress 컨트롤러에서 HTTPS 연결을 활성화하는 데 필요한 두 가지 구성 요소를 나타냅니다.
인증서와 개인 키는 모두 Let's Encrypt 에서 발급됩니다. TLS 인증서의 작동 방식에 대한 전체 설명은 DigiCert의 게시물 TLS/SSL 인증서의 작동 방식을 참조하세요.
Kubernetes에서는 이 두 구성 요소는 Secret 으로 저장됩니다. Kubernetes 워크로드( NGINX Ingress Controller 및 cert-manager 등)는 이러한 비밀을 쓰고 읽을 수 있으며, Kubernetes 설치에 액세스할 수 있는 사용자도 이를 관리할 수 있습니다.
cert-manager 프로젝트는 Kubernetes 및 OpenShift와 함께 작동하는 인증서 컨트롤러입니다. Kubernetes에 배포하면 cert-manager는 Ingress 컨트롤러에 필요한 인증서를 자동으로 발급하고 이러한 인증서가 유효하고 최신 상태인지 확인합니다. 또한, 인증서 만료 날짜를 추적하고 구성된 시간 간격으로 갱신을 시도합니다. 이 플랫폼은 다양한 공공 및 민간 발급자와 함께 작동하지만 Let's Encrypt와의 통합을 보여드리겠습니다.
Let's Encrypt를 사용하면 모든 인증서 관리가 자동으로 처리됩니다. 이렇게 하면 많은 편리함을 얻을 수 있지만, 다음과 같은 문제도 발생합니다. 이 서비스는 사용자가 해당 FQDN(정규화된 도메인 이름)을 소유하고 있는지 어떻게 확인합니까?
이 문제는 특정 도메인의 DNS 레코드에 액세스할 수 있는 사람만이 제공할 수 있는 확인 요청에 답하도록 요구하는 챌린지를 사용하여 해결됩니다. 도전은 두 가지 형태 중 하나를 취합니다.
HTTP-01은 DNS 공급자에 직접 접근할 필요가 없으므로 인증서를 생성하는 가장 간단한 방법입니다. 이러한 유형의 문제는 항상 포트 80(HTTP)을 통해 수행됩니다. HTTP-01 챌린지를 사용하는 경우 cert-manager는 Ingress 컨트롤러를 활용하여 챌린지 토큰을 제공합니다.
Ingress 컨트롤러는 클러스터 외부에서 트래픽을 가져오고 이를 내부 Pod (하나 이상의 컨테이너 그룹)로 부하 분산하고, 송신 트래픽을 관리하는 Kubernetes용 전문 서비스입니다. 또한 Ingress 컨트롤러는 Kubernetes API를 통해 제어되며 Pod가 추가, 제거 또는 실패할 때 로드 밸런싱 구성을 모니터링하고 업데이트합니다.
Ingress 컨트롤러에 대해 자세히 알아보려면 다음 블로그를 읽어보세요.
아래 예에서는 F5 NGINX가 개발하고 유지 관리하는 NGINX Ingress Controller를 사용하겠습니다.
이러한 예제에서는 테스트할 수 있는 Kubernetes 설치가 있고, 설치가 외부 IP 주소(Kubernetes LoadBalancer 개체)를 할당할 수 있다고 가정합니다. 또한, HTTP-01 챌린지를 사용하는 경우 포트 80과 포트 443에서 모두 트래픽을 수신할 수 있고, DNS-01 챌린지를 사용하는 경우 포트 443에서만 트래픽을 수신할 수 있다고 가정합니다. 다음 예제는 Mac OS X를 사용하여 설명되었지만, Linux나 WSL에서도 사용할 수 있습니다.
또한 A 레코드를 조정할 수 있는 DNS 공급자와 FQDN이 필요합니다. HTTP-01 챌린지를 사용하는 경우 A 레코드를 추가하는 기능만 있으면 됩니다(또는 자동으로 추가해 달라고 요청하면 됩니다). DNS-01 챌린지를 사용하는 경우 지원되는 DNS 공급자 또는 지원되는 웹훅 공급자 에 대한 API 액세스가 필요합니다.
가장 쉬운 방법은 Helm을 통해 배포하는 것입니다. 이 배포에서는 Kubernetes Ingress와 NGINX Virtual Server CRD를 모두 사용할 수 있습니다.
$ helm repo add nginx-stable https://helm.nginx.com/stable "nginx-stable"이 귀하의 저장소에 추가되었습니다.
$ helm repo update 차트 저장소에서 최신 소식을 가져오는 동안 잠시만 기다려 주세요...
..."nginx-stable" 차트 저장소에서 성공적으로 업데이트를 받았습니다.
업데이트 완료. ⎈행복한 헬밍!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
이름: nginx-kic
마지막 배포: 목 9월 1일 15:58:15 2022
네임스페이스: nginx-ingress
상태: 배포됨
개정: 1
테스트 모음: 없음
참고사항:
NGINX Ingress Controller가 설치되었습니다.
$ kubectl get deployments --namespace nginx-ingress 이름 준비 최신 사용 가능 연령
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
이름 유형 클러스터-IP 외부-IP 포트 연령
nginx-kic-nginx-ingress 로드 밸런서 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30초
여기의 프로세스는 귀하의 DNS 제공자에 따라 달라집니다. 이 DNS 이름은 Let's Encrypt 서버에서 확인할 수 있어야 하며, 작동하려면 레코드가 전파될 때까지 기다려야 할 수도 있습니다. 이에 대한 자세한 내용은 SiteGround 문서 DNS 전파란 무엇이며, 왜 그렇게 오랜 시간이 걸리나요?를 참조하세요.
선택한 FQDN을 확인할 수 있으면 다음 단계로 넘어갈 준비가 된 것입니다.
$ host cert.example.com cert.example.com의 주소는 www.xxx.yyy.zzz입니다.
다음 단계는 최신 버전의 cert-manager를 배포하는 것입니다. 다시 한번, 우리는 배포에 Helm을 사용할 것입니다.
$ helm repo add jetstack https://charts.jetstack.io "jetstack"이 귀하의 저장소에 추가되었습니다.
$ helm repo update 차트 저장소에서 최신 소식을 가져오는 동안 잠시만 기다려 주세요...
... "nginx-stable" 차트 저장소에서 성공적으로 업데이트를 받았습니다.
... "jetstack" 차트 저장소에서 성공적으로 업데이트를 받았습니다.
업데이트가 완료되었습니다. ⎈행복한 헬밍!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --version v1.9.1 --set installCRDs=true 이름: cert-manager 마지막 배포: 목 9월 1일 16:01:52 2022 네임스페이스: cert-manager 상태: 배포됨 개정: 1 테스트 모음: 없음 참고사항: cert-manager v1.9.1이 성공적으로 배포되었습니다!
인증서 발급을 시작하려면 ClusterIssuer 또는 Issuer 리소스를 설정해야 합니다(예: 'letsencrypt-staging' 발급자를 생성).
다양한 유형의 발급자와 이를 구성하는 방법에 대한 자세한 내용은 다음 설명서에서 확인할 수 있습니다.
https://cert-manager.io/docs/configuration/
Ingress 리소스에 대한 인증서를 자동으로 프로비저닝하도록 cert-manager를 구성하는 방법에 대한 자세한 내용은 `ingress-shim` 문서를 참조하세요.
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deployments --namespace cert-manager 이름 준비 최신 사용 가능 연령
cert-manager 1/1 1 1 4분 30초
cert-manager-cainjector 1/1 1 1 4분 30초
cert-manager-webhook 1/1 1 1 4분 30초
NGINX Cafe 예제를 사용하여 백엔드 배포 및 서비스를 제공하려고 합니다. 이는 NGINX에서 제공하는 설명서에서 사용되는 일반적인 예입니다. 우리는 이것의 일부로 Ingress를 배포하지 않을 것입니다.
$ git clone https://github.com/nginxinc/kubernetes-ingress.git 'kubernetes-ingress'로 복제 중...
원격: 객체 열거: 44979, 완료.
원격: 사물 세기: 100% (172/172), 완료.
원격: 객체 압축: 100% (108/108), 완료.
원격: 총 44979(델타 87), 재사용 120(델타 63), 팩 재사용 44807
수신 객체: 100% (44979/44979), 60.27 MiB | 27.33 MiB/s, 완료.
델타 해결: 100% (26508/26508), 완료.
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee 생성됨
service/coffee-svc 생성됨
deployment.apps/tea 생성됨
service/tea-svc 생성됨
kubectl
get 명령을 사용하여 배포 및 서비스를 검증합니다. Pod가 READY(준비)
로 표시되고, 서비스가 Running(실행 중)
으로 표시되는지 확인하세요. 아래 예는 당신이 찾고 있는 것의 대표적인 샘플을 보여줍니다. Kubernetes
서비스는 NGINX Cafe 예제와 동일한 네임스페이스(기본값)에서 실행되는 시스템 서비스입니다.$ kubectl get deployments,services --namespace default 이름 준비 최신 사용 가능 연령
deployment.apps/coffee 2/2 2 2 69초
deployment.apps/tea 3/3 3 3 68초
이름 유형 클러스터-IP 외부-IP 포트(들) 연령
service/coffee-svc 클러스터IP 10.128.154.225 <없음> 80/TCP 68초
서비스/쿠버네티스 클러스터IP 10.128.0.1 <없음> 443/TCP 29분
서비스/tea-svc 클러스터IP 10.128.96.145 <없음> 80/TCP 68초
cert-manager 내에서 ClusterIssuer를 사용하여 인증서를 발급할 수 있습니다. 이는 모든 네임스페이스에서 참조할 수 있고 정의된 인증서 발급 기관이 있는 모든 인증서 요청에서 사용할 수 있는 클러스터 범위 개체입니다. 이 예에서 Let's Encrypt 인증서에 대한 모든 인증서 요청은 이 ClusterIssuer를 통해 처리할 수 있습니다.
선택한 챌린지 유형에 맞게 ClusterIssuer를 배포합니다. 이 게시물의 범위를 벗어나지만 ClusterIssuer에서 여러 개의 확인자(선택기 필드를 기준으로 선택)를 지정할 수 있는 고급 구성 옵션이 있습니다.
ACME(자동 인증서 관리 환경) 프로토콜은 사용자가 도메인 이름을 소유하고 있는지 확인하고 이에 따라 Let's Encrypt 인증서를 발급받을 수 있는지 확인하는 데 사용됩니다. 이번 과제에서는 전달해야 할 매개변수가 다음과 같습니다.
이 예제에서는 HTTP-01 챌린지를 사용하여 도메인 소유권을 증명하고 인증서를 수신하기 위해 ClusterIssuer를 설정하는 방법을 보여줍니다.
$ cat << EOF | kubectl 적용 -f api버전: cert-manager.io/v1
종류: ClusterIssuer
메타데이터:
이름: prod-issuer
사양:
acme:
이메일: example@example.com
서버: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
이름: prod-issuer-account-key
해결사:
- http01:
수신:
클래스: nginx
EOF
clusterissuer.cert-manager.io/prod-issuer 생성됨
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 34초
이 예제에서는 DNS-01 챌린지를 사용하여 도메인 소유권을 인증하기 위해 ClusterIssuer를 설정하는 방법을 보여줍니다. DNS 제공자에 따라 토큰을 저장하기 위해 Kubernetes Secret을 사용해야 할 가능성이 높습니다. 이 예에서는 Cloudflare를 사용하고 있습니다. 네임스페이스 사용에 주목하세요. cert-manager 네임스페이스에 배포된 cert-manager 애플리케이션은 Secret에 액세스할 수 있어야 합니다.
이 예에서는 계정에서 생성할 수 있는 Cloudflare API 토큰이 필요합니다. 아래의 줄에 입력해야 합니다. Cloudflare를 사용하지 않는 경우 공급업체의 설명서를 따라야 합니다.
$ cat << EOF | kubectl apply -f apiVersion: v1
종류: 비밀
메타데이터:
이름: cloudflare-api-token-secret
네임스페이스: cert-manager
유형: 불투명
stringData:
api-token: <API 토큰>
EOF
$ cat << EOF | kubectl 적용 -f api버전: cert-manager.io/v1
종류: ClusterIssuer
메타데이터:
이름: prod-issuer
사양:
acme:
이메일: example@example.com
서버: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
이름: prod-issuer-account-key
해결사:
- dns01:
cloudflare:
apiTokenSecretRef:
이름: cloudflare-api-token-secret
키: api-token
EOF
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 31m
이것이 바로 우리가 지향해 온 지점, 즉 애플리케이션에 Ingress 리소스를 배포하는 것입니다. 이렇게 하면 이전에 배포한 NGINX Cafe 애플리케이션으로 트래픽이 라우팅됩니다.
표준 Kubernetes Ingress 리소스를 사용하는 경우 다음 배포 YAML을 사용하여 Ingress를 구성하고 인증서를 요청합니다.
apiVersion: networking.k8s.io/v1 종류: Ingress
메타데이터:
이름: cafe-ingress
주석:
cert-manager.io/cluster-issuer: prod-issuer
acme.cert-manager.io/http01-edit-in-place: "true"
사양:
ingressClassName: nginx
tls:
- 호스트:
- cert.example.com
secretName: cafe-secret
규칙:
- 호스트: cert.example.com
http:
경로:
- 경로: /tea
경로 유형: 접두사
백엔드:
서비스:
이름: tea-svc
포트:
번호: 80
- 경로: /커피
경로 유형: 접두사
백엔드:
서비스:
이름: coffee-svc
포트:
번호: 80
매니페스트의 핵심 부분 중 일부를 검토해 보는 것이 좋습니다.
acme.cert-manager.io/http01-edit-in-place를
"true"로 설정하는 metadata.annotations
아래에 있습니다. 이 값은 필수이며, 챌린지가 제공되는 방식을 조정합니다. 자세한 내용은 지원되는 주석 문서를 참조하세요. 마스터/미니언 설정을 사용하여 이를 처리할 수도 있습니다.spec.ingressClassName
은 우리가 설치하고 사용할 NGINX Ingress 컨트롤러를 참조합니다.spec.tls.secret
Kubernetes Secret 리소스는 Let's Encrypt에서 인증서가 발급될 때 반환되는 인증서 키를 저장합니다. spec.tls.hosts
및 spec.rules.host
에 대해 cert.example.com
의 호스트 이름이 지정되었습니다. 이는 ClusterIssuer가 인증서를 발급한 호스트 이름입니다.spec.rules.http
섹션은 경로와 해당 경로에서 요청을 처리하는 백엔드 서비스를 정의합니다. 예를 들어, /tea
로의 트래픽은 tea-svc
의 포트 80으로 전송됩니다.spec.rules.host
및 spec.tls.hosts
값을 변경하는 것이 포함되지만 구성의 모든 매개변수를 검토해야 합니다. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe가 생성됨
$ kubectl 인증서 가져오기 이름 준비 비밀 나이
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
NGINX CRD를 사용하는 경우 다음 배포 YAML을 사용하여 Ingress를 구성해야 합니다.
api버전: k8s.nginx.org/v1
종류: VirtualServer
메타데이터:
이름: cafe
사양:
호스트: cert.example.com
tls:
비밀: cafe-secret
cert-manager:
클러스터-발급자: prod-발급자
업스트림:
- 이름: tea
서비스: tea-svc
포트: 80
- 이름: 커피
서비스: 커피 서비스
포트: 80
경로:
- 경로: /tea
작업:
패스: tea
- 경로: /coffee
작업:
패스: coffee
다시 한번, 매니페스트의 핵심 부분을 검토해 보는 것이 좋습니다.
spec.tls.secret
Kubernetes Secret 리소스는 Let's Encrypt에서 인증서가 발급될 때 반환되는 인증서 키를 저장합니다. spec.host
에 cert.example.com
의 호스트 이름이 지정되었습니다. 이는 ClusterIssuer가 인증서를 발급한 호스트 이름입니다.spec.upstreams
값은 포트를 포함한 백엔드 서비스를 가리킵니다.spec.routes
는 경로와 해당 경로에 도달했을 때 수행할 작업을 모두 정의합니다.spec.host
값을 변경하는 것이 포함되지만 구성의 모든 매개변수를 검토해야 합니다. $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe가 생성됨
$ kubectl get VirtualServers 이름 상태 호스트 IP 포트 연령
cafe 유효 cert.example.com www.xxx.yyy.zzz [80,443] 51m
Kubernetes API를 통해 인증서를 볼 수 있습니다. 그러면 인증서의 크기 및 연관된 개인 키를 포함한 인증서에 대한 세부 정보가 표시됩니다.
$ kubectl describe secret cafe-secret 이름: cafe-secret 네임스페이스: default 레이블: <없음> 주석: cert-manager.io/alt-names : cert.example.com cert-manager.io/certificate-name : cafe-secret cert-manager.io/common-name : cert.example.com cert-manager.io/ip-sans : cert-manager.io/issuer-group : cert-manager.io/issuer-kind : ClusterIssuer cert-manager.io/issuer-name : prod-issuer cert-manager.io/uri-sans : 유형: kubernetes.io/tls 데이터 ==== tls.crt: 5607 바이트 tls.key: 1675바이트
실제 인증서와 키를 보고 싶다면 다음 명령을 실행하면 됩니다. (메모: 이는 Kubernetes Secrets의 약점을 보여줍니다. 즉, 필요한 접근 권한이 있는 사람이라면 누구나 읽을 수 있습니다.)
$ kubectl get secret cafe-secret -o yaml
인증서를 테스트합니다. 여기서는 원하는 방법을 사용할 수 있습니다. 아래 예에서는 cURL을 사용합니다. 성공은 서버 이름, 서버의 내부 주소, 날짜, 선택한 URI(경로)(커피 또는 티), 요청 ID를 포함하는 이전에 표시된 것과 유사한 블록으로 표시됩니다. 실패는 HTTP 오류 코드의 형태를 띠며, 대부분 400 또는 301입니다.
$ curl https://cert.example.com/tea
서버 주소: 10.2.0.6:8080
서버 이름: tea-5c457db9-l4pvq
날짜: 02/9/2022:15:21:06 +0000
URI: /tea
요청 ID: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
서버 주소: 10.2.2.6:8080
서버 이름: coffee-7c86d7d67c-kjddk
날짜: 02/9/2022:15:21:10 +0000
URI: /coffee
요청 ID: 4ea3aa1c87d2f1d80a706dde91f31d54
처음에 우리는 이 접근 방식을 사용하면 인증서 갱신을 관리할 필요성이 없어질 것이라고 약속했습니다. 하지만 아직 그 방법을 설명하지 못했습니다. 왜? 이는 cert-manager의 핵심 내장 기능이기 때문입니다. 이 자동 프로세스에서 cert-manager는 인증서가 없거나 만료되었거나 만료일이 15일 이내인 경우 또는 사용자가 CLI를 통해 새 인증서를 요청하는 경우 자동으로 새 인증서를 요청합니다 . 이보다 더 쉬운 일은 없습니다.
NGINX Plus 구독자라면 NGINX Ingress Controller를 설치하기만 하면 됩니다. 위에 제공된 Helm 명령을 수정하여 이를 달성하는 방법에 대한 지침은 NGINX 문서의 Helm 설치 섹션을 참조하세요.
이는 주로 사용 사례에 따라 달라집니다.
HTTP-01 챌린지 방법을 사용하려면 포트 80이 인터넷에 열려 있어야 하며 DNS A 레코드가 Ingress 컨트롤러의 IP 주소에 대해 올바르게 구성되어 있어야 합니다. 이 접근 방식은 A 레코드를 만드는 것 외에는 DNS 공급자에 대한 액세스가 필요하지 않습니다.
DNS-01 챌린지 방법은 포트 80을 인터넷에 노출할 수 없는 경우 사용할 수 있으며, cert-manager에게 DNS 공급자에 대한 출구 액세스 권한만 필요합니다. 하지만 이 방법을 사용하려면 DNS 공급자의 API에 액세스할 수 있어야 하며, 필요한 액세스 수준은 공급자마다 다릅니다.
쿠버네티스는 너무 복잡하기 때문에 구체적인 문제 해결 정보를 제공하기가 어렵습니다. 문제가 발생하면 NGINX 커뮤니티 Slack 에서 문의해주세요(NGINX Plus 구독자는 일반 지원 옵션을 이용할 수 있습니다).
NGINX App Protect WAF 및 DoS가 포함된 NGINX Ingress Controller의 무료 30일 체험판을 요청하고, 항상 무료인 NGINX Service Mesh를 다운로드하여 시작하세요.
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."