手把手教你使用 cert-manager 簽發免費證書


概述

隨着 HTTPS 不斷普及,越來越多的網站都在從 HTTP 升級到 HTTPS,使用 HTTPS 就需要向權威機構申請證書,需要付出一定的成本,如果需求數量多,也是一筆不小的開支。cert-manager 是 Kubernetes 上的全能證書管理工具,如果對安全級別和證書功能要求不高,可以利用 cert-manager 基於 ACME 協議與 Let's Encrypt 來簽發免費證書並自動續期,實現永久免費使用證書。

cert-manager 工作原理

cert-manager 部署到 Kubernetes 集群后,它會 watch 它所支持的 CRD 資源,我們通過創建 CRD 資源來指示 cert-manager 為我們簽發證書並自動續期:

img

解釋下幾個關鍵的資源:

  • Issuer/ClusterIssuer: 用於指示 cert-manager 用什么方式簽發證書,本文主要講解簽發免費證書的 ACME 方式。ClusterIssuer 與 Issuer 的唯一區別就是 Issuer 只能用來簽發自己所在 namespace 下的證書,ClusterIssuer 可以簽發任意 namespace 下的證書。
  • Certificate: 用於告訴 cert-manager 我們想要什么域名的證書以及簽發證書所需要的一些配置,包括對 Issuer/ClusterIssuer 的引用。

免費證書簽發原理

Let’s Encrypt 利用 ACME 協議來校驗域名是否真的屬於你,校驗成功后就可以自動頒發免費證書,證書有效期只有 90 天,在到期前需要再校驗一次來實現續期,幸運的是 cert-manager 可以自動續期,這樣就可以使用永久免費的證書了。如何校驗這個域名是否屬於你呢?主流的兩種校驗方式是 HTTP-01 和 DNS-01,詳細校驗原理可參考 Let's Encrypt 的運作方式,下面將簡單描述下。

HTTP-01 校驗原理

HTTP-01 的校驗原理是給你域名指向的 HTTP 服務增加一個臨時 location ,Let’s Encrypt 會發送 http 請求到 http:///.well-known/acme-challenge/YOUR_DOMAIN 就是被校驗的域名,TOKEN 是 ACME 協議的客戶端負責放置的文件,在這里 ACME 客戶端就是 cert-manager,它通過修改或創建 Ingress 規則來增加這個臨時校驗路徑並指向提供 TOKEN 的服務。Let’s Encrypt 會對比 TOKEN 是否符合預期,校驗成功后就會頒發證書。此方法僅適用於給使用 Ingress 暴露流量的服務頒發證書,並且不支持泛域名證書。

DNS-01 校驗原理

DNS-01 的校驗原理是利用 DNS 提供商的 API Key 拿到你的 DNS 控制權限, 在 Let’s Encrypt 為 ACME 客戶端提供令牌后,ACME 客戶端 (cert-manager) 將創建從該令牌和您的帳戶密鑰派生的 TXT 記錄,並將該記錄放在 _acme-challenge.。 然后 Let’s Encrypt 將向 DNS 系統查詢該記錄,如果找到匹配項,就可以頒發證書。此方法不需要你的服務使用 Ingress,並且支持泛域名證書。

校驗方式對比

HTTP-01 的校驗方式的優點是: 配置簡單通用,不管使用哪個 DNS 提供商都可以使用相同的配置方法;缺點是:需要依賴 Ingress,如果你的服務不是用 Ingress 暴露流量的就不適用,而且不支持泛域名證書。

DNS-01 的校驗方式的優點是沒有 HTTP-01 校驗方式缺點,不依賴 Ingress,也支持泛域名;缺點就是不同 DNS 提供商的配置方式不一樣,而且 DNS 提供商有很多,cert-manager 的 Issuer 不可能每個都去支持,不過有一些可以通過部署實現了 cert-manager 的 Webhook 的服務來擴展 Issuer 進行支持,比如 DNSPod 和 阿里 DNS,詳細 Webhook 列表請參考: https://cert-manager.io/docs/configuration/acme/dns01/#webhook

