Kubernetes/Istio

Cert Manager with SSL Domain with AWS Route53

김 정출 2024. 8. 15. 11:46

Cert Manager with SSL Domain with AWS Route53

지난번 글에서 Istio를 구축하였습니다.

https://jeongchul.tistory.com/720

금번 글에서는 AWS Route53을 통해 할당받은 도메인을 SSL 인증서를 Cert ManagerLet's Encrypt를 통해 발급받고, 이를 Istio GatewayVirtualService에 적용해보겠습니다.

AWS Route 53

AWS Route 53에 들어가 구매한 도메인에 대해 다음의 A records를 추가합니다.

  • Record name: 각각 공백, * 입력하여 두 개의 records 생성
  • Type: A
  • Value: Kubernetes Cluster IP(xxx.xxx.xxx.xxx)

최종적으로 A Type에 대해 다음과 같이 설정됩니다.

Hosted zone details에 들어가 Hosted zone ID를 확인합니다.
Hosted zone IDCluster Issuer 생성 시에 사용됩니다.

AWS IAM

새로운 user를 생성합니다. 저는 dev_env라는 유저로 생성하였습니다.

Create access key 버튼을 클릭하여 Access Key를 생성합니다.

Local code를 선택합니다.

최종적으로 생성된 Access key와 Secret access key를 확인합니다.

추가적으로 Route53에 대한 Permission policies를 추가해야 합니다.
추가 하지 않을 시 인증서 발급 시에 이슈가 발생됩니다.

Kubernetes Secret for AWS Access, Secret 생성

Access Key ID와 Secret Access Key를 Base 64 인코딩하여 값을 넣습니다.

apiVersion: v1
kind: Secret
metadata:
  name: aws-route53-secret-access-key-secret
  namespace: cert-manager
data:
  access-key-id: [YOUR_AWS_USER_ACCESS_KEY]
  secret-access-key: [YOUR_AWS_SECRET_ACCESS_KEY]

위의 secret을 생성합니다.

kubectl create -f secret.yaml

kubectl get -ncert-manager secret
---
NAME                                   TYPE                 DATA   AGE
aws-route53-secret-access-key-secret   Opaque               2      19h

Cert Manager 설치

Cert Manager를 설치하는 방법은 다음과 같습니다.

https://cert-manager.io/docs/installation/

여기서는 Helm을 활용하여 설치를 진행하겠습니다.

Helm repo에 jetstack을 추가합니다.

helm repo add jetstack <https://charts.jetstack.io>
helm repo update

Cert Manager 설치를 진행합니다.

helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.15.2 --set crds.enabled=true

설치된 내용을 확인해보겠습니다.

https://cert-manager.io/docs/installation/

여기서는 Helm을 활용하여 설치를 진행하겠습니다.

Helm repo에 jetstack을 추가합니다.

helm repo add jetstack <https://charts.jetstack.io>
helm repo update

Cert Manager 설치를 진행합니다.

helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.15.2 --set crds.enabled=true

설치된 내용을 확인해보겠습니다.

Cluster Issuer 생성

다음은 전체 Kubernetes 클러스터에 적용하기 위해 Cluster Issuer 를 생성해보겠습니다. Issuer 는 생성된 namespace 내에서만 인증을 진행합니다.

  • server는 Let’s Encrypt에서 제공하는 ACME 서버입니다.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-istio
spec:
  acme:
    email: [YOUR_EMAIL_ADDRESS] # Change your email
    privateKeySecretRef:
      name: letsencrypt-prod-istio
    server: <https://acme-v02.api.letsencrypt.org/directory>
    solvers:
      - selector:
          dnsZones:
            - "[YOUR_DOMAIN](example.com)"
        dns01:
          route53:
            region: ap-northeast-2
            hostedZoneID: [YOUR_AWS_ROUTE53_HOSTED_ZONE_ID]
            secretAccessKeySecretRef:
              name: aws-route53-secret-access-key-secret
              key: secret-access-key
            accessKeyIDSecretRef:
              name: aws-route53-secret-access-key-secret
              key: access-key-id

위의 ClusterIssuer를 생성해봅시다.

kubectl create -f cluster-issuer.yaml

kubectl get ClusterIssuer -A

Certificates 생성

  • commonName: 등록하려는 도메인을 입력합니다.
  • dnsNames: 등록하려는 도메인과 서브 도메인을 입력합니다.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: [YOUR_DOMAIN_NAME]-cert
  namespace: istio-system
spec:
  secretName: [YOUR_DOMAIN_NAME]-key-pair
  commonName: [YOUR_DOMAIN](example.com) # Change your domain
  dnsNames:
  - [YOUR_DOMAIN](example.com)
  - www.[YOUR_DOMAIN](example.com)
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  isCA: true
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-prod-istio
  secretName: [YOUR_DOMAIN_NAME]-cert
  privateKey:
    algorithm: RSA
    encoding: PKCS1
    size: 4096

Certificate를 받기 위한 컴포넌트는 다음과 같습니다.

  • CertificateRequest: TLS 인증서를 요청합니다.
  • Order: CertificateRequest를 기반으로 CA에게 인증서를 요청하는 주문입니다.
  • Challenge: 인증서를 발급하기 위해 CA가 인증서를 발급할 수 있는지 확인하는 방법을 나타냅니다.

 

