Kubernetes中Nginx Ingress + Cert-Manager實(shí)現(xiàn)自動(dòng)Https
在Kubernetes集群中使用 HTTPS 協(xié)議,需要一個(gè)證書管理器、一個(gè)證書自動(dòng)簽發(fā)服務(wù),主要通過(guò) Ingress 來(lái)發(fā)布 HTTPS 服務(wù),因此需要Ingress Controller并進(jìn)行配置,啟用 HTTPS 及其路由。
cert-manager作為一系列部署資源在Kubernetes集群中運(yùn)行。
CustomResourceDefinitions(CRD) 用于配置證書頒發(fā)機(jī)構(gòu)和請(qǐng)求證書。
安裝cert-manager
使用常規(guī)清單安裝
所有的資源(CRD cert-manager, namespace和webhook組件), 都包含在單個(gè)的YAML清單中。
安裝CRD和cert-manager。
# Kubernetes 1.16+
$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.yaml
# Kubernetes <1.16
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager-legacy.yaml
PS: 請(qǐng)根據(jù)k8s版本自行選擇。
使用helm安裝
準(zhǔn)備工作:
#為cert-manager創(chuàng)建名稱空間
$ kubectl create namespace cert-manager
#添加Jetstack Helm存儲(chǔ)庫(kù)
helm repo add jetstack https://charts.jetstack.io
#更新您的本地Helm圖表存儲(chǔ)庫(kù)緩存
$ helm repo update
CustomResourceDefinition使用kubectl以下命令安裝資源:
# Kubernetes 1.15+
$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager.crds.yaml
# Kubernetes <1.15
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.1.0/cert-manager-legacy.crds.yaml
也可以選擇在進(jìn)行helm安裝時(shí)設(shè)置--set installCRDs=true:
# Helm v3+
$ helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.1.0 \
# --set installCRDs=true
$ helm install \
cert-manager --namespace cert-manager \
--set ingressShim.defaultIssuerName=letsencrypt-prod \
--set ingressShim.defaultIssuerKind=ClusterIssuer \
--version v1.1.0 jetstack/cert-manager
# 上訴命令中的兩個(gè)set;用于支持kubernetes.io/tls-acme: "true"annotation 來(lái)自動(dòng)化 TLS
# Helm v2
$ helm install \
--name cert-manager \
--namespace cert-manager \
--version v1.1.0 \
jetstack/cert-manager \
# --set installCRDs=true
驗(yàn)證安裝:
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-bcd4f8795-zpxsw 1/1 Running 0 2m
cert-manager-cainjector-78cbd59555-dhsp8 1/1 Running 0 2m
cert-manager-webhook-756d477cc4-j5mzt 1/1 Running 0 2m
確認(rèn)正確設(shè)置了證書管理器并能夠頒發(fā)基本證書類型:
$ cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager-test
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: test-selfsigned
namespace: cert-manager-test
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: selfsigned-cert
namespace: cert-manager-test
spec:
dnsNames:
- example.com
secretName: selfsigned-cert-tls
issuerRef:
name: test-selfsigned
EOF
創(chuàng)建測(cè)試資源:
$ kubectl apply -f test-resources.yaml
#檢查新創(chuàng)建證書的狀態(tài)。您可能需要等待幾秒鐘,然后cert-manager才能處理證書請(qǐng)求
$ kubectl describe certificate -n cert-manager-test
#最后清理測(cè)試資源
$ kubectl delete -f test-resources.yaml
創(chuàng)建證書簽發(fā)服務(wù)
cert-manager安裝之后, 需要定義cluster issuer 或者 issuer資源, 才能進(jìn)行證書的頒發(fā). 這些資源代表特定的簽名機(jī)構(gòu). 說(shuō)明請(qǐng)參考官方文檔. 支持多種的簽發(fā)類型,這里我們使用的ACME。
關(guān)于ACME有以下兩種認(rèn)證方式:
- HTTP01:適用于有公網(wǎng)IP的ingress資源. 且配置簡(jiǎn)單。
- DNS01:適用于內(nèi)網(wǎng)的k8s集群,且沒有公網(wǎng)IP,實(shí)現(xiàn)自動(dòng)化,需借助第三方工具;當(dāng)然cert-manager也是默認(rèn)支持一些域名服務(wù)商的,對(duì)于不支持的廠商也提供了通用的webhook方法. 如果沒有在列表中,可以自己通過(guò)example實(shí)現(xiàn)。
簡(jiǎn)單理解,HTTP01就是通過(guò)請(qǐng)求你需要申請(qǐng)證書的域名下的;例如:www.example.com/.well-know/acme-challenge/CR-WBJ-XXXXXXXX 的路徑,去校驗(yàn)網(wǎng)站; 前提是此路徑必須公網(wǎng)能正常訪問(wèn),且域名解析已正確指向你的ingress的公網(wǎng)地址. 就是校驗(yàn)ingress的公網(wǎng)IP以及域名解析的指向都必須是正確的. 才能頒發(fā)證書。
簡(jiǎn)單理解,DNS01就是acme服務(wù)器會(huì)給你生成一個(gè)txt的解析,需要您正確添加到您的DNS管理后臺(tái)當(dāng)中,且當(dāng)ACME服務(wù)器校驗(yàn)解析生效時(shí),才能證明域名的歸屬權(quán)是屬于您的,才能給您頒發(fā)證書。
HTTP01
配置ClusterIssuer:
PS: letsencrypt-staging 這里官網(wǎng)提供的實(shí)例是臨時(shí), 如果采用此ClusterIssuer默認(rèn)會(huì)生成暫存 Lets Encrypt 證書,暫存證書由 CN=Fake LE Intermediate X1 頒發(fā),如果你得到此證書,也表示已自動(dòng)申請(qǐng)成功.將ClusterIssuer YAML 中的 ACME 服務(wù)器替換為https://acme-v02.api.letsencrypt.org/directory;并創(chuàng)建一個(gè)新的名字為letsencrypt-prod的ClusterIssuer;且郵箱也必須替換。
官方示例:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: user .com
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: example-issuer-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx
letsencrypt-prod:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: yang-li@live.cn
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource that will be used to store the account's private key.
name: letsencrypt-prod-account-key
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx
然后可直接創(chuàng)建這個(gè)Clusterissuer資源:
$ kubectl create -f cluster-issuer.yaml
clusterissuer.certmanager.k8s.io "letsencrypt-prod" created
$ kubectl get clusterissuer
NAME AGE
letsencrypt-prod 16s
這里由于我沒有公網(wǎng)的ingress資源以及集群,這里我就不再進(jìn)行測(cè)試了。
DNS01
這里由于我的k8s集群在內(nèi)網(wǎng),且ingress使用的是二層的metalb. 所以我只能使用DNS01這種ACME的校驗(yàn)方式進(jìn)行證書的申請(qǐng)。
由于我們的域名解析服務(wù)使用的是阿里云的.這里我們通過(guò)官方鏈接找到了alidns-webhook 按照官方的步驟,我們成功申請(qǐng)到了證書。
Install alidns-webhook:
# Install alidns-webhook to cert-manager namespace.
kubectl apply -f https://raw.githubusercontent.com/pragkent/alidns-webhook/master/deploy/bundle.yaml
Create secret contains alidns credentials:
這里的要進(jìn)行base64轉(zhuǎn)碼:
apiVersion: v1
kind: Secret
metadata:
name: alidns-secret
namespace: cert-manager
data:
access-key: YOUR_ACCESS_KEY
secret-key: YOUR_SECRET_KEY
關(guān)于Accesskey 和 AccessSecret大家可以在自己的阿里云賬戶中創(chuàng)建,并進(jìn)行DNS域名管理的相關(guān)授權(quán),也可以給阿里云提工單。
Clusterissuer:
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Change to your letsencrypt email
email: zhangsan@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- dns01:
webhook:
groupName: acme.yourcompany.com
solverName: alidns
config:
region: ""
accessKeySecretRef:
name: alidns-secret
key: access-key
secretKeySecretRef:
name: alidns-secret
key: secret-key
PS: 如果替換groupName,則需要在開始的bundle.yaml文件中也進(jìn)行替換.
配置ingress:
這里由于我的系統(tǒng)當(dāng)中已經(jīng)安裝了kuboard的web-ui; 所以這里我們直接創(chuàng)建ingress即可。
如何安裝kuboard:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: kuboard-dashboard
namespace: kube-system
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/force-ssl-redirect: 'true' #強(qiáng)制HTTPS跳轉(zhuǎn)
spec:
tls:
- hosts:
- kuboard.it.example.cn
secretName: kuboard.it.example.cn-tls
rules:
- host: kuboard.it.example.cn
http:
paths:
- path: /
backend:
serviceName: kuboard
servicePort: 80
以下排錯(cuò)和校驗(yàn)命令:
$ kubectl get certificate
$ kubectl describe certificate NAME
$ kubectl describe secret NAME
$ kubectl describe order NAME
$ kubectl describe challenge NAME
最后的PS: 這里由于我內(nèi)網(wǎng)的DNS服務(wù)器對(duì)我申請(qǐng)的域名證書進(jìn)行了攔截,導(dǎo)致報(bào)錯(cuò)如下:
E1202 04:05:41.696710 1 sync.go:182] cert-manager/controller/challenges "msg"="propagation check failed" "error"="DNS record for \"kuboard.it.example.cn\" not yet propagated" "dnsName"="kuboard.it.example.cn" "resource_kind"="Challenge" "resource_name"="kuboard.it.example.cn-tls-m5tmf-601210408-2135799246" "resource_namespace"="kube-system" "resource_version"="v1" "type"="DNS-01"