kubernetes-the-hard-way 数据加密实战:生成 Encryption Key 与 Encryption Config,为 Secrets 开启静态加密
本篇基于 kubernetes-the-hard-way 教程中的实验 Generating the Data Encryption Config and Key,讲解如何手工生成一个用于静态数据加密的密钥(Encryption Key),基于仓库中的模板 configs/encryption-config.yaml 渲染出 Encryption Config 文件,并最终通过 kube-apiserver 的 --encryption-provider-config 参数为集群中的 Secrets 开启静态加密(Encryption at Rest)。读完本篇,你将掌握加密密钥的生成原理、Encryption Configuration 文件的字段结构,以及如何通过检查 etcd 中的实际存储数据来验证加密真正生效。
为什么需要静态数据加密
Kubernetes 会把大量数据——集群状态、应用配置以及 Secrets——持久化到 etcd 中。在 kubernetes-the-hard-way 的拓扑中(一台 server 承载全部控制平面组件,加 node-0、node-1 两台工作节点,见 README),etcd 直接以明文形式落在 server 机器的磁盘上。如果攻击者拿到了 etcd 的数据文件或磁盘镜像,Secrets 中的凭据、令牌等内容将一览无余。
Kubernetes 支持静态数据加密(Encrypting data at rest)能力:由 kube-apiserver 在数据写入 etcd 之前完成加密、从 etcd 读取之后完成解密,客户端(kubectl 等)完全无感知。本实验就是整个教程中为这一步做准备的环节:生成一个加密密钥,并生成一份适合加密 Secrets 的 Encryption Config。
实验在教程中的位置
该实验位于 docs/06-data-encryption-keys.md,紧跟在 生成 kubeconfig 文件 之后、引导 etcd 集群 之前。之所以排在这个位置,是因为:
- 加密密钥和配置文件属于
server机器上控制平面启动前必须就位的物料之一——后续 Bootstrapping the Kubernetes Control Plane 会把encryption-config.yaml移动到/var/lib/kubernetes/目录; - 密钥生成依赖
envsubst做模板渲染,与证书、kubeconfig 一样都在jumpbox上完成,生成后再分发到目标机器。
教程使用的组件版本(见 downloads-amd64.txt)为 Kubernetes v1.32.3 与 etcd v3.6.x,运行环境为 Debian 12 (bookworm) 的 4 台 ARM64 或 AMD64 机器,要求见 docs/01-prerequisites.md。
生成加密密钥(The Encryption Key)
实验的第一步是生成一个加密密钥,命令如下:
export ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
这条命令值得逐段拆解:
| 片段 | 作用 |
|---|---|
head -c 32 /dev/urandom |
从操作系统的随机源读取 32 个随机字节(256 位) |
| base64 |
将这 32 字节编码为 Base64 字符串(约 44 个字符) |
export ENCRYPTION_KEY=... |
导出为环境变量,供下一步的模板渲染读取 |
这里的 32 字节正是 AES-256 密钥的长度,与下一步配置文件中 aescbc(AES-CBC 模式)provider 对密钥的要求相吻合:aescbc 的 secret 字段要求填入 Base64 编码的 32 字节密钥。/dev/urandom 是内核提供的密码学安全随机源,保证每次执行生成的密钥都不同且不可预测。
注意:
export只在当前 shell 会话内有效。如果关闭了终端或切换了 shell,需要重新执行该命令生成新的ENCRYPTION_KEY(或者沿用同一会话继续操作)。
解析 Encryption Config 模板文件
仓库中的 configs/encryption-config.yaml 是一份待渲染的模板,完整内容如下:
kind: EncryptionConfiguration
apiVersion: apiserver.config.k8s.io/v1
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: ${ENCRYPTION_KEY}
- identity: {}
各字段含义如下:
kind: EncryptionConfiguration与apiVersion: apiserver.config.k8s.io/v1:声明这是一份 API Server 的静态加密配置文件,采用apiserver.config.k8s.io/v1版本;resources:定义加密规则,本教程只对secrets这一种资源启用静态加密。即只有 Secret 对象写入 etcd 时会被加密,其他资源仍按原样存储;providers:按优先级排列的加密算法列表。第一条是aescbcprovider:aescbc表示使用 AES 算法的 CBC(Cipher Block Chaining)工作模式;keys是密钥列表,name: key1是该密钥在数据中的标识名,secret: ${ENCRYPTION_KEY}是一个占位符,等待模板渲染时替换为前面生成的 Base64 密钥;
identity: {}作为兜底 provider:当数据无法(或无需)由前面的 provider 处理时(例如读取历史明文数据),按原样存取。
${ENCRYPTION_KEY} 是标准的 shell 变量占位符,这正是下一步使用 envsubst 的原因。
渲染配置文件并分发到 Controller 机器
在 jumpbox 上执行模板渲染,生成真正包含密钥的 encryption-config.yaml:
envsubst < configs/encryption-config.yaml \
> encryption-config.yaml
envsubst 会读取当前 shell 环境变量中的 ENCRYPTION_KEY,替换模板中的占位符,输出到当前目录的 encryption-config.yaml。渲染完成后,可用 cat encryption-config.yaml 确认 secret: 字段已经是真实的 Base64 密钥字符串。
然后把配置文件复制到每台 controller 实例(本教程即 server 机器):
scp encryption-config.yaml root@server:~/
后续 Bootstrapping the Kubernetes Control Plane 实验中会执行以下操作,把 encryption-config.yaml 与其他 TLS 证书、Service Account 密钥一起移入 kube-apiserver 的配置目录:
{
mkdir -p /var/lib/kubernetes/
mv ca.crt ca.key \
kube-api-server.key kube-api-server.crt \
service-accounts.key service-accounts.crt \
encryption-config.yaml \
/var/lib/kubernetes/
}
kube-apiserver 如何消费这份配置
配置文件生效的入口在 units/kube-apiserver.service 这个 systemd 单元文件中,其 ExecStart 一行的第 18 行明确挂载了该文件:
ExecStart=/usr/local/bin/kube-apiserver \
...
--etcd-servers=http://127.0.0.1:2379 \
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \
...
从源码结构看,整条链路是:kube-apiserver 启动时加载 --encryption-provider-config 指定的文件,按照其中的 resources 规则匹配资源类型,对命中的对象(本例为 secrets)使用 providers 列表中第一个可用的 provider(aescbc/key1)做 AES-CBC 加密后写入 etcd;读取时再按数据头部携带的 provider 与密钥标识反向解密。etcd 本身不需要感知加密,数据加密完全发生在 API Server 进程内——这也解释了为什么本教程中 etcd 以 http://127.0.0.1:2379 的本地明文方式运行,而 Secrets 在磁盘上依然不是明文。
验证加密真正生效:检查 etcd 中的实际数据
教程在最后的 Smoke Test 实验("Data Encryption" 一节)中给出了验证方法。先创建一个 generic secret:
kubectl create secret generic kubernetes-the-hard-way \
--from-literal="mykey=mydata"
再登上 server 机器,直接查看 etcd 中该 Secret 的原始字节:
ssh root@server \
'etcdctl get /registry/secrets/default/kubernetes-the-hard-way | hexdump -C'
输出形如:
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret|
00000010 73 2f 64 65 66 61 75 6c 74 2f 6b 75 62 65 72 6e |s/default/kubern|
00000020 65 74 65 73 2d 74 68 65 6d 68 61 72 64 2d 77 61 |etes-the-hard-wa|
00000030 79 0a 6b 38 73 3a 65 6e 63 3a 61 65 73 63 62 63 |y.k8s:enc:aescbc|
00000040 3a 76 31 3a 6b 65 79 31 3a 4f 1b 80 d8 89 72 f4 |:v1:key1:O....r.|
...
判断加密生效的依据是数据值的前缀 k8s:enc:aescbc:v1:key1,它逐段声明了存储形态:
k8s:enc:—— 该值经过 API Server 静态加密处理后存储;aescbc—— 使用的 provider 类型是 AES-CBC;v1—— 该 provider 的版本;key1—— 使用的是密钥列表中名为key1的密钥。
前缀之后的字节即为密文,mydata 等明文内容不会出现在 etcd 数据中。
密钥管理与后续演进注意事项
基于本实验的产物,有几点值得在实际操作中留意:
- 配置文件本身包含明文密钥。渲染后的
encryption-config.yaml以${ENCRYPTION_KEY}替换结果的形式保存了密钥的 Base64 值,因此应严格控制其文件权限(例如chmod 600),避免server机器上的其他账户读取。这是由"密钥内嵌于配置文件"这一静态加密方案的结构特点决定的运维要求; - 密钥轮转的扩展方式:
aescbcprovider 的keys是一个列表,从配置结构看,可以推断向列表中添加新的密钥项(如key2)后,新的写入与解密将引用新密钥,而旧数据仍按数据头部携带的key1标识找到对应密钥解密——这是该配置结构天然支持轮转能力的原因; - 加密范围有限:当前配置只对
secrets资源生效,集群状态中的其他对象(如 Deployment、ConfigMap 等)在 etcd 中依然是明文,这是本教程为聚焦教学而做的取舍。
相关资源索引
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00