一、K8S 是什么

K8s 是一台 “管理很多台机器的操作系统”。

在我们熟悉的操作系统里,程序要读文件、开网络,得通过「系统调用」找内核。K8s 里一模一样:任何操作都是向 API Server 发一个 HTTP 请求。
这是理解后面所有攻击手法的关键——渗透 K8s 就是想办法拿到一个「有权限的身份」,然后用这个身份发 HTTP 请求。

K8S 核心组件

  • API Server:负责所有请求的入口、鉴权、校验
  • etcd:存储集群的所有状态
  • kubelet:负责容器的生命管理
  • Scheduler:决定 Pod 跑在哪台机器上
  • Controller:对比「期望状态」和「实际状态」,不一致就修
  • kubectl: HTTP 客户端,帮忙给 API Server 发请求

K8S 核心对象

  • Node:物理机或虚拟机
  • Pod:最小运行单元,里面装一个或多个容器
  • Namespace:逻辑分区,权限和命名都按它隔离
  • ServiceAccount (SA):Pod 的身份
  • Role / ClusterRole:定义权限规则
  • RoleBinding:绑定权限规则给 SA
  • Secret:存放密码、密钥、token
  • ConfigMap:存放非敏感的配置

Pod 用某个 SA 的身份运行 → SA 通过 Binding 拿到某些权限 → 于是这个 Pod 就能对 API Server 做某些操作。

实操

Windows 安装环境

# 1. 装 Docker Desktop(kind 需要它来当"机器")—— 装完要启动它
winget install Docker.DockerDesktop --source winget --accept-source-agreements

# 2. 装 kubectl(客户端)
winget install Kubernetes.kubectl --source winget --accept-source-agreements

# 3. 装 kind(用来一键造一个本地集群)
winget install Kubernetes.kind--source winget --accept-source-agreements
  • 验证
docker version
kubectl version --client
kind version

起集群

kind create cluster --name lab

kubectl get nodes                          # 看有几台"机器"
kubectl get namespaces                     # 看有哪些"楼层"
kubectl get pods -A                        # 看所有楼层里的 Pod(-A = 所有命名空间)
kubectl cluster-info                       # 看办事大厅的地址

kubectl get pods -v=8 2>&1 | Select-String "GET https"   # 看 kubectl 实际发的请求

二、身份:ServiceAccount 与 RBAC

集群里运行的每个 Pod,都挂着一张 ServiceAccount 。

RBAC(基于角色的访问控制)

谁能做(Subject: ServiceAccount)
↕ 通过 Binding 绑定
能做什么(Role/ClusterRole: 一堆 动词+资源 的规则)

规则示例:

verbs: ["get","list"] + resources: ["pods"] = "能查看 Pod"。
  • Role 是命名空间内的,ClusterRole 是全集群的。一个命名空间里的 Role 管不到别的命名空间。
  • list 和 get 是两种不同权限。get 是拿一个,list 是拿一批。
  • list secrets 拿到的是完整对象,包含明文数据(base64 只是编码,不是加密)。

实操

  • 一个 K8S 配置文件
apiVersion: v1
kind: ServiceAccount
metadata:
  name: lab-reader
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: lab-reader-binding
  namespace: default
subjects:
- kind: ServiceAccount
  name: lab-reader
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io
kubectl apply -f 01-sa-rbac.yaml
kubectl get sa,role,rolebinding -n default
  • 使用 can-i 看 SA 的权限
# 你是集群管理员,所以可以"假装成"别人提问(--as)
kubectl auth can-i list pods   --as=system:serviceaccount:default:lab-reader
# → yes

kubectl auth can-i list secrets --as=system:serviceaccount:default:lab-reader
# → no

kubectl auth can-i list secrets --as=system:serviceaccount:default:lab-reader -n kube-system
# → no

# 看看这个身份"全部"能干什么
kubectl auth can-i --list --as=system:serviceaccount:default:lab-reader

防御视角

  • 一个 SA 的权限应该刚好够它干活。给它 list secrets 就等于给它全楼保险柜的钥匙。
  • 检查集群里哪些 SA 权限过大:kubectl get clusterrolebindings -o wide,重点看绑到 cluster-admin 的。

—— "给 SA 加了权限"和"给 Pod 加了权限"是同一件事。

三、Pod 详解

K8S 会自动在 Pod 中加两个文件

/var/run/secrets/kubernetes.io/serviceaccount/
├── token       ← SA 的电子凭证(一个 JWT)
├── ca.crt      ← 用来验证 API Server 是不是真的(HTTPS 证书)
└── namespace   ← 我自己在哪个命名空间

配合两个环境变量

KUBERNETES_SERVICE_HOST=10.96.0.1     ← API Server 的地址
KUBERNETES_SERVICE_PORT=443

Pod 里天然就能操作集群 —— 不需要密码,不需要配置。这叫 in-cluster 访问,是所有 K8s 攻击的起点:只要你能在集群里跑一个容器,你就能拿到一个身份。

实操

进入 Pod

# 起一个带 shell 的临时 Pod,退出即销毁
kubectl run shell --rm -it --restart=Never --image=curlimages/curl:8.10.1 -- sh

进入 Pod 后:

# 1. 看自动挂载的身份文件
ls -l /var/run/secrets/kubernetes.io/serviceaccount/
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace

# 2. 看环境变量
env | grep KUBERNETES

# 3. 拿工牌去 Server API 问一句"我是谁"
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
     -H "Authorization: Bearer $TOKEN" \
     https://kubernetes.default.svc/api/v1/namespaces/default/pods | head -c 300

解码 SA

  • 在 Pod 中
# 在 pod 里
cut -d. -f2 /var/run/secrets/kubernetes.io/serviceaccount/token | base64 -d 2>/dev/null; echo
  • 使用 python 调用
python -c "import base64,json,sys; p=sys.argv[1].split('.')[1]; p+='='*(-len(p)%4); print(json.dumps(json.loads(base64.urlsafe_b64decode(p)), indent=2, ensure_ascii=False))" "<把token粘这里>"

防御视角

  • automountServiceAccountToken: false 可以让不需要操作集群的 Pod 没有 SA —— 但很多集群没这么配。
  • 真正的防线是:这个 SA 背后的权限要足够小。如果 SA 一定会被偷到,问题是偷到之后能干什么。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