新訊:在TwitterMastodon取得專案更新資訊

trust-manager

trust-manager 是在 Kubernetes 和 OpenShift 叢集中管理信任套件的最簡單方法。

它協調受信任的 X.509 憑證套件,這些憑證主要用於在 TLS 握手期間驗證憑證,但也可以用於其他情況。

概述

trust-manager 是一個小型的 Kubernetes 運算子,旨在幫助減少在叢集中管理 TLS 信任套件的開銷。

它新增了 Bundle 自訂 Kubernetes 資源 (CRD),該資源可以從各種來源讀取輸入,並將產生的憑證合併到一個可供應用程式使用的套件中。

trust-manager 確保快速且容易地保持您的受信任憑證是最新的,並使叢集管理員能夠輕鬆地自動提供安全的套件,而無需擔心重建容器來更新信任儲存。

它旨在與 cert-manager 相輔相成,並且在從 cert-manager IssuerClusterIssuer 使用 CA 憑證時效果很好,但如果需要,可以完全獨立於 cert-manager 使用。

安裝

請參閱安裝指南以獲取有關如何安裝 trust-manager 的說明。

用法

trust-manager 的設計非常簡單,僅新增了一個 Kubernetes CustomResourceDefintionBundle

Bundle 代表一組應在叢集中分發的 X.509 憑證。

所有 Bundle 都是叢集範圍的。

Bundle 包含一個 sources 列表,trust-manager 將從這些來源組裝最終套件,以及一個 target,用於描述如何以及在何處寫入結果套件。

一個範例 Bundle 可能如下所示

apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: my-org.com # The bundle name will also be used for the target
spec:
sources:
# Include a bundle of publicly trusted certificates which can be
# used to validate most TLS certificates on the internet, such as
# those issued by Let's Encrypt, Google, Amazon and others.
- useDefaultCAs: true
# A Secret in the "trust" namespace; see "Trust Namespace" below for further details
- secret:
name: "my-db-tls"
key: "ca.crt"
# Here is another Secret source, but this time using a label selector instead of a Secret's name.
- secret:
selector:
matchLabels:
fruit: apple
key: "ca.crt"
# A ConfigMap in the "trust" namespace; see "Trust Namespace" below for further details
- configMap:
name: "my-org.net"
key: "root-certs.pem"
# Here is another ConfigMap source, but this time using a label selector instead of a ConfigMap's name.
- configMap:
selector:
matchLabels:
fruit: apple
key: "ca.crt"
# A manually specified string
- inLine: |
-----BEGIN CERTIFICATE-----
MIIC5zCCAc+gAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
....
0V3NCaQrXoh+3xrXgX/vMdijYLUSo/YPEWmo
-----END CERTIFICATE-----
target:
# Sync the bundle to a ConfigMap called `my-org.com` in every namespace which
# has the label "linkerd.io/inject=enabled"
# All ConfigMaps will include a PEM-formatted bundle, here named "root-certs.pem"
# and in this case we also request binary formatted bundles in JKS and PKCS#12 formats,
# here named "bundle.jks" and "bundle.p12".
configMap:
key: "root-certs.pem"
additionalFormats:
jks:
key: "bundle.jks"
pkcs12:
key: "bundle.p12"
namespaceSelector:
matchLabels:
linkerd.io/inject: "enabled"

Bundle 資源目前支援多種來源類型

  • configMap - trust-manager 名稱空間中的 ConfigMap 資源
  • secret - trust-manager 名稱空間中的 Secret 資源
  • inLine - 手動指定的字串,其中包含至少一個憑證
  • useDefaultCAs - 通常,一組公開信任的憑證

ConfigMap 是預設目標類型,但從 v0.7.0 開始,trust-manager 也支援 Secret 資源作為目標。

必須在 trust-manager 控制器中明確啟用對 Secret 目標的支援;請參閱下方的「啟用 Secret 目標」瞭解詳細資訊。

