Vault
Vault
Issuer
代表憑證授權單位 Vault - 一個多用途的密鑰儲存庫,可用於簽署您的公鑰基礎架構 (PKI) 的憑證。Vault 是 cert-manager 的外部專案,因此,本指南將假設它已正確設定和部署,隨時可以進行簽署。您可以在此閱讀更多關於如何將 Vault 設定為憑證授權單位的資訊。
當 Vault 已在您的基礎架構中使用,或者您想利用其功能集(CA 簽發者本身無法提供)時,通常會使用此 Issuer
類型。
部署
所有 Vault 簽發者都共用請求憑證的通用設定,即伺服器、路徑和 CA 憑證包。
- 伺服器是 Vault 可訪問的 URL。
- 路徑是將用於簽署的 Vault 路徑。請注意,路徑必須使用
sign
端點。 - CA 憑證包表示一個可選的欄位,其中包含用於信任 Vault 連線的憑證授權單位的 base64 編碼字串。當使用
https
URL 時,通常始終需要此欄位。
以下是連線 Vault 伺服器的設定範例。
警告:此設定不完整,因為尚未新增任何驗證方法。
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: sandboxspec:vault:path: pki_int/sign/example-dot-comserver: https://vault.localcaBundle: <base64 encoded CA Bundle PEM file>auth:...
使用強制 mTLS 存取 Vault 伺服器
在某些使用案例中,Vault 伺服器可能會設定為強制用戶端出示用戶端憑證,這些用戶端憑證只是傳輸層的強制執行,並未向 Vault API 本身提供任何驗證和授權機制。
📖 閱讀關於設定 Vault 伺服器 TCP 接聽器的資訊。
請按照以下步驟設定強制 mTLS 的 Vault
- 產生憑證包 CA 和伺服器 TLS 憑證
step certificate create "Example Server Root CA" server_ca.crt server_ca.key \--profile root-ca \--not-after=87600h \--no-password \--insecurestep certificate create vault.vault server.crt server.key \--profile leaf \--not-after=8760h \--ca ./server_ca.crt \--ca-key server_ca.key \--no-password \--insecure
- 產生 Vault 用戶端憑證和 CA
step certificate create "Example Client Root CA" client_ca.crt client_ca.key \--profile root-ca \--not-after=87600h \--no-password \--insecurestep certificate create client.vault client.crt client.key \--profile leaf \--not-after=8760h \--ca ./client_ca.crt \--ca-key client_ca.key \--no-password \--insecure
- 準備 Vault 安裝,假設您將使用官方 Helm 圖表將 Vault 安裝在 Kubernetes 叢集中
- 建立 Vault 命名空間
kubectl create ns vault
- 在將安裝 Vault 的同一命名空間中建立 Kubernetes Secret,並新增產生的 PKI 檔案如下
kubectl create secret generic vault-tls \--namespace vault \--from-file=server.key \--from-file=server.crt \--from-file=client_ca.crt \--from-file=client.crt \--from-file=client.key
- 使用以下值檔案部署 Vault
⚠️ 這些設定僅適用於快速本機測試。它們是不安全的,不適合用於生產環境。
# vault-values.yamlglobal:tlsDisable: falseinjector:enabled: falseserver:dataStorage:enabled: falsestandalone:enabled: trueconfig: |listener "tcp" {address = "[::]:8200"cluster_address = "[::]:8201"tls_disable = falsetls_client_ca_file = "/vault/tls/client_ca.crt"tls_cert_file = "/vault/tls/server.crt"tls_key_file = "/vault/tls/server.key"tls_require_and_verify_client_cert = true}extraArgs: "-dev-tls -dev-listen-address=[::]:8202"extraEnvironmentVars:VAULT_TLSCERT: /vault/tls/server.crtVAULT_TLSKEY: /vault/tls/server.keyVAULT_CLIENT_CERT: /vault/tls/client.crtVAULT_CLIENT_KEY: /vault/tls/client.keyvolumes:- name: vault-tlssecret:defaultMode: 420secretName: vault-tlsvolumeMounts:- mountPath: /vault/tlsname: vault-tlsreadOnly: true
helm upgrade vault hashicorp/vault --install --namespace vault --create-namespace --values vault-values.yaml
- 為 Kubernetes 驗證設定 Vault 伺服器
kubectl -n vault exec pods/vault-0 -- \vault auth enable --tls-skip-verify kuberneteskubectl -n vault exec pods/vault-0 -- \vault write --tls-skip-verify \auth/kubernetes/role/vault-issuer \bound_service_account_names=vault-issuer \bound_service_account_namespaces=application-1 \audience="vault://application-1/vault-issuer" \policies=vault-issuer \ttl=1mkubectl -n vault exec pods/vault-0 -- \vault write --tls-skip-verify \auth/kubernetes/config \kubernetes_host=https://kubernetes.default
- 建立應用程式命名空間
kubectl create ns application-1
- 建立服務帳戶
kubectl create serviceaccount -n application-1 vault-issuer
- 建立角色和繫結
# rbac.yamlapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:name: vault-issuernamespace: application-1rules:- apiGroups: ['']resources: ['serviceaccounts/token']resourceNames: ['vault-issuer']verbs: ['create']---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: vault-issuernamespace: application-1subjects:- kind: ServiceAccountname: cert-managernamespace: cert-managerroleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: vault-issuer
kubectl apply -f rbac.yaml
- 建立 Vault 用戶端憑證密鑰
kubectl create secret generic vault-client-tls \--namespace application-1 \--from-file=client.crt \--from-file=client.key \--from-file=server_ca.crt
- 建立簽發者
# vault-issuer.yamlapiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: application-1spec:vault:path: pki_int/sign/application-1server: https://vault.vault:8200caBundleSecretRef:key: server_ca.crtname: vault-client-tlsclientCertSecretRef:name: vault-client-tlskey: client.crtclientKeySecretRef:name: vault-client-tlskey: client.keyauth:kubernetes:role: vault-issuermountPath: /v1/auth/kubernetesserviceAccountRef:name: vault-issuer
kubectl apply -f vault-issuer.yaml
- 檢查簽發者狀態
kubectl describe issuer -n application-1
驗證
為了要求 Vault 簽署憑證,簽發者必須能夠正確地對其進行驗證。cert-manager 提供了多種驗證 Vault 的方法,詳細資訊如下。
Vault 驗證類型 | cert-manager 簽發者設定 |
---|---|
AppRole | A. 使用 Vault AppRole 驗證 |
Token (權杖) | B. 使用 Vault 權杖驗證 |
JWT/OIDC | C. 使用 Kubernetes 服務帳戶驗證 > 使用 JWT/OIDC 驗證 |
Kubernetes | C. 使用 Kubernetes 服務帳戶驗證 > 使用 Kubernetes 驗證 |
A. 透過 AppRole 驗證
AppRole 是一種透過使用其內部角色原則系統向 Vault 驗證的方法。此驗證方法要求簽發者擁有 SecretID
密鑰、要假設的角色 RoleID
和應用程式角色路徑。首先,密鑰 ID 金鑰必須儲存在與 Issuer
相同的命名空間中,或在 Cluster Resource Namespace
中(如果使用 ClusterIssuer
),Kubernetes Secret
中。
apiVersion: v1kind: Secrettype: Opaquemetadata:name: cert-manager-vault-approlenamespace: sandboxdata:secretId: "MDI..."
一旦建立了 Secret
,Issuer
即可準備好部署,它會參考此 Secret
,以及儲存密鑰 ID 的欄位的資料金鑰。
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: sandboxspec:vault:path: pki_int/sign/example-dot-comserver: https://vault.localcaBundle: <base64 encoded caBundle PEM file>auth:appRole:path: approleroleId: "291b9d21-8ff5-..."secretRef:name: cert-manager-vault-approlekey: secretId
B. 使用權杖驗證
此驗證方法使用從 Vault 支援的許多驗證後端之一產生的權杖字串。這些權杖具有到期日,因此需要定期更新。您可以在此處閱讀更多關於 Vault 權杖的資訊。
注意:cert-manager 不會自動更新這些權杖,因此必須實施另一個程序來執行此操作。
首先,權杖將儲存在與 Issuer
相同的命名空間中,或在 Cluster Resource Namespace
中(如果使用 ClusterIssuer
),Kubernetes Secret
中。
apiVersion: v1kind: Secrettype: Opaquemetadata:name: cert-manager-vault-tokennamespace: sandboxdata:token: "MjI..."
提交後,可以使用權杖驗證建立 Vault 簽發者,方法是參考此 Secret
以及儲存權杖資料的欄位的金鑰。
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: sandboxspec:vault:path: pki_int/sign/example-dot-comserver: https://vault.localcaBundle: <base64 encoded caBundle PEM file>auth:tokenSecretRef:name: cert-manager-vault-tokenkey: token
C. 使用 Kubernetes 服務帳戶驗證
ℹ️ 此文件適用於 cert-manager >= v1.12.0。
Vault JWT/OIDC 驗證和Vault Kubernetes 驗證允許 cert-manager 使用 Kubernetes 服務帳戶權杖向 Vault 驗證,以便使用 Vault 作為憑證授權單位頒發憑證。
Vault 驗證方法
選項 1. Vault 驗證方法:使用 JWT/OIDC 驗證
當Kubernetes 驗證方法時,應使用JWT/OIDC 驗證方法:
- 您的 Kubernetes 叢集 OIDC 探索端點可從 Vault 伺服器存取(如果您執行的是自託管的 Kubernetes 或 OpenShift 叢集,則可能不是這種情況)。
- 您的 Vault 伺服器未在 Kubernetes 叢集中執行。
注意:透過使用 JWT 驗證而不是 Kubernetes 驗證,將不再檢查權杖的撤銷。
「注意:JWT 驗證引擎在驗證期間不使用 Kubernetes 的 TokenReview API,而是使用公鑰密碼學來驗證 JWT 的內容。這表示 Kubernetes 撤銷的權杖在到期時間之前仍會被 Vault 視為有效。為了減輕此風險,請為服務帳戶權杖使用較短的 TTL,或使用 Kubernetes 驗證,它會使用 TokenReview API。」
這不是問題,因為 cert-manager 使用會在 10 分鐘後到期的短期權杖。
以下步驟將引導您完成 JWT 驗證方法的設定(基於Vault 文件)以及如何將其與 cert-manager 一起使用。
若要設定 Vault 的 JWT 驗證,您需要取得簽發者 URL。
ISSUER="$(kubectl get --raw /.well-known/openid-configuration | jq -r '.issuer')"
檢查 URL 是否有效,且可從 Vault 伺服器存取。例如,回應應類似如下
$ curl "$ISSUER/.well-known/openid-configuration"{"issuer": "https://container.googleapis.com/v1/projects/project001/locations/europe-west1-b/clusters/cert-manager-cluster","jwks_uri": "https://container.googleapis.com/v1/projects/project001/locations/europe-west1-b/clusters/cert-manager-cluster/jwks",...}$ curl "<jwks_uri value>"{"keys": [{"kty": "RSA","e": "AQAB","use": "sig","kid": "key-id","alg": "RS256","n": "..."}]}
下一步是在 Vault 中設定 JWT 驗證。您需要為每個 Kubernetes 叢集建立一個 JWT 驗證路徑,因為每個叢集都有自己的 JSON Web 金鑰集和 OIDC 探索端點。
vault auth enable -path=jwt-cluster001 jwtkubectl config view --minify --flatten -ojson \| jq -r '.clusters[].cluster."certificate-authority-data"' \| base64 -d >/tmp/cacrtvault write auth/jwt-cluster001/config \oidc_discovery_url="${ISSUER}" \oidc_discovery_ca_pem=@/tmp/cacrt
然後,建立一個 Kubernetes 服務帳戶和一個符合的 Vault 角色
kubectl create serviceaccount -n sandbox vault-issuer
然後新增 RBAC 角色,以便 cert-manager 可以取得 ServiceAccount 的權杖
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:name: vault-issuernamespace: sandboxrules:- apiGroups: ['']resources: ['serviceaccounts/token']resourceNames: ['vault-issuer']verbs: ['create']---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: vault-issuernamespace: sandboxsubjects:- kind: ServiceAccountname: cert-managernamespace: cert-managerroleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: vault-issuer
接著,建立 Vault 角色
vault write auth/jwt-cluster001/role/vault-issuer-role \role_type="jwt" \bound_audiences="vault://sandbox/vault-issuer" \user_claim="sub" \bound_subject="system:serviceaccount:sandbox:vault-issuer" \policies="default" \ttl=1m
建議每個 Issuer 或 ClusterIssuer 使用不同的 Vault 角色。 audience
允許您將 Vault 角色限制為單一的 Issuer 或 ClusterIssuer。語法如下:
"vault://<namespace>/<issuer-name>" # For an Issuer."vault://<cluster-issuer-name>" # For a ClusterIssuer.
最後,您可以建立您的 Issuer
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: sandboxspec:vault:path: pki_int/sign/example-dot-comserver: https://vault.localauth:kubernetes:role: vault-issuer-rolemountPath: /v1/auth/jwt-cluster001serviceAccountRef:name: vault-issuer
選項 2. Vault 驗證方法:使用 Kubernetes Auth
當以下情況時,應使用Kubernetes 驗證方法
- 您的 Vault 伺服器在 Kubernetes 叢集內執行。
- 或者,您的 Kubernetes 叢集的 OIDC 探索端點無法從 Vault 伺服器存取,但 Vault 可以存取 Kubernetes API 伺服器。
以下步驟將引導您完成 Kubernetes 驗證方法的配置(基於Vault 文件),以及如何將其與 cert-manager 一起使用。
Kubernetes 驗證方法需要一個 token_reviewer_jwt
,這是一個 JWT 令牌,Vault 使用它來調用 Kubernetes API 伺服器的 TokenReview API。然後,此端點用於驗證 cert-manager 提供的 JWT 令牌。有三種方法可以提供此 token_reviewer_jwt
令牌:
- 當在 Kubernetes 叢集內執行 Vault 時,您可以使用掛載到 Vault Pod 中的 Kubernetes 服務帳戶令牌。
✅ 當 Vault 自動偵測到它在 Kubernetes 叢集中執行時啟用 - 當在 Kubernetes 叢集外執行 Vault 時,您可以建立一個長期有效的服務帳戶令牌,並將其提供給 Vault。
✅ 當您設定token_reviewer_jwt
參數時啟用 - 當在 Kubernetes 叢集外執行 Vault 時,您可以重複使用 cert-manager 提供的 JWT 令牌來向 Vault 驗證。在這種情況下,該 JWT 令牌的 audiences 必須包含 Kubernetes API 伺服器的 audience。
✅ 當省略token_reviewer_jwt
參數,且 Vault 不在 Kubernetes 叢集中執行時啟用
vault auth enable -path=kubernetes-cluster001 kuberneteskubectl config view --minify --flatten -ojson \| jq -r '.clusters[].cluster."certificate-authority-data"' \| base64 -d >/tmp/cacrtvault write auth/kubernetes-cluster001/config \kubernetes_host=<kubernetes-api-server-url> \kubernetes_ca_cert=@/tmp/cacrt
注意:如果 Vault 在 Kubernetes 叢集外執行,您可以提供一個
token_reviewer_jwt
令牌,Vault 將使用它來向 Kubernetes API 伺服器進行驗證。這個令牌可以是長期有效的服務帳戶令牌,可以如這裡所述取得。確保令牌連結到具有調用 TokenReview API 必要權限的服務帳戶。 vault 命令看起來會像這樣:vault write auth/kubernetes-cluster001/config \token_reviewer_jwt="<TokenReview API SA token>"kubernetes_host=<kubernetes-api-server-url> \kubernetes_ca_cert=@/tmp/cacrt
然後,建立一個 Kubernetes 服務帳戶和一個符合的 Vault 角色
kubectl create serviceaccount -n sandbox vault-issuer
然後新增 RBAC 角色,以便 cert-manager 可以取得 ServiceAccount 的權杖
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:name: vault-issuernamespace: sandboxrules:- apiGroups: ['']resources: ['serviceaccounts/token']resourceNames: ['vault-issuer']verbs: ['create']---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: vault-issuernamespace: sandboxsubjects:- kind: ServiceAccountname: cert-managernamespace: cert-managerroleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: vault-issuer
接著,建立 Vault 角色
vault write auth/kubernetes/role/vault-issuer-role \bound_service_account_names=vault-issuer \bound_service_account_namespaces=sandbox \audience="vault://sandbox/vault-issuer" \policies=default \ttl=1m
建議每個 Issuer 或 ClusterIssuer 使用不同的 Vault 角色。 audience
允許您將 Vault 角色限制為單一的 Issuer 或 ClusterIssuer。語法如下:
"vault://<namespace>/<issuer-name>" # For an Issuer."vault://<cluster-issuer-name>" # For a ClusterIssuer.
最後,您可以建立您的 Issuer
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: vault-issuernamespace: sandboxspec:vault:path: pki_int/sign/example-dot-comserver: https://vault.localauth:kubernetes:role: vault-issuer-rolemountPath: /v1/auth/kubernetes-cluster001serviceAccountRef:name: vault-issuer
注意:如果您要重複使用 cert-manager 提供的 JWT 令牌來向 Vault 驗證,則在配置 Kubernetes Vault 驗證方法時,可以省略
token_reviewer_jwt
參數。但是您還必須配置 cert-manager,以在 JWT 令牌中包含 Kubernetes API 伺服器的 audience。這是通過設定serviceAccountRef
欄位中的audiences
欄位來完成的。此選項僅在 cert-manager >= v1.15.0 中可用。KUBE_API_AUDIENCE="$(kubectl create token default | jq -R 'gsub("-";"+") | gsub("_";"/") | split(".") | .[1] | @base64d | fromjson | .aud[0]')"...kubernetes:...serviceAccountRef:name: vault-issueraudiences: [ $KUBE_API_AUDIENCE ]使用
audiences
時,JWT 仍將包含產生的 audiencevault://namespace/issuer-name
或vault://cluster-issuer
。產生的 audience 對於限制對特定 issuer 的 Vault 角色的存取非常有用。
驗證 issuer 的部署
一旦 Vault issuer 部署完成,如果配置有效,它將被標記為準備就緒。如果已部署 clusterissuers
,請將下面的 issuers
替換為 clusterissuers
。
Vault issuer 通過查詢 v1/sys/health
端點來測試您的 Vault 實例,以確保您的 Vault 實例在請求憑證之前已解封和初始化。該查詢的結果將填充 STATUS
列。
$ kubectl get issuers vault-issuer -n sandbox -o wideNAME READY STATUS AGEvault-issuer True Vault verified 2m
現在可以使用 sandbox
命名空間中名為 vault-issuer
的 Vault issuer 請求憑證。