Linkerd 2.10(Step by Step)—3. 自動輪換控制平面 TLS &Webhook TLS 憑證

為少發表於2021-06-15

Linkerd 2.10 系列

Linkerd 2.10 中文手冊持續修正更新中:

自動輪換控制平面 TLS 憑證

Linkerd 的自動 mTLS 功能使用一組 TLS 憑據(TLS credentials)為代理生成 TLS 證書(TLS certificates):
信任錨(trust anchor)、頒發者證書(issuer certificate)和私鑰(private key)。
雖然 Linkerd24 小時自動輪換資料平面代理的 TLS 證書,
但它不會輪換用於頒發這些證書的 TLS 憑據。
在本文件中,我們將描述如何使用外部解決方案
自動輪換頒發者證書和私鑰。

(請注意,Linkerd 的信任錨仍然必須在 long-lived 叢集上手動輪換)

Cert manager

Cert-manager
是一個流行的專案,用於使來自外部來源的 TLS 憑證(TLS credentials)可用於 Kubernetes 叢集。

第一步,在您的叢集上安裝
cert-manager

如果您要安裝 cert-manager >= 1.0
則需要 kubernetes >= 1.16
cert-manager 中用於 kubernetes <= 1.15
的傳統自定義資源定義沒有 keyAlgorithm 選項,
因此證書將使用 RSA 生成並且與 linkerd 不相容。

有關版本要求的更多詳細資訊,請參閱 v0.16 到 v1.0 升
級說明

Cert manager 作為叢集上的證書頒發機構(CA)

在這種情況下,我們不會從外部來源(external source)獲取憑據,
而是將其配置為叢集上的
CA
並讓它定期重新頒發 Linkerd 的頒發者證書(issuer certificate)和私鑰(private key)。

首先,建立 cert-manager 將用來儲存其 Linkerd 相關資源的名稱空間。
為簡單起見,我們建議使用預設的 Linkerd 控制平面名稱空間:

kubectl create namespace linkerd

將簽名金鑰對(signing key pair)儲存為 Secret

接下來,使用 step 工具,
建立一個簽名金鑰對(signing key pair)並將其儲存在上面建立的
名稱空間中的 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

對於壽命更長(longer-lived)的信任錨證書,將 --not-after 引數傳遞
給具有所需值(desired value)的 step 命令(例如 --not-after=87600h)。

建立引用金鑰(referencing the secret)的頒發者(Issuer)

有了 Secret,我們可以建立一個引用它的 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

頒發證書(Issuing certificates)並將它們寫入一個 secret

最後,我們可以建立一個 cert-manager "Certificate" 資源,
它使用這個 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 小時,而 renewBefore key 指示
cert-manager 將嘗試在當前證書到期前 25 小時頒發新證書。
這些值可以根據您的喜好定製。)

此時,cert-manager 現在可以使用此證書資源(Certificate resource)
獲取 TLS 憑據(TLS credentials),
該憑據將儲存在名為 linkerd-identity-issuer 的 secret 中。
要驗證您新頒發的證書,您可以執行:

kubectl get secret linkerd-identity-issuer -o yaml -n linkerd

現在我們只需要通知 Linkerd 使用這些憑據。

由於 cert-manager 中的
bug
如果您將 cert-manager 版本 0.15 與實驗控制器(experimental controllers)一起使用,
則它頒發的證書與 Linkerd 版本 <= stable-2.8.1 不相容。

您的 linkerd-identity pod 可能會因以下日誌輸出而崩潰:

"Failed to initialize identity service: failed to read CA from disk:
unsupported block type: 'PRIVATE KEY'"

