"The best secret is one that is never exposed — OpenBao keeps it that way."
在现代云原生架构中,密钥管理(Secrets Management) 是安全基础设施的核心。数据库密码、API Key、TLS 证书、SSH 密钥……这些敏感信息散落在环境变量、配置文件、CI/CD Pipeline 中,成为攻击者的首要目标。
OpenBao 是 HashiCorp Vault 的 开源分叉(Fork),由 Linux Foundation 旗下的 LF Edge 项目托管,采用 MPL-2.0 许可证,完全开源、社区驱动。它继承了 Vault 强大的密钥管理能力,同时摆脱了商业许可变更的风险,是生产环境中管理密钥的理想选择。
本文将从零开始,由浅入深地带你全面掌握 OpenBao 的核心概念、安装配置、实战操作,直到生产级部署与运维。
2023 年 8 月,HashiCorp 将 Vault 等核心产品从 MPL-2.0 许可证转为 BSL(Business Source License),限制竞争性使用。作为回应,社区发起了 OpenBao 项目,旨在维护一个完全开源的 Vault 替代品。
| 对比项 | HashiCorp Vault | OpenBao |
|---|
| 许可证 | BSL 1.1(限制竞争使用) | MPL-2.0(真正开源) |
| 治理 | HashiCorp 公司主导 | Linux Foundation / LF Edge 社区治理 |
| API 兼容性 | - | 与 Vault API 高度兼容 |
| CLI 命令 | vault | bao |
| 当前版本 | 1.x | 2.5.x |
| 定位 | 企业级密钥管理 | 社区驱动的开源密钥管理 |
OpenBao 提供以下核心能力:
在深入之前,先熟悉这些核心术语:
| 术语 | 说明 |
|---|
| Secret Engine | 存储、生成或加密数据的插件化组件,通过路径挂载(如 secret/、database/、pki/) |
| Auth Method | 身份认证方式,验证用户/应用身份后颁发 Token |
| Token | 认证成功后获得的凭证,携带策略信息,用于后续 API 调用 |
| Policy | ACL 策略,定义 Token 对特定路径的权限(read / create / update / delete / list) |
| Lease | 密钥的有效期,到期后自动撤销。可续租(renew)或主动撤销(revoke) |
| Seal / Unseal | OpenBao 的加密保护机制。启动时为 Sealed 状态,需提供 Unseal Key 才能解密数据 |
| Barrier | 加密屏障,所有写入存储的数据都经过 Barrier 加密 |
| Root Key | 加密 Keyring 的主密钥,由 Unseal Key 保护 |
| Integrated Storage | 内置的 Raft 共识存储后端,无需外部依赖 |
OpenBao 的架构可以分为以下几个层次:
OpenBao 的安全核心是多层加密架构。理解这个加密链非常重要:
- Unseal Key 不存储在任何地方 — 它由操作员持有(Shamir 分片分散在多人手中),或由外部 KMS/HSM 托管。
- Root Key 加密存储 — 即使攻击者获取了存储后端的全部数据,没有 Unseal Key 也无法解密。
- Encryption Key 支持轮换 — 可以在不停机的情况下定期轮换加密密钥,旧密钥保留在 Keyring 中用于解密历史数据。
当客户端发起一个读取密钥的请求时,OpenBao 内部的处理流程如下:
OpenBao 提供多种安装方式,以下按推荐顺序列出:
brew install openbao
bao version
pacman -Sy openbao
dnf install -y epel-release
dnf install -y openbao
wget https://github.com/openbao/openbao/releases/download/v2.5.0/bao_2.5.0_linux_amd64.deb
sudo dpkg -i bao_2.5.0_linux_amd64.deb
docker pull ghcr.io/openbao/openbao:latest
docker pull quay.io/openbao/openbao:latest
docker pull docker.io/openbao/openbao:latest
docker run --rm -p 8200:8200 \
--memory-swappiness=0 \
--name openbao-dev \
ghcr.io/openbao/openbao:latest server -dev
安装后建议进行以下加固措施:
- 禁用 Swap:防止密钥数据被写入持久化交换空间。Linux 上 systemd 服务文件默认设置
MemorySwapMax=0。
- Docker 启动时:添加
--memory-swappiness=0 标志。
- macOS:默认 Swap 已加密,无需额外处理。
mkdir -p $GOPATH/src/github.com/openbao && cd $_
git clone https://github.com/openbao/openbao.git
cd openbao
make bootstrap
make dev
Dev 模式是学习和测试的最佳起点。它以内存存储、自动 Unseal、预配置 Root Token 的方式启动,绝不能用于生产环境。
在另一个终端中配置环境变量并开始操作:
export BAO_ADDR='http://127.0.0.1:8200'
export BAO_TOKEN='hvs.xxxxxxxxxxxxxxxxxxxxxxxx'
bao status
KV(Key-Value)Secret Engine 是最基础的密钥存储引擎。Dev 模式下默认启用了 KV v2 引擎,挂载在 secret/ 路径。
bao kv put secret/myapp/config \
db_host="postgres.prod.internal" \
db_port="5432" \
db_user="app_user" \
db_password="S3cur3P@ssw0rd!"
bao kv get secret/myapp/config
bao kv get -field=db_password secret/myapp/config
bao kv get -format=json secret/myapp/config | jq '.data.data'
bao kv put secret/myapp/config \
db_host="postgres.prod.internal" \
db_port="5432" \
db_user="app_user" \
db_password="N3wS3cur3P@ss!"
bao kv get -version=1 secret/myapp/config
bao kv list secret/myapp/
bao kv delete secret/myapp/config
bao kv undelete -versions=2 secret/myapp/config
bao kv destroy -versions=1 secret/myapp/config
| 特性 | KV v1 | KV v2 |
|---|
| 版本控制 | ❌ 不支持 | ✅ 支持(每次写入创建新版本) |
| 软删除 | ❌ 不支持 | ✅ 支持(可恢复) |
| 元数据 | ❌ 不支持 | ✅ 支持(created_time、custom_metadata) |
| Check-and-Set | ❌ 不支持 | ✅ 支持(CAS,防止并发写入覆盖) |
| 路径前缀 | secret/<path> | secret/data/<path>(API 路径) |
| 推荐使用 | 简单场景 | 生产环境推荐 |
除了 CLI,OpenBao 的所有操作都可以通过 HTTP API 完成:
curl -s \
--header "X-Vault-Token: $BAO_TOKEN" \
--request POST \
--data '{
"data": {
"api_key": "sk-abc123def456",
"api_secret": "super-secret-value"
}
}' \
$BAO_ADDR/v1/secret/data/myapp/api-keys | jq
curl -s \
--header "X-Vault-Token: $BAO_TOKEN" \
$BAO_ADDR/v1/secret/data/myapp/api-keys | jq '.data.data'
curl -s $BAO_ADDR/v1/sys/health | jq
Secret Engine 是 OpenBao 的核心组件,它们可以存储、生成或加密数据。每个引擎通过路径挂载来隔离,这意味着挂载在 database/ 路径的引擎和挂载在 secret/ 路径的引擎互不影响。
| 引擎 | 类型 | 用途 | 典型场景 |
|---|
| KV | 存储 | 键值对存储 | 应用配置、API Key、凭证 |
| Database | 动态 | 动态生成数据库凭证 | MySQL、PostgreSQL、MongoDB 临时账号 |
| PKI | 动态 | 签发 X.509 证书 | 内部 TLS 证书、mTLS |
| Transit | 加密 | 加密即服务(EaaS) | 数据加密/解密/签名,无需管理密钥 |
| SSH | 动态 | SSH 密钥/证书签发 | 服务器 SSH 访问控制 |
| TOTP | 动态 | 生成/验证 TOTP 码 | 双因素认证 |
| Transform | 加密 | 数据转换(FPE/掩码) | 信用卡号、身份证号脱敏 |
动态密钥是 OpenBao 最强大的特性之一 — 按需生成有时效性的数据库凭证,到期自动撤销。
bao secrets enable database
bao write database/config/my-postgresql-db \
plugin_name="postgresql-database-plugin" \
allowed_roles="readonly,readwrite" \
connection_url="postgresql://{{username}}:{{password}}@postgres.internal:5432/myapp?sslmode=require" \
username="vault_admin" \
password="vault_admin_password"
bao write database/roles/readonly \
db_name="my-postgresql-db" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
bao write database/roles/readwrite \
db_name="my-postgresql-db" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
revocation_statements="DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="30m" \
max_ttl="2h"
bao read database/creds/readonly
bao lease renew database/creds/readonly/abcd1234...
bao lease revoke database/creds/readonly/abcd1234...
- 始终设置合理的 TTL —
default_ttl 建议 ≤ 1 小时,max_ttl 建议 ≤ 24 小时。
- 应用侧要处理凭证续租 — 在 Lease 到期前主动 renew,或使用 OpenBao Agent 自动续租。
- 轮换初始管理员密码 — 配置完数据库连接后,执行
bao write -force database/rotate-root/my-postgresql-db 轮换管理员密码,阻止人工直接登录数据库。
PKI 引擎可以让 OpenBao 成为你的内部 CA(Certificate Authority),自动签发和管理 TLS 证书。
bao secrets enable pki
bao secrets tune -max-lease-ttl=87600h pki
bao write -field=certificate pki/root/generate/internal \
common_name="My Organization Root CA" \
issuer_name="root-2026" \
ttl=87600h > root_ca.crt
bao secrets enable -path=pki_int pki
bao secrets tune -max-lease-ttl=43800h pki_int
bao write -field=csr pki_int/intermediate/generate/internal \
common_name="My Organization Intermediate CA" \
issuer_name="intermediate-2026" > pki_intermediate.csr
bao write -field=certificate pki/root/sign-intermediate \
csr=@pki_intermediate.csr \
format=pem_bundle \
ttl=43800h > intermediate_ca.crt
bao write pki_int/intermediate/set-signed \
certificate=@intermediate_ca.crt
bao write pki_int/roles/internal-service \
allowed_domains="internal.mycompany.com,svc.cluster.local" \
allow_subdomains=true \
max_ttl="720h" \
key_type="ec" \
key_bits=256
bao write pki_int/issue/internal-service \
common_name="api.internal.mycompany.com" \
ttl="72h"
Transit 引擎提供加密即服务,应用不需要管理加密密钥,只需调用 API 即可完成加密/解密/签名操作。数据不会存储在 OpenBao 中,Transit 只负责加密操作。
bao secrets enable transit
bao write -f transit/keys/my-app-key
bao write transit/encrypt/my-app-key \
plaintext=$(echo -n "my-secret-data" | base64)
bao write transit/decrypt/my-app-key \
ciphertext="vault:v1:xxxxxxxxxxxxxxxxxxxxxx"
bao write -f transit/keys/my-app-key/rotate
bao write transit/rewrap/my-app-key \
ciphertext="vault:v1:xxxxxxxxxxxxxxxxxxxxxx"
OpenBao 支持多种认证方式,以适应不同场景:
| Auth Method | 适用对象 | 说明 |
|---|
| Token | 所有场景 | 核心认证方式,所有其他方式最终都返回 Token |
| UserPass | 人类用户 | 用户名/密码认证,适合开发和测试 |
| AppRole | 应用/服务 | 基于 Role ID + Secret ID 的双因素认证 |
| Kubernetes | K8s Pod | 使用 Service Account Token 自动认证 |
| LDAP | 企业用户 | 集成企业目录服务 |
| JWT/OIDC | SSO 用户 | 集成 Okta、Auth0、Google 等身份提供商 |
| TLS Certificates | mTLS 场景 | 使用客户端证书认证 |
AppRole 是应用和服务最推荐的认证方式。它采用双因素模型:Role ID(公开部分) + Secret ID(敏感部分)。
bao auth enable approle
bao write auth/approle/role/my-app \
token_policies="my-app-policy" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=10m \
secret_id_num_uses=1
bao read auth/approle/role/my-app/role-id
bao write -f auth/approle/role/my-app/secret-id
bao write auth/approle/login \
role_id="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" \
secret_id="yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
BAO_TOKEN=hvs.CAESIXXXXXXXXXXXXXXXXXX \
bao kv get secret/myapp/config
在 Kubernetes 环境中,Pod 可以使用 Service Account Token 直接向 OpenBao 认证:
bao auth enable kubernetes
bao write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443"
bao write auth/kubernetes/role/my-app \
bound_service_account_names=my-app-sa \
bound_service_account_namespaces=production \
policies=my-app-policy \
ttl=1h
OpenBao 中有两种 Token 类型:
| 类型 | 说明 | 适用场景 |
|---|
| Service Token | 可续租、可撤销、有持久化存储的 Token | 长期运行的服务 |
| Batch Token | 轻量级、不可续租、无持久化的 Token | 高吞吐量的短期操作 |
Token 还有层级概念:
Root Token
├── Service Token A (policy: admin)
│ ├── Service Token A1 (policy: readonly)
│ └── Service Token A2 (policy: readwrite)
└── Service Token B (policy: app)
└── Batch Token B1 (policy: app)
- Root Token 拥有一切权限,相当于 Linux 的 root 用户。
- 生产环境中,初始化完成后应立即撤销 Root Token:
bao token revoke <root-token>
- 需要时可通过
bao operator generate-root 临时生成新的 Root Token(需要多个 Unseal Key 持有者共同授权)。
策略定义了 Token 对特定路径的访问权限。OpenBao 采用白名单模式 — 未显式授予的权限一律拒绝。
# ================================================
# 文件:my-app-policy.hcl
# 描述:应用 my-app 的 ACL 策略
# ================================================
# 允许读取和列出应用配置
path "secret/data/myapp/*" {
capabilities = ["read", "list"]
}
# 允许读取和更新特定路径
path "secret/data/myapp/config" {
capabilities = ["create", "read", "update"]
}
# 允许获取数据库动态凭证
path "database/creds/readonly" {
capabilities = ["read"]
}
# 允许续租和撤销自己的 Lease
path "sys/leases/renew" {
capabilities = ["update"]
}
path "sys/leases/revoke" {
capabilities = ["update"]
}
# 允许查看自己的 Token 信息
path "auth/token/lookup-self" {
capabilities = ["read"]
}
# 允许续租自己的 Token
path "auth/token/renew-self" {
capabilities = ["update"]
}
# 拒绝访问元数据(使用 deny 覆盖其他策略)
path "secret/metadata/*" {
capabilities = ["deny"]
}
| Capability | 说明 | 对应 HTTP 方法 |
|---|
create | 创建新数据 | POST |
read | 读取数据 | GET |
update | 更新已有数据 | POST/PUT |
delete | 删除数据 | DELETE |
list | 列出路径下的键 | LIST |
sudo | 允许访问需要 root 权限的路径 | - |
deny | 显式拒绝(最高优先级) | - |
bao policy write my-app-policy my-app-policy.hcl
bao policy list
bao policy read my-app-policy
bao policy delete my-app-policy
bao token create -policy="my-app-policy" -ttl=2h
OpenBao 支持在策略中使用模板变量,实现基于身份的动态权限:
# 每个用户只能访问自己的密钥空间
path "secret/data/users/{{identity.entity.name}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# 每个团队只能管理自己的密钥
path "secret/data/teams/{{identity.groups.names.*.id}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
OpenBao 默认使用 Shamir's Secret Sharing 算法将 Unseal Key 分割为多个分片。只需要一定数量(阈值)的分片即可重建 Unseal Key。
bao operator init -key-shares=5 -key-threshold=3
bao operator unseal
bao operator unseal
bao operator unseal
| 场景 | 分片数 | 阈值 | 说明 |
|---|
| 开发/测试 | 1 | 1 | 单人即可 Unseal |
| 小团队 | 3 | 2 | 至少 2 人同时在场 |
| 生产环境 | 5 | 3 | 容忍 2 个分片丢失,需要 3 人同时授权 |
| 企业级 | 7 | 4 | 高安全性要求 |
Shamir 分片的手动 Unseal 过程增加了运维复杂度(每次重启都需要收集足够的 Key)。Auto Unseal 将 Unseal Key 的保管委托给外部的 KMS 或 HSM,实现自动解封。
支持的 Auto Unseal Provider:
| Provider | 配置块名称 | 说明 |
|---|
| AWS KMS | seal "awskms" | AWS Key Management Service |
| GCP Cloud KMS | seal "gcpckms" | Google Cloud KMS |
| Azure Key Vault | seal "azurekeyvault" | Azure 密钥保管库 |
| AliCloud KMS | seal "alicloudkms" | 阿里云密钥管理服务 |
| OCI KMS | seal "ocikms" | Oracle Cloud KMS |
| HSM (PKCS#11) | seal "pkcs11" | 硬件安全模块 |
| Transit | seal "transit" | 使用另一个 OpenBao 实例 |
AWS KMS 配置示例:
seal "awskms" {
region = "ap-southeast-1"
kms_key_id = "arn:aws:kms:ap-southeast-1:123456789:key/abcd-1234-..."
# 可选:指定 AWS 凭证(推荐使用 IAM Role)
# access_key = "..."
# secret_key = "..."
}
使用 Auto Unseal 会创建 OpenBao 对外部 KMS 的生命周期依赖:
- 如果 KMS 密钥不可用(如 AWS 服务故障),OpenBao 无法启动。
- 如果 KMS 密钥被永久删除,OpenBao 数据将永远无法恢复,即使从备份恢复也不行。
- 建议:使用 AWS SCP(Service Control Policies)等机制保护 KMS 密钥不被删除。
在不同 Seal 类型之间迁移(如从 Shamir 迁移到 Auto Unseal)是可能的,但需要停机操作:
以下是一个完整的生产级 OpenBao 配置文件,使用 Integrated Storage(Raft):
# ================================================
# /etc/openbao/config.hcl - 生产级配置
# ================================================
# --- 集群标识 ---
cluster_name = "prod-openbao-cluster"
# --- 网络地址 ---
# API 地址:客户端访问地址
api_addr = "https://openbao.mycompany.com:8200"
# 集群通信地址:节点间通信
cluster_addr = "https://10.0.1.10:8201"
# --- Web UI ---
ui = true
# --- 日志 ---
log_level = "info"
log_format = "json"
log_file = "/var/log/openbao/openbao.log"
log_rotate_duration = "24h"
log_rotate_max_files = 30
# --- Listener (TLS) ---
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
# TLS 配置(生产环境必须启用)
tls_cert_file = "/etc/openbao/tls/server.crt"
tls_key_file = "/etc/openbao/tls/server.key"
tls_client_ca_file = "/etc/openbao/tls/ca.crt"
# TLS 最低版本
tls_min_version = "tls12"
# 禁用弱密码套件
tls_cipher_suites = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
# 请求限制
max_request_duration = "90s"
max_request_size = 33554432 # 32MB
}
# --- Storage (Raft / Integrated Storage) ---
storage "raft" {
path = "/opt/openbao/data"
node_id = "node-1"
# 性能调优
performance_multiplier = 1 # 生产环境设为 1(低延迟调优)
# Autopilot 配置(自动管理集群成员)
autopilot {
cleanup_dead_servers = true
dead_server_last_contact_threshold = "24h"
min_quorum = 3
}
# 集群对端节点
retry_join {
leader_api_addr = "https://10.0.1.11:8200"
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
retry_join {
leader_api_addr = "https://10.0.1.12:8200"
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
}
# --- Auto Unseal (可选,推荐) ---
seal "awskms" {
region = "ap-southeast-1"
kms_key_id = "arn:aws:kms:ap-southeast-1:123456789:key/abcd-1234-..."
}
# --- Telemetry (可观测性)---
telemetry {
# Prometheus 指标端点
prometheus_retention_time = "24h"
disable_hostname = true
# StatsD(可选)
# statsd_address = "statsd.internal:8125"
}
# --- Lease 管理 ---
default_lease_ttl = "768h" # 默认 32 天
max_lease_ttl = "768h" # 最大 32 天
# --- 审计 (在初始化后通过 API 启用) ---
# bao audit enable file file_path=/var/log/openbao/audit.log
# /etc/systemd/system/openbao.service
[Unit]
Description=OpenBao - A tool for managing secrets
Documentation=https://openbao.org/docs/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/openbao/config.hcl
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
Type=notify
User=openbao
Group=openbao
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/bin/bao server -config=/etc/openbao/config.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
LimitNOFILE=65536
LimitMEMLOCK=infinity
MemorySwapMax=0
[Install]
WantedBy=multi-user.target
sudo mkdir -p /opt/openbao/data
sudo chown openbao:openbao /opt/openbao/data
sudo mkdir -p /etc/openbao/tls
sudo cp server.crt server.key ca.crt /etc/openbao/tls/
sudo chown openbao:openbao /etc/openbao/tls/*
sudo chmod 600 /etc/openbao/tls/server.key
sudo systemctl enable openbao
sudo systemctl start openbao
export BAO_ADDR='https://openbao.mycompany.com:8200'
export BAO_CACERT='/etc/openbao/tls/ca.crt'
bao operator init \
-key-shares=5 \
-key-threshold=3
bao operator unseal
bao operator unseal
bao operator unseal
bao status
bao login $ROOT_TOKEN
bao audit enable file file_path=/var/log/openbao/audit.log
bao token revoke $ROOT_TOKEN
OpenBao 推荐使用 Integrated Storage(基于 Raft 共识算法) 作为存储后端,它无需外部依赖(如 Consul),且内置 HA 能力。
Node 1(Leader 候选):
storage "raft" {
path = "/opt/openbao/data"
node_id = "node-1"
retry_join {
leader_api_addr = "https://10.0.1.11:8200"
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
retry_join {
leader_api_addr = "https://10.0.1.12:8200"
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
}
Node 2 & 3(类似配置,修改 node_id 和 retry_join 节点列表):
storage "raft" {
path = "/opt/openbao/data"
node_id = "node-2" # 或 node-3
retry_join {
leader_api_addr = "https://10.0.1.10:8200"
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
retry_join {
leader_api_addr = "https://10.0.1.12:8200" # 或 10.0.1.11
leader_ca_cert_file = "/etc/openbao/tls/ca.crt"
}
}
sudo systemctl start openbao
bao operator unseal
bao operator unseal
bao operator unseal
bao operator raft list-peers
bao status
bao operator step-down
bao operator raft snapshot save backup-$(date +%Y%m%d).snap
bao operator raft snapshot restore backup-20260409.snap
bao operator raft remove-peer node-3
- 节点数量:生产环境建议 3 或 5 个节点(奇数节点,满足 Raft 多数派要求)。
- 网络延迟:同 Region 部署,节点间延迟应 < 10ms。
- 负载均衡:所有节点都可以处理读请求;写请求会被自动转发到 Leader。
- 备份策略:每日自动执行
raft snapshot save,保存到安全的外部存储。
- 监控:关注
vault.raft.leader.lastContact 和 vault.raft.commitTime 指标。
审计日志是安全合规的基础。OpenBao 支持三种审计设备:
bao audit enable file file_path=/var/log/openbao/audit.log
bao audit enable syslog tag="openbao" facility="AUTH"
bao audit enable socket address="logserver.internal:9090" socket_type="tcp"
bao audit list -detailed
每条审计记录包含完整的请求和响应信息(敏感数据会被 HMAC 哈希处理):
{
"time": "2026-04-09T11:00:00.000000Z",
"type": "response",
"auth": {
"client_token": "hmac-sha256:abcdef...",
"accessor": "hmac-sha256:123456...",
"display_name": "approle",
"policies": ["default", "my-app-policy"],
"token_type": "service"
},
"request": {
"id": "request-uuid",
"operation": "read",
"path": "secret/data/myapp/config",
"remote_address": "10.0.2.50"
},
"response": {
"data": {
"data": {
"db_host": "hmac-sha256:xxxxxx...",
"db_password": "hmac-sha256:yyyyyy..."
}
}
}
}
- 必须至少启用一个审计设备。如果所有审计设备都失败,OpenBao 将拒绝处理任何请求,以确保不会出现未记录审计的操作。
- 审计日志中的敏感数据使用 HMAC-SHA256 哈希,无法逆向还原。但如果你知道原始值,可以通过
bao audit hash 命令计算哈希值进行比对。
- 建议将审计日志实时转发到集中式日志系统(如 ELK、Loki、Splunk)。
OpenBao 官方提供了 Helm Chart,可以方便地在 Kubernetes 上部署 HA 集群:
helm repo add openbao https://openbao.github.io/openbao-helm
helm repo update
cat <<EOF > openbao-values.yaml
global:
enabled: true
tlsDisable: false
server:
image:
repository: ghcr.io/openbao/openbao
tag: "2.5.0"
# HA 模式(3 副本)
ha:
enabled: true
replicas: 3
raft:
enabled: true
config: |
ui = true
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/vault/tls/tls.crt"
tls_key_file = "/vault/tls/tls.key"
}
storage "raft" {
path = "/vault/data"
retry_join {
leader_api_addr = "https://openbao-0.openbao-internal:8200"
leader_ca_cert_file = "/vault/tls/ca.crt"
}
retry_join {
leader_api_addr = "https://openbao-1.openbao-internal:8200"
leader_ca_cert_file = "/vault/tls/ca.crt"
}
retry_join {
leader_api_addr = "https://openbao-2.openbao-internal:8200"
leader_ca_cert_file = "/vault/tls/ca.crt"
}
}
service_registration "kubernetes" {}
# 持久化存储
dataStorage:
enabled: true
size: 10Gi
storageClass: "gp3"
# 资源限制
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 2000m
memory: 1Gi
# 审计日志存储
auditStorage:
enabled: true
size: 5Gi
# Agent Injector(自动注入 sidecar)
injector:
enabled: true
replicas: 2
resources:
requests:
cpu: 100m
memory: 64Mi
# Web UI
ui:
enabled: true
serviceType: ClusterIP
EOF
helm install openbao openbao/openbao \
-n openbao --create-namespace \
-f openbao-values.yaml
OpenBao Agent Injector 是一个 Kubernetes Mutating Webhook,它通过注解(Annotations)为 Pod 自动注入 Sidecar,从 OpenBao 获取密钥并写入文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "my-app"
vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/readonly"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "database/creds/readonly" -}}
DB_HOST=postgres.internal
DB_PORT=5432
DB_USER={{ .Data.username }}
DB_PASSWORD={{ .Data.password }}
{{- end }}
vault.hashicorp.com/agent-inject-secret-app-config: "secret/data/myapp/config"
vault.hashicorp.com/agent-inject-template-app-config: |
{{- with secret "secret/data/myapp/config" -}}
API_KEY={{ .Data.data.api_key }}
API_SECRET={{ .Data.data.api_secret }}
{{- end }}
spec:
serviceAccountName: my-app-sa
containers:
- name: my-app
image: my-app:latest
command: ["/bin/sh", "-c"]
args:
- |
source /vault/secrets/db-creds
source /vault/secrets/app-config
exec ./my-app-binary
ports:
- containerPort: 8080
密钥将被注入到 Pod 的 /vault/secrets/ 目录下:
/vault/secrets/
├── db-creds # 数据库凭证
└── app-config # 应用配置
除了 Agent Injector,还可以使用 CSI(Container Storage Interface)Provider 将密钥作为卷挂载:
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app-sa
containers:
- name: my-app
image: my-app:latest
volumeMounts:
- name: secrets
mountPath: "/mnt/secrets"
readOnly: true
volumes:
- name: secrets
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "openbao-secrets"
---
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
name: openbao-secrets
spec:
provider: vault
parameters:
roleName: "my-app"
vaultAddress: "https://openbao.openbao.svc:8200"
objects: |
- objectName: "db-password"
secretPath: "secret/data/myapp/config"
secretKey: "db_password"
OpenBao Agent 是一个客户端守护进程,提供以下能力:
- Auto Auth:自动认证并获取 Token
- Caching:本地缓存 Token 和 Lease,减少对 Server 的请求
- Templating:将密钥渲染到配置文件模板中
- API Proxy:代理 API 请求,自动注入认证信息
# /etc/openbao/agent.hcl
# 如何认证
auto_auth {
method "approle" {
config = {
role_id_file_path = "/etc/openbao/role-id"
secret_id_file_path = "/etc/openbao/secret-id"
remove_secret_id_file_after_reading = true
}
}
# 将 Token 保存到文件
sink "file" {
config = {
path = "/tmp/openbao-token"
mode = 0640
}
}
}
# 缓存配置
cache {
use_auto_auth_token = true
}
# API 代理
listener "tcp" {
address = "127.0.0.1:8100"
tls_disable = true
}
# 服务器连接
vault {
address = "https://openbao.mycompany.com:8200"
tls_ca_cert = "/etc/openbao/tls/ca.crt"
}
# 模板渲染
template {
source = "/etc/openbao/templates/db-config.tpl"
destination = "/app/config/database.env"
perms = 0640
command = "systemctl reload my-app" # 渲染后执行的命令
}
模板文件示例 (db-config.tpl):
{{ with secret "database/creds/readonly" }}
DB_USER={{ .Data.username }}
DB_PASSWORD={{ .Data.password }}
{{ end }}
{{ with secret "secret/data/myapp/config" }}
API_KEY={{ .Data.data.api_key }}
{{ end }}
bao agent -config=/etc/openbao/agent.hcl
OpenBao Proxy 与 Agent 类似,但专注于API 代理功能,不包含模板渲染。适合作为应用的 Sidecar 使用。
| 指标 | 说明 | 告警阈值 |
|---|
vault.core.handle_request | 请求处理延迟 | P99 > 500ms |
vault.barrier.get | Barrier 读取延迟 | P99 > 100ms |
vault.barrier.put | Barrier 写入延迟 | P99 > 200ms |
vault.raft.leader.lastContact | Follower 到 Leader 的最后联系时间 | > 500ms |
vault.raft.commitTime | Raft 提交时间 | P99 > 50ms |
vault.expire.num_leases | 当前活跃 Lease 数 | 根据容量规划 |
vault.runtime.alloc_bytes | 内存分配量 | 接近 limits |
vault.core.unsealed | 是否已 Unseal | = 0 表示 Sealed |
scrape_configs:
- job_name: "openbao"
metrics_path: "/v1/sys/metrics"
params:
format: ["prometheus"]
scheme: "https"
tls_config:
ca_file: "/etc/prometheus/openbao-ca.crt"
authorization:
credentials_file: "/etc/prometheus/openbao-token"
static_configs:
- targets:
- "openbao-0.internal:8200"
- "openbao-1.internal:8200"
- "openbao-2.internal:8200"
BACKUP_DIR="/opt/openbao/backups"
DATE=$(date +%Y%m%d-%H%M%S)
RETENTION_DAYS=30
bao operator raft snapshot save "${BACKUP_DIR}/openbao-${DATE}.snap"
aws s3 cp "${BACKUP_DIR}/openbao-${DATE}.snap" \
"s3://my-backups/openbao/openbao-${DATE}.snap" \
--sse aws:kms
find "${BACKUP_DIR}" -name "*.snap" -mtime +${RETENTION_DAYS} -delete
echo "[$(date)] Backup completed: openbao-${DATE}.snap"
echo "0 3 * * * /opt/openbao/scripts/backup.sh >> /var/log/openbao/backup.log 2>&1" \
| crontab -
bao operator rotate
bao read sys/key-status
| 方式 | 复杂度 | 适用场景 | 优缺点 |
|---|
| 直接 API | 高 | 需要精细控制的应用 | 灵活但需自行管理 Token 生命周期 |
| Agent | 中 | 虚拟机/裸金属部署 | 自动认证+缓存+模板,推荐 |
| K8s Injector | 低 | Kubernetes 环境 | 注解驱动,零代码侵入 |
| CSI Provider | 低 | Kubernetes 环境 | 标准 Volume 接口 |
package main
import (
"context"
"fmt"
"log"
"time"
vault "github.com/openbao/openbao/api/v2"
)
func main() {
config := vault.DefaultConfig()
config.Address = "https://openbao.mycompany.com:8200"
client, err := vault.NewClient(config)
if err != nil {
log.Fatalf("unable to initialize OpenBao client: %v", err)
}
resp, err := client.Logical().Write("auth/approle/login", map[string]interface{}{
"role_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"secret_id": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
})
if err != nil {
log.Fatalf("unable to login: %v", err)
}
client.SetToken(resp.Auth.ClientToken)
secret, err := client.KVv2("secret").Get(context.Background(), "myapp/config")
if err != nil {
log.Fatalf("unable to read secret: %v", err)
}
dbHost := secret.Data["db_host"].(string)
dbPassword := secret.Data["db_password"].(string)
fmt.Printf("DB Host: %s\n", dbHost)
fmt.Printf("DB Password: %s\n", dbPassword)
go func() {
for {
time.Sleep(30 * time.Minute)
_, err := client.Auth().Token().RenewSelf(3600)
if err != nil {
log.Printf("token renewal failed: %v", err)
}
}
}()
select {}
}
import hvac
import os
import time
import threading
class SecretsManager:
"""OpenBao 密钥管理封装"""
def __init__(self):
self.client = hvac.Client(
url=os.environ.get('BAO_ADDR', 'https://openbao.mycompany.com:8200'),
verify='/etc/openbao/tls/ca.crt'
)
self._login()
self._start_renewal()
def _login(self):
"""使用 AppRole 登录"""
result = self.client.auth.approle.login(
role_id=os.environ['BAO_ROLE_ID'],
secret_id=os.environ['BAO_SECRET_ID']
)
self.client.token = result['auth']['client_token']
self.lease_duration = result['auth']['lease_duration']
def get_secret(self, path: str, key: str = None) -> dict | str:
"""读取 KV v2 密钥"""
response = self.client.secrets.kv.v2.read_secret_version(
path=path,
mount_point='secret'
)
data = response['data']['data']
return data[key] if key else data
def get_db_credentials(self, role: str = 'readonly') -> dict:
"""获取动态数据库凭证"""
response = self.client.read(f'database/creds/{role}')
return {
'username': response['data']['username'],
'password': response['data']['password'],
'lease_id': response['lease_id'],
'lease_duration': response['lease_duration']
}
def _start_renewal(self):
"""后台自动续租 Token"""
def renew():
while True:
time.sleep(self.lease_duration * 0.7)
try:
self.client.auth.token.renew_self()
except Exception as e:
print(f"Token renewal failed: {e}")
self._login()
thread = threading.Thread(target=renew, daemon=True)
thread.start()
secrets = SecretsManager()
db_password = secrets.get_secret('myapp/config', 'db_password')
db_creds = secrets.get_db_credentials('readonly')
print(f"Dynamic DB User: {db_creds['username']}")
在将 OpenBao 部署到生产环境之前,请逐一确认以下事项:
本文从零开始,全面深入地解析了 OpenBao 的各个方面:
-
架构理解 — 掌握了 Barrier 加密屏障、多层密钥保护、请求处理流程等核心设计。
-
安装上手 — 从 Dev 模式体验到各平台安装方式,快速建立第一个实操经验。
-
Secret Engines — 深入学习了 KV v2(静态密钥)、Database(动态凭证)、PKI(证书管理)、Transit(加密即服务)等核心引擎。
-
认证授权 — 掌握了 AppRole、Kubernetes Auth、Token 体系和 ACL Policy 的设计与实践。
-
Seal/Unseal — 理解了 Shamir 分片与 Auto Unseal 的工作原理及迁移方案。
-
生产部署 — 完成了 TLS、Raft HA 集群、systemd 服务、审计日志的完整配置。
-
Kubernetes 集成 — 掌握了 Helm 部署、Agent Injector、CSI Provider 的使用方式。
-
应用集成 — 通过 Go 和 Python 示例,展示了应用如何安全地获取和续租密钥。
OpenBao 作为 HashiCorp Vault 的开源替代品,在保持 API 兼容性的同时,完全由社区驱动,是云原生时代密钥管理的理想选择。
本文内容基于 OpenBao 官方文档校验,以下为核心参考链接: