vault初始化生成的5个解封密钥须由至少3个不同角色分持并协同输入,不可合并存储;根令牌需立即替换为短期令牌,kv-v2引擎必须按业务域显式指定-path隔离路径,动态凭证应通过database引擎而非kv存储。

vault operator init 生成的5个密钥必须分存,别全塞进一个KMS条目里
云服务器集群启动Vault时,vault operator init 输出的5个解封密钥(unseal keys)和1个根令牌(root token)是集群可用的前提。但很多人直接把5个密钥拼成一行存进KMS或写进配置中心,这等于把保险箱钥匙贴在锁芯上。
真实生产环境要求:5个密钥至少由3个不同角色分别保管,比如运维A持#1#3、安全工程师持#2#5、DBA持#4。每次解封需3人协同输入——这是Vault Raft模式下法定人数(quorum)的硬性逻辑,不是可选项。
常见错误现象:vault operator unseal 执行后返回 Seal status: sealed 且无报错,其实是某位成员输错了密钥(Base64末尾的=被截断、空格粘连),但Vault不会提示“第几把密钥错”,只会沉默失败。
- 用
vault operator raft list-peers确认节点是否已加入集群,再执行解封;单节点误配成集群模式会导致此问题 - 密钥字符串必须原样复制,包括末尾换行符——建议用
printf "%s" "$KEY" | vault operator unseal避免shell截断 - 根令牌有效期默认为0(永不过期),但首次使用后应立即用
vault token create -policy=default -ttl=1h生成短期令牌供日常操作
kv-v2 引擎挂载路径必须按业务域隔离,别用默认 kv/
直接运行 vault secrets enable kv-v2 会挂载到 kv/ 路径,看似省事,但在多团队共用集群时,权限策略会迅速失控。比如支付服务读取 kv/payment/db,用户服务却能通过 list kv/ 看到所有前缀,违反最小权限原则。
正确做法是按业务线或环境显式指定 -path:
vault secrets enable \ -path=kv-payment \ -description="Payment service secrets" \ kv-v2
这样后续策略就能精确控制:
path "kv-payment/data/*" {
capabilities = ["read", "list"]
}
- 避免在路径中使用下划线(如
kv_payment),Vault内部会将其转义为kv%5Fpayment,导致API调用失败 -
kv-v2的data/是强制前缀,写入时路径为kv-payment/data/db-conn,读取时也必须带data/ - 如果已有服务依赖旧路径,用
vault secrets tune -default-lease-ttl=30m kv/临时延长租约,而非强行迁移
集群节点间 api_addr 和 cluster_addr 必须用内网地址,别填localhost或0.0.0.0
Vault集群通信依赖两个地址:对外提供API的 api_addr(给客户端调用),和节点间同步状态的 cluster_addr(Raft协议用)。很多人在 vault.hcl 里写 api_addr = "http://0.0.0.0:8200",结果Kubernetes里Pod互相无法发现。
真实配置必须区分网络平面:
-
api_addr应设为负载均衡器域名(如https://vault.internal.company.com)或节点公网IP(仅限跳板机直连场景) -
cluster_addr必须是节点内网IP+端口(如https://10.100.2.15:8201),且所有节点的防火墙要放行该端口 - 若用Consul做存储后端,
storage "consul"块里的address也必须填Consul服务的内网地址,不能填localhost
验证方法:在节点A上执行 curl -k https://10.100.2.15:8201/v1/status,应返回JSON状态;若超时,说明内网路由或安全组未通。
动态凭证必须走 database 引擎,别把数据库密码硬编码进 kv
集群环境下最危险的操作之一,是把MySQL root密码存在 kv-production/data/db 里,然后让所有应用共享这个静态密钥。一旦泄露,就得全量轮换——而Vault的真正价值在于动态凭证。
正确流程是启用 database 引擎并配置MySQL插件:
vault secrets enable -path=db-mysql database
vault write db-mysql/config/mysql \
plugin_name=mysql-database-plugin \
connection_url="{{username}}:{{password}}@tcp(10.100.3.22:3306)/" \
allowed_roles="readonly,admin"
然后定义角色:
vault write db-mysql/roles/readonly \
sql="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; GRANT SELECT ON *.* TO '{{name}}'@'%';" \
default_ttl="1h" \
max_ttl="24h"
- 应用每次启动时调用
vault read db-mysql/creds/readonly获取临时账号,用完即焚 - MySQL服务端必须开启
validate_password插件,否则Vault生成的强密码会被拒绝 - 不要用
mysql-database-plugin的root_credentials_rotate自动轮换,它只改Vault里存的root密码,不通知MySQL——得配合mysqladmin手动同步
动态凭证的租约到期后,Vault会自动回收账号权限,但MySQL进程里可能还缓存着旧连接。所以应用层必须实现连接池自动重连,不能指望Vault替你管连接生命周期。











