容器逃逸防御是 Kubernetes 生产环境安全建设中最关键的课题。容器逃逸指攻击者利用内核漏洞、错误权限配置或危险挂载,突破容器隔离边界直接控制宿主机。一旦逃逸成功,攻击者可访问集群所有节点与敏感数据。本教程从权限模型、内核安全机制与运行时策略三个层面,给出可落地的加固配置。
适用场景
- Kubernetes 集群承载生产业务,需强化容器运行时隔离
- 存在运行不受信镜像的 CI 环境或多租户场景
- 安全合规要求(如等保 2.0、CIS Benchmarks)需要容器加固基线
- 发生过容器内入侵,需要防止攻击者横向提权
前置条件
- Kubernetes 1.24+ 集群,具备管理员权限(kubectl 可操作)
- 节点已启用 AppArmor(Debian/Ubuntu)或 SELinux(RHEL 系)
- 了解 Pod 安全上下文(SecurityContext)基本概念
- 具备
kubectl get nodes与auditd查看权限
原理说明
容器逃逸的主要路径有三类:内核漏洞利用(如 CVE-2022-0185、Dirty Pipe)、特权容器与错误挂载(如挂载宿主机 / 目录或 Docker Socket)、Capabilities 过度授权(如授予 SYS_ADMIN)。防御的核心是纵深防御:通过 SecurityContext 限制运行权限、裁剪 Capabilities,通过 Seccomp/AppArmor 限制系统调用,通过 PodSecurity Admission 强制准入策略,最后用 RuntimeClass + gVisor 对不可信负载做虚拟机级隔离。
操作步骤
步骤一:为工作负载设置最小权限 SecurityContext
以 Deployment 为例,禁止特权模式、非 root 运行并裁剪 Capabilities:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.example.com/web-app:1.4.2
securityContext:
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
要点:drop: [“ALL”] 删除全部 Linux capabilities,仅按需添加(如监听 80 端口加 NET_BIND_SERVICE);allowPrivilegeEscalation: false 禁止提权;seccompProfile: RuntimeDefault 启用运行时默认系统调用过滤。
步骤二:配置 AppArmor 强制访问控制
在节点上创建策略文件 /etc/apparmor.d/container-profile:
#include <tunables/global>
profile container-profile flags=(attach_disconnected) {
#include <abstractions/base>
network inet tcp,
network inet udp,
deny mount,
deny ptrace,
file,
}
加载策略并应用到 Pod:
# 在节点上加载 AppArmor 配置
sudo apparmor_parser -r /etc/apparmor.d/container-profile
# Pod 中通过注解启用(容器运行时为 containerd)
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/container-profile
步骤三:启用 PodSecurity 准入策略
# 为生产命名空间启用 restricted 级别的 Pod 安全标准
kubectl label --overwrite ns production pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=v1.24
# 验证:创建特权 Pod 应被拒绝
kubectl -n production run test-priv --image=nginx --privileged
# 预期输出:Error from server (Forbidden): ... violates PodSecurity "restricted:latest"
步骤四:对不可信负载启用 gVisor 运行时
# 安装 gVisor 运行时类(需先在各节点部署 runsc)
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
---
# 对不可信工作负载指定 runtimeClassName
spec:
runtimeClassName: gvisor
template:
spec:
runtimeClassName: gvisor
gVisor 在用户态拦截并虚拟化系统调用,即使容器内发生逃逸尝试,也无法直接触达宿主机内核。
配置验证
# 验证 capabilities 已裁剪(应无 SYS_ADMIN 等危险能力)
kubectl exec -it deploy/web-app -- capsh --print | grep Current
# 验证 Seccomp 生效(查看进程 seccomp 模式应为 2)
kubectl exec -it deploy/web-app -- cat /proc/self/status | grep Seccomp
# 验证只读根文件系统生效
kubectl exec -it deploy/web-app -- touch /tmp/test 2>&1
# 逃逸尝试:验证无法挂载宿主机目录(应被拒绝)
kubectl exec -it deploy/web-app -- mount -t proc proc /proc 2>&1
常见问题
FAQ 1:启用 restricted 策略后原有 Pod 无法启动怎么办?
restricted 是最高安全级别,会强制非 root 运行、禁用特权容器。先使用 kubectl get pods -n production -o yaml | grep -A5 securityContext 排查不满足项,通过补充 securityContext(设置 runAsNonRoot、drop capabilities)逐步收敛;确需提升权限的组件可用 baseline 级别并在命名空间例外名单中登记。
FAQ 2:Seccomp 导致应用系统调用被拦截如何排查?
先用 RuntimeDefault(Docker/containerd 默认 profile,仅过滤危险调用)而非自定义 profile 起步;若仍拦截,通过 journalctl -u kubelet | grep seccomp 或 auditd 查看被拒调用号,再使用 strace 分析应用实际系统调用,在 Seccomp profile 中按需放行并做最小化增补。
总结
容器逃逸防御没有银弹,必须组合多层机制:SecurityContext 落实最小权限与能力裁剪,Seccomp/AppArmor 收紧系统调用面,PodSecurity 从准入层强制安全基线,gVisor 为不可信负载提供虚拟机级隔离。建议将上述配置固化为集群的 GitOps 模板,并周期性使用 kube-bench 与 trivy 复核基线,同时保持节点内核及时打补丁,压缩已知逃逸漏洞的利用窗口。