学习目标
- 理解容器逃逸的攻击路径
- 掌握 Security Context 的核心配置
- 理解 Pod Security Admission(PSA)三级标准
- 能为 Pod 配置安全基线
1. 容器逃逸风险
容器本质是进程(Lesson 01),隔离靠内核特性而非硬件虚拟化。如果容器拥有过高权限,攻击者可能:
| 攻击路径 | 条件 |
|---|---|
| 内核漏洞利用(如 DirtyCow) | 容器以 root 运行 |
| 挂载宿主机文件系统 | Pod 挂载了 hostPath / |
| 特权容器逃逸 | privileged: true |
| Linux Capabilities 滥用 | 拥有 SYS_ADMIN 等危险能力 |
核心认知:容器不是虚拟机。虚拟机有独立的内核,容器共享宿主机内核。一旦攻击者在容器内获得 root 权限并利用内核漏洞,就有可能"逃逸"到宿主机,控制整个节点乃至整个集群。
2. Security Context 加固配置
yamlspec:
securityContext: # Pod 级别安全上下文
runAsNonRoot: true # 强制以非 root 用户运行
runAsUser: 1000 # 指定用户 ID
fsGroup: 2000 # 文件系统组 ID
containers:
- name: app
image: my-app:v1
securityContext: # 容器级别安全上下文
allowPrivilegeEscalation: false # 禁止提权
readOnlyRootFilesystem: true # 根文件系统只读
capabilities:
drop: ["ALL"] # 移除所有 Linux Capabilities
add: ["NET_BIND_SERVICE"] # 仅保留绑定低端口的能力
每项配置的防御效果
| 配置 | 防御效果 |
|---|---|
runAsNonRoot: true | 即使镜像默认用 root,K8s 也会拒绝启动 |
runAsUser: 1000 | 强制容器进程以 UID 1000 运行,降低权限 |
readOnlyRootFilesystem: true | 攻击者无法写入恶意文件到容器文件系统 |
allowPrivilegeEscalation: false | 阻断 SUID 类提权攻击(如 sudo) |
capabilities.drop: ["ALL"] | 移除所有特殊内核能力,最小权限原则 |
fsGroup: 2000 | 所有挂载的 Volume 文件归属 GID 2000,控制文件访问权限 |
最佳实践:生产环境的 Pod YAML 应该默认包含以上所有安全配置。只在明确需要时才放开特定的 Capability(如
NET_BIND_SERVICE用于绑定 80 端口)。
3. Pod Security Admission(PSA)
PSA 是 K8s 内置的 Pod 安全准入控制器,在 Namespace 级别强制执行安全策略。
三个安全级别
| 级别 | 限制 | 适用场景 |
|---|---|---|
| Privileged | 无限制 | 系统组件(kube-system) |
| Baseline | 禁止特权容器、禁止 hostNetwork | 一般业务应用 |
| Restricted | 必须非 root、只读 rootfs、移除所有 capabilities | 安全敏感应用 |
设计意图:PSA 解决了一个管理难题——即使你在文档中要求开发者配置 Security Context,也无法保证每个人都会遵守。PSA 在 Namespace 级别强制拦截不合规的 Pod 创建请求,从制度上杜绝"裸奔"容器。
为 Namespace 启用 PSA
bash# 对命名空间强制执行 restricted 级别
kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted
参数解读:
enforce=restricted:强制拦截——不合规的 Pod 直接拒绝创建warn=restricted:警告模式——不合规的 Pod 仍然可以创建,但会输出警告信息
动手实验
实验 15.1:验证 PSA 拦截不合规 Pod
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 创建受限命名空间
kubectl create namespace secure-ns
kubectl label namespace secure-ns pod-security.kubernetes.io/enforce=restricted
预期输出:
bashnamespace/secure-ns created
namespace/secure-ns labeled
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 尝试创建一个不合规的 Pod(Nginx 默认以 root 运行)
kubectl run root-pod --image=docker.m.daocloud.io/library/nginx -n secure-ns
预期输出:
bashError from server (Forbidden): pods "root-pod" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "root-pod" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "root-pod" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "root-pod" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "root-pod" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
输出解读:PSA 一次性列出了这个 Pod 违反的所有 restricted 策略条款:
allowPrivilegeEscalation != false:没有显式禁止提权unrestricted capabilities:没有 drop 所有 capabilitiesrunAsNonRoot != true:没有强制非 root 运行seccompProfile:没有设置 Seccomp 安全计算模式Pod 被直接拒绝创建,不会有任何容器启动。这就是
enforce模式的硬拦截效果。
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 创建一个完全合规的 Pod
kubectl apply -n secure-ns -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: docker.m.daocloud.io/library/busybox
command: ['sleep', '3600']
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
EOF
预期输出:
bashpod/secure-pod created
输出解读:合规 Pod 成功创建!每一项安全配置都满足了 restricted 级别的要求。
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 验证 Pod 正常运行
kubectl get pod -n secure-ns
预期输出:
bashNAME READY STATUS RESTARTS AGE
secure-pod 1/1 Running 0 3s
输出解读:Pod 以 UID 1000(非 root)正常运行,所有安全策略满足。这就是"安全+可用"的正确姿势。
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===
# 清理
kubectl delete namespace secure-ns
预期输出:
bashnamespace "secure-ns" deleted
为什么不需要先手动删除
secure-pod?在 Kubernetes 中,Namespace 是资源的隔离边界。当你删除一个 Namespace 时,K8s 会级联删除(Cascading Delete)该命名空间下的所有资源——包括 Pod、Service、ConfigMap、Secret、NetworkPolicy 等。也就是说,
kubectl delete namespace secure-ns执行后,之前创建的pod/secure-pod会被自动清理,无需单独删除。你可以在删除前通过
kubectl get all -n secure-ns查看命名空间内的全部资源,删除后再执行同样的命令验证——此时会提示命名空间不存在。
检查点
- 能说出至少 3 种容器逃逸攻击路径
- 能编写带 Security Context 的安全 Pod YAML
- 理解 PSA 三个级别的区别
- 能为 Namespace 启用 PSA 强制执行
Comments NOTHING