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

KCSA — Kubernetes 安全助理

网格的身份、集群的证书、两种眼睛

在 TT Lab 中继续学习

一句话总结

服务网格的 mTLS 为每个工作负载提供基于 ServiceAccount 的身份并加密链路,但 不做授权,对于没有被 sidecar 拦截的流量,它也保护不了。集群 PKI 有三个 CA(kubernetes-ca、etcd-ca、front-proxy-ca),下面挂着服务器证书和客户端证书,而 kubeadm 创建的客户端证书一年后就会过期。审计日志告诉你“谁向 API 请求了什么”,Falco 这类运行时检测告诉你“在节点和容器里实际发生了什么”,两者互相弥补对方的盲区。

为什么需要它

KCSA 的平台安全领域,问的不是集群本身,而是叠加在它之上的那些层。装了网格就安全,证书 kubeadm 会自己处理,打开了审计日志就什么都看得见——这三句话,都只对了一半。如果不知道对到哪里、错从哪里开始,在你以为有安全控制的地方就会留下漏洞。前面的模块里看到的 API 服务器绕过风险 文档写道,对 kubelet API 和 etcd 的直接访问 不会留在审计日志里,这就是那个漏洞的一个例子。

工作原理

服务网格——它替你做的和做不到的

根据 Istio 安全概念 文档,网格通过客户端一侧和服务器一侧的 Envoy 代理,对服务之间的通信做隧道转发。客户端 Envoy 与服务器 Envoy 进行 mTLS 握手的同时,通过 secure naming 检查,确认服务器证书里的 ServiceAccount 有没有权限运行那个服务,连接建立之后,由服务器 Envoy 对请求进行授权。所以网格 替你做的事 很清楚——为每个应用签发、分发、轮换 TLS 证书的工作,以及不靠 IP,而靠 ServiceAccount 身份来确认“这个请求来自哪个工作负载”的工作。

Istio 安全最佳实践 文档列出了 做不到的事。第一,默认的 PERMISSIVE 模式也接收明文,所以在改成 STRICT 之前,不能保证加密。第二,mTLS 只提供认证,所以任何持有有效证书的人都能触及服务,要锁住它,就得把 AuthorizationPolicy 设成 default-deny 的模式(例如在全部拒绝的 allow-nothing 策略之后,再加带条件的允许)。对于一个策略都没有的工作负载,Istio 会放行所有请求。第三,sidecar 只拦截 TCP,UDP 和 ICMP 会直接通过,包括 22 号端口在内的几个端口不在 inbound 捕获之列,而且应用和 sidecar 处于同一个网络和进程命名空间,应用可以删除重定向规则,从而绕过 sidecar。文档的结论是“相信所有流量都一定会被捕获,是不安全的”,所以建议叠加 NetworkPolicy 做纵深防御。AuthorizationPolicy 通过 selector 或 targetRefs 来选定适用对象,放在根命名空间里,就会作用于整个网格。

网格提供的 网格不提供的
工作负载身份(ServiceAccount 证书) 授权——需要另外配置 AuthorizationPolicy
sidecar 之间的加密 应用与 sidecar 之间的保护(同一个 Pod 内是明文)
STRICT 下拒绝明文 对 UDP、ICMP、被排除的端口、被绕过的流量的保护
基于 L7 属性的策略 L3/L4 边界——用 NetworkPolicy 补强

PKI——三个 CA 和过期时钟

PKI 证书与要求 文档把集群需要的证书分成两类。服务器证书在 API 服务器端点、etcd 服务器、每个节点的 kubelet,以及可选的 front-proxy 上。客户端证书用于 kubelet 向 API 服务器认证时、API 服务器向 etcd 认证时、控制器管理器、调度器、kube-proxy 向 API 服务器认证时,以及管理员。给这些证书签名的 CA 有三个。

CA 文件 用途
kubernetes-ca /etc/kubernetes/pki/ca.crt Kubernetes 通用 CA
etcd-ca /etc/kubernetes/pki/etcd/ca.crt 所有与 etcd 相关的证书
kubernetes-front-proxy-ca /etc/kubernetes/pki/front-proxy-ca.crt 用于 front-end proxy

此外还有用于给 ServiceAccount 令牌签名的密钥对 sa.key、sa.pub。如果想把 CA 做成层级结构,管理员可以从自己掌控的一个根 CA 创建中间 CA,把其余的签发交给 Kubernetes。在威胁模型里重要的,是每个 CA 的分量——etcd-ca 签发的客户端证书,就等于对 etcd 所有数据的访问(绕过风险文档:“etcd 所信任的 CA 所签发的任何证书,都允许对 etcd 内数据的完全访问”),而用 kubernetes-ca 创建 O=system:masters 的证书,就是超级用户。文档写道,kube-apiserver 的 kubelet 客户端证书可以使用权限较低的组,而不是 system:masters,kubeadm 使用的是 kubeadm:cluster-admins 组。

