为什么审计从配置文件起步,策略从默认拒绝起步
一句话总结
CIS Benchmark 从检查 /etc/kubernetes/manifests 中的文本文件开始,与网络策略必须从“默认拒绝”开始,原因相同:两者的默认值都向危险方向开放。
为什么需要它
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 | 用 ipBlock 的 except 阻止节点元数据 |
阻断凭据窃取路径 |
漏掉第 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 本身也会失效,因此必须使用 crictl 和 journalctl 检查。不知道这一点,会在考试中把自己困住。
记住几个常见选项。
| 目的 | 选项 |
|---|---|
| 阻止匿名访问 | --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.anonymous 与 authorization.mode 常被出题。修改此文件后,必须直接重启 kubelet(systemctl restart kubelet)。
修改后务必执行一行验证命令。 只有确认题目要求确实已经生效,答案才算完整。
下一项实验要做什么
在 cks-net 命名空间应用默认拒绝策略,叠加只允许 DNS 的 egress 规则,再通过 ipBlock.except 排除访问节点元数据的路径。创建类型为 kubernetes.io/tls 的 Ingress TLS Secret,最后亲自编写符合 CIS 建议的 kube-apiserver 清单,手动确认哪些选项必须存在、哪些选项必须不存在。