- 文档/教程
【免费下载链接】kubernetes-the-hard-way
Bootstrap Kubernetes the hard way on Vagrant on Local Machine. No scripts.
Kubernetes 将集群状态、应用配置与 Secret 等敏感数据持久化在 etcd 中,本指南演示在 kubernetes-the-hard-way 本地集群搭建流程的第 06 步:生成一个 32 字节的加密密钥与一份encryption-config.yaml加密配置文件,并把该文件分发到两台控制平面节点,为后续 kube-apiserver 启用「静态加密(encryption at rest)」做好准备。读完本节,你将掌握 EncryptionConfig 的完整结构、aescbc与identityprovider 的协作语义,以及如何在 smoke test 阶段用hexdump验证 Secret 确实以密文形式落盘。
本节在整套实验中的位置
kubernetes-the-hard-way 的引导流程严格按序推进,每一节的产物都会被后续步骤消费。本节(第 06 步)处于如下位置:
| 前序步骤 | 本节内容 | 后续步骤 |
|---|---|---|
| 生成 Kubernetes 认证配置文件(kubeconfigs) | 生成数据加密密钥与encryption-config.yaml | 引导 etcd 集群 → 引导 Kubernetes 控制平面 |
本节的产出encryption-config.yaml会在第 08 步被kube-apiserver通过--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml参数引用(见 引导 Kubernetes 控制平面),并在第 16 步的 smoke test 中接受密文验证(见 冒烟测试:数据加密验证)。因此,本节虽然只有寥寥几条命令,却是集群数据安全的关键一环。
为什么需要加密 etcd 中的数据
Kubernetes 会在 etcd 中持久化多种数据:集群整体状态、应用配置(ConfigMap)、以及敏感程度最高的Secret。默认情况下这些数据以明文形式存储——任何能读取 etcd 数据文件或通过 etcd 客户端查询的人,都能直接看到 Secret 的原始内容。
Kubernetes 原生支持「数据静态加密(encryption at rest)」,即在数据写入 etcd 之前由 kube-apiserver 加密、读取时再解密,从而使落盘的数据始终以密文形态存在。本节生成的内容正是为了让 kube-apiserver 具备这一能力:
- 一个加密密钥——32 字节随机数据,base64 编码后作为对称加密密钥;
- 一份加密配置文件——声明「哪些资源需要加密」以及「使用哪个 provider、哪把密钥加密」。
注意:执行位置为
controlplane01(本实验将其设定为管理客户端节点),即文档内[//]: # (host:controlplane01)注释所标识的主机。
前置条件
- 已完成 证书颁发机构与 TLS 证书生成(第 04 步)与 kubeconfigs 生成(第 05 步);
- 已通过
vagrant ssh(VirtualBox 环境)或multipass shell(Apple Silicon 环境)登录到controlplane01; - 已按 安装客户端工具 配置好节点间的免密 SSH,使
controlplane01能对controlplane02执行scp/ssh。
第一步:生成加密密钥
在controlplane01上执行:
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)这条命令做了两件事:
head -c 32 /dev/urandom从 Linux 内核的随机源/dev/urandom中读取32 字节随机数据。32 字节即 256 比特,是 AES 等对称加密算法的主密钥长度标准;base64将二进制随机数编码为可打印的 base64 字符串,使其能安全嵌入 YAML 文件。
结果保存在 shell 环境变量ENCRYPTION_KEY中,供下一步 heredoc 写文件时直接引用。该变量仅存在于当前 shell 会话,重新登录后需要重新生成——这也是本实验刻意采用「裸命令」而非脚本的原因:让每一步都可复现、可理解。
第二步:创建加密配置文件
使用 heredoc 将加密配置写入encryption-config.yaml:
cat > encryption-config.yaml <<EOF kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: ${ENCRYPTION_KEY} - identity: {} EOF生成的文件内容如下(其中secret字段为 base64 随机串):
kind: EncryptionConfig apiVersion: v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: <32字节随机数据的base64编码> - identity: {}配置字段逐项解析
| 字段 | 取值 | 含义 |
|---|---|---|
kind | EncryptionConfig | 声明这是一份 Kubernetes 静态加密配置;kube-apiserver 通过该 kind 识别文件 |
apiVersion | v1 | 配置文件的 API 版本 |
resources | 数组 | 声明对哪些 API 资源启用加密。这里只列了secrets,即仅对 Secret 生效,ConfigMap、Event 等仍按默认行为存储 |
providers | 数组 | 声明加密 provider 及其优先级列表。列表顺序是有语义的(见下文) |
aescbc.keys[].name | key1 | 密钥的标识名。它会在 etcd 中随密文一同记录(形如k8s:enc:aescbc:v1:key1),用于告诉 kube-apiserver 解密时应选用哪把密钥,也是后续做密钥轮换的关键锚点 |
aescbc.keys[].secret | ${ENCRYPTION_KEY} | 真正的对称加密密钥(base64 编码的 32 字节随机数) |
identity: {} | 空对象 | 透传 provider,不加密直接存储,作为兜底解密 provider |
aescbc 与 identity 的协作语义
aescbc:使用 AES-CBC 模式对资源数据进行对称加密,并附带密钥标识前缀。本实验把它放在providers列表的第一位,意味着新建/写入的 Secret 一律先用key1这把密钥加密;identity:不加密、原样存储数据。它被放在第二位,充当解密兜底:当读取到历史上以明文写入的旧数据(例如在启用加密之前就已经存在的 Secret)时,kube-apiserver 会沿 provider 列表向下寻找能解密的 provider,最终由identity原样放行,避免老数据因「无法解密」而变得不可读。
这正是「第一 provider 用于加密新数据、其余 provider 用于解密存量数据」这一顺序语义在本配置中的体现。如果调换顺序把identity放在第一位,新写入的 Secret 将不会加密,验证阶段(见下文)就观察不到k8s:enc:aescbc前缀了。
顺带一提:密钥轮换的扩展思路
本节只创建了key1一把密钥。生产环境中做密钥轮换的通用做法是:在aescbc.keys数组的首位新增一把key2,同时保留key1:
providers: - aescbc: keys: - name: key2 # 新密钥:新写入数据用它加密 secret: <key2 的 base64> - name: key1 # 旧密钥:仍保留用于解密存量密文 secret: <key1 的 base64>这样新数据立即使用新密钥加密,而旧数据在轮换过渡期内仍可被解密读取。待存量数据全部重写(或主动重加密)之后,再移除旧密钥条目。本实验的key1命名与验证输出中的前缀一致,正是为了演示这一机制而设计的。
第三步:分发加密配置文件到两台控制平面节点
for instance in controlplane01 controlplane02; do scp encryption-config.yaml ${instance}:~/ done这里将encryption-config.yaml同时复制到controlplane01与controlplane02。原因是本实验搭建的是双控制平面高可用集群:两台节点各自运行一个 kube-apiserver,都可能承担 Secret 的写入或读取请求。只有两台节点的 apiserver 持有同一把key1和同一份 provider 顺序配置,才能保证「节点 A 加密写入的数据,节点 B 能够解密读取」,否则会出现跨节点读写失败或密文不可识别的问题。
第四步:移动到 apiserver 约定的配置目录
for instance in controlplane01 controlplane02; do ssh ${instance} sudo mkdir -p /var/lib/kubernetes/ ssh ${instance} sudo mv encryption-config.yaml /var/lib/kubernetes/ done两步分别完成:
mkdir -p /var/lib/kubernetes/——创建 Kubernetes 主数据目录。该目录之后还会承载pki/子目录(证书与私钥)以及各控制面组件的 kubeconfig(见 引导 Kubernetes 控制平面 中的sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/等步骤);sudo mv encryption-config.yaml /var/lib/kubernetes/——把配置文件从家目录移动到上述目录,路径恰好对应后续 kube-apiserver 单元文件中的--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml。
安全建议:
encryption-config.yaml内含对称加密密钥(base64 明文),等同敏感凭据。生产实践中建议进一步收紧其访问权限,例如sudo chmod 600 /var/lib/kubernetes/encryption-config.yaml,并仅允许 apiserver 运行账户读取;同时密钥本身应纳入密钥管理机制保护。本实验出于教学目的未显式 chmod,但你应知晓该文件不应被普通用户可读。
与后续步骤的衔接:kube-apiserver 如何启用加密
本节生成的配置文件本身不会自动生效,必须由 kube-apiserver 显式加载。在第 08 步「引导 Kubernetes 控制平面」中,两台控制平面节点会创建kube-apiserver.servicesystemd 单元文件,其ExecStart中包含(见 docs/08-bootstrapping-kubernetes-controllers.md):
--encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml该参数让 apiserver 在启动时读取/var/lib/kubernetes/encryption-config.yaml,此后每一条 Secret 的写入都会按 provider 列表执行加密。整个调用链可以概括为:
kubectl create secret → kube-apiserver → 读取 /var/lib/kubernetes/encryption-config.yaml → 命中第一个 provider aescbc / key1 加密 → 写入 etcd(键值带 k8s:enc:aescbc:v1:key1 前缀)同理,apiserver 也依赖本配置读取旧数据:读取时按 provider 列表顺序尝试,密文前缀决定实际使用哪把密钥,最终由aescbc或identity完成解密返回。
验证加密是否真正生效(Smoke Test 中的做法)
配置正确与否,在集群跑通后可以用第 16 步 smoke test 的方法直接验证(详见 冒烟测试:数据加密)。核心思路是:创建一个 Secret,然后直接从 etcd 中把它读出来,看是否已是密文。
在controlplane01上先创建一个测试 Secret:
kubectl create secret generic kubernetes-the-hard-way \ --from-literal="mykey=mydata"再通过 etcd 客户端读取并转储十六进制内容:
sudo ETCDCTL_API=3 etcdctl get \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/etcd/ca.crt \ --cert=/etc/etcd/etcd-server.crt \ --key=/etc/etcd/etcd-server.key \ /registry/secrets/default/kubernetes-the-hard-way | hexdump -C输出中关键的一行(十六进制转储的 ASCII 列)为:
00000030 79 0a 6b 38 73 3a 65 6e 63 3a 61 65 73 63 62 63 |y.k8s:enc:aescbc|也就是说,etcd 中该键对应的值以k8s:enc:aescbc:v1:key1前缀开头,hexdump里能看到明文的人类可读部分只有/registry/secrets/...键路径与加密元数据前缀,mykey=mydata的真实内容已被aescbcprovider 用key1加密成不可读的随机字节。
这段前缀k8s:enc:aescbc:v1:key1本身就是一份「自描述」:enc表示静态加密开启,aescbc表示所用 provider 算法,v1表示 provider 版本,key1表示加密所用密钥的名称——与你在本节配置文件中定义的name: key1一一对应。验证完毕后清理测试资源:
kubectl delete secret kubernetes-the-hard-way若在 smoke test 中看到identity前缀(k8s:enc:identity)或完全没有前缀,说明 Secret 未加密写入,应回头检查本节的 provider 顺序与 apiserver 的--encryption-provider-config参数是否生效。
关键要点小结
- 加密密钥:32 字节(256 位)随机数据经 base64 编码,保存在
ENCRYPTION_KEY变量中,用于 AES-CBC 对称加密; - 加密配置:
kind: EncryptionConfig、apiVersion: v1,对secrets资源启用加密;provider 列表首位的aescbc负责加密新数据,第二位的identity负责解密存量明文数据; - 高可用一致性:
encryption-config.yaml必须同时部署到controlplane01与controlplane02,保证两个 apiserver 用同一把密钥读写 Secret; - 落地位置:
/var/lib/kubernetes/encryption-config.yaml,与 kube-apiserver 的--encryption-provider-config参数路径严格一致(见 docs/08-bootstrapping-kubernetes-controllers.md); - 生效时机:本步仅完成「生成与分发」,加密在 apiserver 启动后才会实际生效,最终通过 smoke test 中 etcd 键值前缀
k8s:enc:aescbc:v1:key1验证(见 docs/16-smoke-test.md)。
下一步进入 引导 etcd 集群,将两台控制平面节点上的 etcd 组装为高可用存储后端;上一步回顾可查看 生成 Kubernetes 认证配置文件。
- 文档/教程
【免费下载链接】kubernetes-the-hard-way
Bootstrap Kubernetes the hard way on Vagrant on Local Machine. No scripts.
相关推荐
12 个 Doctrine ORM 常见坑与解决方案:新手必看的 FAQ 排错指南
12 个 Doctrine ORM 常见坑与解决方案:新手必看的 FAQ 排错指南 如果你正在使用 Doctrine ORM (PHP 生态最流行的对象关系映射
云原生教程3步替换游戏里的DLSS版本:DLSS Swapper上手指南
3步替换游戏里的DLSS版本:DLSS Swapper上手指南 朋友的游戏和你的硬件一模一样,画面却锐利一圈、帧数也更稳。问了一圈,答案不是新显卡,而是游戏里那
桌面应用Kubernetes The Hard Way 项目中的加密配置问题解析
Kubernetes The Hard Way 项目中的加密配置问题解析 在Kubernetes集群部署过程中,数据加密是一个至关重要的安全环节。Kuberne
教程云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考