LabHub
学习 学习路径 课程

CKS — Kubernetes 安全专家

为什么审计从配置文件起步,策略从默认拒绝起步

在 LabHub 中继续学习

一句话总结

CIS Benchmark 从检查 /etc/kubernetes/manifests 中的文本文件开始,与网络策略必须从“默认拒绝”开始,原因相同:两者的默认值都向危险方向开放。

概念图: 修改静态 Pod 清单后,kubelet 会自动重新启动组件。 · 该组件会完全无法启动 · crictl 和 journalctl 检查 · 记住几个常见选项。

为什么需要它

Kubernetes 选择了对开发者友好的默认值。Pod 可以与任何 Pod 通信;API 服务器会把匿名请求接收为名叫 system:anonymous 的用户;kubelet 过去还能开放无需认证的只读端口。这些默认值都具有“未配置就保持开放”的特点。因此,安全检查的第一个问题不是“阻止了什么”,而是“还有什么没有阻止”。

CIS Benchmark 与 kube-bench 从配置文件审计开始,也是这个原因。大多数控制平面组件的行为,都由一份静态 Pod 清单中的 command 数组决定。仅仅缺少 --anonymous-auth=false 这一项,就意味着任何能够到达集群的人都可以在未经认证的情况下探测 API。读取一行文本比观察运行进程更快,最重要的是还能留下证据。

工作原理

NetworkPolicy 是白名单。某个 Pod 没有任何策略时,会允许全部通信。一旦至少一个策略选中该 Pod,策略 policyTypes 中列出的方向就会变成“只允许明确写出的内容”。因此,实际工作中的顺序始终如下。

顺序 策略 效果
1 podSelector: {} + policyTypes: [Ingress, Egress],无规则 阻止整个命名空间
2 只开放 DNS egress(发往 kube-dns 的 UDP/TCP 53) 恢复名称解析
3 只开放必需 Pod 对之间的 ingress 最小连接
4 ipBlockexcept 阻止节点元数据 阻断凭据窃取路径

漏掉第 2 步是最常见的事故。应用默认拒绝后,DNS 查询也属于 egress,会同时被阻断。应用不会以“连接被拒绝”退出,而会以“找不到名称”失败,因此往往要过很久才发现原因是网络策略。

ipBlock.except 是 CKS 反复考查的知识点。云实例元数据地址 169.254.169.254 可能在无需认证的情况下返回节点 IAM 凭据。一个 Pod 被攻破,就可能把该节点的全部云权限交给攻击者。即使把 egress 开放到 0.0.0.0/0,也应使用 except 单独排除这个地址。

实际现场中的情况

作者的 7 节点家庭实验室以 eBPF 模式运行 Cilium 1.20.1,完全没有安装 kube-proxy。在这里确认到:相同 IP、相同 80 端口,GET 返回 200,POST 却返回 403。没有注入 sidecar,应用代码也未改变。只查看 L3/L4 的基本 NetworkPolicy 无法做到这一点,这说明 CNI 的选择就是安全表达能力的选择。反之,在 Flannel 默认配置这类根本不实现 NetworkPolicy 的 CNI 上,无论策略对象写得多仔细,也不会阻止任何流量。对象创建成功与流量确实被阻断,是两件不同的事。

同一家庭实验室还有一次更痛苦的配置文件事故。DHCP 把控制平面 IP 从 10.0.0.111 改为 10.0.0.120,而 apiserver 证书的 SAN 中没有新地址。etcd 和 kube-apiserver 试图绑定不存在的地址,陷入 CrashLoopBackOff;kube-scheduler 与 controller-manager 绑定 127.0.0.1,进程却仍正常存活。这种“一半仍活着”的状态最难诊断。这次事故让人切身体会到,一份文件中的数值足以决定整个集群的生死。

考试中真正需要动手的位置

CKS 的集群设置领域通常以“修改配置文件并恢复组件”的形式出现。有几个位置必须形成肌肉记忆。

修改静态 Pod 清单后,kubelet 会自动重新启动组件。 /etc/kubernetes/manifests/ 下的文件一旦修改就会生效。语法写错时,该组件会完全无法启动,因此修改前要保留副本。

cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# 고친 뒤
crictl ps | grep apiserver          # 새 컨테이너가 떴는지
journalctl -u kubelet -f            # 안 뜨면 이유가 여기 남는다

如果错误修改 API 服务器,kubectl 本身也会失效,因此必须使用 crictljournalctl 检查。不知道这一点,会在考试中把自己困住。

记住几个常见选项。

目的 选项
阻止匿名访问 --anonymous-auth=false
审计日志 --audit-policy-file--audit-log-path--audit-log-maxage
etcd 加密 --encryption-provider-config
准入控制 --enable-admission-plugins=NodeRestriction,...

审计策略文件也必须一并挂载。 只添加选项而没有增加 volumes/volumeMounts,API 服务器会因为找不到文件而无法启动。这是静态 Pod 中最常见的错误。

启用 etcd 加密后,必须重新写入现有数据才能生效。

kubectl get secrets -A -o json | kubectl replace -f -

还要检查 kubelet。 /var/lib/kubelet/config.yaml 中的 authentication.anonymousauthorization.mode 常被出题。修改此文件后,必须直接重启 kubeletsystemctl restart kubelet)。

修改后务必执行一行验证命令。 只有确认题目要求确实已经生效,答案才算完整。

下一项实验要做什么

cks-net 命名空间应用默认拒绝策略,叠加只允许 DNS 的 egress 规则,再通过 ipBlock.except 排除访问节点元数据的路径。创建类型为 kubernetes.io/tls 的 Ingress TLS Secret,最后亲自编写符合 CIS 建议的 kube-apiserver 清单,手动确认哪些选项必须存在、哪些选项必须不存在。