SAN 也是安全项目。如果用不在证书 hosts(SAN)里的名字去连接,TLS 验证就会失败,所以 kube-apiserver 证书里,除了主机名、主机 IP、advertise IP,还有负载均衡器的地址,以及 kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster、kubernetes.default.svc.cluster.local。以后如果更改了负载均衡器的地址,因为没有 SAN 而导致连接中断,是典型的事故。

kubeadm 证书管理 文档的第一句话就是时钟——kubeadm 创建的客户端证书 一年后过期。kubeadm certs check-expiration 会显示 /etc/kubernetes/pki 中的证书,以及写在 admin.conf、controller-manager.conf、scheduler.conf 里的客户端证书的过期时间,在文档的示例输出里,CA 剩余 9 年。续期有两条路。kubeadm 会在 控制平面升级时更新所有证书,所以一年内升级一次,就不用再做别的(要关掉,用 --certificate-renewal=false),手动则使用 kubeadm certs renew,如果是多副本的控制平面,要在所有节点上运行,运行后还得重启控制平面 Pod(不会动态重新加载)。同一篇文档里还有一点:kubelet 的 serving 证书默认是自签名的,所以外部服务无法用 TLS 验证它。

安全可观测性——审计日志与运行时检测

根据审计 文档,审计是按时间顺序记录集群的行为,回答什么事、什么时候、谁、对什么、在哪里发生的。记录从 kube-apiserver 内部开始。请求的每个阶段(RequestReceived、ResponseStarted、ResponseComplete、Panic)都会产生事件,由策略决定是否留下以及级别,再由后端(日志文件或 webhook)存储。策略把规则按顺序比较,第一条匹配的规则 决定级别,级别有 None、Metadata、Request、RequestResponse 四种。如果不指定 --audit-policy-file,什么都不会被记录,规则为 0 条的策略是非法的。审计会增加 API 服务器的内存使用。

审计日志的盲区,绕过风险文档已经写得很准确——直接访问 kubelet API、直接访问 etcd、运行时套接字,既不经过准入,也不经过审计日志。审计日志只显示 API 服务器看到的东西。

能看到这个盲区的眼睛,是运行时检测。根据 Falco 文档,Falco 是在主机、容器、Kubernetes、云环境中提供运行时安全的 CNCF 毕业项目,它实时解析内核的系统调用,与规则引擎比对,规则一旦被违反就发出告警。它会把容器运行时和 Kubernetes 的元数据附到事件上,告诉你“在哪个命名空间的哪个 Pod 里”,并通过插件接收系统调用之外的事件源。文档举出默认规则能抓到的有:利用特权容器的权限提升、用 setns 之类的工具更改命名空间、对已知目录的读写。告警要送到 SIEM 或数据湖,用于调查。

两只眼睛在不同的位置看同一件事。如果有人用 exec 进入 Pod,审计日志里会留下对 pods/exec 子资源的请求和请求者,而在 Falco 里,会留下那个容器里出现了 shell 的系统调用。如果审计日志里没有,而只有 Falco 看到了 shell,就可以怀疑走的是不经过 API 服务器的路径(kubelet API、运行时套接字、static Pod);如果审计日志里有,Falco 里却什么都没有,那就是请求被拒绝了,或者还没有执行。只打开一个,就无法做这种比对。

在现场相遇的样子

一年后的某一天,kubectl 报出 x509 错误。这是建好 kubeadm 集群后,一年多都没有升级的情况。admin.conf 里的证书和控制平面组件的证书在同一天过期,所以即使 API 服务器本身还在运行,也没有人能和它对话。如果把 check-expiration 放进定期检查,几个月前就能看到剩余时间。

审计日志里没有任何痕迹的入侵。如果通过 SSH 进入节点的攻击者,用运行时套接字启动了容器,API 服务器什么都看不到。这时唯一的记录,是看节点系统调用的运行时检测,以及节点的认证日志,这也是绕过风险文档要求限制对节点本身的访问的原因。

下一项测验要确认什么

测验会问:mTLS 不提供什么,sidecar 拦截不到的流量,三个集群 CA 的作用,kubeadm 证书的过期与续期,审计策略的规则评估方式,以及审计日志和运行时检测各自看到什么。