La interfaz está localizada; las descripciones y documentos de las Skills se traducen automáticamente con IA a su idioma. El código, los comandos, rutas y términos técnicos se mantienen en el original, y prevalece el documento del autor.
Explorar Skills
Productividad · SKILL.md

implementing-hashicorp-vault-dynamic-secrets

implementing-hashicorp-vault-dynamic-secrets

为数据库凭据、AWS IAM 密钥和 PKI 证书实施 HashiCorp Vault 动态密钥引擎, 支持自动生成、租约管理和凭据轮换, 以消除应用程序配置中的静态密钥。 适用于 Vault 密钥引擎配置、动态数据库凭据、 短暂云凭据或自动密钥轮换相关请求。

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentación de la Skill

GitHub

name: implementing-hashicorp-vault-dynamic-secrets description: > 为数据库凭据、AWS IAM 密钥和 PKI 证书实施 HashiCorp Vault 动态密钥引擎, 支持自动生成、租约管理和凭据轮换, 以消除应用程序配置中的静态密钥。 适用于 Vault 密钥引擎配置、动态数据库凭据、 短暂云凭据或自动密钥轮换相关请求。 domain: cybersecurity subdomain: identity-access-management tags: [HashiCorp-Vault, dynamic-secrets, secrets-management, database-credentials, AWS-secrets, PKI] version: "1.0" author: mahipal license: Apache-2.0

实施 HashiCorp Vault 动态密钥

适用场景

  • 应用程序使用存储在配置文件或环境变量中的静态数据库凭据
  • AWS IAM 访问密钥是长期有效的并在服务间共享
  • 需要通过生成短期按需密钥来消除凭据蔓延
  • 合规要求强制凭据轮换(PCI-DSS 要求 8、NIST 800-53 IA-5)
  • 实施零信任密钥管理,凭据不会静态存储
  • 从手动凭据管理迁移到自动化密钥生命周期

不适用于存储无法动态生成的静态密钥(改用 Vault 的 KV 密钥引擎);动态密钥适用于可以在目标系统上以编程方式创建和撤销的凭据。

前置条件

  • HashiCorp Vault 1.15+(社区版或企业版)
  • Vault 服务器已初始化并解封,并配置了自动解封(AWS KMS、Azure Key Vault 或 Transit)
  • 目标数据库系统具有管理员凭据,供 Vault 创建/撤销动态账户
  • AWS IAM 账户具有创建/删除 IAM 用户和访问密钥的权限
  • 从 Vault 到所有目标系统的网络连接
  • 已为使用应用程序配置了 Vault 策略和认证方法

工作流程

步骤 1:部署和配置 Vault 服务器

使用生产级配置初始化 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

步骤 2:配置数据库密钥引擎

为 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

步骤 3:配置 AWS 密钥引擎

生成短暂的 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 分钟

步骤 4:配置 PKI 密钥引擎以动态颁发证书

按需生成短期 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

步骤 5:将应用程序与 Vault 集成

配置应用程序使用动态密钥:

"""
与 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"] 进行数据库连接

步骤 6:监控 Vault 操作和租约管理

跟踪动态密钥使用情况和租约生命周期:

# 监控活跃租约
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)立即使动态凭据失效,在事件响应期间用于撤销被入侵路径的所有凭据

工具与系统

  • HashiCorp Vault:提供动态密钥、加密即服务和基于身份的访问控制的密钥管理平台
  • Vault Agent:处理 Vault 认证、令牌续约和应用程序密钥缓存的 Sidecar 进程
  • Vault Secrets Operator:将 Vault 密钥同步到 Kubernetes Secrets 以供 Pod 使用的 Kubernetes Operator
  • hvac:用于 HashiCorp Vault API 操作的 Python 客户端库

常见场景

场景:消除微服务中的静态数据库凭据

背景:50 个微服务共享 3 个静态 PostgreSQL 凭据,这些凭据存储在 Kubernetes 部署的环境变量中。凭据泄露需要同时轮换所有 50 个服务。

处理方式:

  1. 在 Kubernetes 中使用 Raft 存储部署 3 节点 HA 集群的 Vault
  2. 使用管理员凭据配置数据库密钥引擎的 PostgreSQL 连接
  3. 使用最小权限 SQL 授权为每个服务创建 Vault 角色
  4. 部署 Vault Secrets Operator 将动态凭据注入 Pod 环境变量
  5. 更新应用程序连接逻辑以通过租约续约处理凭据轮换
  6. 轮换 Vault 根凭据以删除人对管理员密码的知晓
  7. 监控租约生命周期并为续约失败设置告警

注意事项:

  • 不在应用程序连接池中处理凭据轮换(使用过期凭据的连接会失败)
  • 将 TTL 设置得太短会对数据库造成过多的凭据生成负载
  • 不配置适当的撤销语句会导致租约过期后遗留孤儿数据库用户
  • 在没有 HA 的情况下运行 Vault 会为所有应用程序认证创建单点故障

输出格式

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(权限拒绝)
Skills guardadas