K8S 里的密码去哪了?凭据管理系统在容器环境的四种集成实践

摘要:很多人以为把数据库密码塞进 Kubernetes Secret 就"安全"了,其实 Secret 默认只是 base64 编码,在 etcd 里近乎明文躺着。本文从 K8S 原生凭据方案的真实痛点出发,拆解如何用外部凭据管理系统(SMS)实现"密码不进 YAML、不落 etcd、动态注入、自动轮转",并给出 Init Container、Sidecar、CSI Driver、SDK 直连四种集成方式的完整落地示例。


一、先泼一盆冷水:K8S Secret 并不"安全"

几乎每个上云团队都踩过这个坑------把密码写进 Deployment 的环境变量太丑,于是改用 Secret,然后就心安理得了。但 Secret 有三个致命误区:

1.1 Secret 不是加密,是 base64 编码

bash 复制代码
# 你以为的"加密"
$ echo -n 'Admin@123' | base64
QWRtaW5AMTIz

# 任何人拿到就能还原
$ echo 'QWRtaW5AMTIz' | base64 -d
Admin@123

base64 是编码,不是加密,任何拿到 Secret YAML 或 etcd 快照的人都能一秒还原。

1.2 etcd 默认不加密存储

Secret 最终存在 etcd 里。如果没有开启 EncryptionConfiguration,etcd 备份文件、快照、甚至误配置的备份 OSS 桶,都等于把生产库密码明文送人。

1.3 GitOps 让 Secret 进了 Git 仓库

ArgoCD / Flux 流行后,大家习惯把 K8S 清单全放进 Git。Secret 也跟着进了仓库------哪怕用了 SealedSecrets,密钥管理和轮转依然是老大难。

一句话总结痛点: K8S 原生方案解决的是"密码怎么传给 Pod",没解决"密码本身怎么被安全地存储、轮转、审计"。


二、容器环境凭据治理的六个真实痛点

结合大量落地案例,K8S 场景下凭据管理的痛点可以归纳为下面这张表:

# 痛点 后果 等保/合规影响
1 Secret base64 明文可还原 拿到 YAML/etcd 即泄露 高危项
2 密码硬编码进镜像/ConfigMap 镜像分发即泄露 一票否决
3 密码无法自动轮转 改密码要重启全部 Pod 长期不轮转高危
4 多集群多命名空间凭据分散 无统一管理入口 审计困难
5 谁读了 Secret 无记录 无法追溯泄露源 审计缺失
6 离职/外包人员仍能读 Secret 权限回收滞后 访问控制不达标

这些痛点的共性是:凭据的生命周期(存储、分发、轮转、销毁、审计)散落在 K8S 之外,没有统一治理面。


三、思路:把凭据管理下沉到专用系统

解法不是给 K8S Secret 打补丁,而是引入一个独立的凭据管理系统(Secret Management System,下文简称 SMS),让 K8S 只负责调度,凭据的存储与治理全部交给 SMS:

复制代码
┌─────────────────────────────────────────────┐
│                Kubernetes 集群               │
│  ┌────────────┐   ┌────────────┐            │
│  │  业务 Pod  │   │  业务 Pod  │            │
│  │ (无密码)   │   │ (无密码)   │            │
│  └─────┬──────┘   └─────┬──────┘            │
│        │ 动态获取临时凭据 │                    │
│        └────────┬────────┘                    │
└─────────────────┼─────────────────────────────┘
                  │ ① ServiceAccount 身份鉴权
                  ▼
        ┌───────────────────────┐
        │   SMS 凭据管理系统    │
        │ • 统一加密存储        │
        │ • 动态临时凭据 (TTL)  │
        │ • 自动轮转            │
        │ • 全程访问审计        │
        └──────────┬────────────┘
                  │ ② 返回带 TTL 的临时凭据
                  ▼
        ┌───────────────────────┐
        │  数据库 / 中间件 / API │
        └───────────────────────┘

这里用到两个关键能力:

  • 动态临时凭据:Pod 每次拿到的都是带有效期(TTL,如 1 小时)的临时密码,用完即毁,密码不进 YAML、不落 etcd。
  • 自动轮转:数据库真实密码由 SMS 按周期(如 ≤90 天)自动轮转,业务 Pod 无感知,不用重启。

四、四种集成方法(含完整示例)

在 K8S 里接入 SMS,主流有四条路径,按"侵入性从低到高"排列。

4.1 方法一:Init Container 预拉取

启动前用 Init Container 向 SMS 申请凭据,写入共享的 emptyDir 内存卷,业务容器直接读文件。业务代码零改造

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  template:
    spec:
      serviceAccountName: order-sa      # 用 SA 向 SMS 鉴权
      volumes:
        - name: creds
          emptyDir:
            medium: Memory              # 内存卷,不落磁盘
      initContainers:
        - name: fetch-creds
          image: registry/sms-agent:latest
          args: ["fetch", "--id=order-db", "--out=/creds/db.json"]
          volumeMounts:
            - { name: creds, mountPath: /creds }
      containers:
        - name: app
          image: registry/order-service:1.0
          volumeMounts:
            - { name: creds, mountPath: /creds, readOnly: true }

适用:凭据在 Pod 生命周期内基本不变的场景。缺点是长时间运行的 Pod 拿不到轮转后的新凭据(需配合 Sidecar)。

4.2 方法二:Sidecar 常驻刷新

注入一个 sidecar 容器常驻 Pod,定时向 SMS 续期/刷新凭据,写入共享内存卷。轮转后 sidecar 自动拉新,业务无感知。

