北美时间 11 月 26 日,Kubernetes 爆出严重安全漏洞,该漏洞由 Rancher Labs 联合创始人及首席架构师 Darren Shepherd 发现。该漏洞 CVE-2018-1002105(又名 Kubernetes 特权升级漏洞,https://github.com/kubernetes/kubernetes/issues/71411)被确认为 严重性 9.8 分(满分 10 分) ,恶意用户可以使用 Kubernetes API 服务器连接到后端服务器以发送任意请求,并通过 API 服务器的 TLS 凭证进行身份验证。这一安全漏洞的严重性更在于它可以远程执行,攻击并不复杂,不需要用户交互或特殊权限。
漏洞被发现并验证后,Kubernetes 快速响应并已经发布了修补版本 v1.10.11、v1.11.5、v1.12.3 和 v1.13.0-rc.1。仍在使用 Kubernetes v1.0.x 至 Kubernetes v1.9.x 版本的用户,被建议即刻停止并升级到修补版本。
本文由 该漏洞的发现者 、Rancher Labs 联合创始人及首席架构师 Darren Shepherd 所写。他描述了自己发现这一漏洞的完整经过,剖析了问题的机制与原理,并分享了相应的解决方案以及他本人对 Kubernetes、对开源社区的看法。
Rancher Labs 联合创始人及首席架构师 Darren Shepherd,同时也是 Docker 生态核心组织 Docker 治理委员会(DGAB)的全球仅有的四位个人顶级贡献者之一。
Amazon ALB 的问题
这一切都始于 2016 年,当时 Rancher Labs 刚发布了 Rancher 1.6。2016 年年中的时候,亚马逊发布了 ALB,这是一个新的 HTTP(7 层)负载均衡器。ALB 的设置比 ELB 容易得多,因此我们会建议用户使用 ALB。随后很快,我们开始收到有关 ALB 后端设置失败的报告,很多随机请求只会得到 401、403、404、503 的报错。然而,Rancher Labs 的团队 无法重现这些错误 ,我们从社区成员那里得到的日志都没有没法成为参考。我们看到了 HTTP 请求和响应,但无法将其与代码相关联。那个时候,我们只好认为是因为 ALB 发布不久、产品本身可能存在些错误。除了 ALB,我们之前从未遇到任何其他负载均衡器的问题。因此那时,我们只得最终告诉用户不要使用 ALB。
时间到了今年 8 月,又有 Rancher 社区成员向 Rancher 2.1 提交了同样的问题(https://github.com/rancher/rancher/issues/14931)。仍和以前一样,使用 ALB 会导致奇数 401 和 403 错误。这极大地引起了我的关注,因为 Rancher 1.x 和 2.x 之间没有共同的代码,而且 ALB 现在应该也已经相当成熟了。反复深入研究后,我发现 问题与不处理非 101 响应和反向代理缓存 TCP 连接有关 。若您想要真正理解这个问题,您必须了解 TCP 连接重用、websockets 如何使用 TCP 连接以及 HTTP 反向代理。
TCP 连接重用
在一种非常天真的 HTTP 方法中,客户端将打开 TCP socket,发送 HTTP 请求,读取 HTTP 响应,然后关闭 TCP socket。很快你就会发现你花了太多时间打开和关闭 TCP 连接。因此,HTTP 协议具有内置的机制,以便客户端可以跨请求重用 TCP 连接。
WebSockets
Websockets 是双向通信,其工作方式与 HTTP 请求/响应流不同。为了使用 websockets,客户端首先会发送 HTTP 升级请求,服务器会以 HTTP 101 Switch Protocols 响应来响应这一请求。收到 101 之后,TCP 连接将专用于 websocket。在 TCP 连接的剩余生命周期中,它是被认为是专用于该 websocket 连接的。这就意味着 此 TCP 连接永远不会被重新使用 。
HTTP 反向代理
HTTP 反向代理(负载均衡器是一种反向代理)从客户端接收请求,然后将它们发送到不同的服务器。对于标准 HTTP 请求,它只写入请求,读取响应,然后将响应发送到客户端。这种逻辑相当直接,而且 Go 也包含一个内置的反向代理:https://golang.org/pkg/net/http/httputil/#ReverseProxy。
相比之下 Websockets 就复杂一点。对于 websocket,你必须查看请求,看到它是一个升级请求,然后发送请求,读取 101 响应,然后劫持 TCP 连接,然后开始来回复制字节。对于反向代理,它不会在此之后查看连接的内容,它只是创建一个“废弃管道”。标准 Go 库中不存在此逻辑,许多开源项目都编写了代码来执行此操作。
错误所在
关于错误所在,太长不看版的解释是,Kubernetes 在启动“废弃管道”之前没有检查 101 响应。在代码的防御中,不检查 101 是挺常见的。(这也是我们上文所说的 Rancher 用户会发现的 Rancher 1.x 和 Rancher 2.x 的问题的原因,即使 Rancher 1.x 和 Rancher 2.x 使用的是完成不同的代码。)错误的场景如下:
客户端发送 websocket 升级请求
反向代理向后端服务器发送升级请求
后端服务器以 404 响应
反向代理启动复制循环并将 404 写入客户端
客户端看到 404 响应并将 TCP 连接添加到“空闲连接池”
在这种情况下,如果客户端重新使用 TCP 连接,它将向 TCP 连接写入请求,它将通过反向代理中的“废弃管道”并将其发送到前一个后端。通常这不会很糟糕,例如在负载均衡器的情况下,因为所有请求都会转到同一组同类后端。但是,当反向代理是智能的,并且是由其执行身份验证、授权和路由(即 Kubernetes 所做的全部工作)时,就会出现此问题。
安全漏洞
因为 101 未被处理,所以客户端最终使用 TCP 连接,该连接是对某些先前访问的后端服务的“废弃管道”。这将导致特权升级。问题是,Kubernetes 将仅在反向代理中执行许多请求的授权。这意味着如果我执行一个授权失败的 websocket 请求路由到一个 kubelet,我可以保持与该 kubelet 的持久连接,然后运行我选择的任何 API 命令,无论我是否被授权。例如,您可以在任何 pod 上运行 exec 并复制出 secrets。因此,在这种情况下, 已经授权的用户基本上可以获得对 kubelet 的完全 API 访问 (同样的事情适用于通过 kube-aggregation 运行的服务)。
当您添加另一个反向代理时,会出现另一个问题。在这种情况下,您将 HTTP 负载均衡器放在 Kubernetes API(非 4 层负载均衡器)之前。如果执行此操作,那个通过了身份验证的、运行着“废弃管道” 的 TCP 连接,将会被添加到一个任何用户都可以访问的空闲池中。那么,用户 A 创建了 TCP 连接,之后用户 B 仍可重新使用该连接。这样一来, 未经过身份验证的用户就可以访问您的 Kubernetes 集群了 。
此时你可能会感到恐慌,因为当然每个人都会在 kube-apiserver 前放置一个负载均衡器。唔……首先,您必须运行 HTTP 负载均衡器,而不是 TCP 负载均衡器。负载均衡器必须了解 HTTP 语义才能产生此问题。其次,幸运的是大多数反向代理并不关心 101 个回复。这就是为什么这个问题其实(在不少开源项目中)存在已久而未被发现的原因。 大多数负载均衡器在看到升级请求而非 101 响应后不会重用 TCP 连接 。所以,如果您会受到这一漏洞的影响,那么您的 Kubernetes 设置应该已经不可靠了,您应该能看到随机失败或无法完成的请求。至少我知道 ALB 就是这样工作的,所以你升级到了已修补该漏洞的 Kubernetes 版本之前,不要使用 ALB。
简而言之,Kubernetes 的这一安全漏洞会允许具有正确权限的任何经过身份验证的用户获得更多权限。如果您正在运行 硬件多租户集群(内含不受信任的用户) ,您确实应该担心并且及时应对。如果您 不担心用户主动互相攻击 (大多数多租户集群都是这样),那么不要惊慌,只需升级到已修补该漏洞的 Kubernetes 版本即可。最坏的情况,如果真的有未经身份验证的用户可以进入您的集群, 您的负载均衡器也有可能会阻止这种情况 。只要不是将 API 暴露给世界,并且有在其上放置一些适当的 ACL,也许你的集群也还是安全的。
Rancher 为 Kubernetes 保驾护航
对于使用 Rancher Kubernetes 平台的用户,你们更无须紧张。
对于 把集群部署在内网的用户 ,完全不需要过于担心此问题,因为外部无法直接入侵。
通过 Rancher2.0 或 RKE 部署的 kubernetes 集群的用户同样不用过于担心。因为通过 Ranche2.0 或 RKE 部署的集群默认是禁止和匿名用户访问 。针对通过 pod exec/attach/portforward 权限提权问题,目前 Kubernetes 发布通用的修复方法是通过升级到指定 Kubernetes 版本来修复,针对此 Rancher 也已经发布修复程序 ,具体修复方法请参考:https://forums.rancher.com/t/rancher-security-advisory-kubernetes-cve-2018-1002105/12598
感谢开源
我深刻地感到,这次 Kubernetes 的这个安全漏洞的最初发现、修复和最终交付,证明了开源社区强大的生命力。我第一次发现这个问题,也是因为 Rancher 的非付费开源用户给予我们的反馈。事实上, 我们已经确认了这一问题并没有影响 Rancher 2.x 的付费客户,因为 Rancher 的 HA 架构恰好否定了 ALB 的行为 ,但我们还是去研究并解决了这个问题,因为我们太爱我们的开源用户了。也正是在研究和修复这个问题的过程中,我发现了 Kubernetes 自身存在的安全隐患,并通过已建立的安全公开流程向 Kubernetes 社区反馈了该问题。
评论