基于HashiCorp Vault的K8S外部密钥管理实践

537 阅读15分钟

引言 (Introduction)

随着云计算和容器化技术的快速发展,企业在构建现代化应用时对安全性和合规性的要求日益提高。然而,在容器化环境中,敏感数据(如 API 密钥、数据库凭据、TLS 证书)的安全管理成为保障系统安全的关键挑战。

 

HashiCorp Vault 作为业界领先的开源秘密管理工具,提供了集中化、动态化和高安全性的解决方案,能够有效应对这些挑战。其通过动态秘密生成、细粒度访问控制和加密即服务等功能,为 TKE 环境中的外部密钥管理提供了灵活且强大的支持。

 

本文将结合 Vault 的核心架构与 TKE 的容器化特性,介绍具体的集成方法、配置步骤和最佳实践,旨在帮助企业构建安全、高效的云原生密钥管理体系,满足合规性要求并提升 DevOps 效率。

 

Vault 介绍

What is Vault ?

Vault 提供集中化的Secret管理、数据加密和访问控制,解决传统硬编码凭据或分散管理带来的安全风险。其主要功能包括:

 

● Secret管理:安全存储和管理敏感数据,如 API 密钥、数据库密码、证书等,支持动态生成和定期轮换。

● 数据加密:通过加密即服务(Encryption as a Service)保护数据,支持静态数据加密和传输中数据加密。

● 身份认证与访问控制:基于角色和策略的访问控制(RBAC),支持多种身份认证方式(如 LDAP、Kubernetes、JWT 等)。

● 动态Secret

● :为数据库、云服务等生成临时凭据,降低泄露风险。例如,Vault 可以为数据库生成短生命周期的用户名和密码。

● 证书管理:自动生成、轮换和管理 TLS 证书,支持与公共或私有 CA 集成。

● 加密密钥管理:管理加密密钥的生命周期,支持密钥轮换和版本控制。

 

Why Vault ?

Kubernetes 原生提供了 Secret 对象来管理敏感信息。然而,原生 Secret 存在一些局限性:

 

● 静态存储: Secret 通常以 Base64 编码存储在 etcd(K8s 的数据存储)中。虽然可以加密 etcd,但这仍然是集群管理员需要额外关注的一个静态存储点。

● 权限管理粒度:RBAC 控制对 Secret 的访问,但配置相对复杂,且 Secret 一旦被 Pod 挂载,其内容对整个 Pod 内的所有容器都是可见的。

● 缺乏与外部密钥管理系统的原生集成: 企业通常使用专门的密钥管理系统(如 HashiCorp Vault、AWS Secrets Manager、Azure Key Vault、Google Secret Manager)来集中、安全地管理密钥。原生 Secret 需要手动或通过外部工具同步这些密钥。

● 密钥轮换: 当外部密钥管理系统中的密钥更新后,需要手动更新 K8s Secret 并重启相关 Pod 才能生效。

 

 

Vault 架构

 

 

Vault 的架构由以下关键组件构成:

 

● 核心 (Core) :负责 Vault 的核心逻辑,包括请求路由、认证、授权、审计和密钥管理。请求处理:解析客户端请求,调用认证方法、秘密引擎和策略检查。

● 存储后端 (Storage Backend):Vault 不直接存储数据,而是依赖外部存储后端保存加密的秘密和元数据。数据以加密形式存储,Vault 使用 Master Key(主密钥)进行加密/解密。

● 秘密引擎 (Secrets Engines):模块化组件,负责生成、管理和提供特定类型的秘密。每个秘密引擎独立配置,可按需启用,支持动态秘密生成以降低泄露风险。

● 认证方法 (Auth Methods):用于验证客户端身份,生成访问 Vault 的令牌(Token)。认证后,Vault 分配令牌,关联特定策略以控制访问权限。

● 策略 (Policies):定义客户端的访问权限,使用 HCL(HashiCorp Configuration Language)编写。基于角色的访问控制(RBAC),支持细粒度权限管理。

 

Vault 后端存储

Overview

