在kubernetes上使用cert-manager自動更新Let’s Encrypt TLS憑證

Ken Chen
10 min readApr 18, 2018

--

先前的文章中我使用kube-lego來幫我的cluster取得/更新let’s encrypt的TLS憑證,但最近發現kube-lego只支援到k8s 1.8了,接手的是cert-manager,使用了k8s的Issuer/Certificate feature,支援更多驗證方式,更好管理。不過目前還在開發階段,所以除非你很勇敢,不然請不要使用在production環境中!期待1.0的來到吧!

先來個架構圖,從這邊可以看出未來將會支援Let’s encrypt以外的認證機構。

具體差別可參考這個表格

安裝Helm跟Tiller

首先你需要安裝Helm跟Tiller,這是k8s的套件管理工具,如果已經安裝過的就可以跳過這一段。

完整安裝說明可以參考這個,我用mac所以當然就用homebrew啦~ 先裝helm:

$ brew update && brew install helm

update: Helm v3之後不需要安裝Tiller了裝完helm就可以跳下一步囉

然後是Tiller,k8s在1.7之後版本預設啟用RBAC,而cert-manager是運行在kube-system的namespace中,所以要先幫Tiller新增一個service account然後role binding成cluster-admin,不然這個Tiller是沒有權限幫你裝cert-manager的。

rbac-config.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
--
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
— kind: ServiceAccount
name: tiller
namespace: kube-system

執行!

$ kubectl create -f rbac-config.yaml
serviceaccount "tiller" created
clusterrolebinding "tiller" created

$ helm init --service-account tiller

以上就完成Helm跟Tiller的安裝!

安裝cert-manager

使用Helm安裝非常簡單,只需要一行指令:

$ helm install \
--name cert-manager \
--namespace kube-system \
stable/cert-manager

一般來說這樣就完成了,可以開始設定了!如果失敗的話要看一下log,一般來說跟前面的service account權限有關。

設定Issuer or ClusterIssuer

接著就要設定從哪邊取得憑證啦,k8s提供Issuer跟ClusterIssuer兩種,前者是單一namespace使用,後者是cluster wide也就是每個namespace都可以reference到,要用哪種就隨你啦

選好Issuer kind之後就要選擇取得憑證的方式,目前cert-manager支援ACME跟CA方式,而ACME又分DNS/HTTP兩種,這兩種都要先建好ingress controller!(GKE的使用者就不用了)

下面是最常用的HTTP issuer的yaml:

apiVersion: certmanager.k8s.io/v1alpha1 
kind: Issuer
metadata:
name: letsencrypt-staging
namespace: default
spec:
acme:
# The ACME server URL
server: https://acme-staging.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
http01: {}

這邊server指定的是Let’s encrypt的staging環境,是讓你測試用的免得一下把quota都用完了(staging一樣有rate limit但是比較寬鬆),這個環境發出的憑證是不被信任的,所以測試ok之後就可以把這一個換成https://acme-v01.api.letsencrypt.org/directory 來取得正式憑證。email是用來向ACME註冊的,而憑證快過期時也會發信到這個信箱提醒你。privateKeySecretRef就是儲存向ACME註冊的private key。http01:{}就是啟用HTTP-01 challenge不用特別設定。

如果要設定ClusterIssuer就是把上面yaml中kind換成ClusterIssuer以及拿掉namespace設定就行了。

取得Certificate

apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
name: example-com
namespace: default
spec:
secretName: example-com-tls
issuerRef:
name: letsencrypt-staging
commonName: example.com
dnsNames:
- www.example.com
acme:
config:
- http01:
ingressClass: nginx
domains:
- example.com
- http01:
ingress: my-ingress
domains:
- www.example.com

前面就不多說了,secretName是取得的憑證會用這個名稱儲存在secrets裡。issuerRef就是前面設定好的Issuer,如果是ClusterIssuer要加上 kind: ClusterIssuer。commonName就是憑證的commonName。dnsNames就是各種SANs( Subject Alternative Names)。

接下來定義取得方式,這邊使用http01,cert-manager會自動產生需要的pod, service, ingress class來完成認證,完成後也會自動清除。http01中有兩種,一個是ingress 一個是ingressClass,前者是使用現有的ingress適合使用GCLB ingress controller的使用者(GKE),後者會自動建立一個新的ingress resource,可以視需要使用。如果兩個都寫上去的話會優先吃ingress。

好了,這樣就完成憑證的取得了,可以用kubectl describe certificate去查看。也可以直接kubectl get secret example-com-tls -o yaml 看base64編碼的憑證。

在憑證過期的前30天cert-manager會自動更新。

如果是從kube-lego搬過來的…

首先把你的kube-lego deployment scale to 0

$ kubectl scale deployment kube-lego \
--namespace kube-lego \
--replicas=0 \

然後deploy cert-manager:

$ helm install \
--name cert-manager \
--namespace kube-system \
stable/cert-manager

為了繼續使用同樣身份來管理憑證資源,先要取得你原本在ACME註冊的private key,我們先把這個secrets匯出:

$ kubectl get secret kube-lego-account -o yaml \
--namespace kube-lego \
--export > kube-lego-account.yaml

修改 metadata.name然後在kube-system namespace中建立這個sercrets

$ kubectl create -f kube-lego-account.yaml \
--namespace kube-system

利用原本的ACME email及URL建立一個ClusterIssuer,email及URL可在configmap裡面找到。

$ kubectl get configmap kube-lego -o yaml \
--namespace kube-lego \
--export

clusterissuer.yaml:

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
# Adjust the name here accordingly
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key from step 3
privateKeySecretRef:
name: letsencrypt-private-key
# Enable the HTTP-01 challenge provider
http01: {}

設定ingress-shim使用剛建立的ClusterIssuer,ingress-shim會尋找含有 kubernetes.io/tls-acme: "true" annotation的ingress resource並且管理他的憑證。

helm upgrade cert-manager \
stable/cert-manager \
--namespace kube-system \
--set ingressShim.extraArgs='{--default-issuer-name=letsencrypt-staging,--default-issuer-kind=ClusterIssuer}'

這樣就大功告成了,describe certificate應該可以看到cert-manager已驗證並且排定多久以後要更新。

--

--