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=30m
Key Value
--- -----
token hvs.qp5hXcAcllG6ytHtZXEBxIeB
token_accessor eYQSFsK1pIuhgrG1zkNxj2Tj
token_duration 30m
token_renewable true
token_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 engine
path "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 engine
path "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 engine
path "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 engine
path "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 engine
path "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 kubernetes
Success! 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-id
Key Value
--- -----
role_id 675a50e7-cfe0-be76-e35f-49ec009731ea
生成secretID:
$ vault write -force auth/approle/role/jenkins/secret-id
Key Value
--- -----
secret_id ed0a642f-2acf-c2da-232f-1b21300d5f29
secret_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.ncEw5bAZJqvGJgl8pBDM0C5h
token_accessor gIQFfVhUd8fDsZjC7gLBMnQu
token_duration 1h
token_renewable true
token_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.254602Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
db_name users
password passw0rd
username admin
受策略限制,如果执行删除命令,则返回403:
$ VAULT_TOKEN=$APP_TOKEN vault kv delete secret/mysql/webapp
Error deleting secret/mysql/webapp: Error making API request.
URL: DELETE http://127.0.0.1:8200/v1/secret/data/mysql/webapp
Code: 403. Errors:
* 1 error occurred:
* permission denied
封装SecretID
SecretID类似密码,为了防止明文传递,可以对SecretID进行封装:
$ vault write -wrap-ttl=60s -force auth/approle/role/jenkins/secret-id
Key Value
--- -----
wrapping_token: s.yzbznr9NlZNzsgEtz3SI56pX
wrapping_accessor: Smi4CO0Sdhn8FJvL8XvOT30y
wrapping_token_ttl: 1m
wrapping_token_creation_time: 2021-06-07 20:02:01.019838 -0700 PDT
wrapping_token_creation_path: auth/approle/role/jenkins/secret-id
解封装的SecretID:
$ VAULT_TOKEN="s.yzbznr9NlZNzsgEtz3SI56pX" vault unwrap
Key Value
--- -----
secret_id c4086c73-4569-90c9-fd73-72c879e3b7b4
secret_id_accessor 3a2e9483-a7d2-dc19-7480-b1a025daeccc
secret_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/secret
Key Value
--- -----
accessor kv_0b42315d
config map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]
deprecation_status supported
description key/value secret storage
external_entropy_access false
local false
options map[version:2]
plugin_version n/a
running_plugin_version v0.21.0+builtin
running_sha256 n/a
seal_wrap false
type kv
uuid 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=s3cr3t
Success! 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/kv2
Key Value
--- -----
accessor kv_825ea02f
config map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]
deprecation_status supported
description n/a
external_entropy_access false
local false
options map[version:2]
plugin_version n/a
running_plugin_version v0.21.0+builtin
running_sha256 n/a
seal_wrap false
type kv
uuid 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.673767Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 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.830407Z
deletion_time 2021-10-31T00:16:25.860618Z
destroyed false
====== Version 5 ======
Key Value
--- -----
created_time 2021-10-31T00:15:01.892226Z
deletion_time 2021-10-31T00:16:25.860619Z
destroyed 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: v1
kind: ServiceAccount
metadata:
name: test-service-account-with-generated-token
namespace: test
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: test-role-list-pods
namespace: test
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list"]
- apiGroups: [""]
resources: ["serviceaccounts", "serviceaccounts/token"]
verbs: ["create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: test-role-abilities
namespace: test
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: test-role-list-pods
subjects:
- kind: ServiceAccount
name: test-service-account-with-generated-token
namespace: test
Kubernetes 1.24+还需要创建secret:
apiVersion: v1
kind: Secret
metadata:
name: vault-auth-secret
namespace: test
annotations:
kubernetes.io/service-account.name: test-service-account-with-generated-token
type: 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=test
Key Value
--- -----
lease_id kubernetes/creds/my-role/TrHUCplToMe5kv8E77IG45Wr
lease_duration 16h40m
lease_renewable false
service_account_name test-service-account-with-generated-token
service_account_namespace test
service_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=20m
Key Value
–-- -----
lease_id kubernetes/creds/my-role/31d771a6-...
lease_duration 20m0s
lease_renwable false
service_account_name new-service-account-with-generated-token
service_account_namespace test
service_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/ca X509v3 CRL Distribution Points: Full Name: URI:http://vault.example.com:8200/v1/pki/crl X509v3 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) |
创建一个role example-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/issuers Key Value --- ----- default 8ceb6b59-7042-25e3-da6f-f06b745773ab default_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 -<<EOF
pid_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: v1
kind: ServiceAccount
metadata:
name: vault-auth
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: role-tokenreview-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: vault-auth-secret
annotations:
kubernetes.io/service-account.name: vault-auth
type: kubernetes.io/service-account-token
生成demo数据,后续用于Vault Agent渲染模版:
$ vault policy write myapp-kv-ro - <<EOF
path "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
命名空间的serviceaccount vault-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: v1
data:
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: ConfigMap
metadata:
name: example-vault-agent-config
namespace: default
最后执行渲染,并通过卷共享给其他pod:
apiVersion: v1
kind: Pod
metadata:
name: vault-agent-example
namespace: default
spec:
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/app
vault.hashicorp.com/agent-inject-secret-bar: consul/creds/app
vault.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: v1
kind: ServiceAccount
metadata:
name: app-example
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-example-deployment
spec:
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: v1
kind: ConfigMap
metadata:
name: my-configmap
data:
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 aws
Success! 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 devices
path "sys/audit/*"
{
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# To list enabled audit devices, 'sudo' capability is required
path "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=webapp
Key Value
--- -----
token s.IcTMGNOug5Cx3wBqpGvI5X4e
token_accessor s2FhMCQssibpiGeBzVWhxJmn
token_duration 768h
token_renewable true
token_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_id -
address
: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/l
Unseal Key 2: Rx3xYfOuLJrRThLTbAauc7gn6E10w3I4BM6WqJ0lKNGO
Unseal Key 3: JBYOsGqAbocP75by6MvE4Zyoodv5p33LzFmBMOSsWslc
Unseal Key 4: f1CmSy/QzzsJWZAcpa+1bIapffldSJhwFVYwXa+KV0uo
Unseal Key 5: L/cXSbU5v4UizDup0GP/EAu8jJ4ZdFfXU15HRssKlHtI
Initial Root Token: hvs.eV6KNABVAdV5uSvsficEJpCx
Vault initialized with 5 key shares and a key threshold of 3...
这种情况下vault的数据是机密的,需要通过unseal过程来获得加密密钥,否则不能操作vault。unseal的过程如下,执行如下命令3次,输入3个不同的unseal key即可:
vault operator unseal
常用命令
vault secrets list
vault kv list apps/
#查看role和policy
vault auth list
vault list auth/dragon_kubernetes_drg1-prd-asf-quarantine/role/dragon-kubernetes-namespace-vault-role
vault policy list
vault policy read app_ro_dragon_kubernetes_drg1-prd-asf-quarantine
vault read auth/dragon_kubernetes_drg1-dev-central/role/dragon-logging
vault read auth/dragon_kubernetes_drg1-dev-central/config
修改vault的kv值:
vault kv get -format=json apps/dragon_prime_imageservice/credentials
vault kv put apps/dragon_prime_imageservice/credentials @/tmp/data.json