特性RaftConsulS3/GCS/AzurePostgreSQL
存储类型本地(BoltDB)分布式键值存储云对象存储关系型数据库
HA 支持是(3-5 节点)是(云厂商托管)是(视数据库配置)
运维复杂性低(内置)高(需 Consul 集群)低(云厂商托管)高(需数据库集群)
性能高(本地存储)中(网络依赖)中(网络依赖)中(事务开销)
跨集群适用性高(只需网络访问 Vault)高(需网络访问 Consul)中(云厂商绑定)中(需数据库集群)
成本低(仅磁盘)中(Consul 集群资源)高(云厂商收费)中(数据库资源)

 

Integrated Storage (Raft)

Integrated Storage (Raft) 是 Vault 的内置存储后端,官方推荐。基于 Raft 协议实现的一种分布式一致性存储后端,适合中小型生产环境。它通过 Raft 协议和 BoltDB 提供加密存储、高可用性和高性能,无需外部依赖,简化部署和运维。

 

Integrated Storage 的优势:

 

● 架构简单:无需外部存储服务,Vault 内置 Raft 逻辑,部署和维护简单。

● 高可用:支持多节点集群(建议 3-5 节点),自动故障转移,提供强一致性读写,适合生产环境。

● 加密存储:所有数据在写入 BoltDB 前加密,使用 Vault 的加密密钥。

● 快照与备份:支持定期快照和手动备份,方便数据迁移和灾难恢复。

● 性能优化:本地存储(BoltDB)减少网络延迟,适合高性能场景。

● 动态扩展:支持动态添加或移除节点,新节点自动同步数据,无需手动干预。

 

配置样例

disable_mlock = true
ui = true
cluster_name = "vault-integrated-storage"
storage "raft" { 
   path    = "/vault/data/"
}# 配置后端存储为raft
listener "tcp" {
   address = "[::]:8200"
   cluster_address = "[::]:8201"
   tls_disable = "true"
}

Vault Secret Engine

Overview

Secret Engine功能描述典型用途
Key/Value (KV)存储静态键值对秘密(如配置、密码),支持版本控制、元数据。配置文件、API密钥、数据库密码
AWS动态生成AWS IAM凭据、STS令牌AWS EC2, S3, Lambda 等
Azure动态生成Azure服务主体(Service Principal)Azure VM, Key Vault, SQL DB
Database统一接口:为多种数据库动态生成短期账号/密码MySQL, PostgreSQL, Oracle, MongoDB 等
PKI动态签发X.509证书(TLS/SSL)替代传统CA,支持自动轮换
SSH生成一次性SSH OTP或签署SSH公钥安全SSH服务器/客户端访问
Consul动态生成Consul ACL令牌安全访问Consul服务发现
LDAP绑定OpenLDAP或其他LDAP服务管理用户企业目录服务集成
OIDC提供OpenID Connect身份提供商(IdP)功能单点登录(SSO)

 

 

key/value

Key/Value 秘密引擎是 Vault 最简单、最通用的秘密存储方式,用于存储和管理任意键值对数据。支持版本控制、软删除和数据轮换,适合需要历史记录和恢复的场景。

 

使用流程

 

 

 

 

 

使用样例

# 启用KV v2引擎
vault secrets enable -path=secret kv-v2
# 写入秘密
vault kv put secret/db-config \
  username="admin" \
  password="s3cr3tP@ss"
# 读取最新版本
vault kv get secret/db-config
# 读取特定版本
vault kv get -version=1 secret/db-config
# 列出路径
vault kv list secret/
# 删除版本 (标记删除)
vault kv delete -versions=2 secret/db-config
# 彻底销毁版本
vault kv destroy -versions=2 secret/db-config

 

database

Database 秘密引擎为数据库(如 MySQL)提供动态凭据管理,Vault 自动生成和管理数据库用户凭据,具有短生命周期和自动轮换功能,增强安全性。

 

使用流程

 

使用样例

 

# 启用数据库引擎
vault secrets enable database
# 配置MySQL连接
vault write database/config/mysql-prod \
  plugin_name=mysql-database-plugin \
  connection_url="{{username}}:{{password}}@tcp(10.0.0.5:3306)/" \
  allowed_roles="app-role" \
  username="vault-admin" \
  password="vault-admin-password"
