Using SSL certificates in Kubernetes ingress via cert-manager
之前將 SSL 憑證交給 Cloudflare 託管,要使用該服務就必須開啟 Cloudflare proxy 功能,但會造成網路效能降低([[cloudflare-proxy-slow-issue]])。 改用憑證管理工具 cert-manager 向 Let's Encrypt 申請憑證。
Install cert-manager
使用 helm 來安裝 cert-manager 並指定版本為 v1.5.1。
1bash$: helm repo add jetstack https://charts.jetstack.io
2bash$: helm repo update
3bash$: helm install --namespace cert-manager --create-namespace cert-manager jetstack/cert-manager --version v1.5.1 --set installCRDs=true
Get Cloudflare token
在 cert-manager acme 中有兩種申請憑證的方案,這邊採用 DNS01 方案去申請域名的 wildcard certificate。
- HTTP01:透過 HTTP 請求來認證網站
- DNS01:透過 DNS 供應商來認證網站
使用 DNS01 的方案,所以需要在 Cloudflare 上建立 token 來做認證。進入 Cloudflare User Profile,依照下面配置建立 token。
1Permissions:
2 * Zone - DNS - Edit
3 * Zone - Zone - Read
4Zone Resources:
5 * Include - All Zones
Create Issuer / ClusterIssuer
建立 ACME 的發證服務,其中 kind 類型有 Issuer、ClusterIssuer,差別在於 Issuer
和 Certificate
必須在同一個 namespace 下;而 ClusterIssuer
和 Certificate
可以在不同 namespace 下。
Cloudflare
Let's Encrypt 有提供測試環境 Staging Environment,在使用額度的部分會比較多。
- production environment server:
https://acme-v02.api.letsencrypt.org/directory
- staging environment server:
https://acme-staging-v02.api.letsencrypt.org/directory
1apiVersion: v1
2kind: Secret
3metadata:
4 name: cloudflare-api-token-secret
5 namespace: cert-manager
6type: Opaque
7stringData:
8 api-token: pleace-change-cloudflare-secret-token
9---
10apiVersion: cert-manager.io/v1
11kind: ClusterIssuer
12metadata:
13 name: letsencrypt-dns01
14 namespace: cert-manager
15spec:
16 acme:
17 email: user@example.com
18 server: https://acme-v02.api.letsencrypt.org/directory
19 privateKeySecretRef:
20 name: letsencrypt-dns01
21 solvers:
22 - dns01:
23 cloudflare:
24 email: my-cloudflare-acc@example.com
25 apiTokenSecretRef:
26 name: cloudflare-api-token-secret
27 key: api-token
AWS route53
1apiVersion: cert-manager.io/v1
2kind: ClusterIssuer
3metadata:
4 name: letsencrypt-dns01
5 namespace: cert-manager
6spec:
7 acme:
8 email: my-route53@example.com
9 privateKeySecretRef:
10 name: letsencrypt-dns01
11 server: https://acme-v02.api.letsencrypt.org/directory
12 solvers:
13 - selector:
14 dnsZones:
15 - "example1.com"
16 dns01:
17 route53:
18 region: ap-southeast-1
19 hostedZoneID: please-change-hosted-zone-id
20 - selector:
21 dnsZones:
22 - "example2.com"
23 dns01:
24 route53:
25 region: ap-southeast-1
26 hostedZoneID: please-change-hosted-zone-id
透過下面指令可以查詢服務狀況。
1bash$ kubectl get cert-manager -n cert-manager
2NAME READY AGE
3clusterissuer.cert-manager.io/letsencrypt-dns01 True 62m
Create Certificate
cert-manager 的 Certificate 會更新憑證並且將憑證保存至定義的 secret 中。 在 yaml 中定義好預期要申請的域名後,會跟 Issuer、ClusterIssuer 發起申請憑證。
1apiVersion: cert-manager.io/v1
2kind: Certificate
3metadata:
4 name: example1.com
5spec:
6 secretName: example1.com
7 duration: 2160h # 90d
8 renewBefore: 360h # 15d
9 issuerRef:
10 name: letsencrypt-dns01
11 kind: ClusterIssuer
12 dnsNames:
13 - 'example1.com'
14 - '*.example1.com'
15---
16apiVersion: cert-manager.io/v1
17kind: Certificate
18metadata:
19 name: example2.com
20spec:
21 secretName: example2.com
22 duration: 2160h # 90d
23 renewBefore: 360h # 15d
24 issuerRef:
25 name: letsencrypt-dns01
26 kind: ClusterIssuer
27 dnsNames:
28 - 'example2.com'
29 - '*.example2.com'
查詢服務狀況。第一次發起申請憑證的時候,狀態從 False -> True 大約等了 10 分鐘。
1bash$ kubectl get cert-manager
2NAME STATE AGE
3order.acme.cert-manager.io/example.com-certificate-bk8vh-2305456588 valid 54m
4
5NAME APPROVED DENIED READY ISSUER REQUESTOR AGE
6certificaterequest.cert-manager.io/example.com-certificate-bk8vh True True letsencrypt-dns01 system:serviceaccount:cert-manager:cert-manager 54m
7
8NAME READY SECRET AGE
9certificate.cert-manager.io/example.com-certificate True example.com-certificate 54m
10
11NAME READY AGE
12clusterissuer.cert-manager.io/letsencrypt-dns01 True 63m
查詢憑證狀況。
1bash$ kubectl get secret
2NAME TYPE DATA AGE
3example.com-certificate kubernetes.io/tls 2 70m
4
5bash$ kubectl describe secret/example.com-certificate
6Name: example.com-certificate
7Namespace: default
8Labels: <none>
9Annotations: cert-manager.io/alt-names: *.example.com,example.com
10 cert-manager.io/certificate-name: example.com-certificate
11 cert-manager.io/common-name: example.com
12 cert-manager.io/ip-sans:
13 cert-manager.io/issuer-group:
14 cert-manager.io/issuer-kind: ClusterIssuer
15 cert-manager.io/issuer-name: letsencrypt-dns01
16 cert-manager.io/uri-sans:
17
18Type: kubernetes.io/tls
19
20Data
21====
22tls.crt: 5615 bytes
23tls.key: 1675 bytes
Modified Nginx ingress
確認申請憑證完成後,修改 ingress 的配置,這邊我的 ingress 是使用 ingress-nginx 搭配 AWS 的 network load balance。
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3...
4spec:
5 tls:
6 - hosts:
7 - justin.example.com
8 secretName: acme-letsencrypt-certificate
9 rules:
10 - host: "example.com-certificate"
11 http:
12 paths:
13 - path: /
14 pathType: Prefix
15 backend:
16 service:
17 name: golang
18 port:
19 number: 80