容器逃逸攻击防御实战:K8s 运行时隔离加固配置

容器逃逸防御是 Kubernetes 生产环境安全建设中最关键的课题。容器逃逸指攻击者利用内核漏洞、错误权限配置或危险挂载,突破容器隔离边界直接控制宿主机。一旦逃逸成功,攻击者可访问集群所有节点与敏感数据。本教程从权限模型、内核安全机制与运行时策略三个层面,给出可落地的加固配置。

适用场景

  • Kubernetes 集群承载生产业务,需强化容器运行时隔离
  • 存在运行不受信镜像的 CI 环境或多租户场景
  • 安全合规要求(如等保 2.0、CIS Benchmarks)需要容器加固基线
  • 发生过容器内入侵,需要防止攻击者横向提权

前置条件

  • Kubernetes 1.24+ 集群,具备管理员权限(kubectl 可操作)
  • 节点已启用 AppArmor(Debian/Ubuntu)或 SELinux(RHEL 系)
  • 了解 Pod 安全上下文(SecurityContext)基本概念
  • 具备 kubectl get nodesauditd 查看权限

原理说明

容器逃逸的主要路径有三类:内核漏洞利用(如 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 seccompauditd 查看被拒调用号,再使用 strace 分析应用实际系统调用,在 Seccomp profile 中按需放行并做最小化增补。

总结

容器逃逸防御没有银弹,必须组合多层机制:SecurityContext 落实最小权限与能力裁剪,Seccomp/AppArmor 收紧系统调用面,PodSecurity 从准入层强制安全基线,gVisor 为不可信负载提供虚拟机级隔离。建议将上述配置固化为集群的 GitOps 模板,并周期性使用 kube-benchtrivy 复核基线,同时保持节点内核及时打补丁,压缩已知逃逸漏洞的利用窗口。