Certificate를 받기 위한 과정은 다음과 같습니다.

  1. CertificateRequest 를 생성하면, Cert Manager는 해당 요청에 대한 Order를 생성
  2. Order가 생성되면, Cert Manager는 해당 Order에 대한 Challenge를 생성
  3. Challenge가 생성되면, Cert Manager는 해당 Challenge를 완료하기 위한 방법을 제공합니다. DNS-01 or HTTP-01 방식의 ACME 프로토콜을 통해 도메인 소유권을 확인
  4. Challenge가 완료되면, Cert Manager는 도메인 소유를 확인하고 Order를 완료하며, 인증서를 발급

생성에 이슈가 있다면 다음을 확인해보세요

kubectl get certificaterequests.cert-manager.io -n istio-system

kubectl get challenges.acme.cert-manager.io -n istio-system

kubectl describe challenges.acme.cert-manager.io -n istio-system xxxx-cert-x-xxxxxxx-xxxxxx

저의 경우에는 aws-route53-secret-access-key-secret을 못찾고 있었습니다.

  Normal   Started       3m10s               cert-manager-challenges  Challenge scheduled for processing
  Warning  PresentError  44s (x6 over 3m9s)  cert-manager-challenges  Error presenting challenge: error getting route53 secret access key id: secrets "aws-route53-secret-access-key-secret" not found

시간이 수분 정도 지나면 다음의 결과를 얻습니다.

kubectl get certificate -nistio-system

발급받은 TLS Key, Cert 확인을 해보겠습니다.

kubectl get secret -nistio-system
---
NAME              TYPE                DATA   AGE
[YOUR_DOMAIN_NAME]-cert      kubernetes.io/tls   2      3m49s

kubectl describe secret -nistio-system [YOUR_DOMAIN_NAME]-cert
---
Namespace:    istio-system
Labels:       controller.cert-manager.io/fao=true
Annotations:  cert-manager.io/alt-names: [YOUR_DOMAIN_NAME]
              cert-manager.io/certificate-name: [YOUR_DOMAIN_NAME]-cert
              cert-manager.io/common-name: [YOUR_DOMAIN_NAME]
              cert-manager.io/ip-sans:
              cert-manager.io/issuer-group:
              cert-manager.io/issuer-kind: ClusterIssuer
              cert-manager.io/issuer-name: letsencrypt-prod-istio
              cert-manager.io/uri-sans:

Type:  kubernetes.io/tls

...

Data
====
tls.crt:  3558 bytes
tls.key:  1679 bytes

secret에 있는 tls.crt를 확인하여 Base64 디코딩을 진행하고 tls.crt 파일을 생성합니다.

kubectl get secret [YOUR_DOMAIN_NAME]-cert -nistio-system -o jsonpath='{.data.tls\\.crt}' > tls.crt.base64

base64 -d tls.crt.base64 > tls.crt

cat tls.crt
----
-----BEGIN CERTIFICATE-----
MIIE5TCCA82gAwIBAgISAx+feKAWokBXv8a54bXL+slrMA0GCSqGSIb3DQEBCwUA

...

/N+kW5MjiRKVED9KY/xcDtJIvi3rRTcw2luxGDgPiFiwJo7hZiqKZdqf3CjNToMP
D2F1d1zwfyip
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFBjCCAu6gAwIBAgIRAIp9PhPWLzDvI4a9KQdrNPgwDQYJKoZIhvcNAQELBQAw
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh

...

6FlWkWYtbt4pgdamlwVeZEW+LM7qZEJEsMNPrfC03APKmZsJgpWCDWOKZvkZcvjV
uYkQ4omYCTX5ohy+knMjdOmdH9c7SpqEWBDC86fiNex+O0XOMEZSa8DA
-----END CERTIFICATE-----

openssl을 활용해 해당 Certificate 내용을 확인해보겠습니다.

openssl x509 -in ./tls.crt -text -noout

결론적으로 Cert-Manager가 AWS Route53을 통해 도메인의 소유권을 확인하고, Let’s Encrypt가 CA로 인증서를 발급하여 key와 cert 파일을 얻었습니다.

Gateway

Gateway에 TLS를 적용해보겠습니다.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: cluster-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "[YOUR_DOMAIN_NAME]"
    - "www.[YOUR_DOMAIN_NAME]"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: istio-system/[YOUR_DOMAIN_NAME]-cert
    hosts:
    - "[YOUR_DOMAIN_NAME]"
    - "www.[YOUR_DOMAIN_NAME]"

위의 Gateway를 생성해보겠습니다.

kubectl create -f gateway.yaml

kubectl get gateway -A

VirtualService

다음은 virtual service를 생성해보겠습니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-virtual-service
spec:
  hosts:
    - "[YOUR_DOMAIN_NAME]"
    - "www.[YOUR_DOMAIN_NAME]"
  gateways:
    - cluster-gateway
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: httpbin.default.svc.cluster.local
            port:
              number: 80

위의 virtual service를 생성해보겠습니다.

kubectl create -f virtualservicey.yaml

Service

다음은 service를 생성해보겠습니다.

apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: httpbin

위의 service를 생성해보겠습니다.

kubectl create -f service.yaml

Deployment

다음은 deployment를 생성해보겠습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
        - image: docker.io/kong/httpbin
          imagePullPolicy: IfNotPresent
          name: httpbin
          ports:
            - containerPort: 80

위의 deployment를 생성해보겠습니다.

최종적으로 테스트해보겠습니다. Istio의 443 포트로 바인딩된 Node Port를 확인해보겠습니다.

kubectl get service -nistio-system

브라우저에서 https://[YOUR_DOMAIN]:[443_NODE_PORT/ 로 이동해봅니다.

완성되었습니다.

AWS Route53에서 할당받은 도메인을 Kubernetes Istio와 Cert Manager를 활용해 적용해보았습니다.

감사합니다.