Vault
The Vault
Issuer
represents the certificate authority Vault - a multi-purpose secret store that can be used to sign certificates for your Public Key Infrastructure (PKI). Vault is an external project to cert-manager and as such, this guide will assume it has been configured and deployed correctly, ready for signing. You can read more on how to configure Vault as a certificate authority here.
This Issuer
type is typically used when Vault is already being used within your infrastructure, or you would like to make use of its feature set where the CA issuer alone cannot provide.
部署
所有 Vault 发行者共享用于请求证书的通用配置,即服务器、路径和 CA 捆绑包
- 服务器是 Vault 可访问的 URL。
- 路径是用于签名的 Vault 路径。请注意,路径 *必须* 使用
sign
端点。 - CA 捆绑包表示一个可选字段,其中包含证书颁发机构的 base64 编码字符串,以信任 Vault 连接。在使用
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 图表 在 Kubernetes 集群中安装 Vault
- 创建 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 进行身份验证 |
令牌 | B. 使用 Vault 令牌进行身份验证 |
JWT/OIDC | C. 使用 Kubernetes 服务帐户进行身份验证 > 使用 JWT/OIDC 身份验证 |
Kubernetes | C. 使用 Kubernetes 服务帐户进行身份验证 > 使用 Kubernetes 身份验证 |
A. 通过 AppRole 进行身份验证
一个 AppRole 是通过使用其内部角色策略系统对 Vault 进行身份验证的一种方法。这种身份验证方法要求发行者拥有 SecretID
密钥、要假定的角色的 RoleID
以及 app 角色路径。首先,秘密 ID 密钥必须存储在 Kubernetes Secret
中,该密钥位于与 Issuer
相同的命名空间中,或者在使用 ClusterIssuer
的情况下位于 集群资源命名空间
中。
apiVersion: v1kind: Secrettype: Opaquemetadata:name: cert-manager-vault-approlenamespace: sandboxdata:secretId: "MDI..."
创建 Secret
后,就可以部署引用此 Secret
以及存储秘密 ID 的字段的数据密钥的 Issuer
了。
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 令牌的更多信息 here。
注意:cert-manager 不会自动刷新这些令牌,因此必须设置另一个过程来执行此操作。
首先,令牌将存储在 Kubernetes Secret
中,该 Secret
位于与 Issuer
相同的命名空间中,或者在使用 ClusterIssuer
的情况下位于 集群资源命名空间
中。
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 身份验证
当出现以下情况时,应使用 JWT/OIDC 身份验证方法 而不是 Kubernetes 身份验证方法:
- 您的 Kubernetes 集群 OIDC 发现端点可从 Vault 服务器访问(如果您运行的是自托管的 Kubernetes 或 OpenShift 集群,则可能并非如此)。
- 您的 Vault 服务器没有在 Kubernetes 集群中运行。
注意:使用 JWT 身份验证而不是 Kubernetes 身份验证时,将不再检查令牌的吊销。
"注意:JWT 身份验证引擎在身份验证期间不使用 Kubernetes 的 TokenReview API,而是使用公钥加密来验证 JWT 的内容。这意味着,即使 Kubernetes 已吊销令牌,Vault 仍将认为这些令牌在过期时间之前有效。为了缓解这种风险,请为服务帐户令牌使用较短的 TTL,或使用确实使用 TokenReview API 的 Kubernetes 身份验证。"
这不是问题,因为 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 可以获取服务帐户的令牌
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
建议每个发行者或 ClusterIssuer 使用不同的 Vault 角色。使用 audience
可以将 Vault 角色限制为单个发行者或 ClusterIssuer。语法如下
"vault://<namespace>/<issuer-name>" # For an Issuer."vault://<cluster-issuer-name>" # For a ClusterIssuer.
最后,您可以创建发行者
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 身份验证
当出现以下情况时,应使用 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 令牌的受众还必须包括 Kubernetes API 服务器受众。
✅ 当省略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 可以获取服务帐户的令牌
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
建议每个发行者或 ClusterIssuer 使用不同的 Vault 角色。使用 audience
可以将 Vault 角色限制为单个发行者或 ClusterIssuer。语法如下
"vault://<namespace>/<issuer-name>" # For an Issuer."vault://<cluster-issuer-name>" # For a ClusterIssuer.
最后,您可以创建发行者
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 将 Kubernetes API 服务器受众包含在 JWT 令牌中。这可以通过在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 仍将包含生成的受众vault://namespace/issuer-name
或vault://cluster-issuer
。生成的受众对于将对 Vault 角色的访问权限限制到某个颁发者很有用。
验证颁发者部署
部署 Vault 颁发者后,如果配置有效,它将被标记为已准备就绪。如果已部署,请将以下 issuers
替换为 clusterissuers
。
Vault 颁发者通过查询 v1/sys/health
端点来测试您的 Vault 实例,以确保您的 Vault 实例在请求证书之前已解除密封并初始化。该查询的结果将填充 STATUS
列。
$ kubectl get issuers vault-issuer -n sandbox -o wideNAME READY STATUS AGEvault-issuer True Vault verified 2m
现在可以使用名为 vault-issuer
的 Vault 颁发者在 sandbox
命名空间中请求证书。