Kubernetes 監控:CertManager 自動 HTTPS
cert-manager 是一種自動執行證書管理的工具,它可以與 Istio Gateway 整合以管理 TLS 證書,當然也可以很方便地和前面我們配置的 ingress-nginx 或者 traefik 配合使用。對於和 Istio 的整合使用,無需特殊配置即可。
在進行本節實驗之前記得將前面章節的實驗內容進行清空。
安裝
要在 Kubernetes 叢集上安裝 cert-manager 也非常簡單,官方提供了一個單一的資源清單檔案,包含了所有的資源物件,所以直接安裝即可:
# Kubernetes 1.15+ $ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.16.1/cert-manager.yaml # Kubernetes <1.15 $ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.16.1/cert-manager-legacy.yaml
上面的命令會建立一個名為 cert-manager 的名稱空間,安裝大量的 CRD 以及 AdmissionWebhook 物件,可以通過如下命令來檢視是否安裝成功:
$ kubectl get pods --namespace cert-manager NAME READY STATUS RESTARTS AGE cert-manager-7ddc5b4db-56nln 1/1 Running 0 4m46s cert-manager-cainjector-6644dc4975-zthcj 1/1 Running 0 4m47s cert-manager-webhook-7b887475fb-9fbp4 1/1 Running 0 4m45s
正常情況下可以看到 cert-manager、cert-manager-cainjector 以及 cert-manager-webhook 這幾個 Pod 處於 Running 狀態。我們可以通過下面的測試來驗證下是否可以簽發基本的證書型別,建立一個 Issuer 資源物件來測試 webhook 工作是否正常(在開始簽發證書之前,必須在群集中至少配置一個 Issuer 或 ClusterIssuer 資源):
$ cat <<EOF > test-resources.yaml apiVersion: v1 kind: Namespace metadata: name: cert-manager-test --- apiVersion: cert-manager.io/v1alpha2 kind: Issuer metadata: name: test-selfsigned namespace: cert-manager-test spec: selfSigned: {} # 配置自簽名的證書機構型別 --- apiVersion: cert-manager.io/v1alpha2 kind: Certificate metadata: name: selfsigned-cert namespace: cert-manager-test spec: dnsNames: - example.com secretName: selfsigned-cert-tls issuerRef: name: test-selfsigned EOF
這裡我們建立了一個名為 cert-manager-test 的名稱空間,建立了一個 Issuer 的證書頒發機構,然後使用這個 Issuer 來建立一個證書 Certificate 物件,直接建立上面的資源清單即可:
$ kubectl apply -f test-resources.yaml
namespace/cert-manager-test created
issuer.cert-manager.io/test-selfsigned created
certificate.cert-manager.io/selfsigned-cert created
建立完成後可以檢查新建立的證書狀態,在 cert-manager 處理證書請求之前,可能需要稍微等幾秒:
$ kubectl describe certificate -n cert-manager-test
Name: selfsigned-cert
Namespace: cert-manager-test
......
Spec:
Dns Names:
example.com
Issuer Ref:
Name: test-selfsigned
Secret Name: selfsigned-cert-tls
Status:
Conditions:
Last Transition Time: 2020-08-16T01:50:40Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2020-11-14T01:50:39Z
Not Before: 2020-08-16T01:50:39Z
Renewal Time: 2020-10-15T01:50:39Z
Revision: 1
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 64s cert-manager Issuing certificate as Secret does not exist
Normal Generated 64s cert-manager Stored new private key in temporary Secret resource "selfsigned-cert-25ftw"
Normal Requested 64s cert-manager Created new CertificateRequest resource "selfsigned-cert-p2sbx"
Normal Issuing 63s cert-manager The certificate has been successfully issued
從上面的 Events 事件中我們可以證書已經成功簽發了,到這裡證明我們的 cert-manager 已經安裝成功了。而且我們需要注意的是 cert-manager 的功能非常強大,不只是可以支援 ACME 型別的證書籤發,還支援其他眾多的型別,比如 SelfSigned(自簽名)、CA、Vault、Venafi、External、ACME,只是我們一般主要是使用 ACME 來幫我們生成自動化的證書。
環境配置
由於通過 ACME 做自動化證書的時候,需要暴露 80 和 443 埠,當然如果我們使用 DNS 校驗方式也可以,但是有時候我們根本就沒有域名的情況下想要實現自動化證書,我們可以使用 xip.io 這類的服務來實現。前面我們部署 istio-ingressgateway 的時候是通過 NodePort 類暴露的服務,所以我們需要在前面加一個 LB 來轉發下請求。這裡為了簡單,我直接使用 haproxy 來監聽節點的 80 和 443 埠,將請求轉發到後端的 NodePort 埠。
首先安裝 haproxy:
$ yum install -y haproxy
然後配置 haproxy,配置檔案 /etc/haproxy/haproxy.cfg,內容如下所示:
listen stats
bind *:9000
mode http
stats enable
stats hide-version
stats uri /stats
stats refresh 30s
stats realm Haproxy\ Statistics
stats auth Admin:Password
frontend istio-https
bind *:443
mode tcp
option tcplog
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
default_backend istio-https-svc
backend istio-https-svc
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server istio-http-svc-1 127.0.0.1:30951 check
frontend istio-http
bind *:80
mode tcp
option tcplog
default_backend istio-http-svc
backend istio-http-svc
mode tcp
option tcplog
option tcp-check
balance roundrobin
default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
server istio-http-svc-1 127.0.0.1:32193 check
其中的 32193 與 30951 埠是 istio-ingressgateway 的 NodePort 埠:
$ kubectl get svc istio-ingressgateway -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.102.120.128 <none> 15020:31093/TCP,80:32193/TCP,443:30951/TCP,31400:31871/TCP,15443:31367/TCP 68d
配置完成後直接啟動 haproxy 即可:
$ sudo systemctl start haproxy
$ sudo systemctl enable haproxy
$ sudo systemctl status haproxy
然後我們可以通過上面 9000 埠監控我們的 haproxy 的執行狀態:
與 Istio 整合
這裡我們來通過 cert-manager 為前面的 httpbin 應用配置自動的 HTTPS,首先單獨建立一個使用者 cert-manager 的自定義的 Gateay,如下所示:
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: acme-gateway
labels:
app: ingressgateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- "*.xip.io"
port:
number: 80
name: http
protocol: HTTP
# tls:
# httpsRedirect: true
- hosts:
- "*.xip.io"
port:
name: https
number: 443
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: "xip-cert"
---
EOF
gateway.networking.istio.io/acme-gateway created
需要注意的是在配置 Gateway 的時候 tls 的 credentialName 代表的是 cert-manager 自動生成的證書名稱。
接下來建立 VirtulService 物件,這裡我們使用 https 方式的 ACME 證書校驗方式,除了 http 方式之外還有 tls 與 dns 方式的校驗,dns 方式的證書校驗支援萬用字元的域名。所以我們需要為 /.well-known/ 這個 PATH 路徑做一個正確的配置,方便進行 http 校驗:
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
gateways:
- acme-gateway
hosts:
- httpbin.123.59.188.12.xip.io
http:
- route:
- destination:
host: httpbin
port:
number: 8000
EOF
virtualservice.networking.istio.io/httpbin created
部署完成後我們可以通過訪問 http://httpbin.123.59.188.12.xip.io 來驗證是否已經部署成功:
接下來創兩個 ClusterIssuer,一個用於測試,一個用於正式使用:
$ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: istio
EOF
clusterissuer.cert-manager.io/letsencrypt-staging created
$ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1beta1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: istio
EOF
clusterissuer.cert-manager.io/letsencrypt-prod created
$ kubectl describe clusterissuer
......
Status:
Acme:
Last Registered Email: [email protected]
Uri: https://acme-v02.api.letsencrypt.org/acme/acct/94057742
Conditions:
Last Transition Time: 2020-08-16T07:37:40Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>
然後建立一個 Certificate 物件來獲取證書,由於 Istio 需要在它的名稱空間下面有證書,所以我們需要在 istio-system 這個名稱空間下面建立:
$ cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1beta1
kind: Certificate
metadata:
name: httpbin-cert
namespace: istio-system # 這裡必須是 istio-system 空間
spec:
secretName: xip-cert # 這個就是上面 gateway 所配置的證書名稱
issuerRef:
kind: ClusterIssuer
name: letsencrypt-staging
commonName: httpbin.123.59.188.12.xip.io
dnsNames:
- httpbin.123.59.188.12.xip.io
EOF
certificate.cert-manager.io/httpbin-cert created
建立完成後這時候檢視 istio-system 空間應該會有一個 cm-acme-http-solver- 這個開頭的 Pod、Servie、Ingress 資源物件:
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
cm-acme-http-solver-8tpbn 1/1 Running 0 16s
$ kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cm-acme-http-solver-w6sf7 NodePort 10.106.174.20 <none> 8089:32135/TCP
$ kubectl get ingress -n istio-system
NAME HOSTS ADDRESS PORTS AGE
cm-acme-http-solver-jkrs2 httpbin.123.59.188.12.xip.io 80 45s
隔一小會兒去檢視上面部署的 Certificate 物件的狀態:
$ kubectl describe certificate httpbin-cert -n istio-system
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 13m cert-manager Issuing certificate as Secret does not exist
Normal Generated 13m cert-manager Stored new private key in temporary Secret resource "http-cert-6cml8"
Normal Requested 13m cert-manager Created new CertificateRequest resource "http-cert-9wcg2"
Normal Issuing 6m17s cert-manager The certificate has been successfully issued
看到最後的資訊 The certificate has been successfully issued 證明我們的證書獲取成功了,但是這個時候如果我們通過 https 去訪問的話還是會提示證書錯誤的,因為我們獲取的是 staging 環境的證書:
這個時候我們重新更新 httpbin-cert 這個 Certificate 物件中引用的 issuer,更改為正式環境的 issuer,或者使用正式的簽名機構新建一個證書物件,正常就可以獲得線上環境的證書了。