Using SSL certificates in Kubernetes ingress via cert-manager

之前將 SSL 憑證交給 Cloudflare 託管,要使用該服務就必須開啟 Cloudflare proxy 功能,但會造成網路效能降低(Cloudflare proxy issue)。 改用憑證管理工具 cert-managerLet's Encrypt 申請憑證。

Install cert-manager

使用 helm 來安裝 cert-manager 並指定版本為 v1.5.1。

1bash$: kubectl create cert-manager
2
3bash$: helm repo add jetstack https://charts.jetstack.io
4bash$: helm repo update
5bash$: helm install cert-manager jetstack/cert-manager -n 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 Cloudflare secret

建立 secret 存放 Cloudflare 的 token,訪問 Cloudflare API。

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

Create Issuer / ClusterIssuer

建立 ACME 的發證服務,其中 kind 類型有 Issuer、ClusterIssuer,差別在於 IssuerCertificate 必須在同一個 namespace 下;而 ClusterIssuerCertificate 可以在不同 namespace 下。

另外 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: cert-manager.io/v1
 2kind: ClusterIssuer
 3metadata:
 4  name: letsencrypt-dns01
 5  namespace: cert-manager
 6spec:
 7  acme:
 8    email: user@example.com
 9    server: https://acme-v02.api.letsencrypt.org/directory
10    privateKeySecretRef:
11      name: letsencrypt-dns01
12    solvers:
13    - dns01:
14        cloudflare:
15          email: my-cloudflare-acc@example.com
16          apiTokenSecretRef:
17            name: cloudflare-api-token-secret
18            key: api-token

透過下面指令可以查詢服務狀況。

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: example.com-certificate
 5spec:
 6  secretName: example.com-certificate
 7  issuerRef:
 8    name: letsencrypt-dns01
 9    kind: ClusterIssuer
10  dnsNames:
11    - 'example.com'
12    - '*.example.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
comments powered by Disqus