TT Lab
开始
学习 学习路径 课程

KCSA — Kubernetes 安全助理

打开 etcd,密码以明文掉了出来

在 TT Lab 中继续学习

目标

亲自打开集群唯一的真相存储 etcd,用眼睛确认 Secret 是以明文存储的。并对比观察:RBAC 挡住的只是经过 apiserver 的 API 路径,直接连到 etcd 就绕过了这条边界。

为什么重要

Kubernetes 的所有对象最终都存储在 etcd 里。Secret 的 data 值显示为 base64,看起来像是被遮住了,但 base64 是编码,不是加密。如果不打开静态加密(encryption at rest),任何能够触及 etcd 的人,都能原样读到密码原文。

另一方面,RBAC 虽然强大,但它的控制 只适用于经过 apiserver 的请求。对于直接访问节点、etcd 数据目录,或者没有设置认证的 etcd 端口的路径,RBAC 无法介入。所以必须用 TLS、双向认证和静态加密,另外保护 etcd 本身。这个实验先以没有防御时的样子,展示“为什么需要这些”。

步骤

  1. 创建命名空间 kcsa-etcd。
  2. 创建 Secret db-cred(password)和 ConfigMap app-cfg(mode)。
  3. 不带证书连接 etcd 端点,把状态保存成文件。
  4. 直接从 etcd 读取 Secret,把密码原文提取到文件里。
  5. 判定该值是明文而不是加密,并记录下来。
  6. 用只读取 ConfigMap 的最小权限 ServiceAccount 确认 RBAC 边界。
  7. 整理保护 etcd 的三项控制(client TLS、peer TLS、静态加密)。
  8. 把观察结果以账簿的形式留在 /root/kcsa-etcd/report.txt 中。

参考

打开观察对象命名空间

创建命名空间 kcsa-etcd。你要直接从 etcd 里窥视这个空间里的 Secret。

命名空间用 kubectl create namespace 创建。想让它运行多次也安全,就用 --dry-run=client -o yaml | kubectl apply -f -。

把一个密码托付给集群

在命名空间 kcsa-etcd 中创建 generic Secret db-cred,其中放入字面量 password=Pl4inEtcd!。在同一个命名空间中创建 ConfigMap app-cfg,放入键 mode=prod。

用 kubectl create secret generic <이름> --from-literal=키=값(占位符依次为名称、键与值)创建 Secret,用 kubectl create configmap <이름> --from-literal=키=값(占位符依次为名称、键与值)创建 ConfigMap。Secret 的 data 值是以 base64 编码存储的(这不是加密)。

不带证书就能直接连到 etcd

用 etcdctl 把 etcd 端点的状态以表格形式取出,保存到 /root/kcsa-etcd/etcd-status.txt。请确认不需要客户端证书就能连到 127.0.0.1:2379。

用 ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 endpoint status -w table 查看状态。这个集群的 etcd 没有设置 TLS 和认证,所以不带证书选项就能直接响应。把输出重定向到文件。

从存储底层捡起密码

直接读取 etcd 中存储的 db-cred Secret,只把其中的密码字符串写入 /root/kcsa-etcd/leaked.txt(只写值,不带多余的换行)。文件内容必须与真正的密码完全相同。

Secret 在 etcd 键 /registry/secrets/<네임스페이스>/<이름>(占位符依次为命名空间与名称)之下。etcdctl get <키>(占位符为键)的结果里混有二进制,所以用 grep -a 只筛出文本。值是什么,要你亲自找出来——把 Secret 的 data 做 base64 解码,就能知道原文,再确认这个字符串在 etcd 原始数据里也是原样存在的。

判定这不是加密

判定放在磁盘(etcd)上的那个值是否是加密的。如果没有加密,就在 /root/kcsa-etcd/encrypted.txt 里只写一个单词 no。

base64 是编码,不是加密。如果在 etcd 原始数据里用 grep 能直接抓到密码原文,就说明静态加密(encryption at rest)是关着的。判定结果请用小写的一个单词写下来。

RBAC 只守 API 路径

在命名空间 kcsa-etcd 中创建 ServiceAccount apponly 和 Role cfg-only(只对 ConfigMap 有 get、list),并把这个 Role 绑定到 apponly。这样 apponly 应该能读 ConfigMap,但读不了 Secret。

用 kubectl create role <이름> --verb=get --verb=list --resource=configmaps(占位符为名称)创建最小权限的 Role,用 kubectl create rolebinding 把它绑定到 ServiceAccount。可以用 kubectl auth can-i <동사> <자원> -n <ns> --as=system:serviceaccount:<ns>:<sa>(占位符依次为动词与资源)确认实际的判定。

写下保护 etcd 的三件事

以阅读材料为依据,把保护 etcd 的三项控制,恰好写成三行,保存到 /root/kcsa-etcd/mitigations.txt——client-tls=required、peer-tls=required、encryption-at-rest=required。

etcd 的安全有三条轴:客户端与 etcd 之间的 TLS 和双向认证,etcd 节点之间(peer)的 TLS,以及 apiserver 的静态加密(EncryptionConfiguration)。三个键都写成 required。键名和拼写必须与指示原样一致。

把观察到的以账簿的形式留下来

在 /root/kcsa-etcd/report.txt 中恰好写四行——etcd-auth=none、secret-on-disk=plaintext、rbac-blocks-apponly=yes、etcd-bypasses-rbac=yes。值必须与前面步骤观察到的实际状态一致。

评分器会把这四行与实际状态核对。请用前面步骤的结果来填写:etcd 是否不经认证就连上了(none)、Secret 是否以明文存放(plaintext)、apponly 是否读不了 Secret(yes),以及直接访问 etcd 是否绕过了那条 RBAC 边界(yes)。