Cert Manager with SSL Domain with AWS Route53
Cert Manager with SSL Domain with AWS Route53
지난번 글에서 Istio
를 구축하였습니다.
https://jeongchul.tistory.com/720
금번 글에서는 AWS Route53
을 통해 할당받은 도메인을 SSL 인증서를 Cert Manager
와 Let's Encrypt
를 통해 발급받고, 이를 Istio Gateway
와 VirtualService
에 적용해보겠습니다.
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 ID
는 Cluster 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 인코딩하여 값을 넣습니다.
- https://www.base64encode.org/
echo [YOUR_AWS_USER_ACCESS_KEY] | base64
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/
- GitHub에 있는 cert-manager의 Releases에 있는 cert-manager.yaml을 활용해 배포합니다.
- https://github.com/cert-manager/cert-manager/
- https://github.com/cert-manager/cert-manager/releases/tag/v1.15.2
kubectl apply -f <https://github.com/cert-manager/cert-manager/releases/download/v1.15.2/cert-manager.yaml>
- Helm을 활용하여 배포합니다.
여기서는 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/
- GitHub에 있는 cert-manager의 Releases에 있는 cert-manager.yaml을 활용해 배포합니다.
- https://github.com/cert-manager/cert-manager/
- https://github.com/cert-manager/cert-manager/releases/tag/v1.15.2
kubectl apply -f <https://github.com/cert-manager/cert-manager/releases/download/v1.15.2/cert-manager.yaml>
- Helm을 활용하여 배포합니다.
여기서는 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를 받기 위한 과정은 다음과 같습니다.
- CertificateRequest 를 생성하면, Cert Manager는 해당 요청에 대한 Order를 생성
- Order가 생성되면, Cert Manager는 해당 Order에 대한 Challenge를 생성
- Challenge가 생성되면, Cert Manager는 해당 Challenge를 완료하기 위한 방법을 제공합니다. DNS-01 or HTTP-01 방식의 ACME 프로토콜을 통해 도메인 소유권을 확인
- 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을 못찾고 있었습니다.
- https://github.com/cert-manager/cert-manager/issues/540
- https://cert-manager.io/docs/faq/cluster-resource/
- Secret을 cert-manager namespace에 배포하여 해결하였습니다.
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를 활용해 적용해보았습니다.
감사합니다.