K8s 是怎么被攻破的:威胁模型与攻击树入门
Posted on 三 29 7月 2026 in Tech
| Abstract | K8s 是怎么被攻破的:威胁模型与攻击树入门 |
|---|---|
| Authors | Walter Fan |
| Category | Cloud Native Security |
| Status | v1.0 |
| Updated | 2026-07-29 |
| License | CC-BY-NC-ND 4.0 |
K8s 是怎么被攻破的:威胁模型与攻击树入门
前阵子帮团队看一个 K8s 集群的安全,我发现一个有意思的现象:大家都在装各种扫描器、连各种告警,可要是问一句"攻击者到底会怎么打进来、一步步走到哪一步",多半答不上来。
这就有点像家里装了一屋子摄像头,却没想过"小偷最可能从哪扇窗户进"。工具是"术",威胁模型才是"道"。 你得先知道对手会怎么想,才知道该把钱花在哪把锁上。
这篇咱们不堆工具,就聊两件事:K8s 的威胁模型(这座"城"有哪些门、哪些墙、哪些值钱东西),以及攻击树(攻击者从一个小 Pod 出发,怎么一格一格爬到"城主")。内容主要参考 ControlPlane 为 CNCF 做的 Kubernetes 威胁模型,以及 Kubernetes 官方 2019 年的安全审计(含官方的《Attacking Kubernetes》攻击树和威胁模型报告)——都是公开、权威的资料,我尽量翻成大白话。
提醒:本文是防御视角的科普,讲清"攻击者会怎么想"是为了知道"我该怎么守"。所有内容用于加固自己的系统,别拿去干坏事。
先把这座"城"画出来
要谈威胁,先得知道保护的是什么。K8s 的核心组件,我用一座城来打比方:
flowchart TB
subgraph CP["控制平面 Control Plane(市中心)"]
API["kube-apiserver<br/>市政大厅:所有请求的唯一入口"]
ETCD["etcd<br/>档案馆:存着全部配置和 Secrets"]
SCHED["scheduler<br/>调度局:决定 Pod 去哪个节点"]
CM["controller-manager<br/>执法队:让现状逼近期望状态"]
API <--> ETCD
API <--> SCHED
API <--> CM
end
subgraph N1["工作节点 Node(一个街区)"]
KUBELET["kubelet<br/>物业管家:管本街区的 Pod"]
PROXY["kube-proxy<br/>门卫:管网络转发"]
CRI["容器运行时 containerd<br/>包工头:真正把容器跑起来"]
POD1["Pod<br/>租户:你的业务容器"]
KUBELET --> CRI --> POD1
KUBELET --> PROXY
end
USER["开发者 / CI<br/>(拿 kubeconfig 的人)"] -->|kubectl / HTTPS| API
API -->|下发指令| KUBELET
classDef cp fill:#1E3A5F,stroke:#12263a,color:#fff;
classDef node fill:#E8F4EA,stroke:#2C7A3F,color:#1a1a1a;
classDef ext fill:#FCF3CF,stroke:#B7950B,color:#1a1a1a;
class API,ETCD,SCHED,CM cp;
class KUBELET,PROXY,CRI,POD1 node;
class USER ext;
几个关键角色,记住它们的"身份",后面攻击树全靠它们:
- kube-apiserver(市政大厅):整个集群唯一的正门。所有人——开发者、其他组件、每个节点——都得通过它办事。它管三件事:认证(你是谁)、鉴权(你能干啥)、准入控制(这事合不合规矩)。攻破它,基本等于拿下全城。
- etcd(档案馆):一个键值数据库,存着集群的全部状态——包括所有的 Secrets(数据库密码、Token、证书)。注意:K8s 里的 Secret 默认只是 base64 编码,不是加密。谁能直接读 etcd,谁就拿到了全城的钥匙串。
- kubelet(物业管家):每个节点上都有一个,负责按 apiserver 的指令把 Pod 跑起来。它有个要命的接口,后面会讲。
- 容器运行时(包工头):真正干活的,比如 containerd。容器"逃逸"到宿主机,逃的就是它这一层。
- kubeconfig / ServiceAccount Token(通行证):人用 kubeconfig,Pod 用 ServiceAccount Token。这些"通行证"一旦泄露,攻击者就能冒充你办事。
一句话:K8s 安全的核心,就是守住 apiserver 这道正门、看住 etcd 这本账、别让通行证乱跑。
威胁模型:用 STRIDE 给每个组件"过筛子"
知道有哪些资产后,怎么系统地想"它们会遭什么罪"?业界常用一个叫 STRIDE 的清单——它是六类威胁的首字母,你可以理解成给每个组件挨个问六个问题:
| 字母 | 威胁 | 一句话理解 | K8s 里的典型例子 |
|---|---|---|---|
| S | Spoofing 假冒 | 有人冒充别人 | 伪造 ServiceAccount Token 冒充某个 Pod |
| T | Tampering 篡改 | 数据被偷改 | 直接改 etcd 里的对象;篡改镜像 |
| R | Repudiation 抵赖 | 干了坏事不认账 | 关掉审计日志,事后查无对证 |
| I | Information Disclosure 泄露 | 该保密的看见了 | 从 etcd / 环境变量里读出 Secrets |
| D | Denial of Service 拒绝服务 | 把服务打瘫 | 一个 Pod 吃光节点资源,拖垮 kubelet |
| E | Elevation of Privilege 提权 | 小权限变大权限 | 从普通 Pod 一路爬到 cluster-admin |
STRIDE 不是让你背首字母,而是给你一副"防御眼镜":看到任何一个组件,就挨个问"它会不会被假冒、被篡改、被抵赖、被泄露、被打瘫、被提权"。
下面挑三个最要害的组件,戴上这副眼镜看看。
apiserver:守好正门这一关
apiserver 是唯一入口,所以它的认证 → 鉴权 → 准入三道关,任何一道松了都出大事:
- Spoofing / Elevation:如果 RBAC(基于角色的权限控制)配得太松——比如给某个 ServiceAccount 绑了
cluster-admin——那这个身份被偷了就等于全盘皆输。最常见的翻车不是被"攻破",而是权限本来就给大了。 - Information Disclosure:早期集群允许匿名访问(
--anonymous-auth=true)或开放未授权的只读端口,等于正门虚掩。 - Denial of Service:apiserver 被海量请求打满,整个集群就"失联"了——所有组件都靠它协调。
etcd:这本账最值钱
官方审计里反复强调:etcd 的数据分类是 Critical。 因为它存着一切,尤其是 Secrets。
- Information Disclosure:如果 etcd 没开加密(
--encryption-provider-config),或者备份文件随手扔在某个没人管的对象存储里,那一份 etcd 快照 = 全城钥匙。 - Tampering:能直接写 etcd,就能凭空造出一个 cluster-admin 绑定,绕过 apiserver 所有的准入检查。
- 所以 etcd 必须:只让 apiserver 访问、双向 TLS、静态加密、备份也要加密。
kubelet:那个"要命的接口"
kubelet 有个历史包袱:它自带一个 API(默认 10250 端口),能执行 exec(进容器执行命令)、读日志、列 Pod。官方审计里明确写了,kubelet 到 apiserver 之间默认可以不校验证书——配置不当时,这个端口若开放了未授权访问,攻击者就能直接命令 kubelet 去任意 Pod 里执行命令。
- Elevation:拿下 kubelet ≈ 拿下这个节点上所有 Pod,包括别人的、可能权限更高的 Pod。
- 防御要点:kubelet 开启鉴权与认证(
--authorization-mode=Webhook、--anonymous-auth=false),关掉不必要的只读端口。
攻击树:从一个 Pod 爬到"城主"
威胁模型告诉你"哪里有风险",攻击树告诉你"攻击者会怎么把这些风险串成一条路"。
攻击树的读法很简单:根节点是攻击者的终极目标,往下是达成它的各种途径,一层层拆到具体手法。 我们设一个最经典的目标——拿到 cluster-admin(当上城主),看攻击者有哪些爬法:
flowchart TD
GOAL["🎯 终极目标<br/>拿到 cluster-admin"]
GOAL --> A["路径 A<br/>偷通行证"]
GOAL --> B["路径 B<br/>从 Pod 里往外爬"]
GOAL --> C["路径 C<br/>直捣档案馆 etcd"]
GOAL --> D["路径 D<br/>供应链下毒"]
A --> A1["翻到泄露的<br/>kubeconfig / Token<br/>(Git、日志、CI 变量)"]
A --> A2["RBAC 配太松<br/>普通身份能建高权绑定"]
B --> B1["攻破一个业务 Pod<br/>(Web 漏洞 / RCE)"]
B1 --> B2["读挂载的<br/>ServiceAccount Token"]
B1 --> B3["容器逃逸到宿主机<br/>(特权容器 / 挂了<br/>docker.sock / 内核漏洞)"]
B2 --> B4["Token 权限过大<br/>直接调 apiserver 提权"]
B3 --> B5["拿到节点<br/>= 拿到本节点所有 Pod"]
B5 --> B6["攻击 kubelet 10250<br/>横向到其他 Pod"]
C --> C1["etcd 未加认证 / 暴露"]
C --> C2["拿到 etcd 快照备份"]
C1 --> C3["直接读 / 写全部 Secrets"]
C2 --> C3
D --> D1["恶意镜像 / 依赖<br/>混进流水线"]
D --> D2["被投毒的 Helm Chart<br/>/ Operator"]
A1 --> WIN["👑 达成"]
A2 --> WIN
B4 --> WIN
B6 --> WIN
C3 --> WIN
D1 --> WIN
D2 --> WIN
classDef goal fill:#7B241C,stroke:#511414,color:#fff;
classDef win fill:#1E3A5F,stroke:#12263a,color:#fff;
class GOAL goal;
class WIN win;
真实世界里,最常走的是路径 B——攻击者很少一上来就能碰到 etcd 或偷到 admin 的 kubeconfig,更现实的是:先靠一个 Web 漏洞拿下一个不起眼的业务 Pod,然后:
- 读 Token:Pod 默认会把自己的 ServiceAccount Token 挂在
/var/run/secrets/kubernetes.io/serviceaccount/里。攻击者第一件事就是读它。 - 看这个 Token 能干啥:如果它权限过大(比如能
list secrets、能create pod),那基本就赢了一半。 - 尝试逃逸:如果这个 Pod 是特权容器(
privileged: true)、或者挂载了宿主机的docker.sock、或者挂了宿主机根目录,那攻击者能直接"逃"到宿主机,拿下整个节点。 - 横向移动:拿下一个节点后,通过 kubelet 去碰这个节点上别人的 Pod——其中说不定就有个权限大的。
你看,每一步都不算"高深漏洞",但一串小疏忽连起来,就是一条从 Pod 到城主的通天梯。这正是攻击树的价值:它逼你看清那条"链",而不是只盯着单个孤立的洞。
信任边界:真正该设防的那几道"墙"
把攻击树倒过来看,就知道钱该花在哪。K8s 里最关键的几道信任边界(trust boundary,即"跨过这条线就该重新验明正身"的地方):
flowchart LR
EXT["外部世界<br/>用户 / 互联网"]
POD["Pod 内部"]
NODE["宿主机节点"]
CP["控制平面"]
EXT -.->|"墙①<br/>认证+RBAC"| CP
POD -.->|"墙②<br/>容器隔离<br/>防逃逸"| NODE
NODE -.->|"墙③<br/>kubelet 鉴权"| CP
CP -.->|"墙④<br/>只有 apiserver<br/>能碰 etcd"| ETCD["etcd"]
classDef b fill:#FCF3CF,stroke:#B7950B,color:#1a1a1a;
class EXT,POD,NODE,CP,ETCD b;
- 墙① 外部 → 控制平面:靠认证 + RBAC。这是最常被配松的一道。
- 墙② Pod → 宿主机:靠容器隔离,防的就是"逃逸"。别用特权容器、别乱挂 hostPath 和 socket。
- 墙③ 节点 → 控制平面:靠 kubelet 鉴权,防的是"拿下一个节点后进一步渗透控制平面"。
- 墙④ 控制平面 → etcd:这本账只能让 apiserver 碰,且必须加密。
加固清单:照着做,堵住攻击树上的主要路径
不求一步到位,按"性价比"排了个序,从最容易翻车、最好补的开始:
第一优先级(几乎人人该做)
- [ ] RBAC 最小权限:别乱给
cluster-admin;给 ServiceAccount 的权限按需最小化。定期审一遍谁绑了高权角色。 - [ ] 默认不挂 Token:Pod 用不到 apiserver 就设
automountServiceAccountToken: false,直接砍掉攻击树路径 B 的第一步。 - [ ] 禁掉特权容器:用准入控制(Pod Security Admission 的
restricted级别)拦住privileged、hostPath、hostNetwork这些危险配置。 - [ ] 别把 kubeconfig / Token 写进 Git、日志、CI 明文变量——这是路径 A 最常见的入口。
第二优先级(进阶加固)
- [ ] etcd 静态加密 + 双向 TLS + 备份加密:守住那本最值钱的账。
- [ ] kubelet 关匿名、开鉴权:
--anonymous-auth=false、--authorization-mode=Webhook。 - [ ] apiserver 关匿名访问、开审计日志:让攻击者"抵赖不掉"(对付 STRIDE 里的 R)。
- [ ] NetworkPolicy 做网络分段:默认 K8s 里 Pod 之间是全通的,加策略限制横向移动。
第三优先级(纵深防御)
- [ ] 镜像来源可信 + 签名验证:堵供应链路径 D。
- [ ] 运行时检测(如 Falco 一类):Pod 里突然读 Token、起 shell、连外网,能当场告警。
- [ ] 定期跑威胁建模复盘:集群在变,威胁模型也得跟着更新——参考 ControlPlane 和官方审计,隔段时间重画一次你自己的攻击树。
收个尾
安全这件事,最怕的不是"有漏洞",而是"不知道自己在守什么、对手会怎么打"。
威胁模型和攻击树的价值,就是把模糊的"不安全焦虑",变成一张能指着说话的图:这是我们的城,这是几道门,这是对手最可能爬的那条梯子,我们先把这几格堵上。
咱们做工程的,都懂一个道理——先想清楚,再动手,比闷头装十个工具管用。K8s 安全也是一样:画出你自己的那棵攻击树,你就知道下一块钱该花在哪把锁上了。
图难于其易,为大于其细。守一座城,从堵住那个不起眼的 Pod 开始。
参考资料:
- ControlPlane, Kubernetes Threat Modelling
- Kubernetes SIG-Security, 2019 Security Audit(含 Threat Model 与 Attacking Kubernetes 攻击树)
- Kubernetes 官方文档, Securing a Cluster
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。 欢迎在我的个人网站 https://www.fanyamin.com 访问原文并评论。