Vault使用场景
数据加密
Vault中的所有用户数据在保存和传输时都必须加密。secrets engine用于解决在数据保存、数据传输和为外部系统生成数据时遇到的挑战。如kv secrets engine可以用于数据保存,将数据保存到物理存储上。而transmit secret engine可以用于数据的加密传输,但这种secret engine不会保存任何数据。

访问控制
Vault提供了多种访问控制能力,如ACL、control groups和sentinel策略。此外,Vault还可以提供动态凭据管理能力。
- ACL策略:以声明的方式提供了一种允许或禁止访问以及操作Vault特定路径的方式。
- Control groups:提供了额外的授权因素,只有通过授权的请求才能被Vault处理
- Sentinel策略:支持复杂的逻辑,以角色治理策略(RGP,绑定到特定令牌、身份实体或身份群体)和终端治理策略(Endpoint Governing Policies (EGP),绑定到特定路径)的形式增强访问控制

有时间限制的访问
Vault通过将TTL和相关凭据关联的方式来实现有时间限制的访问。支持在服务层面和插件层面或角色层面配置TTL。
当一个用户使用预先定义的角色以及用户名和密码进行认证时,会接收到一个Vault token和一个附加的TTL值。

灾备恢复
可以主备集群以及Automated data snapshots实现灾备恢复。

基于身份(Identity)的安全性
身份可以帮助Vault通过实体(Entity)或别名(Aliases)识别client,实体或别名绑定到启用策略的token。Vault可以通过identity secrets engine提供身份管理方案。

人类和机器认证
auth methods属于Vault的一种插件,负责为一个用户分配认证所需的身份和策略。
- 人类auth methods包括GitHub、LDAP和 userpass。
- 机器auth methods包括AppRole、AWS、Kubernetes 和 TLS。
大部分情况下,Vault会将身份认证管理和决策委托给相关配置好的外部身份auth methods,如Amazon Web Services、GitHub、Kubernetes等。

静态和动态secrets的Secrets engines
Secrets engines适用于存储、生成和加密数据的插件,secrets engines可以管理两种类型的secrets:静态和动态secrets。
静态secrets不会过期,若要修改此类secrets,则需要人工介入。静态secrets包括第三方tokens、API keys、应用keys、PKI证书、PGP keys、加密keys、用户名和密码。
动态secret是指一定时间后需要吊销的secret。动态secrets通常会和第三方平台进行集成,从Vault接收请求并生成凭据。动态secrets包括数据库keys、云提供商的凭据以及短期secrets。

Install
$ brew tap hashicorp/tap$ brew install hashicorp/tap/vault$ brew upgrade hashicorp/tap/vault
Setup
使用如下命令启动vault:
$ vault server -dev -dev-root-token-id root -dev-tls
在新窗口中执行上面命令打印的vault地址和证书:
export VAULT_ADDR='https://127.0.0.1:8200'export VAULT_CACERT='/var/folders/hp/nqbj0l8x0jj8jfhh_6_62f6w0000gp/T/vault-tls3057506928/vault-ca.pem'
验证vault状态:
$ vault status
登陆:
$ vault login root
plugin

Vault支持3种插件:auth methods, secret engines和 database plugins,这些插件分为内置插件和外部插件两种。可以使用plugin_directory
选项加载外部插件。
Vault Integrations给出了支持的插件列表。
Token
在客户端认证成功之后,vault会颁发一个token,用于校验客户端的访问以及客户端可以执行的操作。
使用vault cli执行命令前,需要设置通过 vault login
登陆或设置VAULT_TOKEN
环境变量。

