从技术底层拆解,K8S 的 Secret 是"被设计用来存放敏感数据的原生 API 对象",但它的安全边界经常被误解------它不是保险箱,而是"带标签的信封",真正的安全取决于你如何封存这个信封。
一、Secret 的本质:它到底是什么?
1.1 数据结构
Secret 在 etcd 中就是一个扁平的键值对资源对象 ,与 ConfigMap 结构几乎一致,区别仅在于 type 字段和语义暗示:
apiVersion: v1
kind: Secret
metadata:
name: db-password
namespace: prod-core
type: Opaque # 类型标记
data:
password: cGFzc3dvcmQxMjM= # base64 编码的字符串
关键认知 :data 字段下的值是 base64 编码 ,不是加密。base64 的目的是"让二进制数据安全地穿越 JSON/YAML 文本协议",而不是"防止人类读取"。任何人拿到这个 YAML,执行 echo cGFzc3dvcmQxMjM= | base64 -d 立刻得到明文 password123。
1.2 三种核心类型
|-------------------------------------------------------------------------------------------------------------------------|------------------------------------|-----------------------------|
| 类型 | 用途 | 银行场景示例 |
| Opaque | 通用敏感数据(密码、API Key、证书私钥) | 数据库连接串、支付网关 AK/SK |
| kubernetes.io/tls | TLS 证书与私钥 | Ingress HTTPS 证书、国密 SM2 双证书 |
| kubernetes.io/dockerconfigjson | 镜像仓库拉取凭证 | Harbor 私有仓库认证 |
| kubernetes.io/service-account-token | ServiceAccount JWT(1.24 后已默认不自动创建) | Pod 访问 APIServer 的身份令牌 |
二、与用户验证的关系:间接但关键
2.1 Secret ≠ 用户身份验证体系
用户验证(Authentication) 是 APIServer 的入口安检,解决"你是谁":
- X509 客户端证书(CN=system:admin)
- OIDC Token(Keycloak/Dex + 银行 AD/LDAP)
- Webhook Token 认证(对接银行统一身份平台)
- Bootstrap Token(节点加入)
Secret 不直接参与上述流程 。用户拿着 kubeconfig 里的证书或 OIDC Token 去敲 APIServer 的门,APIServer 验证的是签发者(CA 或 IdP),而不是 Secret。
2.2 Secret 与授权(Authorization)的桥梁作用
Secret 的真正安全价值在于****"权限载体"****:
|--------------------------|------------------------------------------------------------------------------------------------------------------------------|
| 场景 | Secret 角色 |
| ServiceAccount Token | 虽然 1.24+ 默认使用 TokenRequest 投影(短期 Token),但传统 Secret 形式的 SA Token 曾是 Pod 访问 APIServer 的"通行证"。若该 Secret 泄露,攻击者可冒充 Pod 身份调用 API。 |
| 镜像拉取凭证 | imagePullSecrets 引用 Secret,kubelet 凭此从私有 Harbor 拉取镜像。若泄露,攻击者可拉取银行私有镜像分析漏洞。 |
| 应用层身份 | 业务容器通过挂载 Secret 获取数据库密码或第三方 API Key。Secret 是****"应用向外部系统证明身份"****的凭证,而非 K8S 集群内部的身份验证。 |
一句话澄清 :Secret 是"应用访问外部世界的护照",不是"人类访问 K8S 的身份证"。但护照丢了,应用就能以你的名义干坏事。
三、能看到明文吗?三个层面的真相
3.1 层面一:etcd 中------默认完全明文
如果不做任何额外配置,Secret 在 etcd 中就是裸奔的 protobuf 编码数据。etcd 本身没有内置加密:
直接读取 etcd(如果有权限),你能看到:
key: /registry/secrets/prod-core/db-password
value: {"password":"cGFzc3dvcmQxMjM="} (base64,但 etcd 客户端自动解码后就是明文)
这是 K8S Secret 最大的安全盲区。etcd 是集群的"大脑",一旦攻击者突破 Master 节点或 etcd 节点,所有 Secret 一览无余。
3.2 层面二:APIServer 返回------base64 编码
通过 kubectl get secret db-password -o yaml 或 API 调用,APIServer 返回的是 base64 编码值。这不是加密,是"防传输层意外截断"的编码。任何有 get secret 权限的人都能瞬间解码。
3.3 层面三:容器内------明文文件或环境变量
Secret 被消费时,彻底暴露为明文:
方式一:Volume 挂载(推荐,相对安全)
volumeMounts:
- name: secret-vol
mountPath: /etc/secrets
readOnly: true
容器内:cat /etc/secrets/password → 明文 password123
方式二:环境变量(不推荐,风险高)
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-password
key: password
容器内:echo $DB_PASSWORD → 明文
风险:/proc/<pid>/environ 任何同节点进程可读;docker inspect 可见
结论 :Secret 在全生命周期中默认都是明文可读的 ,它提供的唯一保护是RBAC 权限隔离 (谁能看 Secret)和传输层 TLS,而不是内容本身的机密性。
四、加密机制:原生能力与金融级增强
4.1 K8S 原生静态加密(Encryption at Rest)
K8S 1.7+ 支持通过 EncryptionConfiguration 在写入 etcd 前对 Secret 进行加密:
/etc/kubernetes/enc/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets # 仅加密 Secret,也可加 configmaps
providers: - aescbc: # 算法 1:AES-CBC(已废弃,不推荐)
keys: - name: key1
secret: <base64-encoded-32-byte-key> - aesgcm: # 算法 2:AES-GCM(推荐,认证加密)
keys: - name: key1
secret: <base64-encoded-32-byte-key> - secretbox: # 算法 3:XSalsa20 + Poly1305
keys: - name: key1
secret: <base64-encoded-32-byte-key> - identity: {} # 兜底:不加密(用于轮换过渡)
工作机制:
- 用户创建 Secret → APIServer 接收明文
- APIServer 根据 EncryptionConfiguration 选择 provider
- 在写入 etcd 前,AES-GCM 加密 value 部分
- etcd 中存储的是密文 + 加密元数据
- APIServer 读取时自动解密,对用户透明
关键风险:
- 密钥管理 :encryption-config.yaml 中的密钥是明文写在文件系统上的。如果 Master 节点被攻破,加密密钥和密文在一起,等于没加密。
- 密钥轮换 :原生不支持自动轮换,需手动更新配置并触发全量重写(kubectl get secrets --all-namespaces -o json | kubectl replace -f -),银行大集群耗时极长。
4.2 金融级增强:对接 KMS / Vault
银行不会满足于"文件里放个 AES 密钥",必须对接外部密钥管理系统:
|------------------------|---------------------------------------------------------------|-------------------------|
| 方案 | 原理 | 银行适用性 |
| KMS Provider(云厂商) | APIServer → 阿里云 KMS / AWS KMS / Azure Key Vault → 数据密钥(DEK)加密 | 公有云场景首选,但密钥托管在云厂商 |
| Vault KMS Provider | APIServer → HashiCorp Vault(Transit 引擎)→ 自动密钥轮换 + HSM 保护 | 私有云/信创首选,密钥不出 Vault |
| 国密改造 | 原生 AES-GCM 替换为 SM4-GCM,APIServer 加密 provider 插件化改造 | 等保/信创合规要求 |
对接 Vault 的 EncryptionConfiguration(概念性)
providers:
- kms:
name: vault-kms
endpoint: unix:///var/run/kms-plugin/vault.sock # KMS Plugin sidecar
cachesize: 1000
timeout: 3s
国密特化 :金融核心系统若需 SM4 加密,通常需基于开源 K8s 二次开发或采用博云、青云、华为 CCE 等国产发行版,它们内置了国密加密 provider。
4.3 动态注入:External Secrets Operator(银行最佳实践)
更安全的模式是"Secret 不在 K8S 中长期存储":
Vault(银行 HSM 保护)
│
├──► External Secrets Operator(K8s Operator)
│ │
│ └──► 定期同步为 K8s Secret(存活期 < 1h)
│ │
│ └──► 业务 Pod 挂载
│
└──► 动态数据库凭据(Vault 自动生成,自动过期)
优势:
- 即使 etcd 被攻破,Secret 是短期有效的
- 密码泄露后自动轮转,无需人工改 YAML
- 审计在 Vault 端完成,满足等保要求
五、银行级 Secret 治理实践
5.1 必须遵守的红线
|-----------------|-------------------------------------------------------------------------|
| 风险 | 技术对策 |
| etcd 明文泄露 | 必须启用 EncryptionConfiguration,优先 AES-GCM;信创环境评估 SM4 |
| Secret 权限过宽 | Kyverno/OPA 禁止 default ServiceAccount 自动挂载;禁止 cluster-admin 读取所有 Secret |
| 环境变量泄露 | 强制 Volume 挂载(readOnly: true,权限 0400);禁止 envFrom 引用 Secret |
| Git 仓库泄露 | 禁止 Secret YAML 入 Git;使用 Sealed Secrets / SOPS / External Secrets |
| 镜像层残留 | Dockerfile 禁止 COPY 包含 Secret 的文件;BuildKit Secret 挂载构建 |
| 审计盲区 | 所有 get secret / create secret / delete secret 操作记录审计日志,留存 6 个月 |
5.2 等保与 PIPL 适配
- 等保 2.0/3.0:要求"重要数据存储保密性",Secret 必须静态加密,加密密钥分级保护
- PIPL:若 Secret 包含个人信息(如客户登录 Token),其处理需满足最小必要原则,泄露需上报
六、一句话讲透 Secret
K8S Secret 是一个"权限控制下的 base64 信封",默认不防内鬼、不防 etcd 攻破、不防节点提权。它的安全=RBAC 权限隔离 + 可选的 etcd 静态加密 + 金融级场景下必须叠加的 Vault 动态注入/HSM 国密保护。
它不是保险箱,而是"需要额外上锁的信封"。银行生产环境中,信封里的内容必须被加密,钥匙必须放在 HSM 里,信封的开启必须被审计。