从技术底层拆解,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: {}              # 兜底:不加密(用于轮换过渡)

工作机制

  1. 用户创建 Secret → APIServer 接收明文
  2. APIServer 根据 EncryptionConfiguration 选择 provider
  3. 在写入 etcd 前,AES-GCM 加密 value 部分
  4. etcd 中存储的是密文 + 加密元数据
  5. 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 里,信封的开启必须被审计。

更多推荐