概述
隨著HTTPS不斷普及,越來越多的網(wǎng)站都在從HTTP升級到HTTPS,使用HTTPS就需要向權(quán)威機構(gòu)申請證書,需要付出一定的成本,如果需求數(shù)量多,也是一筆不小的開支。cert-manager是Kubernetes上的全能證書管理工具,如果對安全級別和證書功能要求不高,可以利用cert-manager[1]基于ACME[2]協(xié)議與Let's Encrypt[3]來簽發(fā)免費證書并自動續(xù)期,實現(xiàn)永久免費使用證書。
cert-manager工作原理
cert-manager部署到Kubernetes集群后,它會watch它所支持的CRD資源,我們通過創(chuàng)建CRD資源來指示cert-manager為我們簽發(fā)證書并自動續(xù)期:
解釋下幾個關(guān)鍵的資源:
·Issuer/ClusterIssuer:用于指示cert-manager用什么方式簽發(fā)證書,本文主要講解簽發(fā)免費證書的ACME方式。ClusterIssuer與Issuer的唯一區(qū)別就是Issuer只能用來簽發(fā)自己所在namespace下的證書,ClusterIssuer可以簽發(fā)任意namespace下的證書。
·Certificate:用于告訴cert-manager我們想要什么域名的證書以及簽發(fā)證書所需要的一些配置,包括對Issuer/ClusterIssuer的引用。
免費證書簽發(fā)原理
Let’s Encrypt利用ACME協(xié)議來校驗域名是否真的屬于你,校驗成功后就可以自動頒發(fā)免費證書,證書有效期只有90天,在到期前需要再校驗一次來實現(xiàn)續(xù)期,幸運的是cert-manager可以自動續(xù)期,這樣就可以使用永久免費的證書了。如何校驗這個域名是否屬于你呢?主流的兩種校驗方式是HTTP-01和DNS-01,詳細校驗原理可參考Let's Encrypt的運作方式[4],下面將簡單描述下。
HTTP-01校驗原理
HTTP-01的校驗原理是給你域名指向的HTTP服務增加一個臨時location,Let’s Encrypt會發(fā)送http請求到http:///.well-known/acme-challenge/,YOUR_DOMAIN就是被校驗的域名,TOKEN是ACME協(xié)議的客戶端負責放置的文件,在這里ACME客戶端就是cert-manager,它通過修改或創(chuàng)建Ingress規(guī)則來增加這個臨時校驗路徑并指向提供TOKEN的服務。Let’s Encrypt會對比TOKEN是否符合預期,校驗成功后就會頒發(fā)證書。此方法僅適用于給使用Ingress暴露流量的服務頒發(fā)證書,并且不支持泛域名證書。
DNS-01校驗原理
DNS-01的校驗原理是利用DNS提供商的API Key拿到你的DNS控制權(quán)限,在Let’s Encrypt為ACME客戶端提供令牌后,ACME客戶端(cert-manager)將創(chuàng)建從該令牌和您的帳戶密鑰派生的TXT記錄,并將該記錄放在_acme-challenge.。然后Let’s Encrypt將向DNS系統(tǒng)查詢該記錄,如果找到匹配項,就可以頒發(fā)證書。此方法不需要你的服務使用Ingress,并且支持泛域名證書。
校驗方式對比
HTTP-01的校驗方式的優(yōu)點是:配置簡單通用,不管使用哪個DNS提供商都可以使用相同的配置方法;缺點是:需要依賴Ingress,如果你的服務不是用Ingress暴露流量的就不適用,而且不支持泛域名證書。
DNS-01的校驗方式的優(yōu)點是沒有HTTP-01校驗方式缺點,不依賴Ingress,也支持泛域名;缺點就是不同DNS提供商的配置方式不一樣,而且DNS提供商有很多,cert-manager的Issuer不可能每個都去支持,不過有一些可以通過部署實現(xiàn)了cert-manager的Webhook[5]的服務來擴展Issuer進行支持,比如DNSPod和阿里DNS,詳細Webhook列表請參考:https://cert-manager.io/docs/configuration/acme/dns01/#webhook
選擇哪種方式呢?條件允許的話,建議是盡量用DNS-01的方式,限制更少,功能更全。
操作步驟
安裝cert-manager
通常直接使用yaml方式一鍵安裝cert-manager到集群,參考官網(wǎng)文檔Installing with regular manifests[6]。
cert-manager官方使用的鏡像在quay.io,國內(nèi)拉取可能比較慢,也可以使用下面命令一鍵安裝(使用同步到國內(nèi)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為例:
HTTP-01校驗方式簽發(fā)證書
如果使用HTTP-01的校驗方式,需要用到Ingress來配合校驗。cert-manager會通過自動修改Ingress規(guī)則或自動新增Ingress來實現(xiàn)對外暴露校驗所需的臨時HTTP路徑,這個就是在給Issuer配置http01校驗,指定Ingress的name或class的區(qū)別(見下面的示例)。
TKE自帶的Ingress是每個Ingress資源都會對應一個CLB,如果你使用TKE自帶的Ingress暴露服務,并且使用HTTP-01方式校驗,那么只能使用自動修改Ingress的方式,不能自動新增Ingress,因為自動新增出來的Ingress會自動創(chuàng)建其它CLB,對外的IP地址就與我們后端服務的Ingress不一致,Let's Encrypt校驗時就無法從我們服務的Ingress找到校驗所需的臨時路徑,從而導致校驗失敗,無法簽發(fā)證書。如果使用自建Ingress,比如在TKE上部署Nginx Ingress,同一個Ingress class的Ingress共享同一個CLB,這樣就可以使用自動新增Ingress的方式。
下面給出一些示例。
如果你的服務使用TKE自帶的Ingress暴露服務,不太適合用cert-manager簽發(fā)管理免費證書,因為證書是要上傳到證書管理[7]來引用的,不在K8S中管理。
假設是在TKE上部署Nginx Ingress,且后端服務的Ingress是prod/web,創(chuàng)建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簽發(fā)證書,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 # 指定自動創(chuàng)建的 Ingress 的 ingress class
使用上面的Issuer簽發(fā)證書,cert-manager會自動創(chuàng)建Ingress資源,以暴露校驗所需的臨時路徑。
有了Issuer,接下來就可以創(chuàng)建Certificate并引用Issuer進行簽發(fā)了,示例:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: test-mydomain-com
namespace: prod
spec:
dnsNames:
- test.mydomain.com # 要簽發(fā)證書的域名
issuerRef:
kind: Issuer
name: letsencrypt-http01 # 引用 Issuer,指示采用 http01 方式進行校驗
secretName: test-mydomain-com-tls # 最終簽發(fā)出來的證書會保存在這個 Secret 里面
DNS-01校驗方式簽發(fā)證書
如果使用DNS-01的校驗方式,就需要看你使用的哪個DNS提供商了,cert-manager內(nèi)置了一些DNS提供商的支持,詳細列表和用法請參考Supported DNS01 providers[8],不過cert-manager不可能去支持所有的DNS提供商,如果沒有你所使用的DNS提供商怎么辦呢?有兩種方案:
·方案一:設置Custom Nameserver。在你的DNS提供商后臺設置custom nameserver,指向像cloudflare這種可以管理其它DNS提供商域名的nameserver地址,具體地址可登錄cloudflare后臺查看:
下面是namecheap設置custom nameserver的示例:
最后配置Issuer指定DNS-01驗證時,加上cloudflare的一些信息即可(見下文示例)。
·方案二:使用Webhook。使用cert-manager的Webhook來擴展cert-manager的DNS-01驗證所支持的DNS提供商,已經(jīng)有許多第三方實現(xiàn),包括國內(nèi)常用的DNSPod與阿里DNS,詳細列表參考:Webhook[9]。
下面以cloudflare為例來簽發(fā)證書:
1.登錄cloudflare,點到My Profile>API Tokens>Create Token來創(chuàng)建Token:
復制Token并妥善保管:
將Token保存到Secret中:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token-secret
namespace: cert-manager
type: Opaque
stringData:
api-token: <API Token> # 粘貼 Token 到這里,不需要 base64 加密。
如果是要創(chuàng)建ClusterIssuer,Secret需要創(chuàng)建在cert-manager所在命名空間中,如果是Issuer,那就創(chuàng)建在Issuer所在命名空間中。
創(chuàng)建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
創(chuàng)建Certificate:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: test-mydomain-com
namespace: default
spec:
dnsNames:
- test.mydomain.com # 要簽發(fā)證書的域名
issuerRef:
kind: ClusterIssuer
name: letsencrypt-dns01 # 引用 ClusterIssuer,指示采用 dns01 方式進行校驗
secretName: test-mydomain-com-tls # 最終簽發(fā)出來的證書會保存在這個 Secret 里面
獲取和使用證書
創(chuàng)建好Certificate后,等一小會兒,我們可以kubectl查看是否簽發(fā)成功:
$ kubectl get certificate -n prod
NAME READY SECRET AGE
test-mydomain-com True test-mydomain-com-tls 1m
如果READY為False表示失敗,可以通過describe查看event來排查失敗原因:
$kubectl describe certificate test-mydomain-com-n prod
如果為True表示簽發(fā)成功,證書就保存在我們所指定的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
小結(jié)
本文介紹了cert-manager的工作原理,安裝方法以及簽發(fā)免費證書的兩種校驗方式(HTTP-01與DNS-01)的原理、對比以及操作方法。
參考資料
[1]cert-manager:https://cert-manager.io/
[2]ACME:https://tools.ietf.org/html/rfc8555X
[3]Let's Encrypt:https://letsencrypt.org/
[4]Let's Encrypt的運作方式:https://letsencrypt.org/zh-cn/how-it-works/
[5]Webhook:https://cert-manager.io/docs/concepts/webhook/
[6]Installing with regular manifests:https://cert-manager.io/docs/installation/kubernetes/#installing-with-regular-manifests
[7]證書管理:https://console.cloud.tencent.com/ssl
[8]Supported DNS01 providers:https://cert-manager.io/docs/configuration/acme/dns01/#supported-dns01-providers
[9]Webhook:https://cert-manager.io/docs/configuration/acme/dns01/#webhook
[10]Issuer API文檔:https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.Issuer
[11]Certificate API文檔:https://cert-manager.io/docs/reference/api-docs/#cert-manager.io/v1.Certificate