云原生安全-配置不当或未授权风险
1.Kubernetes集群架构简介
1.1 概述
Kubernetes 集群架构是一个由控制平面(Control Plane)和工作节点(Worker Nodes)组成的分布式系统,旨在自动化容器的部署、扩展和管理。
1.2 整体架构

控制平面:
- API Server(kube-apiserver)
- 集群状态存储(etcd)
- 调度器(kube-scheduler)
- 控制器管理器(kube-controller-manager)
- 云控制管理器(cloud-controller-manager)(可选)
工作节点:
- Kubelet
- Kube Proxy
- 容器运行时(Container Runtime)
一个 Kubernetes 集群的机器节点有两种角色——Master 和 Node,都可由一个或多个节点组成,且同一个节点可以既是 Master 也是 Node。其中 Master 节点负责全局决策、资源调度、Node 与 Pod 管理等等,属于管控节点;Node 节点为工作机器,负责运行容器化应用。
1.3 控制平面组件
控制平面负责管理集群的全局状态、调度任务以及响应集群事件。通常由多个高可用组件组成,以下是核心组件:
1.3.1 API Server(kube-apiserver)
kube-apiserver 是公开 Kubernetes API 的 Kubernetes 集群的中心枢纽。它具有高度可扩展性,可以处理大量并发请求。
使用 kubectl 管理集群时,在后端,您实际上是通过 HTTP REST API 与 API 服务器进行通信。但是,内部集群组件(如 scheduler、controller 等)使用 gRPC 与 API 服务器通信。

1.3.2 集群状态存储(etcd)
Kubernetes 是一个分布式系统,它需要一个高效的分布式数据库,如 etcd 来支持其分布式性质。它既充当后端服务发现,又充当数据库。你可以称它为 Kubernetes 集群的大脑。
简单地说,当你使用 kubectl 获取 kubernetes 对象详细信息时,你是从 etcd 获取的。此外,当您部署像 pod 这样的对象时,会在 etcd 中创建一个条目。
etcd 以键值格式存储所有对象,在 /registry 目录项下。例如,可以在 /registry/pods/default/nginx 下找到默认命名空间中名为 Nginx 的 Pod 的信息

1.3.3 调度器(kube-scheduler)
kube-scheduler 负责在工作节点上调度 Kubernetes Pod。
部署容器时,您可以指定容器要求,例如 CPU、内存、关联性、污点或容错、优先级、持久卷 (PV) 等。调度程序的主要任务是识别创建请求,并为满足要求的 Pod 选择最佳节点。
下图显示了调度程序工作原理的概述。

1.3.4 控制器管理器(kube-controller-manager)
什么是控制器?控制器是运行无限控制循环的程序。这意味着它连续运行并监视对象的实际和所需状态。如果实际状态和期望状态存在差异,则确保 kubernetes 资源/对象处于期望状态。
Kube Controller Manager 是管理所有 Kubernetes 控制器的组件。Kubernetes 资源/对象(如 Pod、命名空间、作业、副本集)由各自的控制器管理。

1.4 工作节点组件
工作节点运行实际的应用负载(Pod),每个节点需要安装以下组件:
1.4.1 Kubelet
Kubelet 是一个 Agent 组件,运行在集群中的每个节点上。Kubelet 不作为容器运行,而是作为守护程序运行,由 systemd 管理。
它负责向 API 服务器注册工作节点,并主要从 API 服务器使用 podSpec(Pod 规范 – YAML 或 JSON)。podSpec 定义了应在 Pod 内运行的容器、它们的资源(例如 CPU 和内存限制)以及其他设置,例如环境变量、卷和标签。
Kubelet 负责以下工作。
- 创建、修改和删除容器。
- 负责处理活跃度、准备情况和启动探测。
- 负责通过读取 Pod 配置并在主机上为卷挂载创建相应的目录来挂载卷。
- 通过调用 API 服务器来收集和报告节点和 Pod 状态 ,实现方式为 cAdvisor 和 CRI。