ConfigMapSecret 也都支援指定標籤選擇器以一次選取多個資源,這在僅在執行時才知道 ConfigMapSecret 名稱的動態環境中很有用。新增來源時,無論是 ConfigMap 還是 Secret 類型,nameselector 欄位是互斥的:**必須**設定其中一個,但不能同時設定兩個。

所有來源和目標選項都記錄在 trust-manager API 參考文件中。

目標

所有 Bundle 目標都寫入名稱與 Bundle 名稱相同的 ConfigMap (和/或 Secret) 中,並且每個目標都包含一個 PEM 格式的套件。

使用者也可以選擇性地將 JKS/PKCS#12 格式的二進制信任儲存區寫入目標。JKS 自 v0.5.0 開始支援,而 PKCS#12 自 v0.7.0 開始支援。

使用 JKS 和 PKCS#12 信任儲存區的應用程式通常需要為了舊版原因而設定密碼。這些密碼通常是安全性劇場 - 它們要么使用非常弱的加密,要么密碼以純文字形式提供在它們加密的檔案旁邊,這使得它們加密的意義消失。

信任套件不包含私鑰,因此對於大多數使用案例而言,加密它們不會有任何安全性優勢。因此,預設情況下,信任儲存區的密碼對於 JKS 設定為 changeit,而對於 PKCS#12 設定為 "" (空字串或「無密碼」)。

最近的版本允許您透過設定套件 YAML 檔案 spec.target.additionalFormats.jks.passwordspec.target.additionalFormats.pkcs12.password 來變更該密碼。

較舊的版本具有硬式編碼的目前預設值,並且無法變更。如需更多資訊,請閱讀為什麼密碼沒有幫助

名稱空間選擇器

目標的 namespaceSelector 用於限制您的 Bundle 的目標應同步到哪些名稱空間。

namespaceSelector 支援 matchLabels 欄位。

請參閱Kubernetes 文件以瞭解有關如何設定標籤選擇器的更多資訊。

如果 namespaceSelector 為空,則 Bundle 的目標將會同步到所有命名空間。

⚠️ trust-manager 未來的更新將會變更此行為,使空的命名空間選擇器預設只會同步到 trust-manager 命名空間。

快速開始範例

讓我們開始建立我們自己的 Bundle 的範例!

首先,我們將建立一個示範叢集

git clone https://github.com/cert-manager/trust-manager trust-manager
cd trust-manager
make demo

一旦我們有一個正在運行的叢集,我們就可以使用 trust-manager 啟動時設定的預設 CA 來建立一個 Bundle。由於我們是使用 Helm 安裝 trust-manager,我們的預設 CA 套件包含來自 Debian 容器的公開信任憑證。

kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOF
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: example-bundle
spec:
sources:
- useDefaultCAs: true
target:
configMap:
key: "trust-bundle.pem"
EOF

這很簡單!現在讓我們檢查一切是否同步正常,並且我們的 ConfigMap 是否已寫入

kubectl --kubeconfig ./bin/kubeconfig.yaml get bundle example-bundle | less
kubectl --kubeconfig ./bin/kubeconfig.yaml get configmap example-bundle -o "jsonpath={.data['trust-bundle\.pem']}" | less

太棒了 - 我們有一個信任捆綁包。我們可以立即將其用於我們的容器,但讓我們更進一步,建立一個虛擬「組織 CA」,我們希望將其包含在我們的 Bundle 中。

我們將使用 cert-manager 產生我們的虛擬組織憑證

kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: trust-manager-selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: trust-manager-example-ca
namespace: cert-manager
spec:
isCA: true
commonName: trust-manager-example-ca
secretName: trust-manager-example-ca-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: trust-manager-selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
EOF

現在讓我們檢查 cert-manager 為我們建立的 Secret

kubectl --kubeconfig ./bin/kubeconfig.yaml get -n cert-manager secret trust-manager-example-ca-secret -o"jsonpath={.data['tls\.crt']}" | base64 -d
# tls.crt will contain a PEM certificate, starting with -----BEGIN CERTIFICATE-----

