Linkerd 2.10(Step by Step) (三)自動(dòng)輪換控制平面 TLS 與 Webhook TLS 憑證
Linkerd 2.10 系列
- 快速上手 Linkerd v2 Service Mesh(服務(wù)網(wǎng)格)
- 騰訊云 K8S 集群實(shí)戰(zhàn) Service Mesh—Linkerd2 & Traefik2 部署 emojivoto 應(yīng)用
- 詳細(xì)了解 Linkerd 2.10 基礎(chǔ)功能,一起步入 Service Mesh 微服務(wù)架構(gòu)時(shí)代
- Linkerd 2.10(Step by Step)—1. 將您的服務(wù)添加到 Linkerd
- Linkerd 2.10(Step by Step)—2. 自動(dòng)化的金絲雀發(fā)布
- Linkerd 2.10 中文手冊(cè)持續(xù)修正更新中:
https://linkerd.hacker-linner.com
自動(dòng)輪換控制平面 TLS 憑證
Linkerd 的自動(dòng) mTLS 功能使用一組 TLS 憑據(jù)(TLS credentials)為代理生成 TLS 證書(TLS certificates):信任錨(trust anchor)、頒發(fā)者證書(issuer certificate)和私鑰(private key)。雖然 Linkerd 每 24 小時(shí)自動(dòng)輪換數(shù)據(jù)平面代理的 TLS 證書, 但它不會(huì)輪換用于頒發(fā)這些證書的 TLS 憑據(jù)。在本文檔中,我們將描述如何使用外部解決方案 自動(dòng)輪換頒發(fā)者證書和私鑰。
(請(qǐng)注意,Linkerd 的信任錨仍然必須在 long-lived 集群上手動(dòng)輪換)
Cert manager
Cert-manager 是一個(gè)流行的項(xiàng)目,用于使來自外部來源的 TLS 憑證(TLS credentials)可用于 Kubernetes 集群。
第一步,在您的集群上安裝 cert-manager。
如果您要安裝 cert-manager >= 1.0, 則需要 kubernetes >= 1.16。cert-manager 中用于 kubernetes <= 1.15 的傳統(tǒng)自定義資源定義沒有 keyAlgorithm 選項(xiàng), 因此證書將使用 RSA 生成并且與 linkerd 不兼容。
有關(guān)版本要求的更多詳細(xì)信息,請(qǐng)參閱 v0.16 到 v1.0 升 級(jí)說明。
Cert manager 作為集群上的證書頒發(fā)機(jī)構(gòu)(CA)
在這種情況下,我們不會(huì)從外部來源(external source)獲取憑據(jù), 而是將其配置為集群上的 CA, 并讓它定期重新頒發(fā) Linkerd 的頒發(fā)者證書(issuer certificate)和私鑰(private key)。
首先,創(chuàng)建 cert-manager 將用來存儲(chǔ)其 Linkerd 相關(guān)資源的命名空間。為簡單起見,我們建議使用默認(rèn)的 Linkerd 控制平面命名空間:
- kubectl create namespace linkerd
將簽名密鑰對(duì)(signing key pair)保存為 Secret
接下來,使用 step 工具, 創(chuàng)建一個(gè)簽名密鑰對(duì)(signing key pair)并將其存儲(chǔ)在上面創(chuàng)建的 命名空間中的 Kubernetes Secret 中:
- step certificate create root.linkerd.cluster.local ca.crt ca.key \
- --profile root-ca --no-password --insecure &&
- kubectl create secret tls \
- linkerd-trust-anchor \
- --cert=ca.crt \
- --key=ca.key \
- --namespace=linkerd
對(duì)于壽命更長(longer-lived)的信任錨證書,將 --not-after 參數(shù)傳遞 給具有所需值(desired value)的 step 命令(例如 --not-after=87600h)。
創(chuàng)建引用密鑰(referencing the secret)的頒發(fā)者(Issuer)
有了 Secret,我們可以創(chuàng)建一個(gè)引用它的 cert-manager "Issuer" 資源:
- cat <<EOF | kubectl apply -f -
- apiVersion: cert-manager.io/v1
- kind: Issuer
- metadata:
- name: linkerd-trust-anchor
- namespace: linkerd
- spec:
- ca:
- secretName: linkerd-trust-anchor
- EOF
頒發(fā)證書(Issuing certificates)并將它們寫入一個(gè) secret
最后,我們可以創(chuàng)建一個(gè) cert-manager "Certificate" 資源, 它使用這個(gè) Issuer 來生成所需的證書:
- cat <<EOF | kubectl apply -f -
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: linkerd-identity-issuer
- namespace: linkerd
- spec:
- secretName: linkerd-identity-issuer
- duration: 48h
- renewBefore: 25h
- issuerRef:
- name: linkerd-trust-anchor
- kind: Issuer
- commonName: identity.linkerd.cluster.local
- dnsNames:
- - identity.linkerd.cluster.local
- isCA: true
- privateKey:
- algorithm: ECDSA
- usages:
- - cert sign
- - crl sign
- - server auth
- - client auth
- EOF
(在上面的 YAML 清單中,duration key 指示 cert-manager 將 證書視為有效 48 小時(shí),而 renewBefore key 指示 cert-manager 將嘗試在當(dāng)前證書到期前 25 小時(shí)頒發(fā)新證書。這些值可以根據(jù)您的喜好定制。)
此時(shí),cert-manager 現(xiàn)在可以使用此證書資源(Certificate resource) 獲取 TLS 憑據(jù)(TLS credentials), 該憑據(jù)將存儲(chǔ)在名為 linkerd-identity-issuer 的 secret 中。要驗(yàn)證您新頒發(fā)的證書,您可以運(yùn)行:
- kubectl get secret linkerd-identity-issuer -o yaml -n linkerd
現(xiàn)在我們只需要通知 Linkerd 使用這些憑據(jù)。
由于 cert-manager 中的 bug, 如果您將 cert-manager 版本 0.15 與實(shí)驗(yàn)控制器(experimental controllers)一起使用, 則它頒發(fā)的證書與 Linkerd 版本 <= stable-2.8.1 不兼容。
您的 linkerd-identity pod 可能會(huì)因以下日志輸出而崩潰:
- "Failed to initialize identity service: failed to read CA from disk:
- unsupported block type: 'PRIVATE KEY'"
解決此問題的一些可能方法是:
- 將 Linkerd 升級(jí)到包含修復(fù)程序的邊緣版本 >= edge-20.6.4。
- 將 cert-manager 升級(jí)到版本 >= 0.16。(如何升級(jí))
- 關(guān)閉 cert-manager 實(shí)驗(yàn)控制器(experimental controllers)。(docs)
替代 CA 提供商
您可以將 Cert Manager 配置為依賴于許多其他解決方案, 例如 Vault, 而不是使用 Cert Manager 作為 CA??梢栽诖颂幷业? 有關(guān)如何設(shè)置現(xiàn)有證書管理器 以使用不同類型的頒發(fā)者的更多詳細(xì)信息。
第三方證書管理解決方案
需要注意的是,Linkerd 提供的機(jī)制也可以在 cert-manager 之外使用。Linkerd 將讀取 linkerd-identity-issuer Secret, 如果它是 kubernetes.io/tls 類型,將使用內(nèi)容作為其 TLS 憑證(TLS credentials)。這意味著任何能夠通過將 TLS 證書(certificates)寫入此密鑰 來輪換它們的解決方案都可用于提供動(dòng)態(tài) TLS 證書管理。
在 CLI 安裝中使用這些憑據(jù)
對(duì)于 CLI 安裝,Linkerd 控制平面應(yīng)該 與 --identity-external-issuer 標(biāo)志一起安裝, 該標(biāo)志指示 Linkerd 從 linkerd-identity-issuer secret 讀取證書。每當(dāng)更新存儲(chǔ)在 secret 中的 certificate 和 key 時(shí), identity 服務(wù)將自動(dòng)檢測(cè)此更改并重新加載新憑據(jù)。
瞧!我們已經(jīng)設(shè)置了 Linkerd 控制平面 TLS 憑據(jù)的自動(dòng)輪換。如果你想監(jiān)控更新過程,你可以檢查服務(wù)發(fā)出的 IssuerUpdated 事件:
- kubectl get events --field-selector reason=IssuerUpdated -n linkerd
使用 Helm 安裝
對(duì)于 Helm 安裝,而不是運(yùn)行 linkerd install, 將 identityTrustAnchorsPEM 設(shè)置為 linkerd-identity-issuer Secret 中 ca.crt 的值:
- helm install linkerd2 \
- --set-file identityTrustAnchorsPEM=ca.crt \
- --set identity.issuer.scheme=kubernetes.io/tls \
- --set installNamespace=false \
- linkerd/linkerd2 \
- -n linkerd
對(duì)于低于 v3 的 Helm 版本,必須專門傳遞 --name 標(biāo)志。在 Helm v3 中,它已被棄用,并且是上面指定的第一個(gè)參數(shù)。
自動(dòng)輪換 Webhook TLS 憑證
Linkerd 控制平面包含幾個(gè)組件,稱為 webhooks, 由 Kubernetes 本身直接調(diào)用。從 Kubernetes 到 Linkerd webhooks 的流量使用 TLS 進(jìn)行保護(hù), 因此每個(gè) webhooks 都需要一個(gè)包含 TLS 憑據(jù)的 secret。這些證書與 Linkerd 代理用于保護(hù) pod 到 pod 通信并 使用完全獨(dú)立的信任鏈的證書不同。
默認(rèn)情況下,當(dāng) Linkerd 與 Linkerd CLI 或 Linkerd Helm chart 一起安裝時(shí), 會(huì)自動(dòng)為所有 webhook 生成 TLS 憑據(jù)。如果這些證書過期或因任何原因需要重新生成, 執(zhí)行 Linkerd upgrade(使用 Linkerd CLI 或使用 Helm)將重新生成它們。
此工作流程適用于大多數(shù)用戶。但是,如果您需要定期自動(dòng)輪換這些 webhook 證書, 則可以使用 cert-manager 來自動(dòng)管理它們。
安裝 Cert manager
第一步,在 您的集群上安裝 cert-manager 并創(chuàng)建 cert-manager 將用于存儲(chǔ)其 webhook 相關(guān)資源的命名空間。為簡單起見,我們建議使用默認(rèn)命名空間 linkerd 使用:
- # control plane core
- kubectl create namespace linkerd
- # viz (ignore if not using the viz extension)
- kubectl create namespace linkerd-viz
- # viz (ignore if not using the jaeger extension)
- kubectl create namespace linkerd-jaeger
將簽名密鑰對(duì)(signing key pair)保存為 Secret
接下來,我們將使用 step 工具創(chuàng)建一個(gè)簽名密鑰對(duì)(signing key pair),用于對(duì)每個(gè) webhook 證書進(jìn)行簽名:
- step certificate create webhook.linkerd.cluster.local ca.crt ca.key \
- --profile root-ca --no-password --insecure --san webhook.linkerd.cluster.local
- kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd
- # ignore if not using the viz extension
- kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-viz
- # ignore if not using the jaeger extension
- kubectl create secret tls webhook-issuer-tls --cert=ca.crt --key=ca.key --namespace=linkerd-jaeger
創(chuàng)建引用 secrets 的發(fā)行者(Issuers)
有了 Secrets,我們就可以創(chuàng)建引用它們的 cert-manager "Issuer" 資源:
- cat <<EOF | kubectl apply -f -
- apiVersion: cert-manager.io/v1
- kind: Issuer
- metadata:
- name: webhook-issuer
- namespace: linkerd
- spec:
- ca:
- secretName: webhook-issuer-tls
- ---
- # ignore if not using the viz extension
- apiVersion: cert-manager.io/v1
- kind: Issuer
- metadata:
- name: webhook-issuer
- namespace: linkerd-viz
- spec:
- ca:
- secretName: webhook-issuer-tls
- ---
- # ignore if not using the jaeger extension
- apiVersion: cert-manager.io/v1
- kind: Issuer
- metadata:
- name: webhook-issuer
- namespace: linkerd-jaeger
- spec:
- ca:
- secretName: webhook-issuer-tls
- EOF
頒發(fā)證書并將其寫入 secrets
最后,我們可以創(chuàng)建 cert-manager "Certificate" 資源, 它使用頒發(fā)者(Issuers)來生成所需的證書:
- cat <<EOF | kubectl apply -f -
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: linkerd-proxy-injector
- namespace: linkerd
- spec:
- secretName: linkerd-proxy-injector-k8s-tls
- duration: 24h
- renewBefore: 1h
- issuerRef:
- name: webhook-issuer
- kind: Issuer
- commonName: linkerd-proxy-injector.linkerd.svc
- dnsNames:
- - linkerd-proxy-injector.linkerd.svc
- isCA: false
- privateKey:
- algorithm: ECDSA
- usages:
- - server auth
- ---
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: linkerd-sp-validator
- namespace: linkerd
- spec:
- secretName: linkerd-sp-validator-k8s-tls
- duration: 24h
- renewBefore: 1h
- issuerRef:
- name: webhook-issuer
- kind: Issuer
- commonName: linkerd-sp-validator.linkerd.svc
- dnsNames:
- - linkerd-sp-validator.linkerd.svc
- isCA: false
- privateKey:
- algorithm: ECDSA
- usages:
- - server auth
- ---
- # ignore if not using the viz extension
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: tap
- namespace: linkerd-viz
- spec:
- secretName: tap-k8s-tls
- duration: 24h
- renewBefore: 1h
- issuerRef:
- name: webhook-issuer
- kind: Issuer
- commonName: tap.linkerd-viz.svc
- dnsNames:
- - tap.linkerd-viz.svc
- isCA: false
- privateKey:
- algorithm: ECDSA
- usages:
- - server auth
- ---
- # ignore if not using the viz extension
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: linkerd-tap-injector
- namespace: linkerd-viz
- spec:
- secretName: tap-injector-k8s-tls
- duration: 24h
- renewBefore: 1h
- issuerRef:
- name: webhook-issuer
- kind: Issuer
- commonName: tap-injector.linkerd-viz.svc
- dnsNames:
- - tap-injector.linkerd-viz.svc
- isCA: false
- privateKey:
- algorithm: ECDSA
- usages:
- - server auth
- ---
- # ignore if not using the jaeger extension
- apiVersion: cert-manager.io/v1
- kind: Certificate
- metadata:
- name: jaeger-injector
- namespace: linkerd-jaeger
- spec:
- secretName: jaeger-injector-k8s-tls
- duration: 24h
- renewBefore: 1h
- issuerRef:
- name: webhook-issuer
- kind: Issuer
- commonName: jaeger-injector.linkerd.svc
- dnsNames:
- - jaeger-injector.linkerd.svc
- isCA: false
- privateKey:
- algorithm: ECDSA
- usages:
- - server auth
- EOF
此時(shí) cert-manager 現(xiàn)在可以使用這些 Certificate resources 來獲取 TLS 憑證, 這些憑證分別存儲(chǔ)在 linkerd-proxy-injector-k8s-tls、linkerd-sp-validator-k8s-tls、tap- k8s-tls、tap-injector-k8s-tls 和 jaeger-injector-k8s-tls 這些 secrets 中。
現(xiàn)在我們只需要通知 Linkerd 使用這些憑據(jù)。
在 CLI 安裝中使用這些憑據(jù)
要將 Linkerd 配置為使用來自 cert-manager 的憑據(jù)而不是生成自己的憑據(jù), 我們生成了一個(gè)補(bǔ)充配置文件:
CA=$(awk '{ print " " $0 }' ca.crt)cat > config.yml <
現(xiàn)在我們可以使用這些配置文件安裝 Linkerd:
- CA=$(awk '{ print " " $0 }' ca.crt)
- cat > config.yml <<EOF
- proxyInjector:
- externalSecret: true
- caBundle: |
- $CA
- profileValidator:
- externalSecret: true
- caBundle: |
- $CA
- EOF
- # ignore if not using the viz extension
- cat > config-viz.yml <<EOF
- tap:
- externalSecret: true
- caBundle: |
- $CA
- tapInjector:
- externalSecret: true
- caBundle: |
- $CA
- EOF
- # ignore if not using the jaeger extension
- cat > config-jaeger.yml <<EOF
- webhook:
- externalSecret: true
- caBundle: |
- $CA
- EOF
現(xiàn)在我們可以使用這些配置文件安裝 Linkerd:
- linkerd install --values=config.yml | kubectl apply -f -
- # ignore if not using the viz extension
- linkerd viz install --values=config-viz.yml | kubectl apply -f -
- # ignore if not using the jaeger extension
- linkerd jaeger install --values=config-jaeger.yml | kubectl apply -f -
使用 Helm 安裝
對(duì)于 Helm 安裝,我們可以直接配置 Helm 值:
- helm install linkerd2 \
- --set installNamespace=false \
- --set proxyInjector.externalSecret=true \
- --set-file proxyInjector.caBundle=ca.crt \
- --set profileValidator.externalSecret=true \
- --set-file profileValidator.caBundle=ca.crt \
- linkerd/linkerd2 \
- -n linkerd
- # ignore if not using the viz extension
- helm install linkerd-viz \
- --set installNamespace=false \
- --set tap.externalSecret=true \
- --set-file tap.caBundle=ca.crt \
- --set tapInjector.externalSecret=true \
- --set-file tapInjector.caBundle=ca.crt \
- linkerd/linkerd-viz \
- -n linkerd-viz
- # ignore if not using the jaeger extension
- helm install linkerd-jaeger \
- --set installNamespace=false \
- --set webhook.externalSecret=true \
- --set-file webhook.caBundle=ca.crt \
- linkerd/linkerd-jaeger \
- -n linkerd-jaeger
使用 Helm 安裝 Linkerd 時(shí), 您還必須提供頒發(fā)者信任根(issuer trust root)和頒發(fā)者憑據(jù)(issuer credentials), 如使用 Helm 安裝 Linkerd 中所述。
對(duì)于低于 v3 的 Helm 版本,必須專門傳遞 --name 標(biāo)志。在 Helm v3 中,它已被棄用,并且是上面指定的第一個(gè)參數(shù)。