1.4.2 Kube proxy
Kube-proxy 是一个守护进程,它作为 daemonset 在每个节点上运行。它是一个代理组件,用于实现 Pod 的 Kubernetes 服务概念。(具有负载均衡功能的一组 Pod 的单个 DNS)。它主要代理 UDP、TCP 和 SCTP,不理解 HTTP。
当您使用 Service(ClusterIP)公开 Pod 时,Kube-proxy 会创建网络规则,将流量发送到 Service 对象下分组的后端 Pod(端点)。这意味着,所有负载均衡和服务发现都由 Kube proxy 处理。
如iptables,实现Service的负载均衡和流量转发

1.4.3 容器运行时(Container Runtime)
Java 运行时 (JRE)。它是在主机上运行 Java 程序所需的软件。同样,容器运行时是运行容器所需的软件组件。
Kubernetes 支持多个符合容器运行时接口 (CRI) 的容器运行时(CRI-O、Docker Engine、containerd 等)。这意味着,所有这些容器运行时都实现了 CRI 接口,并公开了 gRPC CRI API(运行时和镜像服务端点)。
2.容器、容器编排组件API配置不当或未鉴权
就安全问题来说,业界普遍接触最多、最首当其冲的就是容器组件服务的未鉴权问题。Kubernetes架构下常见的开放服务指纹如下
- kube-apiserver: 6443, 8080
- kubectl proxy: 8080, 8081
- kubelet: 10250, 10255, 4149
- dashboard: 30000
- docker api: 2375
- etcd: 2379, 2380
- kube-controller-manager: 10252
- kube-proxy: 10256, 31442
- kube-scheduler: 10251
- weave: 6781, 6782, 6783
- kubeflow-dashboard: 8080
2.1 组件分工
各个组件未鉴权所能造成的风险,其实从它们在Kubernetes集群环境里所能起到的作用就能很明显的判断出来,如 APIServer 是所有功能的主入口,则控制 APIServer 基本上等同控制集群的所有功能;而 kubelet 是单个节点用于进行容器编排的 Agent,所以控制 kubelet 主要是对单个节点下的容器资源进行控制。

2.2 API Server(kube-apiserver)
如果想要攻击 apiserver, 下载 kubectl 是必经之路。
默认情况下,apiserver 都是有鉴权的:

如果配置不当,将 "system:anonymous" 用户绑定到 "cluster-admin" 用户组,则会使得 6443 端口允许匿名用户以管理员权限访问。
正常情况下访问 6443 端口,提示 Forbidden。
执行以下命令将 "system:anonymous" 用户绑定到 "cluster-admin" 用户组
kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous

默认情况下,8080端口是关闭的,需要我们手动开启
vim /etc/kubernetes/manifests/kube-apiserver.yaml

此时请求接口的结果如下:

对于这类的未鉴权的设置来说,访问到 apiserver 一般情况下就获取了集群的权限:
kubectl -s "ip:8080" get pods

对于针对Kubernetes集群的攻击来说,获取 admin kubeconfig 和 apiserver 所在的 master node 权限基本上就是获取主机权限路程的终点。
2.3 Kubelet
每一个Node节点都有一个kubelet服务,kubelet监听了10250,10248,10255等端口。
其中10250端口是kubelet与apiserver进行通信的主要端口,通过该端口kubelet可以知道自己当前应该处理的任务,该端口在最新版Kubernetes是有鉴权的,但在开启了接受匿名请求的情况下,不带鉴权信息的请求也可以使用10250提供的能力。
在新版本Kubernetes中当使用以下配置打开匿名访问时便可能存在kubelet未授权访问漏洞:

如果10250端口存在未授权访问漏洞,那么我们可以先使用/pods接口获取集群的详细信息,如namespace,pods,containers等