🤔 想知道為什麼我們使用 tls.crt 而不是 ca.crt 嗎?詳情請參閱下方

最後,我們將更新我們的 Bundle 以包含我們新的私有 CA

kubectl --kubeconfig ./bin/kubeconfig.yaml apply -f - <<EOF
apiVersion: trust.cert-manager.io/v1alpha1
kind: Bundle
metadata:
name: example-bundle
spec:
sources:
- useDefaultCAs: true
- secret:
name: "trust-manager-example-ca-secret"
key: "tls.crt"
target:
configMap:
key: "trust-bundle.pem"
EOF

我們完成了!example-bundle ConfigMap 應該已經更新了。

如果您再次檢查 ConfigMap,您在清單中看到的最後一個憑證應該是我們新的虛擬 CA。

安全地維護 trust-manager 安裝

如果您在任何 Bundle 資源上選擇 useDefaultCAs 來源,請務必保持您的預設 CA 套件映像檔為最新狀態。否則,就相當於在 Debian 容器中安裝公開信任捆綁包時,未能執行 apt-get upgrade ca-certificates

trust-manager 的設計方式是,任何版本的任何預設 CA 套件都應與任何支援預設 CA 的 trust-manager 版本(v0.4.0 及更高版本)搭配使用。升級不應對 trust-manager 的穩定性造成風險。

如果您使用官方 cert-manager 提供的 Debian CA 套件(預設),您應該檢查您擁有的版本,並與最新的套件版本進行比較。

版本可以使用 Helm 圖表上的 .defaultPackageImage.tag 值進行設定,並且該版本也會寫入到任何使用預設 CA 套件的同步 Bundle 資源上的 status 欄位。

使用 Helm 升級預設 CA 套件

假設我們想要將預設 CA 套件就地升級到標籤版本 XYZ - 而不升級 trust-manager。

我們假設 Helm 版本名為「trust-manager」,並且我們已將其安裝到 cert-manager 命名空間中。

⚠️ 此升級過程假設它是唯一正在執行的項目。如果另一個使用者或程序在您執行此過程時變更了 Helm 值,您可能會覆寫他們的工作。

首先,我們將傾印我們目前的 Helm 值,以免遺失它們

helm get values -n cert-manager trust-manager -oyaml > values.yaml

接下來,如果 defaultPackageImage.tag 已在 values.yaml 中設定,請更新它。否則,請新增它。您可以在 quay.io 上找到可用的標籤。

# values.yaml
...
defaultPackageImage:
tag: XYZ

這些預設套件映像檔標籤的版本直接來自 Debian 中 ca-certificates 套件的版本。

最後,套用回變更,請務必手動指定已安裝的 trust-manager 版本,以避免在更新預設 CA 套件的同時也更新 trust-manager 控制器

# Get the currently installed version. You could do this manually if you find that easier.
TRUST_MANAGER_VER=$(helm list --filter "^trust-manager$" -n cert-manager -ojson | jq -r ".[0].app_version")
# Check the version makes sense
echo $TRUST_MANAGER_VER
# Run the upgrade
helm upgrade -f values.yaml -n cert-manager trust-manager jetstack/trust-manager --version $TRUST_MANAGER_VER

如果使用了不正確的標籤,您的部署將會失敗,而且您可能需要使用 helm rollback 返回到可運作的狀態。

為生產環境做準備

TLS 可能很複雜,而且有很多濫用 TLS 憑證的方法。

以下是在生產環境中運行 trust-manager 之前需要注意的一些潛在陷阱。

如果您打算在生產環境中運行 trust-manager,而且您使用的不只是預設 CA 套件,我們強烈建議您閱讀並了解本節。它可以讓您避免稍後發生中斷。

ℹ️ 這些陷阱並非 trust-manager 所特有,您可能會在使用任何管理 TLS 信任的方法時遇到任何陷阱!

捆綁中繼憑證