yaml 复制代码
      containers:
        - name: app
          image: registry/order-service:1.0
          volumeMounts:
            - { name: creds, mountPath: /creds, readOnly: true }
        - name: sms-sidecar
          image: registry/sms-agent:latest
          args: ["watch", "--id=order-db", "--out=/creds/db.json", "--interval=300"]
          volumeMounts:
            - { name: creds, mountPath: /creds }

适用:长时间运行、需要感知凭据轮转的服务。这是与"自动轮转"配合最好的模式。

4.3 方法三:Secrets Store CSI Driver

通过标准的 CSI Driver 把 SMS 里的凭据以卷的形式挂载进 Pod,云原生程度最高,运维统一。

yaml 复制代码
      volumes:
        - name: creds
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "sms-order-db"

配套的 SecretProviderClass 声明去哪个 SMS 实例、取哪些凭据,权限与集群解耦。

适用:已有平台化运维、希望用统一 CSI 规范管理所有外部密钥的团队。

4.4 方法四:SDK 直连

业务代码里直接调 SMS SDK 动态获取凭据,控制力最强,改动一行配置读取逻辑即可。

python 复制代码
# ❌ 改造前:密码硬编码 / 从 Secret 环境变量读,明文可见
# password = os.environ["DB_PASSWORD"]

# ✅ 改造后:运行时动态获取临时凭据,不落盘
from sms_sdk import CredentialManager

cm = CredentialManager(auth="k8s-sa")          # 用 Pod 的 SA Token 鉴权
cred = cm.get_credential("order-db")           # 返回带 TTL 的临时凭据
conn = mysql.connect(
    host=cred["host"],
    user=cred["user"],
    password=cred["password"],                 # 1 小时后自动失效
)

适用:新项目或愿意做少量改造、追求最小密码暴露窗口的场景。

四种方法对比:

方法 业务改造 支持轮转刷新 云原生度 推荐场景
Init Container 凭据基本不变
Sidecar 长运行+需轮转
CSI Driver 平台化统一运维
SDK 直连 少量 新项目/最小暴露

五、身份鉴权:Pod 凭什么能取凭据?

关键在于用 K8S ServiceAccount 做 Pod 到 SMS 的身份鉴权,而不是再发一个静态 token(否则又回到了"密码换密码"的死循环)。

流程如下:

  1. Pod 挂载自己的 SA Token(K8S 自动注入 /var/run/secrets/...)。
  2. sms-agent / SDK 拿 SA Token 向 SMS 换取短时会话。
  3. SMS 校验 SA 身份(命名空间 + SA 名 + 集群),命中授权策略才签发临时凭据。
  4. 每次签发写入审计日志:哪个 Pod、哪个 SA、取了哪个库、什么时间。

这样即使 Pod 被攻破,攻击者拿到的也只是带 TTL 的临时凭据,且行为全程留痕,可快速定位与止血。


六、落地 checklist

  1. 梳理集群内全量凭据清单(数据库、中间件、API、云账号)。
  2. 部署 SMS,把凭据迁入加密存储,删除 YAML/ConfigMap 里的明文。
  3. 按服务特征选集成方式:短命 Job 用 Init Container,常驻服务用 Sidecar/CSI。
  4. 配置 SA 授权策略,做到"一个命名空间只能取自己的凭据"。
  5. 开启自动轮转(≤90 天)+ 新旧双写过渡,验证 Pod 无感知刷新。
  6. 打开全程审计,日志归档留存,接入 SIEM 告警。

七、写在最后

K8S 把应用调度做到了极致,但凭据治理从来不是它的强项。与其在 Secret 上层层打补丁,不如把凭据的存储、轮转、审计交给专门的凭据管理系统,让 Pod 只在运行时拿到"用完即毁"的临时凭据。密码不进 YAML、不落 etcd、可轮转、可审计------这才是容器环境凭据治理应有的样子。

作者注:本文所述动态临时凭据、自动轮转能力可结合国密算法与硬件密钥模块落地,具体部署方案建议结合集群规模与合规要求评估。

免责声明:本文仅供技术交流,具体合规要求以官方标准文件为准。

相关推荐
张忠琳5 小时前
【NVIDIA】 NVIDIA Container Toolkit v1.19.1 — OCI 模块超深度分析之三
云原生·容器·架构·kubernetes·nvidia
阿里云云原生6 小时前
让 AI Agent 看见正在发生的业务,阿里云 EventHouse 正式商业化
云原生
Elastic 中国社区官方博客7 小时前
将你的 Grafana Kubernetes 仪表板迁移到 Elastic Observability:相同的 PromQL,30 倍更快的查询
大数据·人工智能·elasticsearch·搜索引擎·容器·kubernetes·grafana
AOwhisky8 小时前
Python 学习笔记(第十五期)——运维自动化(下·后篇):堡垒机实战——paramiko高阶篇
运维·python·学习·云原生·自动化·运维开发
风曦Kisaki9 小时前
#企业级docker私有仓库构建:harbor仓库与阿里云镜像仓库
阿里云·docker·容器
期待着20139 小时前
docker 安装 ,在centos7.9
运维·docker·容器
微三云 - 廖会灵 (私域系统开发)10 小时前
电商系统从单体到微服务拆分实践:拆什么、怎么拆、拆完后怎么办
微服务·云原生·架构
骑上单车去旅行12 小时前
Docker Compose 命令完全指南:从构建到运维
docker·容器·eureka
前端Baymax13 小时前
K8s PodCrashLoopBackOff假阳性排查
云原生·容器·kubernetes
spider_xcxc13 小时前
K8s 部署学习笔记
docker·容器·kubernetes·云计算·k8s