LabHub
学习 学习路径 课程

KCSA — Kubernetes 安全助理

攻击面地图 — 从哪儿进来

在 LabHub 中继续学习

一句话总结

Kubernetes 的入口点比想象中少:API server、kubelet、etcd、container registry, 以及 workload 本身。 其余大多只是通往这五者之一的路径。

概念图: 1)kube-apiserver(6443) · 2)kubelet(10250、10255) · 3)etcd(2379/2380) · 4)container registry 与 image supply chain

为什么需要了解这一点

“保护 cluster”之所以含糊,是因为没有列出要保护的对象。 攻击者会带着清单行动——扫描开放端口、尝试匿名访问、计算现有 credential 能够做什么。 防守方也要拥有同样的清单,双方才会对称。

工作原理

五个入口点

1)kube-apiserver(6443) 它是 cluster 唯一的大门,也是最有价值的目标。这里要看三件事: 是否开放匿名访问(--anonymous-auth)、authorization mode 是什么(AlwaysAllow 是灾难), 以及谁拥有什么 credential。

2)kubelet(10250、10255) 它是最常被遗忘的目标。kubelet 有自己的 API,其中包含 Pod 执行、exec 和日志。 如果设置了 --anonymous-auth=true,或 authorization mode 为 AlwaysAllow,那么任何能够通过网络到达 node 的人, 都可以在该 node 的全部 container 中执行命令。read-only port(10255)也不要求认证, 会原样暴露 Pod spec 与环境变量。所以 readOnlyPort=0 是 hardening 标准项目。

3)etcd(2379/2380) 整个 cluster state 都在这里。能读取 etcd 就能读取所有 Secret;能写入 etcd, 就能绕过 apiserver 的全部 authorization check,控制整个 cluster。 默认配置下 Secret 实际上以明文存储,因此获取 etcd backup file、snapshot 或 disk image 的人 也会得到同样的内容。

4)container registry 与 image supply chain 攻击者无需直接接入 cluster,而是让我们主动下载并运行其代码。 tag 会移动——myapp:1.4.2 昨天和今天可能指向不同 image。 如果不固定 digest,就无法确定究竟部署了什么。

5)workload 本身 已经在 cluster 内运行的 Pod 本来就在内部。它有多条向外路径: 使用挂载的 ServiceAccount token 调用 API,通过 hostPath 访问 node filesystem, 以 privileged container 控制 node,或通过 hostNetwork 进入 node 的 network namespace。

经常遗漏的表面

云原生独有的两种特性

动态性。 Pod 不断终止并重新启动,IP 随之改变。基于 IP 的 firewall rule 会失去意义, 因此需要转向以 label 和 identity(例如 SPIFFE)为基础的 policy。

证据会挥发。 被入侵的 Pod 可能已经消失。如果不把日志和 audit record 发送到 cluster 外部, 就不会留下可供调查的对象。

实际工作中的表现

作者博客总结的失败模式中,从 KCSA 角度看尤其有价值的一项,是 Kubernetes Secret 的本质。 执行 etcdctl get /registry/secrets/default/app-db | hexdump -C,可以看到明文的 key path; 一行 kubectl get secret ... -o jsonpath='{.data.password}' | base64 -d 就能得到密码。 base64 没有 key,只需一条命令便能还原,因此不是加密。

再叠加 RBAC 问题:如果在某个 namespace 中拥有 get secrets 权限, 就能读取该 namespace 的全部 Secret。 用作者的话说,“为了方便而给开发人员的 edit 权限, 实际上常常就是读取 production credential 的权限。”

另一个误解与环境变量有关。加载 .env 文件后再删除也没有用—— 内核已经为每个进程保存相应值,可以通过 /proc/PID/environ 读取。 同一 Pod 的 sidecar、node 访问者,以及通过 kubectl debug --target=app 连接的 debug container 都能看到同样的内容。关键在于:环境变量不是存储位置,而是传递方式。

下个测验将检查什么

本模块以测验结束。下一模块会从 apiserver 的 authentication chain 一直深入到 kubelet authorization, 逐组件进行分析;练习中则会亲自编写危险 flag 清单与 EncryptionConfiguration。