如果您曾經使用過 Let's Encrypt 用戶端,例如 Certbot,您可能會看到它產生了數個憑證檔案,例如 cert.pemchain.pemfullchain.pem

提供這些各種檔案是為了支援各種不同的應用程式,這些應用程式可能需要憑證和鏈單獨提供。對於大多數使用者和應用程式而言,fullchain.pem 是唯一正確的選擇。

不幸的是,這些檔案的存在有一個不幸的副作用,那就是人們有時會假設 cert.pem 是正確的選擇,即使 fullchain.pem 才是正確的選擇。這表示在使用憑證時,不會傳送鏈中的其餘部分。

通常,此問題的一個快速修復方法是讓用戶端將鏈新增到其信任存放區中,這似乎可以在短期內修正憑證錯誤。這種「修復」很容易在某處作為其他人可以遵循的解決方案被嵌入。

這種「修復」很危險;這表示在必須更新所有包含該中繼憑證的信任存放區之前,無法安全地輪換中繼憑證。

在這種情況下,中繼憑證會成為事實上的根憑證,這完全破壞了最初使用中繼憑證的目的。

除非您完全確定應該包含中繼憑證,否則請盡可能避免在任何信任存放區中使用中繼憑證。一個可能是可以的情況的範例是交叉簽名,這在一般情況下不太可能需要。

最好是將根憑證複製到新的 ConfigMap 中,並將其用作來源,而不是信任中繼憑證。

cert-manager 整合:ca.crttls.crt

如果您將 trust-manager 指向包含 cert-manager 發行憑證的 Secret,您會看到兩個相關的欄位:ca.crttls.crt。(我們忽略 tls.key - trust-manager 絕對不需要存取它)

這會產生一個明顯的問題:在 ca.crttls.crt 之間,我應該將哪個用於 trust-manager?

不幸的是,一般情況下無法判斷哪個欄位是正確使用的,但我們可以提供一些指引。

tls.crt 通常會包含多個憑證,這些憑證可能並非全部都是簽發者,並且其中一些很可能是中繼憑證。如果是這種情況,您不應該使用 tls.crt 作為來源。(詳細資訊請參閱上面的「綁定中繼憑證」)。

那麼 ca.crt 看起來似乎是更普遍正確的選擇,但重要的是要記住,它只能在盡力而為的基礎上填充。 ca.crt 的內容取決於 Issuer 是否正確配置,並且某些簽發者類型可能永遠無法為此欄位提供有用或正確的條目。

一般而言,您應該優先僅使用根憑證建立 Bundles(同樣,請參閱上文),因此您應該只使用其中包含單個根憑證的欄位。請考慮閱讀以下內容,了解為何您可能不想直接依賴 cert-manager 發行的憑證。

cert-manager 整合:刻意複製 CA 憑證

在 Kubernetes 世界中,提出一個看似使基礎架構自動化更困難的步驟是很奇怪的,但在 TLS 信任儲存的情況下,這可能是一個明智的選擇。

假設您有一個 cert-manager Issuer,其在 ca.crt 中具有您想要信任的根憑證。直接使用 Secret 並指向 ca.crt 似乎很吸引人,但最佳實務是將該根憑證複製到單獨的 ConfigMap(或 Secret)。

原因是 - 與許多 TLS 的陷阱一樣 - 憑證輪換。如果您輪換您的簽發者,使其從新的根憑證發出,trust-manager 將會看到 Secret 更新,並自動更新您的信任綑綁包以包含新的根憑證 - 立即不信任舊的根憑證。

這意味著如果任何服務仍然在使用由舊根憑證發行的憑證,它們將會被不信任並且會中斷。

輪換需要兩個根憑證同時被信任一段時間,或者所有已發行的憑證在舊根憑證之前或同時輪換。

已知問題

kubectl describe

useDefaultCAs 選項在 kubectl describe 內部遇到一個邊緣情況,並呈現為 Use Default C As: true。這純粹是外觀上的問題。