Implements HashiCorp Vault dynamic secrets for database credentials, AWS IAM keys, and PKI certificates with automatic generation, lease management, and rotation to eliminate static secrets. Useful for configuring Vault engines, dynamic DB creds, ephemeral cloud creds, or auto rotation.
npx claudepluginhub killvxk/cybersecurity-skills-zhThis skill uses the workspace's default tool permissions.
- 应用程序使用存储在配置文件或环境变量中的静态数据库凭据
Implements HashiCorp Vault dynamic secrets engines for database credentials, AWS IAM keys, and PKI certificates with generation, lease management, and rotation.
Implements HashiCorp Vault dynamic secrets engines for database credentials, AWS IAM keys, and PKI certificates with generation, lease management, and rotation to replace static secrets. Activates for Vault config, dynamic DB creds, ephemeral AWS creds, secret rotation.
Deploys HashiCorp Vault for centralized secrets management in cloud environments with dynamic database/cloud credentials, Transit Encryption, PKI certs, and Kubernetes integration. Replaces hard-coded secrets in apps and CI/CD.
Share bugs, ideas, or general feedback.
不适用于存储无法动态生成的静态密钥(改用 Vault 的 KV 密钥引擎);动态密钥适用于可以在目标系统上以编程方式创建和撤销的凭据。
使用生产级配置初始化 Vault:
# vault-config.hcl - 生产 Vault 服务器配置
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-1"
retry_join {
leader_api_addr = "https://vault-2.corp.local:8200"
}
retry_join {
leader_api_addr = "https://vault-3.corp.local:8200"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault-cert.pem"
tls_key_file = "/opt/vault/tls/vault-key.pem"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "alias/vault-unseal-key"
}
api_addr = "https://vault-1.corp.local:8200"
cluster_addr = "https://vault-1.corp.local:8201"
telemetry {
prometheus_retention_time = "24h"
disable_hostname = true
}
ui = true
# 初始化 Vault 集群
vault operator init -key-shares=5 -key-threshold=3
# 启用审计日志记录
vault audit enable file file_path=/var/log/vault/audit.log
# 为应用程序启用 AppRole 认证
vault auth enable approle
# 为数据库密钥使用方创建策略
vault policy write db-consumer - <<EOF
# 允许读取动态数据库凭据
path "database/creds/app-readonly" {
capabilities = ["read"]
}
path "database/creds/app-readwrite" {
capabilities = ["read"]
}
# 允许续约和撤销自身的租约
path "sys/leases/renew" {
capabilities = ["update"]
}
path "sys/leases/revoke" {
capabilities = ["update"]
}
# 允许读取自身的令牌信息
path "auth/token/lookup-self" {
capabilities = ["read"]
}
EOF
# 为应用程序创建 AppRole
vault write auth/approle/role/webapp \
token_policies="db-consumer" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=720h \
secret_id_num_uses=0
为 PostgreSQL 和 MySQL 设置动态凭据生成:
# 启用数据库密钥引擎
vault secrets enable database
# 配置 PostgreSQL 连接
vault write database/config/production-postgres \
plugin_name=postgresql-database-plugin \
allowed_roles="app-readonly,app-readwrite,app-admin" \
connection_url="postgresql://{{username}}:{{password}}@db-primary.corp.local:5432/appdb?sslmode=verify-full" \
username="vault_admin" \
password="$VAULT_DB_PASSWORD" \
password_authentication="scram-sha-256"
# 轮换根凭据,使 Vault 独家管理它们
vault write -force database/rotate-root/production-postgres
# 创建只读角色(TTL:1 小时,最大 24 小时)
vault write database/roles/app-readonly \
db_name=production-postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\"; \
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO \"{{name}}\";" \
revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \
DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# 创建读写角色(TTL:30 分钟,最大 8 小时)
vault write database/roles/app-readwrite \
db_name=production-postgres \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\"; \
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO \"{{name}}\";" \
revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \
DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="30m" \
max_ttl="8h"
# 配置 MySQL 连接
vault write database/config/production-mysql \
plugin_name=mysql-database-plugin \
allowed_roles="mysql-readonly,mysql-readwrite" \
connection_url="{{username}}:{{password}}@tcp(mysql-primary.corp.local:3306)/" \
username="vault_admin" \
password="$VAULT_MYSQL_PASSWORD"
vault write database/roles/mysql-readonly \
db_name=production-mysql \
creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; \
GRANT SELECT ON appdb.* TO '{{name}}'@'%';" \
revocation_statements="DROP USER IF EXISTS '{{name}}'@'%';" \
default_ttl="1h" \
max_ttl="24h"
# 测试动态凭据生成
echo "测试 PostgreSQL 动态凭据:"
vault read database/creds/app-readonly
# 返回:username=v-approle-app-read-xxxxx, password=<随机>, lease_id=database/creds/app-readonly/xxxxx
生成短暂的 AWS IAM 凭据:
# 启用 AWS 密钥引擎
vault secrets enable aws
# 使用根凭据配置 AWS 密钥引擎
vault write aws/config/root \
access_key="$AWS_ACCESS_KEY_ID" \
secret_key="$AWS_SECRET_ACCESS_KEY" \
region="us-east-1"
# 配置租约设置
vault write aws/config/lease \
lease="30m" \
lease_max="1h"
# 创建 S3 只读访问的 IAM 用户角色
vault write aws/roles/s3-readonly \
credential_type=iam_user \
policy_document=-<<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::app-data-bucket",
"arn:aws:s3:::app-data-bucket/*"
]
}
]
}
EOF
# 创建 EC2 管理的承担角色(优于 IAM 用户)
vault write aws/roles/ec2-admin \
credential_type=assumed_role \
role_arns="arn:aws:iam::123456789012:role/VaultEC2AdminRole" \
default_sts_ttl="30m" \
max_sts_ttl="1h"
# 创建跨账户访问的联合令牌角色
vault write aws/roles/cross-account-readonly \
credential_type=federation_token \
policy_document=-<<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": "arn:aws:iam::987654321098:role/CrossAccountReadOnly"
}
]
}
EOF
# 测试 AWS 动态凭据
echo "测试 AWS STS 凭据:"
vault read aws/creds/ec2-admin
# 返回:access_key, secret_key, security_token,TTL 为 30 分钟
按需生成短期 TLS 证书:
# 为根 CA 启用 PKI 密钥引擎
vault secrets enable -path=pki pki
vault secrets tune -max-lease-ttl=87600h pki
# 生成根 CA 证书
vault write pki/root/generate/internal \
common_name="Corp Internal Root CA" \
ttl=87600h \
key_type=ec \
key_bits=384
# 为中间 CA 启用 PKI
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int
# 生成中间 CA CSR
vault write pki_int/intermediate/generate/internal \
common_name="Corp Intermediate CA" \
key_type=ec \
key_bits=256
# 用根 CA 签署中间 CA
vault write pki/root/sign-intermediate \
csr=@intermediate.csr \
format=pem_bundle \
ttl=43800h
# 配置颁发 URL
vault write pki_int/config/urls \
issuing_certificates="https://vault.corp.local:8200/v1/pki_int/ca" \
crl_distribution_points="https://vault.corp.local:8200/v1/pki_int/crl"
# 为 Web 服务器证书创建角色(TTL:30 天)
vault write pki_int/roles/web-server \
allowed_domains="corp.local,internal.corp.com" \
allow_subdomains=true \
max_ttl=720h \
key_type=ec \
key_bits=256 \
require_cn=true \
enforce_hostnames=true
# 为服务网格证书创建角色(TTL:24 小时)
vault write pki_int/roles/service-mesh \
allowed_domains="service.consul" \
allow_subdomains=true \
max_ttl=24h \
key_type=ec \
key_bits=256 \
allow_ip_sans=true \
server_flag=true \
client_flag=true
# 颁发证书
vault write pki_int/issue/web-server \
common_name="api.corp.local" \
alt_names="api.internal.corp.com" \
ttl=720h
配置应用程序使用动态密钥:
"""
与 HashiCorp Vault 集成用于动态数据库凭据的应用程序示例。
使用带有自动租约续约的 hvac Python 客户端。
"""
import hvac
import threading
import time
import logging
class VaultDynamicCredentialManager:
def __init__(self, vault_addr, role_id, secret_id):
self.client = hvac.Client(url=vault_addr)
self.role_id = role_id
self.secret_id = secret_id
self.logger = logging.getLogger("vault_credentials")
self._current_creds = None
self._lease_id = None
self._renewal_thread = None
self._stop_event = threading.Event()
def authenticate(self):
"""使用 AppRole 向 Vault 进行认证。"""
response = self.client.auth.approle.login(
role_id=self.role_id,
secret_id=self.secret_id
)
self.client.token = response["auth"]["client_token"]
self.logger.info("已通过 AppRole 向 Vault 认证")
def get_database_credentials(self, role="app-readonly"):
"""从 Vault 请求动态数据库凭据。"""
self.authenticate()
response = self.client.secrets.database.generate_credentials(
name=role
)
self._current_creds = {
"username": response["data"]["username"],
"password": response["data"]["password"],
}
self._lease_id = response["lease_id"]
lease_duration = response["lease_duration"]
self.logger.info(
f"已获取动态凭据:user={self._current_creds['username']}, "
f"lease={self._lease_id}, ttl={lease_duration}s"
)
# 启动后台租约续约
self._start_renewal(lease_duration)
return self._current_creds
def _start_renewal(self, lease_duration):
"""启动后台线程在到期前续约租约。"""
if self._renewal_thread and self._renewal_thread.is_alive():
self._stop_event.set()
self._renewal_thread.join()
self._stop_event.clear()
renewal_interval = lease_duration * 0.7 # 在 TTL 的 70% 时续约
def renew_loop():
while not self._stop_event.wait(renewal_interval):
try:
self.client.sys.renew_lease(
lease_id=self._lease_id,
increment=lease_duration
)
self.logger.info(f"已续约租约:{self._lease_id}")
except hvac.exceptions.InvalidRequest:
self.logger.warning("租约已过期,获取新凭据")
self.get_database_credentials()
break
except Exception as e:
self.logger.error(f"租约续约失败:{e}")
self._renewal_thread = threading.Thread(target=renew_loop, daemon=True)
self._renewal_thread.start()
def revoke_credentials(self):
"""显式撤销当前动态凭据。"""
if self._lease_id:
self._stop_event.set()
self.client.sys.revoke_lease(self._lease_id)
self.logger.info(f"已撤销租约:{self._lease_id}")
self._current_creds = None
self._lease_id = None
def get_aws_credentials(self, role="s3-readonly"):
"""从 Vault 请求动态 AWS 凭据。"""
self.authenticate()
response = self.client.secrets.aws.generate_credentials(
name=role
)
return {
"access_key": response["data"]["access_key"],
"secret_key": response["data"]["secret_key"],
"security_token": response["data"].get("security_token"),
"lease_id": response["lease_id"],
"ttl": response["lease_duration"]
}
# 使用示例
vault_mgr = VaultDynamicCredentialManager(
vault_addr="https://vault.corp.local:8200",
role_id="<approle-role-id>",
secret_id="<approle-secret-id>"
)
db_creds = vault_mgr.get_database_credentials("app-readonly")
# 使用 db_creds["username"] 和 db_creds["password"] 进行数据库连接
跟踪动态密钥使用情况和租约生命周期:
# 监控活跃租约
vault list sys/leases/lookup/database/creds/app-readonly
vault list sys/leases/lookup/aws/creds/s3-readonly
# 检查租约详情
vault write sys/leases/lookup lease_id="database/creds/app-readonly/abcd1234"
# 撤销特定路径的所有租约(紧急凭据轮换)
vault lease revoke -prefix database/creds/app-readonly
# 用于监控的 Vault 指标(Prometheus 格式)
# 需要监控的关键指标:
# vault.expire.num_leases - 总活跃租约数
# vault.expire.revoke - 每秒租约撤销次数
# vault.secret.kv.count - 存储的密钥总数
# vault.runtime.alloc_bytes - 内存分配
# 配置 Vault 审计日志分析
cat > vault_audit_monitor.sh << 'SCRIPT'
#!/bin/bash
# 监控 Vault 审计日志中的可疑活动
AUDIT_LOG="/var/log/vault/audit.log"
# 统计每小时的凭据请求
echo "=== 动态凭据请求(最近 1 小时)==="
jq -r 'select(.type == "response" and .request.path | startswith("database/creds/")) |
"\(.time) \(.request.path) \(.auth.display_name)"' \
"$AUDIT_LOG" | tail -100
# 检测异常凭据请求模式
echo ""
echo "=== 高频凭据使用者 ==="
jq -r 'select(.type == "request" and .request.path | startswith("database/creds/")) |
.auth.display_name' \
"$AUDIT_LOG" | sort | uniq -c | sort -rn | head -10
# 检查认证失败尝试
echo ""
echo "=== 认证失败尝试 ==="
jq -r 'select(.type == "response" and .error != null and
.request.path | startswith("auth/")) |
"\(.time) \(.request.path) \(.error)"' \
"$AUDIT_LOG" | tail -20
SCRIPT
chmod +x vault_audit_monitor.sh
| 术语 | 定义 |
|---|---|
| 动态密钥(Dynamic Secrets) | Vault 按需生成的带有自动过期功能的凭据,确保每个消费者获得唯一的短期凭据 |
| 租约(Lease) | Vault 保证凭据有效的时限协议;消费者必须在到期前续约或请求新凭据 |
| 密钥引擎(Secrets Engine) | 生成、存储或加密数据的 Vault 插件;数据库、AWS、PKI 和 KV 是常见的引擎 |
| AppRole | 为机器间认证设计的 Vault 认证方法,使用角色 ID 和密钥 ID 对 |
| 根凭据轮换(Root Credential Rotation) | 让 Vault 独家接管用于创建动态密钥的管理员凭据的过程,消除人对根密码的知晓 |
| 租约撤销(Lease Revocation) | 立即使动态凭据失效,在事件响应期间用于撤销被入侵路径的所有凭据 |
背景:50 个微服务共享 3 个静态 PostgreSQL 凭据,这些凭据存储在 Kubernetes 部署的环境变量中。凭据泄露需要同时轮换所有 50 个服务。
处理方式:
注意事项:
HASHICORP VAULT 动态密钥报告
=========================================
Vault 版本: 1.16.2 Enterprise
集群状态: HA 活跃(3 个节点)
封印类型: AWS KMS(自动解封)
密钥引擎
database/: PostgreSQL, MySQL(2 个连接)
aws/: IAM 用户、承担角色、联合令牌
pki_int/: 内部 CA(EC P-256)
动态凭据指标(最近 24 小时)
生成的总凭据: 4,287
数据库(PostgreSQL):2,891
数据库(MySQL): 543
AWS STS: 612
PKI 证书: 241
活跃租约
总活跃: 387
database/creds/app-readonly:198
database/creds/app-readwrite:89
aws/creds/s3-readonly: 67
pki_int/issue/web-server: 33
租约生命周期
平均 TTL: 45 分钟
续约(24 小时): 12,847
撤销(24 小时): 3,901
过期(未续约): 12
安全
认证失败尝试(24 小时):3
根凭据已轮换: 是(所有数据库)
审计日志记录: 已启用(文件 + syslog)
策略违规(24 小时): 7(权限拒绝)