選擇哪種方式呢?條件允許的話,建議是盡量用 DNS-01 的方式,限制更少,功能更全。

操作步驟

安裝 cert-manager

通常直接使用 yaml 方式一鍵安裝 cert-manager 到集群,參考官網文檔 Installing with regular manifests

cert-manager 官方使用的鏡像在 quay.io,國內拉取可能比較慢,也可以使用下面命令一鍵安裝(使用同步到國內 CCR 的鏡像):

kubectl apply --validate=false -f https://raw.githubusercontent.com/TencentCloudContainerTeam/manifest/master/cert-manager/cert-manager.yaml

! 以上命令安裝方式要求集群版本不低於 1.16。

配置 DNS

登錄你的 DNS 提供商后台,配置域名的 DNS A 記錄,指向你需要證書的后端服務對外暴露的 IP 地址,以 cloudflare 為例:

img

HTTP-01 校驗方式簽發證書

如果使用 HTTP-01 的校驗方式,需要用到 Ingress 來配合校驗。cert-manager 會通過自動修改 Ingress 規則或自動新增 Ingress 來實現對外暴露校驗所需的臨時 HTTP 路徑,這個就是在給 Issuer 配置 http01 校驗,指定 Ingress 的 nameclass 的區別 (見下面的示例)。

TKE 自帶的 Ingress 是每個 Ingress 資源都會對應一個 CLB,如果你使用 TKE 自帶的 Ingress 暴露服務,並且使用 HTTP-01 方式校驗,那么只能使用自動修改 Ingress 的方式,不能自動新增 Ingress,因為自動新增出來的 Ingress 會自動創建其它 CLB,對外的 IP 地址就與我們后端服務的 Ingress 不一致,Let's Encrypt 校驗時就無法從我們服務的 Ingress 找到校驗所需的臨時路徑,從而導致校驗失敗,無法簽發證書。如果使用自建 Ingress,比如 在 TKE 上部署 Nginx Ingress,同一個 Ingress class 的 Ingress 共享同一個 CLB,這樣就可以使用自動新增 Ingress 的方式。

下面給出一些示例。

如果你的服務使用 TKE 自帶的 Ingress 暴露服務,不太適合用 cert-manager 簽發管理免費證書,因為證書是要上傳到 證書管理 來引用的,不在 K8S 中管理。

假設是 在 TKE 上部署 Nginx Ingress,且后端服務的 Ingress 是 prod/web,創建 Issuer 示例:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-http01
  namespace: prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-http01-account-key
    solvers:
    - http01:
       ingress:
         name: web # 指定被自動修改的 Ingress 名稱

使用上面的 Issuer 簽發證書,cert-manager 會自動修改 prod/web 這個 Ingress 資源,以暴露校驗所需的臨時路徑,這是自動修改 Ingress 的方式,你可以使用自動新增 Ingress 的 方式,示例:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-http01
  namespace: prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-http01-account-key
    solvers:
    - http01:
       ingress:
         class: nginx # 指定自動創建的 Ingress 的 ingress class

使用上面的 Issuer 簽發證書,cert-manager 會自動創建 Ingress 資源,以暴露校驗所需的臨時路徑。

有了 Issuer,接下來就可以創建 Certificate 並引用 Issuer 進行簽發了,示例:

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: test-mydomain-com
  namespace: prod
spec:
  dnsNames:
  - test.mydomain.com # 要簽發證書的域名
  issuerRef:
    kind: Issuer
    name: letsencrypt-http01 # 引用 Issuer,指示采用 http01 方式進行校驗
  secretName: test-mydomain-com-tls # 最終簽發出來的證書會保存在這個 Secret 里面

DNS-01 校驗方式簽發證書