之后再通过 curl -k https://Kubernetes-node-ip:10250/run/// -d “cmd=id” 的方式在任意容器里执行命令
curl -XPOST -k "https://ip:10250/run/kube-system/kube-controller-manager-hcss-ecs-a8e4/kube-controller-manager" -d "cmd=id"

2.4 dashboard
dashboard是Kubernetes官方推出的控制Kubernetes的图形化界面,在Kubernetes配置不当导致dashboard未授权访问漏洞的情况下,通过dashboard我们可以控制整个集群。
在dashboard中默认是存在鉴权机制的,用户可以通过kubeconfig或者Token两种方式登录,当用户开启了enable-skip-login时可以在登录界面点击Skip跳过登录进入dashboard

然而通过点击Skip进入dashboard默认是没有操作集群的权限的,因为Kubernetes使用RBAC(Role-based access control)机制进行身份认证和权限管理,不同的serviceaccount拥有不同的集群权限。我们点击Skip进入dashboard实际上使用的是Kubernetes-dashboard这个ServiceAccount,如果此时该ServiceAccount没有配置特殊的权限,是默认没有办法达到控制集群任意功能的程度的。
但有些开发者为了方便或者在测试环境中会为Kubernetes-dashboard绑定cluster-admin这个ClusterRole(cluster-admin拥有管理集群的最高权限)。
这个极具安全风险的设置,具体如下:
(1)新建dashboard-admin.yaml内容如下(该配置也类似于“利用大权限的 Service Account”一小节的配置 )

(2)执行kubectl create -f dashboard-admin.yaml,此时用户通过点击Skip进入dashboard即可拥有管理集群的权限了。

2.5 集群状态存储(etcd)
etcd被广泛用于存储分布式系统或机器集群数据,其默认监听了2379等端口,如果2379端口暴露到公网,可能造成敏感信息泄露。
Kubernetes默认使用了etcd v3来存储数据,如果我们能够控制Kubernetes etcd服务,也就拥有了整个集群的控制权。
首先读取用于访问apiserver的token:
export ETCDCTL_API=3
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key
etcdctl get / —prefix —keys-only | grep /secrets/kube-system/clusterrole
etcdctl get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-jmrzx

利用token我们可以通过apiserver端口6443控制集群

3.补充:云原生API网关安全
API网关作为集群流量的入口/出口(ingress/egress),管理南北向流量。云原生API网关普遍支持动态配置和远程管理(通过REST API接口),但其远程配置功能的鉴权机制存在隐患——多数主流开源产品的默认安全设置仍有不足。
3.1 Kong利用
当攻击者可以访问到 Kong Admin Rest API 和 Kong Proxy 时(在开源分支里,这个管理入口是没有鉴权能力的),可以通过以下步骤创建一个通往内网的代理:

从外网发往 Kong Proxy 的流量只要 host 头带有 target.com 就会转发到内网的 target.com:443 中。
3.2 APISIX 的 RCE 利用
另外一个值得深入的开源组件就是Apache APISIX,这是一款基于lua语言开发,是一个动态、实时、高性能的 API 网关;
APISIX提供了REST Admin API功能,用户可以使用REST Admin API来管理APISIX,默认情况下只允许127.0.0.1访问,用户可以修改conf/config.yaml中的allow_admin字段,指定允许调用Admin API的ip列表。当用户对外开启了Admin API且未修改硬编码的缺省admin_key的情况下,攻击者可以利用该admin_key执行任意lua代码。

根据apisix官方文档可以知道,在创建路由时用户可以定义一个filter_func参数用于处理请求,filter_func的内容可以是任意的lua代码。

那么我们便可以使用默认的admin_key创建恶意的route并访问以触发lua代码执行,达到rce的目的,下面是具体步骤:
1、创建可用的services:

2、创建恶意的route:

最后访问 http://127.0.0.1:9080/api/tforce_test 即可触发预定义的lua代码执行。

浙公网安备 33010602011771号