Token元数据
一个token有几个重要属性:
- Token duration:token的有效时间,默认为32天。只有root token可以不设置有效时间
- Accessor:token查找、更新和吊销使用的唯一ID,但不能用于登陆vault。token创建之后是无法被查找的,可以使用accessor来间接操作token。
- Policies:表示通过该token授权的vault操作,可以设置一个或多个策略
$ vault token create -period=30mKey Value--- -----token hvs.qp5hXcAcllG6ytHtZXEBxIeBtoken_accessor eYQSFsK1pIuhgrG1zkNxj2Tjtoken_duration 30mtoken_renewable truetoken_policies ["root"]identity_policies []policies ["root"]
Token类型
主要有两种类型:service tokens 和 batch tokens,大部分场景下使用service tokens就可以了。service tokens的格式为hvs.string
,如上面的hvs.qp5hXcAcllG6ytHtZXEBxIeB
。
orphan tokens
当使用一个token创建新的token时,新创建出来的token会作为该token的子token,当吊销一个父token后,其所有的子tokens也会被吊销。如果一个token没有父token,则这类token被称为orphan token。
Policies
Vault的策略用于允许或禁止访问某些权限,由于Vault本身是基于路径的,因此在编写策略时,只需要控制对特定路径的操作即可。
在用户或服务通过Vault认证后,会给对应的token绑定一个策略。需要注意的是,任何时候都可以修改策略,但对于一个已经颁发的token,修改后的策略并不能直接生效到该token上,必须通过吊销token并重新和Vault认证来接收更新后的策略。
策略优先级
策略越具体,则优先级越高,例如下面第一条策略允许在secret/data/creds
路径下执行create
和update
操作,但由于第二条策略更具体,其优先级更高,因此,在secret/data/creds/confidential
路径下只能执行read
操作:
#第一条策略path"secret/data/creds" { capabilities = ["create","update"]}#第二条策略path"secret/data/creds/confidential" { capabilities = ["read"]}
路径相同
若路径相同,则deny
优先:
# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/root" { capabilities = ["read"]}path"dev-secrets/+/root" { capabilities = ["deny"]}
如果没有deny
策略且路径相同,则合并策略,下面允许在路径下执行list
,read
,create
和update
操作:
# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/root" { capabilities = ["list","read"]}path"dev-secrets/+/root" { capabilities = ["create","update"]}
策略通配符
使用通配符,可以允许访问dev-secrets/data/creds-webapp
之类的路径,注意*
只能用在路径末尾:
# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/data/creds*" { capabilities = ["create","list","read","update"]}
若要匹配中间路径,不能使用dev-secrets/*/creds
,应该使用+
:
# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/creds" { capabilities = ["create","list","read","update"]}
使用显示deny策略
下面策略允许在除dev-secrets/data/root
以外的dev-secrets
路径上执行create
,list
,read
和update
操作:
# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/*" { capabilities = ["create","list","read","update"]}path"dev-secrets/+/root" { #更具体,优先级更高 capabilities = ["deny"]}
Role
Vault的role用于给auth method或secret engine添加更多配置。但并不是所有auth methods和secret engines都支持role,如userpass
auth method就不支持role。
可以使用如下方式列出所有的role:
$ vault list auth/{auth_method}/role
下面为kubernetes
auth method创建一个role。
首先启用Kubernetes auth method:
$ vault auth enable kubernetesSuccess! Enabled kubernetes auth method at: kubernetes/
提供连接kubernetes所需的配置:
$ vault write auth/kubernetes/config \ token_reviewer_jwt="$K8S_SERVICE_ACCOUNT_TOKEN" \ kubernetes_host=https://192.168.99.100:443 \ kubernetes_ca_cert=@ca.crt
创建一个名为hashicupsApp
的role,在除该auth method要求的配置外,还提供了颁发token所需的policies
,ttl
和explicit-max-ttl
。
$ vault write auth/kubernetes/role/hashicupsApp \ bound_service_account_names=k8sHashicupsAppSA \ bound_service_account_namespaces=k8sDevNamespace \ policies=default,dev-secrets \ ttl=1h \ explicit-max-ttl=2h
Auth 和secret engine的区别
从Vault使用场景中可以看到,auth
是 Vault 中用于验证身份的机制。它允许用户、服务、应用程序等 以某种方式登录 Vault,获取一个token 来使用 Vault 的功能。而secrets
是 Vault 中用于 生成、存储和管理敏感数据的模块,称为Secrets Engine。
所有auth的mount地址都以auth/
开头。
- auth方法
- secrets engine
AppRole
用于给机器或apps提供认证。

关键参数
RoleID 和 SecretID类似机器或qpp认证所使用的用户名和密码。
RoleID
:当使用AppRole方式登陆endpoint时,需要输入RoleID(role_id
)SecretID
:默认登陆时需要通过secret_id
输入secretID,也可以通过AppRole的bind_secret_id
参数取消登陆时的SecretID参数。可以为role生成128位的随机UUID(pull模式),或自定义值(push模式)。与token类似,SecretID也有使用限制,TTLs和过期时间。
pull和push SecretID模式
如果用于登陆的SecretID是从AppRole获取到的,则为pull模式,如果由客户端设置AppRole的SecretID,则为push模式。大部分场景下推荐使用pull模式。
使用方式
创建role
启用approle
$ vault auth enable approle
创建一个policy:
$ vault policy write jenkins -<<EOF# Read-only permission on secrets stored at 'secret/data/mysql/webapp'path"secret/data/mysql/webapp" { capabilities = ["read" ]}EOF
创建一个role,关联创建出来的策略jenkins
(更多参数):
$ vault write auth/approle/role/jenkins token_policies="jenkins" \ token_ttl=1h token_max_ttl=4h
生成RoleID和secretID
获取RoleID:
$ vault read auth/approle/role/jenkins/role-idKey Value--- -----role_id 675a50e7-cfe0-be76-e35f-49ec009731ea
生成secretID:
$ vault write -force auth/approle/role/jenkins/secret-idKey Value--- -----secret_id ed0a642f-2acf-c2da-232f-1b21300d5f29secret_id_accessor a240a31f-270a-4765-64bd-94ba1f65703c
查看role信息
$ vault read auth/approle/role/jenkins
使用RoleID & SecretID进行登陆
通过auth/approle/login
endpoint进行登陆,输入RoleID和SecretID:
$ vault write auth/approle/login role_id="675a50e7-cfe0-be76-e35f-49ec009731ea" \ secret_id="ed0a642f-2acf-c2da-232f-1b21300d5f29" Key Value--- -----token s.ncEw5bAZJqvGJgl8pBDM0C5htoken_accessor gIQFfVhUd8fDsZjC7gLBMnQutoken_duration 1htoken_renewable truetoken_policies ["default""jenkins"]identity_policies []policies ["default""jenkins"]token_meta_role_name jenkins
使用AppRole token读取secrets
$ export APP_TOKEN="s.ncEw5bAZJqvGJgl8pBDM0C5h"
$ VAULT_TOKEN=$APP_TOKEN vault kv get secret/mysql/webapp====== Secret Path ======secret/data/mysql/webapp======= Metadata =======Key Value--- -----created_time 2025-04-10T03:32:45.254602Zcustom_metadata <nil>deletion_time n/adestroyed falseversion 1====== Data ======Key Value--- -----db_name userspassword passw0rdusername admin
受策略限制,如果执行删除命令,则返回403:
$ VAULT_TOKEN=$APP_TOKEN vault kv delete secret/mysql/webappError deleting secret/mysql/webapp: Error making API request.URL: DELETE http://127.0.0.1:8200/v1/secret/data/mysql/webappCode: 403. Errors:* 1 error occurred: * permission denied
封装SecretID
SecretID类似密码,为了防止明文传递,可以对SecretID进行封装:
$ vault write -wrap-ttl=60s -force auth/approle/role/jenkins/secret-idKey Value--- -----wrapping_token: s.yzbznr9NlZNzsgEtz3SI56pXwrapping_accessor: Smi4CO0Sdhn8FJvL8XvOT30ywrapping_token_ttl: 1mwrapping_token_creation_time: 2021-06-07 20:02:01.019838 -0700 PDTwrapping_token_creation_path: auth/approle/role/jenkins/secret-id
解封装的SecretID:
$ VAULT_TOKEN="s.yzbznr9NlZNzsgEtz3SI56pX" vault unwrapKey Value--- -----secret_id c4086c73-4569-90c9-fd73-72c879e3b7b4secret_id_accessor 3a2e9483-a7d2-dc19-7480-b1a025daecccsecret_id_ttl 0s
Tips
查看role:
# 使用vault auth list查看approle的挂载路径$ vault list /auth/<mount_path>/role
secret engine
secret engine是用于存储、生成或加密数据的组件。
静态和动态secrets
KV secrets engine 通常用于存储静态secrets。此外,Vault还能生成动态secrets,如database secret engine, kubernetes secret engine等。
KV secrets engine
KV secrets engine是常用的键值存储,可以存储单一的键值对,也可以为每个键值对存储多个版本。
下面展示了vault kv
子命令和对应的API endpoints:
注意这些API endpoints并不是vault kv直接访问的路径,而是执行子命令之后,后台访问的API路径。v1无版本概念,因此直接访问实际的key路径即可,v2有版本以及软删除等概念,因此需要不同的路径来存储这些数据。
Command | KV v1 endpoint | KV v2 endpoint |
---|---|---|
vault kv get | secret/<key_path> | secret/data/<key_path> |
vault kv put | secret/<key_path> | secret/data/<key_path> |
vault kv list | secret/<key_path> | secret/metadata/<key_path> |
vault kv delete | secret/<key_path> | secret/data/<key_path> |
此外,KV v2还有如下子命令:
Command | KV v2 endpoint |
---|---|
vault kv patch | secret/data/<key_path> |
vault kv rollback | secret/data/<key_path> |
vault kv undelete | secret/undelete/<key_path> |
vault kv destroy | secret/destroy/<key_path> |
vault kv metadata | secret/metadata/<key_path> |
判断kv secrets engine的版本
下面查看了路径为secret/
的kv secrets engine的信息,map[version:2]
表示版本号为2:
$ vault read sys/mounts/secretKey Value--- -----accessor kv_0b42315dconfig map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]deprecation_status supporteddescription key/value secret storageexternal_entropy_access falselocal falseoptions map[version:2]plugin_version n/arunning_plugin_version v0.21.0+builtinrunning_sha256 n/aseal_wrap falsetype kvuuid f702a289-5bcb-6655-fe97-78518ec26429
KV version 1
非版本的kv
secrets engine,针对一个key,仅存储最新的value。
enable
启用version 1 kv存储:
$ vault secrets enable -version=1 kv
usage
写入数据:
$ vault kv put kv/my-secret my-value=s3cr3tSuccess! Data written to: kv/my-secret
读取数据:
$ vault kv get kv/my-secret
list keys:
$ vault kv list kv/
delete key:
$ vault kv delete kv/my-secret
TTLs
与其他secrets engines不同,KV secrets engine不会强制TTLs过期,切不会移除数据。此处的ttl仅表示建议:
$ vault kv put kv/my-secret ttl=30m my-value=s3cr3t
KV version 2
确定版本号:
$ vault read sys/mounts/kv2Key Value--- -----accessor kv_825ea02fconfig map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]deprecation_status supporteddescription n/aexternal_entropy_access falselocal falseoptions map[version:2]plugin_version n/arunning_plugin_version v0.21.0+builtinrunning_sha256 n/aseal_wrap falsetype kvuuid d92d7308-d526-2f25-050c-e2acbf309432
enable
$ vault secrets enable -path <mount_path> -version=2 kv
write
如在secret/customer/acme
路径下创建keys为customer_name
和contact_email
的数据:
$ vault kv put secret/customer/acme customer_name="ACME Inc." contact_email="john.smith@acme.com"
另一种方式是指定-mount
:
$ vault kv put -mount <mount_path> <secret_path> <list_of_kv_values>
如:
$ vault kv put -mount=secret customer/acme customer_name="ACME Inc." contact_email="john.smith@acme.com"
====== Secret Path ======secret/data/customer/acme======= Metadata =======Key Value--- -----created_time 2022-06-13T13:41:45.673767Zcustom_metadata <nil>deletion_time n/adestroyed falseversion 1
创建之后version会从1开始递增,多次put或patch都会增加version。
使用vault kv metadata
可以查看secrets拥有的版本:
$ vault kv metadata get secret/customer/acme
限制版本数
kv-v2
secrets engine默认可以保存10个版本。使用下面方式可以将版本数限制为4,如果版本超过4,则会删除最老的版本:
$ vault write secret/config max_versions=4
read
读取指定路径下的所有key/value对,如读取上面创建的secret/customer/acme
路径下的所有数据:
$ vault kv get secret/customer/acme
等价于:
$ vault kv get -mount=secret customer/acme
read指定版本
$ vault kv get -version=1 secret/customer/acme
patch
vault kv put
会完全替换当前版本的secrets,使用patch可以修改单个key的value。如仅修改secret/customer/acme
下的contact_email
的值,而保留其他keys不变:
$ vault kv patch secret/customer/acme contact_email="admin@acme.com"
delete
删除特定版本:
$ vault kv delete -versions="4,5" secret/customer/acme
可以看到deletion_time
非n/a
,但destroyed
为false
,表示此时为软删除
$ vault kv metadata get secret/customer/acme##...snip...====== Version 4 ======Key Value--- -----created_time 2021-10-31T00:14:59.830407Zdeletion_time 2021-10-31T00:16:25.860618Zdestroyed false====== Version 5 ======Key Value--- -----created_time 2021-10-31T00:15:01.892226Zdeletion_time 2021-10-31T00:16:25.860619Zdestroyed false##...snip...
可以通过vault kv undelete
找回delete的数据:
$ vault kv undelete -versions=5 secret/customer/acme
destroy
使用destroy可以永久删除某个版本:
$ vault kv destroy -versions=4 secret/customer/acme
或删除一个路径下的所有版本的secret:
$ vault kv metadata delete secret/customer/acme
Kubernetes secrets engine
不推荐使用Vault Kubernetes Auth Method,这种方式会导致在 Vault 中产生许多难以管理的独特身份。
Kubernetes Secrets Engine可以生成kubernetes service account tokens,以及(可选的)service account、role bindings和roles。生成的service account tokens有一个配置的TTL,并在过期后删除所有创建的对象。
启用Kubernetes Secrets Engine:
$ vault secrets enable kubernetes
为vault创建一个service account并创建role和RoleBinding
apiVersion: v1kind: ServiceAccountmetadata: name: test-service-account-with-generated-token namespace: test---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: test-role-list-pods namespace: testrules:- apiGroups: [""] resources: ["pods"] verbs: ["list"]- apiGroups: [""] resources: ["serviceaccounts","serviceaccounts/token"] verbs: ["create","update","delete"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: test-role-abilities namespace: testroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: test-role-list-podssubjects:- kind: ServiceAccount name: test-service-account-with-generated-token namespace: test
Kubernetes 1.24+还需要创建secret:
apiVersion: v1kind: Secretmetadata: name: vault-auth-secret namespace: test annotations: kubernetes.io/service-account.name: test-service-account-with-generated-tokentype: kubernetes.io/service-account-token
/config
endpoint用于配置vault连接Kubernetes,后续可以通过vault read auth/kubernetes/config
读取连接配置:
$ export SA_SECRET_NAME=$(kubectl get secrets --output=json \ | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')$ export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \ --output 'go-template={{ .data.token }}' | base64 --decode)$ export SA_CA_CRT=$(kubectl -n test get secret $SA_SECRET_NAME -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)$ export K8S_HOST=$(kubectl config view --raw --minify --flatten \ --output 'jsonpath={.clusters[].cluster.server}') $ vault write kubernetes/config \ service_account_jwt="$SA_JWT_TOKEN" \ kubernetes_host="$K8S_HOST" \ kubernetes_ca_cert="$SA_CA_CRT"
现在就可以使用 Kubernetes Secrets Engine创建Vault role,绑定到kubernetes的service account test-service-account-with-generated-token
:
注意role的
token_default_ttl
,如果过期,在执行vault write kubernetes/creds/my-role
时会返回permission denied.
$ vault write kubernetes/roles/my-role \ allowed_kubernetes_namespaces="*" \ service_account_name="test-service-account-with-generated-token" \ token_default_ttl="1000m"
在授予role足够的权限之后,在creds
endpoint写入vault role之后就会生成并返回一个新的service account token,其lease_duration
与上面创建的my-role
的ttl相同。注意由于此处要生成kubernetes的serviceaccount token,因此要求my-role
对应的serviceaccount 具有serviceaccounts/token
资源的create
权限:
$ vault write kubernetes/creds/my-role \ kubernetes_namespace=testKey Value--- -----lease_id kubernetes/creds/my-role/TrHUCplToMe5kv8E77IG45Wrlease_duration 16h40mlease_renewable falseservice_account_name test-service-account-with-generated-tokenservice_account_namespace testservice_account_token eyJhbGciOiJSUzI1NiIsImtpZCI6IjY2M3ps...
后续可以使用上述的service_account_token
访问允许的Kubernetes API :
$ curl -sk $(kubectl config view --minify -o 'jsonpath={.clusters[].cluster.server}')/api/v1/namespaces/test/pods \ --header"Authorization: Bearer eyJHbGci0iJSUzI1Ni..."{"kind":"PodList","apiVersion":"v1","metadata": {"resourceVersion":"1624" },"items": []}
此外还可以在创建或调节(tune) Vault role时设置默认的TTL(token_default_ttl
)和最大TTL(token_max_ttl
):
$ vault write kubernetes/roles/my-role \ allowed_kubernetes_namespaces="*" \ service_account_name="new-service-account-with-generated-token" \ token_default_ttl="10m" \ token_max_ttl="2h"
还可以在生成token时指定TTL:
$ vault write kubernetes/creds/my-role \ kubernetes_namespace=test \ ttl=20mKey Value–-- -----lease_id kubernetes/creds/my-role/31d771a6-...lease_duration 20m0slease_renwable falseservice_account_name new-service-account-with-generated-tokenservice_account_namespace testservice_account_token eyJHbGci0iJSUzI1NiIsImtpZCI6ImlrUEE...
还可以为已存在的role重新指定kubernetes role:
$ vault write kubernetes/roles/auto-managed-sa-role \ allowed_kubernetes_namespaces="test" \ kubernetes_role_name="test-role-list-pods"
这样就可以通过vault write kubernetes/creds/auto-managed-sa-role kubernetes_namespace=test
生成token。
PKI secrets engine
使用PKI签发证书时,首先需要创建一个issuer,即CA证书,可以使用vault生成自签CA或使用外部CA。vault pki的几个概念:
issuer:表示一个CA,且需要关联一个key,否则无法颁发证书。可以通过下面命令查看一个issuer关联的key:
$ vault read -field=key_id pki/issuer/:issuer_ref
role:表示创建证书的模版,方便颁发证书,它会关联一个issuer,本质还是通过issuer颁发证书。通过如下命令查看关联的issuer:
$ vault read -field=issuer_ref pki/roles/my-issuer-role
urls:一般包含
issuing_certificates
、ocsp_servers
和crl_distribution_points
。设置好后,会将这些信息嵌入到后续颁发的证书的扩展字段中。这样在客户端验证证书时,可以自动下载 CA 证书链(Issuing CA),可以根据 CRL 或 OCSP URL 检查证书是否已吊销。X509v3 Authority Information Access: CA Issuers - URI:http://vault.example.com:8200/v1/pki/caX509v3 CRL Distribution Points: Full Name: URI:http://vault.example.com:8200/v1/pki/crlX509v3 OCSP: URI:http://vault.example.com:8200/v1/pki/ocsp
构建自己的CA
生成CA和私钥
证书可以来自现有的密钥对,也可以生成自签证书。通常建议维护自己的root CA,然后给vault提供一个signed intermediate CA。
下面使用根证书生成中间证书,然后使用中间证书来为test.example.com
证书:

生成root CA
启用pki secret engine:
$ vault secrets enable pki
设置secret engine的TTL ,默认为30d,下面设置为1年,为全局设置,还可以为单独证书设置TTL。
$ vault secrets tune -max-lease-ttl=8760h pki
下面生成example.com
root CA,设置issuer name并将证书保存到root_2023_ca.crt
文件中:
/pki/root/generate/:type
生成根证书,type
字段可选:exported
,会在响应中返回私钥;internal
,不会返回私钥,且无法被检索到;existing
,使用key_ref
参数查找已有的key来创建CSR。
$ vault write -field=certificate pki/root/generate/internal \ common_name="example.com" \ issuer_name="root-2023" \ ttl=87600h > root_2023_ca.crt
查看证书的issuer:
$ vault list pki/issuers/
通过证书的issuer ID获取证书和其他issuer的元数据(如是否revoked
):
$ vault read pki/issuer/$(vault list -format=json pki/issuers/ | jq -r '.[]') \ | tail -n 6
为root CA创建一个role,pki的role可以帮助颁发证书,但前提是要有CA证书。注意此处省去了参数issuer_ref="default"
:
$ vault write pki/roles/2023-servers allow_any_name=true
配置CA和CRL URLs,下面是全局配置,可以通过AIA字段配置单个issuer的URLs:
$ vault write pki/config/urls \ issuing_certificates="$VAULT_ADDR/v1/pki/ca" \ crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
生成中间CA
使用上面生成的根CA来创建中间CA证书。首先在pki_int
路径下启用secrets engine:
$ vault secrets enable -path=pki_int pki
将pki_int
secrets engine颁发的证书的TTL为43800h:
$ vault secrets tune -max-lease-ttl=43800h pki_int
通过/pki/intermediate/generate/:type
endpoint创建中间CA的CSR,保存为pki_intermediate.csr
:
$ vault write -format=json pki_int/intermediate/generate/internal \ common_name="example.com Intermediate Authority" \ issuer_name="example-dot-com-intermediate" \ | jq -r '.data.csr' > pki_intermediate.csr
使用之前生成的root CA 私钥签发证书,endpoint为/pki/root/sign-intermediate
,保存为intermediate.cert.pem
:
$ vault write -format=json pki/root/sign-intermediate \ issuer_ref="root-2023" \ csr=@pki_intermediate.csr \ format=pem_bundle ttl="43800h" \ | jq -r '.data.certificate' > intermediate.cert.pem
一旦签发CSR,root CA会返回一个证书,将该证书加载回vault:
$ vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem
创建role
role是一个逻辑名称,映射到一系列生成凭据的策略,帮助颁发证书。一个role会通过issuer_ref
关联到一个issuer,默认为default
issuer。
role的配置参数可以控制证书的common names, alternate names等,下面是值得注意的参数:
Param | Description |
---|---|
allowed_domains | 指定role的domains,与allow_bare_domains 和allow-subdomains 选项结合使用 |
allow_bare_domains | 指定clients是否可以请求与实际domain本身的值相匹配的证书 |
allow_subdomains | 指定clients是否可以请求带有CNs的证书,且这些CNs是role允许的CNs的子域(注意:此包含通配符子域)。 |
allow_glob_domains | 允许在allowed_domains 中指定的名称中包含glob模式(如 ftp*.example.com) |
创建一个roleexample-dot-com
,允许子域(allow_subdomains
),并通过issuer_ref
指定issuer:
$ vault write pki_int/roles/example-dot-com \ issuer_ref="$(vault read -field=default pki_int/config/issuers)" \ allowed_domains="example.com" \ allow_subdomains=true \ max_ttl="720h"
请求证书
使用example-dot-com
role为test.example.com
域创建一组新的证书(私钥和证书),并返回颁发证书的CA和完整的CA chain。后续可以通过该操作来为相同的CN颁发新的证书:
$ vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
吊销证书
在吊销一个证书时会重新生成一个CRL,此时vault会移除所有过期的证书。
吊销一个证书时,需要输入证书的序列号:
$ vault write pki_int/revoke serial_number=<serial_number>
移除过期的证书
$ vault write pki_int/tidy tidy_cert_store=true tidy_revoked_certs=true
rotate root CA
用于更换root CA。rotate老的root CA的最大挑战是,可能存在一些长时间离线的设备,但需要在上线之后能够获取到新的root CA。可以使用root bridge CA来关联新老 root CA。
使用外部CA
通过加载外部CA的方式颁发证书。需要提供一对配对的CA和key。可以通过如下方式校验CA和key是否配对:
$ openssl x509 -noout -modulus -in ca.crt | openssl md5$ openssl rsa -noout -modulus -in ca.key | openssl md5
加载外部CA:
$ vault write pki/config/ca pem_bundle=@root-ca.crt
加载外部key,可以通过vault list pki/keys
查看加载的keys:
$ vault write pki/keys/import pem_bundle=@root-ca.key
查看issuer是否自动关联了key:
$ vault read -field=key_id pki/issuer/:issuer_ref
配置证书分发地址:
$ vault write pki/config/urls \ issuing_certificates="$VAULT_ADDR/v1/pki/ca" \ crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
创建 Role,关联上面加载的issuer
:
$ vault write pki/roles/my-issuer-role \ allowed_domains="example.com" \ allow_subdomains=true \ generate_lease=true \ max_ttl="72h" \ issuer_ref="{issuer}"
使用该 Issuer 签发证书:
$ vault write pki/issue/my-issuer-role \ common_name="app.example.com" \ ttl="24h"
Tips
Vault生成证书时并不需要指定私钥,而是需要指定与issuer(CA证书)关联的role。颁发(issue)证书和签发(sign)证书的区别:
基于role(
:name
)颁发证书,包括私钥和证书:Method Path Issuer POST
/pki/issue/:name
Role selected POST
/pki/issuer/:issuer_ref/issue/:name
Path selected 签发证书:
基于给定的CSR和参数来签发一个受限于role(
:name
)的新证书,返回签发的证书和完整的CA chain。Method Path Issuer POST
/pki/sign/:name
Role selected POST
/pki/issuer/:issuer_ref/sign/:name
Path selected 签发中间CA:
使用配置的CA证书来签发一个中间CA:Method Path Issuer POST
/pki/root/sign-intermediate
default
POST
/pki/issuer/:issuer_ref/sign-intermediate
Selected 签发中间CA需要用到CSR,可以使用如下接口生成中间CSR:
Method Path Private key source ( type
)POST
/pki/intermediate/generate/:type
specified per request POST
/pki/issuers/generate/intermediate/:type
specified per request POST
/pki/intermediate/cross-sign
existing
$ vault read pki/config/issuersKey Value--- -----default 8ceb6b59-7042-25e3-da6f-f06b745773abdefault_follows_latest_issuer false
可以通过
vault read pki/cert/ca_chain
或vault read pki/issuer/default
查看默认issuer的证书链。查看证书内容:
首先获取证书的序列号(注意:vault list pki/issuers
获取到的并不是证书中的序列号,而是issuer的UUID),注意,
pki/certs
endpoint不包含下面序列号:但包含vault生成的root证书,该证书有可能是默认issuer。不包含外部加载的证书(root和中间CA证书)。
$ vault list pki/certs
然后通过序列号查看证书的内容:
$ vault read -field=certificate pki/cert/:serial|openssl x509 -noout -text
查看吊销证书,返回证书的序列号。
vault list pki/certs
返回的证书中包含已吊销和未吊销的证书:$ vault list pki/certs/revoked
查看CRL列表:
$ vault read pki/cert/crl$ vault read pki/issuer/:issuer_ref/crl
查看crl的内容:
$ vault read -field=crl pki/issuer/:issuer_ref/crl| openssl crl -text -noout
$ vault pki verify-sign pki/issuer/:issuer_ref pki_int/issuer/:issuer_ref
$ vault write pki/keys/generate/exported -format=json | jq -r '.data.private_key' > root-key.pem
Storage
通过在vault配置文件的storage
字段配置存储后端:
storage [NAME] { [PARAMETERS...]}
如:
storage"file" { path ="/mnt/vault/data"}
Vault支持Integrated Storage(磁盘)和external storage(如Consul、DynamoDB等)
Integrated storage(Raft)后端
使用Integrated Storage时要求提供cluster_addr
,用于指定节点间Raft通信的地址和端口:
storage"raft" { path ="/path/to/raft/data" node_id ="raft_node_1" retry_join { leader_api_addr ="http://127.0.0.4:8200" leader_ca_cert_file ="/path/to/ca3" leader_client_cert_file ="/path/to/client/cert3" leader_client_key_file ="/path/to/client/key3" }}cluster_addr ="http://127.0.0.1:8201"
主要参数:
path
:vault数据存储的文件系统路径node_id
:节点在Raft集群的标识符retry_join
:指定集群的其他节点,可以配置一个或多个,用于帮助本节点加入集群。
Vault agent

Auto-auth
自动认证包含两部分:
当一个工具(vault agent或vault proxy)启用自动认证后,工具会使用配置的认证方式请求一个vault token。如果请求成功,自动认证会将token写入合适的sink中。
Templates
简介
template_config
用于配置template引擎的默认行为。template
用于配置Vault Agent使用Consul Template语言将secrets渲染到文件。可以配置多个template
。template
中可以通过contents
直接提供需要渲染的内容,或通过source
选择引用一个单独的.ctmpl
文件进行渲染,如:
template_config { static_secret_render_interval ="10m" exit_on_retry_failure = true max_connections_per_host = 20}template { source ="/tmp/agent/template.ctmpl" destination ="/tmp/agent/render.txt"}template { contents ="{{ with secret \"secret/my-secret\" }}{{ .Data.data.foo }}{{ end }}" destination ="/tmp/agent/render-content.txt"}
template
需要通过secret
方法或pkiCert
方法进行渲染。前者适用于所有类型的secrets,后者仅适用于PKI secrets engine证书颁发相关的工作。
下面展示了secret
方法,其中Data
字段可选,如果存在,则为vault write
请求,否则为vault read
请求:
{{ secret"<PATH>""<DATA>" }}
下面从KV 存储中读取一个secret:
{{ with secret"secret/my-secret" }}{{ .Data.data.foo }}{{ end }}
如果只需要使用Vault Agent渲染模版,且不需要sink获取到的凭据,则可以忽略auto_auth
的sink
。
更新secrets
Vault Agent会自动更新secrets/tokens。
- 当一个secret或token更新之后,Vault Agent会在secret的租期过去2/3之后更新该secret。
- 如果一个secret或token没有被更新且没有租期,则Vault Agent默认每5分钟拉取一次secret,可以通过static_secret_render_interval进行配置。
- 如果一个secret或token没有被更新且有租期,则Vault Agent默认会在90%的TTL时拉取该secret,可以通过lease_renewal_threshold进行设置
用法
token_file
创建测试数据
$ tee data.json -<<EOF{"organization":"ACME Inc.","customer_id":"ABXX2398YZPIE7391","region":"US-West","zip_code":"94105","type":"premium","contact_email":"james@acme.com","status":"active"}EOF$ vault kv put secret/customers/acme @data.json
创建一个template:
$ tee customer.json.tmpl -<<EOF{ {{ with secret"secret/data/customers/acme" }}"Organization":"{{ .Data.data.organization }}","ID":"{{ .Data.data.customer_id }}","Contact":"{{ .Data.data.contact_email }}" {{ end }}}EOF
创建Vault Agent配置文件:
tee agent-config.hcl -<<EOFpid_file ="./pidfile"vault { address ="$VAULT_ADDR" tls_skip_verify = true}auto_auth { method { type ="token_file" config = { token_file_path ="$HOME/.vault-token" } } sink"file" { config = { path ="$HOME/vault-token-via-agent" } }}template { source ="$HOME/vault-test/customer.json.tmpl" destination ="$HOME/vault-test/customer.json"}EOF
允许Vault Agent,之后就可以在"$HOME/vault-test/customer.json"中看到渲染之后的内容:
$ vault agent -config=agent-config.hcl
可以通过
-config
指定多个配置文件,在运行时会组合成单个配置文件。
Kubernetes
Vault Agent auto auth
💁 注意kubernetes vault agent和kubernetes secret engines的不同之处。前者是在vault中创建绑定到kubernetes的serviceaccount的role,并赋予该role访问vault的某些数据的policy,vault agent就可以在
auto_auth
中使用该role来渲染模版。后者同样会在vault中创建绑定到kubernetes的serviceaccount的role,但会赋予该role操作Kubernetes资源的权限,后续就可以通过该role生成可以操作kubernetes资源的token。
创建Kubernetes的serviceaccount以及相关资源:
apiVersion: v1kind: ServiceAccountmetadata: name: vault-auth namespace: default---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: role-tokenreview-binding namespace: defaultroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegatorsubjects:- kind: ServiceAccount name: vault-auth namespace: default---apiVersion: v1kind: Secretmetadata: name: vault-auth-secret annotations: kubernetes.io/service-account.name: vault-authtype: kubernetes.io/service-account-token
生成demo数据,后续用于Vault Agent渲染模版:
$ vault policy write myapp-kv-ro - <<EOFpath"secret/data/myapp/*" { capabilities = ["read","list"]}EOF$ vault kv put secret/myapp/config \ username='appuser' \ password='suP3rsec(et!' \ ttl='30s'
创建kubernetes auth config。与kubernetes secret engine不同,这里用的是kubernetes auth method:
$ export SA_SECRET_NAME=$(kubectl get secrets --output=json \ | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')$ export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \ --output 'go-template={{ .data.token }}' | base64 --decode)$ export SA_CA_CRT=$(kubectl -n default get secret $SA_SECRET_NAME -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)$ export K8S_HOST=$(kubectl config view --raw --minify --flatten \ --output 'jsonpath={.clusters[].cluster.server}')
$ vault auth enable kubernetes$ vault write auth/kubernetes/config \ token_reviewer_jwt="$SA_JWT_TOKEN" \ kubernetes_host="$K8S_HOST" \ kubernetes_ca_cert="$SA_CA_CRT" \ issuer="https://kubernetes.default.svc.cluster.local"
创建一个role,对应kubernetes中的default
命名空间的serviceaccountvault-auth
,并赋予其访问demo数据的权限myapp-kv-ro
:
$ vault write auth/kubernetes/role/example \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=default \ token_policies=myapp-kv-ro \ ttl=24h
在使用该serviceaccount的pod中验证该role的权限是否正确:
$ KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)$ curl --request POST \ --data '{"jwt":"'"$KUBE_TOKEN"'","role":"example"}' \ $VAULT_ADDR/v1/auth/kubernetes/login | python3 -m json.tool
创建Vault Agent配置文件,其中用到了上面创建的role example
:
apiVersion: v1data: vault-agent-config.hcl: | # Comment this out if running as sidecar instead of initContainer exit_after_auth = true pid_file ="/home/vault/pidfile" auto_auth { method"kubernetes" { mount_path ="auth/kubernetes" #role的挂载路径 config = { role ="example" #role的名称 } } sink"file" { config = { path ="/home/vault/.vault-token" } } } template { destination ="/etc/secrets/index.html" contents = <<EOT <html> <body> <p>Some secrets:</p> {{- with secret"secret/data/myapp/config" }} <ul> <li><pre>username: {{ .Data.data.username }}</pre></li> <li><pre>password: {{ .Data.data.password }}</pre></li> </ul> {{ end }} </body> </html> EOT }kind: ConfigMapmetadata: name: example-vault-agent-config namespace: default
最后执行渲染,并通过卷共享给其他pod:
apiVersion: v1kind: Podmetadata: name: vault-agent-example namespace: defaultspec: serviceAccountName: vault-auth #注意使用与role绑定的serviceaccount volumes: - configMap: items: - key: vault-agent-config.hcl path: vault-agent-config.hcl name: example-vault-agent-config name: config - emptyDir: {} name: shared-data initContainers: #使用vault agent init container进行渲染 - args: - agent - -config=/etc/vault/vault-agent-config.hcl - -log-level=debug env: - name: VAULT_ADDR value: http://EXTERNAL_VAULT_ADDR:8200 image: vault name: vault-agent volumeMounts: - mountPath: /etc/vault name: config - mountPath: /etc/secrets name: shared-data containers: - image: nginx name: nginx-container ports: - containerPort: 80 volumeMounts: - mountPath: /usr/share/nginx/html name: shared-data
Vault Agent Injector(webhook)
这种方式是vault-k8s提供的一种kubernetes mutation webhook方式(上面的configmap是自己指定的initcongtainer),监控pod的CREATE
和UPDATE
事件,如果检测到此类事件,且annotation
中包含vault.hashicorp.com/agent-inject: true
annotation,则会据此变更pod规格。
它有两种方式init和sidecar,init容器会将secret生成到共享内存卷(默认为/vault/secrets
,可以通过vault.hashicorp.com/secret-volume-path
修改),而sidecar则会持续渲染secrets。
使用annotation渲染
与auto auth一样,需要创建一个与kubernetes serviceeaccount对应的role,以及访问策略,后续就可以在annotation vault.hashicorp.com/role
中使用该role:
$ vault write auth/kubernetes/role/internal-app \ bound_service_account_names=internal-app \ bound_service_account_namespaces=default \ policies=internal-app \ ttl=24h
annotation格式为:
vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret
下面例子中,第一个annotation 会被渲染到/vault/secrets/foo
,第二个会被渲染到/vault/secrets/bar
:
vault.hashicorp.com/agent-inject-secret-foo: database/roles/appvault.hashicorp.com/agent-inject-secret-bar: consul/creds/appvault.hashicorp.com/role: 'app'
secret模版格式如下:
vault.hashicorp.com/agent-inject-template-<unique-name>: | < TEMPLATE HERE >
举例如下:
vault.hashicorp.com/agent-inject-secret-foo: 'database/creds/db-app'vault.hashicorp.com/agent-inject-template-foo: | {{- with secret"database/creds/db-app" -}} postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable {{- end }}vault.hashicorp.com/role: 'app'
有用的annotations:
vault.hashicorp.com/role
: role名称,即vault中的auth/<kubernetes>/role/<role_name>
的role_name
vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret
:其中unique-name
是渲染的secret的文件名,/path/to/secret
指定了vault中用于渲染的secret的数据路径。vault.hashicorp.com/secret-volume-path: /apps/conf
:指定渲染文件(即上述的<unique-name>
)的挂载路径的方式。使用vault.hashicorp.com/secret-volume-path-SECRET-NAME
可以将文件和路径映射起来,如vault.hashicorp.com/secret-volume-path-foo
指定了渲染文件foo
的所在路径,如果不指定文件路径映射关系,则表示所有渲染文件的默认路径。vault.hashicorp.com/agent-inject-template-<unique-name>
:指定secret的渲染模板vault.hashicorp.com/agent-inject-status: update
:在injection结束后设置的状态值,vault.hashicorp.com/agent-pre-populate-only:"true"
:如果为true,则不会注入sidecar容器。推荐CronJob
或Job
使用
使用Vault agent configuration map
该方式需要通过vault.hashicorp.com/agent-configmap
指定Vault Agent configuration 文件,配置文件会被挂载到/vault/configs
,配置中需要包含如下文件:
- config-init.hcl:init容器使用,必须将
exit_after_auth
设置为true
. - config.hcl:sidecar容器使用,必须将
exit_after_auth
设置为false
.
---apiVersion: v1kind: ServiceAccountmetadata: name: app-example---apiVersion: apps/v1kind: Deploymentmetadata: name: app-example-deploymentspec: replicas: 1 selector: matchLabels: app: app-example template: metadata: labels: app: app-example annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/agent-configmap: 'my-configmap' vault.hashicorp.com/tls-secret: 'vault-tls-client' spec: containers: - name: app image: 'app:1.0.0' serviceAccountName: app-example---apiVersion: v1kind: ConfigMapmetadata: name: my-configmapdata: config.hcl: |"auto_auth" = {"method" = {"config" = {"role" ="db-app" }"type" ="kubernetes" }"sink" = {"config" = {"path" ="/home/vault/.token" }"type" ="file" } }"exit_after_auth" = false"pid_file" ="/home/vault/.pid""template" = {"contents" ="{{- with secret \"database/creds/db-app\" -}}postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable{{- end }}""destination" ="/vault/secrets/db-creds" }"vault" = {"address" ="https://vault.demo.svc.cluster.local:8200""ca_cert" ="/vault/tls/ca.crt""client_cert" ="/vault/tls/client.crt""client_key" ="/vault/tls/client.key" } config-init.hcl: |"auto_auth" = {"method" = {"config" = {"role" ="db-app" }"type" ="kubernetes" }"sink" = {"config" = {"path" ="/home/vault/.token" }"type" ="file" } }"exit_after_auth" = true"pid_file" ="/home/vault/.pid""template" = {"contents" ="{{- with secret \"database/creds/db-app\" -}}postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable{{- end }}""destination" ="/vault/secrets/db-creds" }"vault" = {"address" ="https://vault.demo.svc.cluster.local:8200""ca_cert" ="/vault/tls/ca.crt""client_cert" ="/vault/tls/client.crt""client_key" ="/vault/tls/client.key" }
官方给出了一些渲染deployment,configmap,将渲染内容注入环境变量等例子。
CLI
Token
capabilities
校验一个token访问某个路径的权限,如下面校验hvs.CAESI...WtiSW5mWUY
对cubbyhole/foo
的访问权限,返回deny
$ vault token capabilities hvs.CAESI...WtiSW5mWUY database/creds/readonly
create
将token绑定到多个策略:
$ vault token create -policy=my-policy -policy=other-policy
创建periodic token,renew的时候使用此period:
$ vault token create -period=30m
还可以通过-ttl
指定token的ttl,用于创建非periodic token。
renew
$ vault token renew 96ddf4bc-d217-f3ba-f9bd-017055595017
为token指定续订的时间,如果无-increment
参数,则使用默认TTL。periodic tokens忽略该参数
$ vault token renew -increment=30m 96ddf4bc-d217-f3ba-f9bd-017055595017
lookup
查看一个token的详细信息:
$ vault token lookup 96ddf4bc-d217-f3ba-f9bd-017055595017
查看一个token绑定的策略:
$ vault token lookup | grep policies
查看所有token
查看所有token的accessors
$ vault list auth/token/accessors
查看特定的accessor:
$ vault token lookup -format json -accessor <accessor>
过滤出root token:
$ vault list -format json auth/token/accessors | jq -r .[] | xargs -I '{}' vault token lookup -format json -accessor '{}' | jq -r 'select(.data.policies | any(. =="root"))'
policy
list
$ vault policy list
read
$ vault policy read my-policy
write
从本地/tmp/policy.hcl
加载策略:
$ vault policy write my-policy /tmp/policy.hcl
标准输入(stdio)加载策略:
$ cat my-policy.hcl | vault policy write my-policy -
delete
$ vault policy delete my-policy
Auth
用于与Vault的auth methods进行交互,如增删改查不同的auth methods。
enable
启用某个auth method
$ vault auth enable userpass
list
查看已启用的auth method
$ vault auth list -detailed
secrets
用于和secrets engines进行交互。
enable
在某个路径下启用一个secrets engine。
$ vault secrets enable awsSuccess! Enabled the aws secrets engine at: aws/
$ vault secrets enable -path=ssh-prod ssh
$ vault secrets enable -max-lease-ttl=30m database
list
查看启用的secrets engines
$ vault secrets list -detailed
kv
参见[KV secrets engine](#KV secrets engine)
metadata
添加metadata
$ vault kv metadata put -custom-metadata=hello="hellotest"
查询metadata
$ vault kv metadata get secret/hello
read
从特定路径读取数据,可以使用-field
指定读取的字段。
write
write
命令可以向指定路径写入数据(凭据、secrets、配置或任意数据)。使用key=value
格式指定数据,如果value前面包含@
,则说明要从文件加载数据,如果一个key的value是-
,则说明从标准输入读取数据。可以通过-field
指定打印的字段。
$ vault write cubbyhole/git-credentials username="student01" password="p@$$w0rd"
-force
允许不带数据的write
操作:
$ vault write -force transit/keys/my-key
$ echo $MY_TOKEN | vault write consul/config/access token=-
operator
Vault成员操作
$ vault operator raft list-peers
$ vault operator raft join [options] <leader-api-addr>#如 vault operator raft join"http://127.0.0.2:8200"
$ vault operator raft remove-peer <server_id>
snapshot
备份和恢复
Usage: vault operator raft snapshot <subcommand> [options] [args] This command groups subcommands for operators interacting with the snapshot functionality of the integrated Raft storage backend.Subcommands: restore Installs the provided snapshot, returning the cluster to the state defined in it save Saves a snapshot of the current state of the Raft cluster into a file
查看snapshot的信息
如keys数目、大小等
$ vault operator raft snapshot inspect <snapshot_file>
troubleshoot
审计日志
Vault的audit记录了所有客户端请求和服务端响应的详细信息。审计日志中的auth.policy_results
字段给出了请求授权结果:
字段名 | 含义 |
---|---|
type | 日志类型,这里是 request,表示是客户端发起的请求(另一种常见的是 response)。 |
time | 请求的时间戳,精确到纳秒(RFC3339 格式) |
auth | 授权信息,表示请求是通过哪个 token、哪些策略授权的。 |
request | 请求的详细信息,包括路径、操作类型、数据等。 |
如果一个client出现"permission denied"之类的错误,可以通过如下方式查看token对应的策略,找出client操作是否在策略要求之内。同时结合审计日志可以更加确定问题根因:
curl \ --silent \ --header"X-Vault-Token: $VAULT_TOKEN" \ $VAULT_ADDR/v1/auth/token/lookup-self \ | jq
启用审计设备
⚠️当启用多审计设备时,Vault会尝试将审计日志发送到所有审计设备,且只要有一个审计设备可用,Vault就可以正常处理请求。因此建议为Vault配置多个审计设备。
审计设备要求如下ACL策略:
# 'sudo' capability is required to manage audit devicespath"sys/audit/*"{ capabilities = ["create","read","update","delete","list","sudo"]}# To list enabled audit devices, 'sudo' capability is requiredpath"sys/audit"{ capabilities = ["read","sudo"]}
下面将审计信息写入了file
类型的设备中:
$ vault audit enable file file_path=/vault/vault-audit.log
审计日志中的token会被hash,如果要查看原始日志,可以启用log_raw=true
:
$ vault audit enable -path=file_raw file \ file_path=/vault/audit-law.log \ log_raw=true
在debug之后记得关闭原始审计功能:
$ vault audit disable file_raw
查看启用的审计设备:
$ vault audit list -detailed
查询审计日志
查看审计日志中的错误信息:
export AUDIT_LOG_FILE="$PWD/learn-vault-monitoring/vault-audit.log"
jq 'select(.error != null) | [.time,.error]' $AUDIT_LOG_FILE
敏感信息出错时会返回HMAC-SHA256 编码后的错误,使用如下方式查看哈希错误以及对应的时间戳:
jq 'select(.response.data.error != null) | [.time,.response.data.error]' \ $AUDIT_LOG_FILE
查看请求路径:
jq -n '[inputs | {Path: .request.path} ] | group_by(.Path) | map({Path: .[0].Path, Count: length}) | sort_by(-.Count) | limit(5;.[])' $AUDIT_LOG_FILE
统计错误次数:
jq -n '[inputs | {Errors: .error} ] | group_by(.Errors) | map({Errors: .[0].Errors, Count: length}) | sort_by(-.Count) | .[]' $AUDIT_LOG_FILE
查看client地址以及访问路径:
jq -s 'group_by(.request.remote_address) | map({"remote_address": .[0].request.remote_address,"access": (group_by(.request.path) | map({"key":.[0].request.path,"value":length}) | from_entries)})' $AUDIT_LOG_FILE
审计日志中的敏感信息是被HMAC过的,如root token的名称、accessor等。
可以使用如下方式计算字符串的HMAC值,然后在审计日志中查找:
$ vault write sys/audit-hash/file input="olAP0Oxb0rvUZAWkRRVcMtYl"Key Value--- -----hash hmac-sha256:d890b3417cef5aa22ee035a3ed685c78ad34f4939a53738c33f87f490a93838b
校验策略权限
有时候会碰到403 permission denied
错误,通常是由于策略权限不足导致的:
$ vault token create -policy=webappKey Value--- -----token s.IcTMGNOug5Cx3wBqpGvI5X4etoken_accessor s2FhMCQssibpiGeBzVWhxJmntoken_duration 768htoken_renewable truetoken_policies ["default""webapp"]identity_policies []policies ["default""webapp"]
可以使用vault token capabilities
命令测试该token是否有访问某个路径的权限:
$ vault token capabilities s.IcTMGNOug5Cx3wBqpGvI5X4e transit/decrypt/phone-number
vault debug
可以生成perf文件。
遥测指标
使用Prometheus metrics查看异常。
CPU指标
cpu.usage_user
cpu.usage_iowait
当cpu.iowait_cpu
大于10%时需要注意。
Network指标
net.bytes_recv
net.bytes_sent
Memory指标
mem.total
mem.used_percent
文件描述符指标
linux_sysctl_fs.file-nr
:主机上使用的文件句柄数linux_sysctl_fs.file-max
当file-nr
超过80%的file-max
时需要注意。
从lost quorum中恢复
vault要求有quorum个正常运行的vault server。从lost quorum中恢复的方法是将集群模式转变为单节点模式。
首先找到
vault-config.hcl
中定义的storage
:storage"raft" { path ="/vault/data" server_id ="vault_1"}....
在
/vault/data/raft
目录中创建一个peers.json
文件,包含可工作的节点信息:id
:server的service_idaddress
:server的地址和端口non_voter
:指定该server是否是non-voter角色
$ cat > /vault/data/raft/peers.json << EOF[ {"id":"vault_1","address":"10.0.101.22:8201","non_voter": false }]EOF
重启vault
Unseal Vault并查看vault状态
$ vault operator unseal$ vault status
校验结果
$ vault operator raft list-peers
如果后续集群恢复,则将其他节点添加到peers.json
文件中即可。
Recovery mode(恢复模式)
主要用于解决由于某些新的bug导致Vault无法启动的问题。
规格
性能测试
Seal/unseal
该功能需要非dev server。vault的配置和启动参见官方文档。在正式环境中,vault启动时会生成5个unseal key和1个root token。在下面最后一行中可以看到如果要unseal的话,需要3个unseal key。
Unseal Key 1: BvaQM7LygUstmn7N34C7KQ4GWKBi/om5v8vVsCTKek/lUnseal Key 2: Rx3xYfOuLJrRThLTbAauc7gn6E10w3I4BM6WqJ0lKNGOUnseal Key 3: JBYOsGqAbocP75by6MvE4Zyoodv5p33LzFmBMOSsWslcUnseal Key 4: f1CmSy/QzzsJWZAcpa+1bIapffldSJhwFVYwXa+KV0uoUnseal Key 5: L/cXSbU5v4UizDup0GP/EAu8jJ4ZdFfXU15HRssKlHtIInitial Root Token: hvs.eV6KNABVAdV5uSvsficEJpCxVault initialized with 5 key shares and a key threshold of 3...
这种情况下vault的数据是机密的,需要通过unseal过程来获得加密密钥,否则不能操作vault。unseal的过程如下,执行如下命令3次,输入3个不同的unseal key即可:
vault operator unseal
常用命令
vault secrets listvault kv list apps/#查看role和policyvault auth listvault list auth/dragon_kubernetes_drg1-prd-asf-quarantine/role/dragon-kubernetes-namespace-vault-rolevault policy list vault policy read app_ro_dragon_kubernetes_drg1-prd-asf-quarantinevault read auth/dragon_kubernetes_drg1-dev-central/role/dragon-loggingvault read auth/dragon_kubernetes_drg1-dev-central/config
修改vault的kv值:
vault kv get -format=json apps/dragon_prime_imageservice/credentialsvault kv put apps/dragon_prime_imageservice/credentials @/tmp/data.json