解決此問題的一些可能方法是:

  • 將 Linkerd 升級到包含修復程式的邊緣版本 >= edge-20.6.4
  • 將 cert-manager 升級到版本 >= 0.16。(如何升級
  • 關閉 cert-manager 實驗控制器(experimental controllers)。(docs)

替代 CA 提供商

您可以將 Cert Manager 配置為依賴於許多其他解決方案,
例如 Vault
而不是使用 Cert Manager 作為 CA。
可以在此處找到
有關如何設定現有證書管理器
以使用不同型別的頒發者的更多詳細資訊。

第三方證書管理解決方案

需要注意的是,Linkerd 提供的機制也可以在 cert-manager 之外使用。
Linkerd 將讀取 linkerd-identity-issuer Secret,
如果它是 kubernetes.io/tls 型別,將使用內容作為其 TLS 憑證(TLS credentials)。
這意味著任何能夠通過將 TLS 證書(certificates)寫入此金鑰
來輪換它們的解決方案都可用於提供動態 TLS 證書管理。

在 CLI 安裝中使用這些憑據

對於 CLI 安裝,Linkerd 控制平面應該
--identity-external-issuer 標誌一起安裝,
該標誌指示 Linkerd 從 linkerd-identity-issuer secret 讀取證書。
每當更新儲存在 secret 中的 certificate 和 key 時,
identity 服務將自動檢測此更改並重新載入新憑據。

瞧!我們已經設定了 Linkerd 控制平面 TLS 憑據的自動輪換。
如果你想監控更新過程,你可以檢查服務發出的 IssuerUpdated 事件:

kubectl get events --field-selector reason=IssuerUpdated -n linkerd

使用 Helm 安裝

對於 Helm 安裝,而不是執行 linkerd install
identityTrustAnchorsPEM 設定為
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

對於低於 v3 的 Helm 版本,必須專門傳遞 --name 標誌。
在 Helm v3 中,它已被棄用,並且是上面指定的第一個引數。

自動輪換 Webhook TLS 憑證

Linkerd 控制平面包含幾個元件,稱為 webhooks,
由 Kubernetes 本身直接呼叫。
從 Kubernetes 到 Linkerd webhooks 的流量使用 TLS 進行保護,
因此每個 webhooks 都需要一個包含 TLS 憑據的 secret。
這些證書與 Linkerd 代理用於保護 pod 到 pod 通訊並
使用完全獨立的信任鏈的證書不同。

預設情況下,當 Linkerd 與 Linkerd CLI 或 Linkerd Helm chart 一起安裝時,
會自動為所有 webhook 生成 TLS 憑據。
如果這些證書過期或因任何原因需要重新生成,
執行 Linkerd upgrade(使用 Linkerd CLI 或使用 Helm)將重新生成它們。

此工作流程適用於大多數使用者。
但是,如果您需要定期自動輪換這些 webhook 證書,
則可以使用 cert-manager 來自動管理它們。

安裝 Cert manager

第一步,在
您的叢集上安裝 cert-manager
並建立 cert-manager 將用於儲存其 webhook 相關資源的名稱空間。
為簡單起見,我們建議使用預設名稱空間 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

將簽名金鑰對(signing key pair)儲存為 Secret

接下來,我們將使用 step
工具建立一個簽名金鑰對(signing key pair),用於對每個 webhook 證書進行簽名:

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

建立引用 secrets 的發行者(Issuers)

有了 Secrets,我們就可以建立引用它們的 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

頒發證書並將其寫入 secrets

最後,我們可以建立 cert-manager "Certificate" 資源,
它使用頒發者(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

此時 cert-manager 現在可以使用這些 Certificate resources 來獲取 TLS 憑證,
這些憑證分別儲存在 linkerd-proxy-injector-k8s-tlslinkerd-sp-validator-k8s-tlstap- k8s-tlstap-injector-k8s-tlsjaeger-injector-k8s-tls 這些 secrets 中。

現在我們只需要通知 Linkerd 使用這些憑據。

在 CLI 安裝中使用這些憑據

要將 Linkerd 配置為使用來自 cert-manager 的憑據而不是生成自己的憑據,
我們生成了一個補充配置檔案:

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

現在我們可以使用這些配置檔案安裝 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 安裝

對於 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 時,
您還必須提供頒發者信任根(issuer trust root)和頒發者憑據(issuer credentials),
使用 Helm 安裝 Linkerd 中所述。

對於低於 v3 的 Helm 版本,必須專門傳遞 --name 標誌。
在 Helm v3 中,它已被棄用,並且是上面指定的第一個引數。

我是為少
微信:uuhells123
公眾號:黑客下午茶
加我微信(互相學習交流),關注公眾號(獲取更多學習資料~)

相關文章