# 创建角色
vault write database/roles/app-role \
  db_name=mysql-prod \
  creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; \
    GRANT SELECT ON app_db.* TO '{{name}}'@'%';" \
  default_ttl="1h" \
  max_ttl="24h"
# 获取动态凭据
vault read database/creds/app-role
# 验证连接
mysql -u v-token-app-role-xyz123 -p'8f4cda6c-12e3-45ef-9b7a' -h 10.0.0.5
# 配置静态根凭据轮换
vault write -force database/rotate-root/mysql-prod
# 查看租约信息
vault lease lookup database/creds/app-role/abcd1234

 

 

 

 

Kubernetes

Kubernetes Secret Engine 允许 Vault 动态生成 Kubernetes 服务账户(Service Account)的对应token,支持动态生成短生命周期的 Service Account Token(TTL 可配置),可以跨多个 Kubernetes 集群、云平台或其他系统统一管理 Service Account Token 的生成、分配和生命周期。

 

 

使用流程

 

 

 

使用样例

 

1.  创建 Vault Server 使用的Service Account

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault
  namespace: vault
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: k8s-minimal-secrets-abilities
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["serviceaccounts", "serviceaccounts/token"]
  verbs: ["create", "update", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings", "clusterrolebindings"]
  verbs: ["create", "update", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["roles", "clusterroles"]
  verbs: ["bind", "escalate", "create", "update", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: vault-token-creator-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: k8s-minimal-secrets-abilities
subjects:
- kind: ServiceAccount
  name: vault
  namespace: vault

2.  创建 测试 使用的Service Account

apiVersion: v1
kind: Namespace
metadata:
  name: test
---
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"]
---
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

 

3.  配置Vault

 

# 启用Kubernetes引擎
vault secrets enable kubernetes
# 创建角色
vault write -f kubernetes/config
vault write kubernetes/roles/my-role allowed_kubernetes_namespaces="*" service_account_name="test-service-account-with-generated-token" token_default_ttl="10m"
# 生成ServiceAccount令牌
vault write kubernetes/creds/my-role kubernetes_namespace=test
# 使用令牌访问K8s API
curl -H "Authorization: Bearer $TOKEN" https://k8s-api:6443/api/v1/pods

 

Vault AuthN method

Overview

 

方法名称认证方式描述适用场景
Userpass用户名+密码认证(用户信息存储在Vault内部)内部用户/开发人员访问
AppRole机器/服务通过角色ID(Role ID)和密钥ID(Secret ID)认证CI/CD流水线、自动化服务
GitHub使用GitHub个人访问令牌(PAT)验证用户/团队身份开发团队协作场景
AWS通过AWS IAM角色/实例元数据/Lambda函数认证AWS EC2, Lambda, IAM用户
Azure使用Azure AD身份/托管身份(Managed Identity)/服务主体认证Azure VM, 应用服务, AAD
OIDC与OpenID Connect提供商(如Okta, Auth0)集成企业SSO(单点登录)
JWT/OIDC直接提交JWT令牌认证(可对接外部OIDC提供商)微服务认证、自定义身份系统集成

 

approle

AppRole 是一种面向机器或应用程序的认证方法,适合自动化工作流。它通过 RoleID(类似用户名)和 SecretID(类似密码)进行认证。

auth 流程

 

使用样例

# 1. 启用AppRole认证
vault auth enable approle
 
# 2. 创建角色并绑定策略
vault write auth/approle/role/ci-cd-role \
  token_policies="prod-db-access" \
  secret_id_ttl=10m \
  token_num_uses=5
 
# 3. 获取Role ID (静态配置)
vault read auth/approle/role/ci-cd-role/role-id
 
# 4. 生成Secret ID (动态获取)
vault write -f auth/approle/role/ci-cd-role/secret-id
 
# 5. 使用凭证登录
vault write auth/approle/login \
  role_id="db-access-role" \
  secret_id="a1b2c3d4-5678"

 

Userpass

UserPass 是一种面向人类用户的认证方法,通过用户名和密码对进行认证。它适用于需要人工交互的场景,如管理员或开发人员通过 CLI 或 UI 访问 Vault。

 

auth 流程

使用样例

# 1. 启用Userpass认证
vault auth enable userpass
 
# 2. 创建用户账户
vault write auth/userpass/users/alice \
  password="s3cr3tP@ss!" \
  policies="dev-readonly" \
  ttl="8h"
 
# 3. 用户登录 (CLI)
vault login -method=userpass username=alice password=s3cr3tP@ss!
 
# 4. 用户登录 (API)
curl --request POST \
  --data '{"password": "s3cr3tP@ss!"}' \
  http://127.0.0.1:8200/v1/auth/userpass/login/alice

 

Use Vault On K8S

Vault agent Injector

GitHub - hashicorp/vault-k8s: First-class support for Vault and Kubernetes.

 

通过Mutating Webhook拦截Pod创建事件,注入Vault Agent Sidecar容器,Agent负责认证Vault、获取Secret并渲染到共享内存卷。

 

功能特性

● 支持所有Vault动态Secret(如数据库凭据、PKI证书),通过Vault Agent管理租约。

● 需为每个集群配置独立的Vault认证方法(如Kubernetes认证),多集群管理复杂。

● Secrets存储在共享内存卷(tmpfs)中,作为文件供Pod消费。

 

安全特性

●  使用Kubernetes Service Account认证

●  Secrets存储在内存卷,降低持久化风险

●  支持所有Vault认证方法

●  提供Secret缓存和自动轮换

 

典型适用场景

●  需要高灵活性,支持多种Vault Secret引擎

●  应用不希望直接依赖Kubernetes Secret

●  需要Secret缓存或复杂模板化

 

部署架构

 

使用样例

# webapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  annotations:
    vault.hashicorp.com/agent-inject: "true"              # 启用注入
    vault.hashicorp.com/role: "db-app"                     # Vault 角色名
    vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/db-role" # Secret路径
spec:
  template:
    spec:
      serviceAccountName: webapp-sa                        # 匹配Vault角色绑定的SA
      containers:
      - name: app
        image: nginx:latest
        volumeMounts:
        - name: vault-secrets
          mountPath: /vault/secrets
      volumes:
      - name: vault-secrets
        emptyDir:
          medium: Memory

 

Vault Secret operator

GitHub - hashicorp/vault-secrets-operator: The Vault Secrets Operator (VSO) allows Pods to consume Vault secrets natively from Kubernetes Secrets.

使用Kubernetes Operator模式,通过CRD(如VaultStaticSecret)同步Vault Secret到Kubernetes Secret,支持动态Secret和滚动重启。

 

功能特性

● 支持多集群,通过SecretStore或ClusterSecretStore配置Vault连接,简化多集群管理。

● 支持静态、动态和PKI秘密,自动处理租约续订和轮换,支持滚动重启以更新应用。

● Secrets同步到Kubernetes原生Secret对象,可作为环境变量或文件使用。

 

安全特性

●  使用Kubernetes Service Account认证

●  Secrets存储在etcd,可能需额外加密

●  提供细粒度访问控制和审计

●  支持动态Secret轮换和滚动重启

 

典型适用场景

●  希望与Kubernetes原生Secret集成

●  需要动态Secret管理和自动轮换

●  适合只需要简单Secret同步的场景

 

部署架构

 

 

使用样例

# vault-connection.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
  name: prod-vault
spec:
  address: "https://vault.prod:8200" #vault server 地址
 
# vault-auth-role.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: kubernetes-auth
spec:
  vaultConnectionRef: prod-vault
  method: kubernetes
  mount: kubernetes
  kubernetes:
    role: db-app
    serviceAccount: webapp-sa
# database-secret.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
  name: db-credentials
spec:
  vaultConnectionRef: prod-vault
  vaultAuthRef: kubernetes-auth
  path: database/creds/db-role
  refreshAfter: "5m"       # 每5分钟轮换凭证
  destination:
    create: true
    name: db-secret        # 生成的K8s Secret名称
# 应用中既可以使用env 挂到pod中:
env:
- name: DB_USERNAME
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: username
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password

 

 

Vault secrets-store-csi-driver

 

GitHub - hashicorp/vault-csi-provider: HashiCorp Vault Provider for Secret Store CSI Driver

使用CSI标准,通过SecretProviderClass CRD配置Vault Secret路径,CSI驱动在Pod创建时挂载Secret到文件系统。

 

功能特性

● 支持多集群,需在每个集群安装CSI驱动和Vault Provider,配置相对简单。

● 支持所有Vault动态Secret,CSI驱动管理租约续订,但无内置滚动重启功能。

● Secrets作为CSI卷挂载到Pod的文件系统中,可选择同步到Kubernetes Secret。

● DaemonSet模式在集群的每个节点上运行,负责处理挂载请求、与 Provider 交互、管理临时卷。

 

安全特性

●  使用Kubernetes Service Account认证

●  Secrets通过CSI卷挂载,降低泄露风险

●  支持TLS/mTLS通信

●  可选同步到Kubernetes Secret

 

典型适用场景

●  希望直接挂载Secret到Pod文件系统

●  需要支持多云或外部Secret管理器

●  适合高安全性需求,不希望Secret存储在etcd

部署架构

 

 

使用样例

需要提前安装secrets-store-csi-driver

# vault-spc.yaml 配置 SecretProviderClass
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-db-creds
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com:8200"
    roleName: "csi-role"
    objects: |
      - objectPath: "kv/data/app/database"
        secretKey: "username"
        path: "db_user"
      - objectPath: "kv/data/app/database"
        secretKey: "password"
        path: "db_pass"
    # 可选:同步到Kubernetes Secret
    secretObjects:
      - secretName: "db-secret"  # 生成的Secret名称
        type: Opaque
        data:
          - key: "username"
            objectName: "db_user"
          - key: "password"
            objectName: "db_pass"
# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
  template:
    spec:
      serviceAccountName: app-sa  # 必须匹配Vault角色
      containers:
      - name: app
        image: nginx:latest
        volumeMounts:
        - name: db-creds
          mountPath: "/etc/secrets"
          readOnly: true
      volumes:
      - name: db-creds
        csi:
          driver: secrets-store.csi.k8s.io
          readOnly: true
          volumeAttributes:
            secretProviderClass: "vault-db-creds"  # 引用SecretProviderClass

 

 

External-Secret

GitHub - external-secrets/external-secrets: External Secrets Operator reads information from a third-party service like AWS Secrets Manager and automatically injects the values as Kubernetes Secrets.

 

通过Operator模式,使用SecretStore和ExternalSecret CRD,从Vault获取Secret并同步到Kubernetes Secret。

 

功能特性

● 支持多集群,通过ClusterSecretStore CRD配置,适合多云或混合环境。

● 仅支持KV Secret引擎,不支持动态Secret(如数据库凭据或PKI)。

● Secrets同步到Kubernetes原生Secret对象,可作为环境变量或文件使用。

 

安全特性

●  使用外部Secret管理器的认证(如Vault的Kubernetes认证)

●  Secrets存储在etcd,需加密保护

●  提供访问控制和审计,但较弱

 

典型适用场景

●  希望简单集成外部Secret管理器(如Vault、AWS Secrets Manager)

●  应用依赖Kubernetes Secret作为环境变量

●  适合多云或混合环境

 

部署架构

 

使用样例

需要提前安装 external-secrets

 

# vault-secretstore.yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.example.com:8200"
      path: "secret"
      version: "v2"  # KV引擎版本
      auth:
        # 使用Kubernetes认证/也可以使用approle认证。
        kubernetes:
          mountPath: "kubernetes"
          role: "eso-role"
          serviceAccountRef:
            name: eso-auth-sa  # 专用ServiceAccount
# database-externalsecret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  refreshInterval: "5m"  # 同步间隔
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: db-secret  # 生成的K8s Secret名称
  data:
  - secretKey: username
    remoteRef:
      key: database/creds/db-role
      property: username  # 指定Secret字段
  - secretKey: password
    remoteRef:
      key: database/creds/db-role
      property: password
# 应用中使用env 挂到pod中:
env:
- name: DB_USER
  valueFrom:
    secretKeyRef:
      name: db-secret  # 匹配ExternalSecret生成的Secret
      key: username
- name: DB_PASS
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password