如果使用 DNS-01 的校驗方式,就需要看你使用的哪個 DNS 提供商了,cert-manager 內置了一些 DNS 提供商的支持,詳細列表和用法請參考 Supported DNS01 providers,不過 cert-manager 不可能去支持所有的 DNS 提供商,如果沒有你所使用的 DNS 提供商怎么辦呢?有兩種方案:

  • 方案一:設置 Custom Nameserver。在你的 DNS 提供商后台設置 custom nameserver,指向像 cloudflare 這種可以管理其它 DNS 提供商域名的 nameserver 地址,具體地址可登錄 cloudflare 后台查看:

    img

    下面是 namecheap 設置 custom nameserver 的示例:

    img

    最后配置 Issuer 指定 DNS-01 驗證時,加上 cloudflare 的一些信息即可(見下文示例)。

  • 方案二:使用 Webhook。使用 cert-manager 的 Webhook 來擴展 cert-manager 的 DNS-01 驗證所支持的 DNS 提供商,已經有許多第三方實現,包括國內常用的 DNSPod 與阿里 DNS,詳細列表參考: Webhook

下面以 cloudflare 為例來簽發證書:

  1. 登錄 cloudflare,點到 My Profile > API Tokens > Create Token 來創建 Token:

    img

    復制 Token 並妥善保管:

    img

    將 Token 保存到 Secret 中:

    apiVersion: v1
    kind: Secret
    metadata:
      name: cloudflare-api-token-secret
      namespace: cert-manager
    type: Opaque
    stringData:
      api-token: <API Token> # 粘貼 Token 到這里,不需要 base64 加密。
    

    ! 如果是要創建 ClusterIssuer,Secret 需要創建在 cert-manager 所在命名空間中,如果是 Issuer,那就創建在 Issuer 所在命名空間中。

    創建 ClusterIssuer:

    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: letsencrypt-dns01
    spec:
      acme:
        privateKeySecretRef:
          name: letsencrypt-dns01
        server: https://acme-v02.api.letsencrypt.org/directory
        solvers:
        - dns01:
            cloudflare:
              email: my-cloudflare-acc@example.com # 替換成你的 cloudflare 郵箱賬號,API Token 方式認證非必需,API Keys 認證是必需
              apiTokenSecretRef:
                key: api-token
                name: cloudflare-api-token-secret # 引用保存 cloudflare 認證信息的 Secret
    

    創建 Certificate:

    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: test-mydomain-com
      namespace: default
    spec:
      dnsNames:
      - test.mydomain.com # 要簽發證書的域名
      issuerRef:
        kind: ClusterIssuer
        name: letsencrypt-dns01 # 引用 ClusterIssuer,指示采用 dns01 方式進行校驗
      secretName: test-mydomain-com-tls # 最終簽發出來的證書會保存在這個 Secret 里面
    

獲取和使用證書

創建好 Certificate 后,等一小會兒,我們可以 kubectl 查看是否簽發成功:

$ kubectl get certificate -n prod
NAME                READY   SECRET                  AGE
test-mydomain-com   True    test-mydomain-com-tls   1m

如果 READYFalse 表示失敗,可以通過 describe 查看 event 來排查失敗原因:

$ kubectl describe certificate test-mydomain-com -n prod

如果為 True 表示簽發成功,證書就保存在我們所指定的 Secret 中 (上面的例子是 default/test-mydomain-com-tls),可以通過 kubectl 查看:

$ kubectl get secret test-mydomain-com-tls -n default
...
data:
  tls.crt: <cert>
  tls.key: <private key>

其中 tls.crt 就是證書,tls.key 是密鑰。

你可以將它們掛載到你需要證書的應用中,或者使用自建的 Ingress,可以直接在 Ingress 中引用 secret,示例:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    kubernetes.io/Ingress.class: nginx
spec:
  rules:
  - host: test.mydomain.com
    http:
      paths:
      - path: /web
        backend:
          serviceName: web
          servicePort: 80
  tls:
    hosts:
    - test.mydomain.com
    secretName: test-mydomain-com-tls

小結

本文介紹了 cert-manager 的工作原理,安裝方法以及簽發免費證書的兩種校驗方式 (HTTP-01 與 DNS-01) 的原理、對比以及操作方法。

參考資料

【騰訊雲原生】雲說新品、雲研新術、雲游新活、雲賞資訊,掃碼關注同名公眾號,及時獲取更多干貨!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM