新消息:在TwitterMastodon取得專案更新

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/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: sandbox
spec:
vault:
path: pki_int/sign/example-dot-com
server: https://vault.local
caBundle: <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 \
--insecure
step 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 \
--insecure
step 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.yaml
global:
tlsDisable: false
injector:
enabled: false
server:
dataStorage:
enabled: false
standalone:
enabled: true
config: |
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_disable = false
tls_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.crt
VAULT_TLSKEY: /vault/tls/server.key
VAULT_CLIENT_CERT: /vault/tls/client.crt
VAULT_CLIENT_KEY: /vault/tls/client.key
volumes:
- name: vault-tls
secret:
defaultMode: 420
secretName: vault-tls
volumeMounts:
- mountPath: /vault/tls
name: vault-tls
readOnly: 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 kubernetes
kubectl -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=1m
kubectl -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.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: vault-issuer
namespace: application-1
rules:
- apiGroups: ['']
resources: ['serviceaccounts/token']
resourceNames: ['vault-issuer']
verbs: ['create']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vault-issuer
namespace: application-1
subjects:
- kind: ServiceAccount
name: cert-manager
namespace: cert-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: 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.yaml
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: application-1
spec:
vault:
path: pki_int/sign/application-1
server: https://vault.vault:8200
caBundleSecretRef:
key: server_ca.crt
name: vault-client-tls
clientCertSecretRef:
name: vault-client-tls
key: client.crt
clientKeySecretRef:
name: vault-client-tls
key: client.key
auth:
kubernetes:
role: vault-issuer
mountPath: /v1/auth/kubernetes
serviceAccountRef:
name: vault-issuer
kubectl apply -f vault-issuer.yaml
  • 檢查簽發者狀態
kubectl describe issuer -n application-1

驗證

為了要求 Vault 簽署憑證,簽發者必須能夠正確地對其進行驗證。cert-manager 提供了多種驗證 Vault 的方法,詳細資訊如下。

Vault 驗證類型cert-manager 簽發者設定
AppRoleA. 使用 Vault AppRole 驗證
Token (權杖)B. 使用 Vault 權杖驗證
JWT/OIDCC. 使用 Kubernetes 服務帳戶驗證 > 使用 JWT/OIDC 驗證
KubernetesC. 使用 Kubernetes 服務帳戶驗證 > 使用 Kubernetes 驗證

A. 透過 AppRole 驗證

AppRole 是一種透過使用其內部角色原則系統向 Vault 驗證的方法。此驗證方法要求簽發者擁有 SecretID 密鑰、要假設的角色 RoleID 和應用程式角色路徑。首先,密鑰 ID 金鑰必須儲存在與 Issuer 相同的命名空間中,或在 Cluster Resource Namespace 中(如果使用 ClusterIssuer),Kubernetes Secret 中。

apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: cert-manager-vault-approle
namespace: sandbox
data:
secretId: "MDI..."

一旦建立了 SecretIssuer 即可準備好部署,它會參考此 Secret,以及儲存密鑰 ID 的欄位的資料金鑰。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: sandbox
spec:
vault:
path: pki_int/sign/example-dot-com
server: https://vault.local
caBundle: <base64 encoded caBundle PEM file>
auth:
appRole:
path: approle
roleId: "291b9d21-8ff5-..."
secretRef:
name: cert-manager-vault-approle
key: secretId

B. 使用權杖驗證

此驗證方法使用從 Vault 支援的許多驗證後端之一產生的權杖字串。這些權杖具有到期日,因此需要定期更新。您可以在此處閱讀更多關於 Vault 權杖的資訊。

注意:cert-manager 不會自動更新這些權杖,因此必須實施另一個程序來執行此操作。

首先,權杖將儲存在與 Issuer 相同的命名空間中,或在 Cluster Resource Namespace 中(如果使用 ClusterIssuer),Kubernetes Secret 中。

apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: cert-manager-vault-token
namespace: sandbox
data:
token: "MjI..."

提交後,可以使用權杖驗證建立 Vault 簽發者,方法是參考此 Secret 以及儲存權杖資料的欄位的金鑰。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: sandbox
spec:
vault:
path: pki_int/sign/example-dot-com
server: https://vault.local
caBundle: <base64 encoded caBundle PEM file>
auth:
tokenSecretRef:
name: cert-manager-vault-token
key: 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 jwt
kubectl config view --minify --flatten -ojson \
| jq -r '.clusters[].cluster."certificate-authority-data"' \
| base64 -d >/tmp/cacrt
vault 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/v1
kind: Role
metadata:
name: vault-issuer
namespace: sandbox
rules:
- apiGroups: ['']
resources: ['serviceaccounts/token']
resourceNames: ['vault-issuer']
verbs: ['create']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vault-issuer
namespace: sandbox
subjects:
- kind: ServiceAccount
name: cert-manager
namespace: cert-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: 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/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: sandbox
spec:
vault:
path: pki_int/sign/example-dot-com
server: https://vault.local
auth:
kubernetes:
role: vault-issuer-role
mountPath: /v1/auth/jwt-cluster001
serviceAccountRef:
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 令牌:

  1. 當在 Kubernetes 叢集內執行 Vault 時,您可以使用掛載到 Vault Pod 中的 Kubernetes 服務帳戶令牌。
    ✅ 當 Vault 自動偵測到它在 Kubernetes 叢集中執行時啟用
  2. 當在 Kubernetes 叢集外執行 Vault 時,您可以建立一個長期有效的服務帳戶令牌,並將其提供給 Vault。
    ✅ 當您設定 token_reviewer_jwt 參數時啟用
  3. 當在 Kubernetes 叢集外執行 Vault 時,您可以重複使用 cert-manager 提供的 JWT 令牌來向 Vault 驗證。在這種情況下,該 JWT 令牌的 audiences 必須包含 Kubernetes API 伺服器的 audience。
    ✅ 當省略 token_reviewer_jwt 參數,且 Vault 不在 Kubernetes 叢集中執行時啟用
vault auth enable -path=kubernetes-cluster001 kubernetes
kubectl config view --minify --flatten -ojson \
| jq -r '.clusters[].cluster."certificate-authority-data"' \
| base64 -d >/tmp/cacrt
vault 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/v1
kind: Role
metadata:
name: vault-issuer
namespace: sandbox
rules:
- apiGroups: ['']
resources: ['serviceaccounts/token']
resourceNames: ['vault-issuer']
verbs: ['create']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: vault-issuer
namespace: sandbox
subjects:
- kind: ServiceAccount
name: cert-manager
namespace: cert-manager
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: 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/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: sandbox
spec:
vault:
path: pki_int/sign/example-dot-com
server: https://vault.local
auth:
kubernetes:
role: vault-issuer-role
mountPath: /v1/auth/kubernetes-cluster001
serviceAccountRef:
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-issuer
audiences: [ $KUBE_API_AUDIENCE ]

使用 audiences 時,JWT 仍將包含產生的 audience vault://namespace/issuer-namevault://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 wide
NAME READY STATUS AGE
vault-issuer True Vault verified 2m

現在可以使用 sandbox 命名空間中名為 vault-issuer 的 Vault issuer 請求憑證。