2. Kubernetes Secret对象深入
在上一节中我们已经明确:Secret 是 Kubernetes 中用于承载敏感配置的 API 对象,它与 ConfigMap 在结构上高度相似,但定位不同——ConfigMap 面向普通配置,Secret 面向口令、令牌、证书、镜像仓库凭据等需要限制可见范围的数据。本节把 Secret 拆开来看:它有哪些创建方式、有哪些内置类型、数据在对象里以什么形式存在、Pod 又是如何把它变成容器里的文件或环境变量,以及最终它落在 etcd 中意味着什么风险。
一、创建方式:命令式与声明式
最直接的方式是 kubectl create secret 命令式创建。以 Opaque 类型为例,从字面量创建:
kubectl create secret generic db-cred \
--from-literal=username=appuser \
--from-literal=password='S3cr3t!2024'
也可以从文件创建,适合证书、密钥这类内容较长的数据:
kubectl create secret generic tls-pair \
--from-file=./server.crt \
--from-file=./server.key
命令式创建方便,但不利于版本管理和审计。生产环境更推荐声明式 YAML,把 Secret 清单纳入 Git 或配置管理流程。声明式清单中,data 字段要求值是 Base64 编码后的字符串,stringData 字段则允许直接写明文,API Server 会在写入时自动转成 Base64 存入 data:
apiVersion: v1
kind: Secret
metadata:
name: db-cred
type: Opaque
stringData:
username: appuser
password: "S3cr3t!2024"
这里必须强调一个常见误解:Base64 不是加密。它只是把二进制或文本转成可安全放进 YAML/JSON 的 ASCII 字符集,任何人拿到编码串都能一条命令还原。因此把 Secret 清单直接提交到公开仓库,等同于泄露明文。
二、内置类型与适用场景
Kubernetes 为常见敏感数据场景预置了若干类型,类型本身不改变存储方式,但会影响字段校验规则和消费方的解析约定:
- Opaque:默认类型,键值对形式,最通用,适合数据库口令、API Key 等自定义数据。
- kubernetes.io/tls:要求包含
tls.crt和tls.key两个键,供 Ingress 终止 TLS 时直接引用。 - kubernetes.io/dockerconfigjson:存放
.dockerconfigjson,即 Docker 的config.json内容,用于拉取私有镜像仓库。 - kubernetes.io/service-account-token:由控制器为 ServiceAccount 自动生成,挂载到 Pod 中供访问 API Server 使用。
- kubernetes.io/basic-auth 与 kubernetes.io/ssh-auth:分别约定
username/password与ssh-privatekey字段。
创建 Docker Registry 凭据时,常用命令式方式一步生成:
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=ci \
--docker-password='P@ssw0rd' \
--docker-email=ci@example.com
生成后在 Pod 的 spec.imagePullSecrets 中引用,kubelet 拉镜像时就会带上该凭据。
三、数据编码与挂载机制
Secret 被 Pod 消费有两条路径:环境变量和卷挂载。环境变量方式通过 envFrom 或 valueFrom.secretKeyRef 注入,简单但有两个明显缺点:一是容器内进程可通过 /proc/self/environ 或崩溃转储泄露;二是 Secret 更新后环境变量不会刷新,必须重建 Pod。
卷挂载方式把每个键写成一个文件,文件名即键名,文件内容为解码后的原始值。kubelet 通过 tmpfs(内存文件系统)承载这些文件,不落磁盘。更重要的是,当 Secret 被更新时,挂载的文件会在约一分钟内自动同步(取决于 kubelet 同步周期与缓存),应用只需重新读取文件即可,无需重启。这也是推荐用卷挂载而非环境变量的核心原因。
下面这张图展示了 Secret 从创建到被容器消费的完整链路,以及 etcd 在其中的位置:
四、etcd 存储风险与缓解
Secret 最终以 API 对象形式存放在 etcd 中。默认情况下,etcd 里保存的是 Base64 编码后的值,并非加密。这意味着:任何能读取 etcd 数据文件或能访问 etcd 端口的人,都能还原出明文。风险主要来自三个方向:
- etcd 备份泄露:快照文件被复制到不受控位置,等于所有 Secret 明文外泄。
- RBAC 过宽:授予了
get/list secrets权限,攻击者可直接通过 API 读取。 - 节点侧泄露:有权限登录节点或进入容器的人,可读取挂载文件或环境变量。
对应的缓解措施是分层的。第一层是启用 etcd 静态加密,在 API Server 启动参数中配置 --encryption-provider-config,指定 aescbc 或 kms 提供者,使写入 etcd 的值先加密。第二层是收紧 RBAC,遵循最小权限,避免通配符授予 Secret 读权限。第三层是启用审计日志,记录谁在何时读取了哪个 Secret。第四层是结合外部密钥管理系统(如 Vault、云厂商 KMS),让 Secret 只在内存中短暂存在,减少持久化暴露面。