1. 认证介绍

Kubelet 在 10250 端口上提供了一个 HTTPS 的 API,通过这个 API 可以控制 Pods。Kubelet 对此 API 设置了几种认证方式,通过--authorization-mode指定:

  • ABAC (Attribute-Based Access Control)
  • RBAC (Role-based access control)
  • Webhook
  • Node
  • AlwaysAllow(不指定 --authorization-mode 时的默认值)
  • AlwaysDeny

在 Kubelet 的老版本(1.5 之前)不支持认证和授权,这就导致攻击者可以通过访问 10250 端口的 API 来获取容器权限。

2. 容器内命令执行

通过 /runningpods 获取正在运行的 Pod 列表:

需要的几个参数:namespace、pod_name 和 container_name。接着利用这几个参数请求 /run

即可在容器中运行任意命令,或者控制容器。

3. 信息泄漏

Kubelet HTTP API 源码文件在 /pkg/kubelet/server/server.go,代码如下:

ws.
    Path(logsPath)
ws.Route(ws.GET("").
    To(s.getLogs).
    Operation("getLogs"))
ws.Route(ws.GET("/{logpath:*}").
    To(s.getLogs).
    Operation("getLogs").
    Param(ws.PathParameter("logpath", "path to the log").DataType("string")))
s.restfulCont.Add(ws)

调用 getLogs 方法,接着调用 ServeLogs

func (kl *Kubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
    // TODO: whitelist logs we are willing to serve
    kl.logServer.ServeHTTP(w, req)
}

定位到:

func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) {
    if kl.logServer == nil {
        kl.logServer = http.StripPrefix("/logs/", http.FileServer(http.Dir("/var/log/")))
    }
    if kl.kubeClient == nil {
        glog.Warning("No api server defined - no node status update will be sent.")
    }

实际上就是启动了一个 FileServer,目录为 /var/log。所以可以通过访问 /logs 来列出 /var/log 的目录,会造成一些信息泄露的问题。

另外如果挂载了 /var/log 到容器内的话,可以通过在容器内创建软链接到 /,再利用 /logs 即可读取容器外的任意文件。不过在实际环境中我暂时还没有遇到过(难受)。

参考

  1. https://github.com/kayrus/kubelet-exploit
  2. https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/server/server.go