找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3986

积分

0

好友

526

主题
发表于 3 小时前 | 查看: 4| 回复: 0

问题背景

容器逃逸(Container Escape)是指攻击者突破容器隔离边界,从容器内部获得宿主机或集群更高权限的行为。这件事过去几年在生产环境里反复出现,运维 / SRE / 安全团队听到的抱怨通常是这些:

  • 红蓝演练里甲方"白帽"在容器里跑了一会儿就拿到了宿主机 root,业务方一脸懵。
  • 公共镜像拉了一个看起来还行的应用,结果基础镜像里有 CVE,跑起来第二天收到云厂商漏洞扫描报告。
  • 业务方为方便调试给 Pod 加了 privileged: true,3 个月后没人记得改回去,被挖矿团伙利用。
  • 容器内日志突然出现 /var/log/syslog 被读、/proc/kcore 访问、cap_sys_admin 调用,平时根本不会有的行为。
  • CI 流水线里某个构建步骤在容器里跑 docker build 然后推到生产集群,几个月后审计发现这一步是攻击面。
  • K8s 节点的 kubelet 配置错误允许匿名认证,攻击者从外部直接拉起一个特权 Pod 接管节点。
  • 业务方反馈应用在容器里能读到宿主机的 SSH key、AWS access key、~/.kube/config,根本原因是 mount 路径不对。
  • 法规合规要求做 CIS Docker Benchmark / CIS Kubernetes Benchmark 评估,跑出来一大堆 fail。
  • 镜像仓库里有一个几年前的镜像,扫描出来 200 多个 CVE,仓库里谁也不敢删。
  • 业务反馈"灰盒应用突然在 Pod 里挖矿",kubectl exec 进去看到一个 cron 起 xmrig

容器逃逸和容器安全不是同一件事。容器安全涵盖镜像、运行时、网络、数据、配置、供应链的全链路;逃逸是其中最危险的一类——攻击者拿到的是宿主机的 root 权限,整台物理机 / 虚拟机上跑的所有 Pod 全部沦陷。

这篇文章面向 K8s / Docker 运维、SRE、安全工程师,整理一套可落地的容器逃逸检测与防御体系,覆盖威胁模型、CVE 与原理、镜像安全、运行时检测、策略执行、网络层、加固清单、应急响应、合规基线、CI/CD 集成、ATT&CK 矩阵映射。所有 CVE、加固项、规则都基于已有知识整理,并以"以实际版本为准"作为版本依赖的兜底说明。

适用读者

  • 维护 K8s / Docker 生产集群的 SRE、运维、平台工程师。
  • 负责容器安全、安全基线、应急响应的安全工程师。
  • 做过红蓝对抗、容器安全审计、攻防演练的同学。
  • 准备做 CIS Docker Benchmark、CIS Kubernetes Benchmark 等合规加固的工程师。
  • 想落地 Zero Trust 容器、机密容器、ATT&CK 容器矩阵落地的平台负责人。

适用场景

  • 公有云 K8s 集群(ACK、TKE、EKS、GKE、AKS)。
  • 私有云 K8s / OpenShift / Rancher / K3s / K0s。
  • 边缘 K8s 集群。
  • 单机 Docker / Podman / Containerd。
  • 互联网、金融、政企、医疗、制造业的容器化业务。
  • 多租户 K8s(业务 / 中间件 / 数据)。
  • 跑 AI / 大模型 / 推理服务的 GPU 集群。
  • 等保 2.0、PCI-DSS、ISO 27001、HIPAA 合规场景。

核心知识点

容器隔离的"层"

要谈逃逸,先搞清楚容器到底隔离了什么。Linux 容器并不是真正的 VM,它是一组由 Linux kernel 提供的隔离原语组合:

1. Namespace 命名空间

UTS、IPC、PID、Network、Mount、User、Cgroup、Time(K8s 1.27+ alpha)。每个容器看到一套独立的"视图",容器内 ps 看不到宿主机进程,hostname 是自己的。

但 namespace 不是真正的隔离:Linux 内核的 sysfs、procfs、sysctl、cgroup、device、tty、内核模块是共享的。一旦攻破内核,整个机器沦陷。

2. Cgroup 控制组

控制 CPU、内存、IO、设备、pids、freezer、huge_tlb。cgroup v1 / v2 都有一系列历史漏洞(CVE-2022-0492、cgroup v1 release_agent 等)。

3. Capability 能力

root 在容器内被裁剪到一组 capability(默认约 14 个:CAP_CHOWNCAP_DAC_OVERRIDECAP_FOWNERCAP_FSETIDCAP_KILLCAP_SETGIDCAP_SETUIDCAP_SETPCAPCAP_NET_BIND_SERVICECAP_NET_RAWCAP_SYS_CHROOTCAP_MKNODCAP_AUDIT_WRITECAP_SETFCAP)。特权容器(privileged: true)会获得全部 capability。

4. Seccomp / AppArmor / SELinux / Landlock

  • seccomp:限制容器可用的 syscall。Kubernetes 提供 RuntimeDefaultLocalhostUnconfined 三种 profile。
  • AppArmor:Ubuntu 系发行版默认开启,配置文件放在 /etc/apparmor.d/
  • SELinux:RHEL / CentOS 默认开启,复杂但隔离性更强。
  • Landlock:Linux 5.13+ 内核新加的 LSM,允许进程自我沙箱。

5. Rootless 容器

Podman、Docker rootless、containerd 在 user namespace 内运行。隔离更好,但兼容性差(不能跑 privileged,端口 80 受限等)。

6. 用户命名空间(User Namespace)

容器内看到的 root(uid 0)实际是宿主机上的非 root uid(典型 100000-165535)。K8s 1.30+ 还在 alpha 阶段,但很多发行版已经支持。

7. 安全沙箱运行时

  • gVisor:Google 出品,user-space 内核拦截 syscall,应用看不见真内核。性能损失 10%-30%。
  • Kata Containers:把每个容器跑在轻量 VM 里,硬件级隔离。性能损失 5%-15%。
  • Firecracker:AWS 出品,microVM,serverless 场景。
  • runsc:gVisor 的 OCI runtime 实现。
  • crun + landlock:轻量方案,对低风险工作负载友好。

容器逃逸的分类

按攻击面把容器逃逸分成五大类。

1. 内核漏洞

任何 Linux kernel 漏洞理论上都可以用来逃逸。常见的近年高危 CVE:

  • CVE-2022-0847(Dirty Pipe):Linux 5.8+ 内核 splice() 实现 bug,任意文件覆盖。PwnKit 是基于此的提权 PoC。修复后补丁回溯到 5.16.11 / 5.15.25 / 5.10.102。
  • CVE-2022-2588(route4 cls_route filter UAF):Linux net/sched/cls_route.c 漏洞。
  • CVE-2022-0185(fsconfig heap overflow):影响 5.1+ 内核,需要 CAP_SYS_ADMIN
  • CVE-2021-22555(netfilter UAF):影响 2.6.19+。
  • CVE-2022-0492(Cgroups v1 release_agent):cgroup v1 release_agent 特权提升。修复后 cgroup v1 默认 release_agent 被禁。
  • CVE-2022-23222(nss_user.c):影响 nss 库,影响范围有限。
  • CVE-2023-0386(FUSE overlayfs):overlayfs + FUSE 配合。
  • CVE-2023-26489 / CVE-2023-32629(GameOver(lay)):overlayfs 提权,影响 Ubuntu 系列。
  • CVE-2024-1086(nf_tables UAF):影响 5.14-6.6,攻击者可在容器内提权到 host root。
  • CVE-2024-1087 / CVE-2024-1088:其他高危内核 UAF。
  • CVE-2025-XXXX 系列:实际生产中需要持续关注 NVD、CVE.org、Ubuntu Security Notice、RHSA 公告。

2. 容器配置不当

容器配置错误导致权限过宽,攻击者直接利用。常见高危配置:

  • privileged: true:容器拿到所有 capability + 设备 + 几乎所有 sysctl。
  • hostPath 挂载:把宿主机目录挂进容器。
  • hostNetwork: true:共享宿主机网络命名空间。
  • hostPID: true:能看到宿主机进程。
  • hostIPC: true:共享宿主机 IPC。
  • 挂载 /var/run/docker.sock:容器能管理宿主机所有容器。
  • 挂载 /proc/sys:敏感信息泄露。
  • capability 显式加 SYS_ADMINNET_ADMINSYS_PTRACESYS_MODULE
  • seccompProfile: Unconfined:syscall 全开。
  • allowPrivilegeEscalation: true + 二进制有 setuid。
  • 不限制 runAsUser,让容器默认以 root 跑。
  • securityContext.fsGroup 配错,导致挂载的卷被攻击者改写。

3. 软件漏洞

容器内软件(runc、containerd、Docker、K8s、CRI 工具链)自身的漏洞:

  • CVE-2019-5736(runc):容器进程能改写宿主机 /usr/bin/runc 反弹 root。
  • CVE-2021-30465(runc symlink TOCTOU):符号链接替换。
  • CVE-2020-15257(containerd):暴露 host 容器网络。
  • CVE-2022-23648(containerd):Pivotal 相关。
  • CVE-2023-28842 / CVE-2023-31147(runhcs / containerd):host 网络。
  • CVE-2024-21626(runc)/proc/self/fd/N 描述符泄漏文件描述符到容器,影响 runc 1.1.11 之前版本。

4. 供应链

  • 公共镜像里的恶意软件或后门。
  • 镜像层注入:CI 被攻陷后中间层加恶意代码。
  • 镜像仓库被攻击者替换镜像。
  • 镜像拉取过程中被中间人。
  • 基础镜像里就有的漏洞(log4jxz-utilslibcrypto)。

5. 应用层

  • SSRF 打到云元数据(169.254.169.254、metadata.google.internal 等)。
  • 内网横向到数据库、配置中心、K8s API。
  • 敏感信息泄露(env、日志、/proc/self/environ、/proc/*/cmdline)。
  • 共享 service account token。

防御体系

1. CIS Benchmark 体系

  • CIS Docker Benchmark:Docker 引擎基线,覆盖 daemon 配置、容器配置、镜像安全、网络、存储。
  • CIS Kubernetes Benchmark:覆盖 control plane、worker、RBAC、PSP/PSA、etcd、kubelet、网络策略、日志。
  • NIST SP 800-190:容器安全官方指南。
  • 自动化检查工具:docker-bench-securitykube-bench

2. 加固层级

供应链层:镜像签名、SBOM、漏洞扫描、镜像仓库策略
       ↓
构建层:CI/CD 集成扫描、门禁、最小基础镜像
       ↓
部署层:PSA / PSP 替代、OPA / Kyverno 策略、Admission 拦截
       ↓
运行时:seccomp / apparmor / selinux、安全 runtime(gVisor / Kata)
       ↓
检测层:Falco / Tetragon / Tracee / 商业方案
       ↓
应急层:自动隔离、取证、复盘

3. 安全运行时选型

  • runc + 默认 seccomp + apparmor:最常见,绝大多数场景够用。
  • runsc (gVisor):多租户、AI/推理、不可信第三方代码。
  • Kata Containers:金融、政企、强隔离。
  • crun + landlock:低端边缘 / IoT。
  • Firecracker:serverless / 函数计算。

环境准备

硬件

  • 至少 4 核 CPU、8 GB 内存、50 GB SSD 起步。
  • 加固 + 检测工具(Falco / Tetragon / Kyverno)需要额外资源:
    • Falco:1 核 1 GB 内存 / 节点。
    • Tetragon:1 核 1 GB 内存 / 节点。
    • Trivy:扫描时 1-2 GB 内存。
    • Kyverno:1 核 512 MB 内存起。

操作系统

  • 内核:Linux 5.10+(5.15 / 5.19 / 6.1 LTS 推荐)。
  • 发行版:Ubuntu 22.04 / 24.04、Debian 11/12、RHEL 8/9、CentOS Stream、Rocky / AlmaLinux 8/9、openEuler 20.03/22.03、Anolis OS。
  • 容器运行时:containerd 1.7+(推荐)、Docker 24+(如果还在用)。
  • 编排:Kubernetes 1.24+(1.27+ 推荐,PSA 稳定)。

软件版本

  • kube-bench 0.6+。
  • docker-bench 0.5+。
  • trivy 0.45+。
  • falco 0.36+。
  • tetragon 0.10+。
  • kyverno 1.10+。
  • gatekeeper 3.14+。
  • cosign 2.x。
  • crane / skopeo
  • policy-controller (cosign 配套)。

实战步骤

第 1 步:建立威胁模型

先梳理自己集群里有什么。

集群资产清单:
  - 控制面:kube-apiserver、etcd、controller-manager、scheduler
  - 网络:CNI(Calico / Cilium / Flannel)
  - 节点:worker 节点 N 台,运行 kubelet + 容器运行时
  - 工作负载:业务 Pod、系统 Pod
  - 镜像来源:内部构建 / 公共仓库 / 第三方镜像
  - 数据:etcd 数据、PV/PVC、对象存储
  - 凭证:ServiceAccount Token、Secret、云厂商 AK/SK
  - 入口:Ingress / Gateway
  - 出口:Egress / 防火墙规则

STRIDE 威胁建模(针对容器场景):

| 类别 | 容器场景威胁 |
| --- | --- |
| Spoofing | ServiceAccount token 伪造、镜像签名绕过 |
| Tampering | 镜像层注入、容器内代码篡改、挂载卷数据被改 |
| Repudiation | 容器内日志被删、审计日志被禁 |
| Information Disclosure | secret 泄露、SSRF 拿元数据、env 泄露 |
| Denial of Service | CPU/内存耗尽、fork bomb、cgroup 攻击 |
| Elevation of Privilege | 容器逃逸、CAP_SYS_ADMIN、privileged |

第 2 步:基线检查

2.1 Docker Bench

docker run --rm --net host --pid host \
  --userns host \
  --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /etc:/etc:ro \
  docker/docker-bench-security

输出 PASS / WARN / INFO 分类,WARN 是要修的项。

2.2 Kube-Bench(K8s 节点)

kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

# 等 job 完成
kubectl get pods -o wide -w

# 看日志
kubectl logs job.batch/kube-bench

2.3 手工检查

# 1. 看特权容器
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | .metadata.namespace + "/" + .metadata.name'

# 2. 看 hostPath / hostNetwork
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.hostNetwork==true or .spec.hostPID==true or (.spec.volumes[]?.hostPath != null)) | .metadata.namespace + "/" + .metadata.name'

# 3. 看 docker.sock 挂载
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.volumes[]?.hostPath.path | contains("docker.sock")) | .metadata.namespace + "/" + .metadata.name'

# 4. 看以 root 跑的容器
kubectl get pods -A -o json | jq -r '.items[] | .spec.containers[] | select((.securityContext.runAsUser // 0) == 0) | .name'

# 5. 看缺资源限制
kubectl get pods -A -o json | jq -r '.items[] | .spec.containers[] | select((.resources.limits.cpu // null) == null) | .name'

第 3 步:加固 Pod 安全

3.1 Pod Security Standards(PSS)

K8s 1.25+ 启用了 Pod Security Admission(PSA),替代弃用的 PodSecurityPolicy。

# namespace 级别启用 restricted
apiVersion: v1
kind: Namespace
metadata:
  name: prod-restricted
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

三级策略:

  • privileged:完全放开,仅系统级组件。
  • baseline:阻止特权容器、host namespace、hostPath、hostPort 等危险配置。
  • restricted:最严格,要求非 root、只读根文件系统、cap drop、seccomp default。

3.2 Pod SecurityContext 模板

apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 10000
    runAsGroup: 10000
    fsGroup: 10000
    fsGroupChangePolicy: OnRootMismatch
    seccompProfile:
      type: RuntimeDefault
    supplementalGroups: []
    sysctls:
    - name: net.core.somaxconn
      value: "1024"
      unsafe: false
  containers:
  - name: app
    image: my-app:1.0.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      runAsUser: 10000
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
      seccompProfile:
        type: RuntimeDefault
      privileged: false
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 512Mi
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: var-cache
      mountPath: /var/cache/nginx
  volumes:
  - name: tmp
    emptyDir: {}
  - name: var-cache
    emptyDir: {}

要点:

  • readOnlyRootFilesystem: true:根只读,攻击者写文件得用 emptyDir 卷。
  • allowPrivilegeEscalation: false:禁 setuid 二进制。
  • capabilities.drop: ["ALL"] + 必要时 add
  • seccompProfile.type: RuntimeDefault:用运行时默认 seccomp profile。
  • 不挂 hostPath、hostSocket、hostNetwork、hostPID、hostIPC。
  • runAsNonRoot: true + 指定 uid。

3.3 seccomp profile 自定义

如果 RuntimeDefault 满足不了需求,可以写自定义:

apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    seccomp.security.alpha.kubernetes.io/pod: "localhost/k8s-app.json"
spec:
  containers:
  - name: app
    image: my-app
    securityContext:
      seccompProfile:
        type: Localhost
        localhostProfile: k8s-app.json

或者用 securityProfile 写:

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["read", "write", "exit", "exit_group", "openat", "close", "fstat", "mmap", "mprotect", "munmap", "brk", "rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "ioctl", "pread64", "pwrite64", "readv", "writev", "access", "pipe", "select", "madvise", "dup", "dup2", "nanosleep", "alarm", "socket", "connect", "sendto", "recvfrom", "sendmsg", "recvmsg", "bind", "listen", "getsockname", "getpeername", "setsockopt", "getsockopt", "clone", "fork", "vfork", "execve", "execveat", "wait4", "kill", "uname", "semget", "semop", "semctl", "shmget", "shmat", "shmctl", "open", "stat", "fstat", "lstat", "poll", "lseek", "mmap", "mprotect", "munmap", "rt_sigaction", "rt_sigprocmask", "sigreturn", "ioctl", "pread64", "pwrite64", "readv", "writev", "access", "pipe", "select", "madvise", "dup", "dup2", "nanosleep", "alarm", "set_tid_address", "restart_syscall", "exit", "exit_group"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

实际操作中自定义 profile 用 strace 抓取允许的 syscall,再加白名单。

3.4 AppArmor 注解

apiVersion: v1
kind: Pod
metadata:
  name: app
  annotations:
    container.apparmor.security.beta.kubernetes.io/app: runtime/default
spec:
  containers:
  - name: app
    image: my-app

或者挂自定义 profile:

metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/app: localhost/app-profile

第 4 步:镜像安全

4.1 Trivy 扫描

# 安装
sudo apt-get install -y wget apt-transport-https gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy

# 镜像扫描
trivy image my-app:1.0.0
trivy image --severity HIGH,CRITICAL my-app:1.0.0
trivy image --format json -o report.json my-app:1.0.0
trivy image --compliance docker-cis my-app:1.0.0

# 文件系统扫描
trivy fs /path/to/code

# 仓库扫描(扫 manifest)
trivy repo https://github.com/org/repo

# K8s 集群扫描
trivy k8s --report summary cluster
trivy k8s cluster --severity HIGH,CRITICAL

输出分级:

  • CRITICAL:必须修。
  • HIGH:尽快修。
  • MEDIUM:计划修。
  • LOW:可接受。
  • UNKNOWN:信息不足。

4.2 Trivy Operator

把 Trivy 装到 K8s 集群,自动扫描镜像。

helm repo add trivy-operator https://aquasecurity.github.io/trivy-operator
helm install trivy-operator trivy-operator/trivy-operator \
  --namespace trivy-system \
  --create-namespace

扫出结果写为 CRD VulnerabilityReport / ConfigAuditReport / ClusterVulnerabilityReport

4.3 cosign 签名 + 验证

# 生成密钥对
cosign generate-key-pair

# 签名镜像
cosign sign --key cosign.key my-registry.example.com/my-app:1.0.0

# 验证签名
cosign verify --key cosign.pub my-registry.example.com/my-app:1.0.0

# 签名 SBOM
cosign sign --key cosign.key --attachment sbom my-registry.example.com/my-app:1.0.0
cosign attach sbom --sbom sbom.spdx.json my-registry.example.com/my-app:1.0.0

# 无密钥模式(用 KMS)
cosign sign --key awskms://alias/myapp-cosign my-registry.example.com/my-app:1.0.0

4.4 Kyverno 验签

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  background: false
  rules:
  - name: verify-signature
    match:
      any:
      - resources:
          kinds:
          - Pod
    verifyImages:
    - imageReferences:
      - "my-registry.example.com/*"
      attestors:
      - entries:
        - keys:
            publicKeys: |-
              -----BEGIN PUBLIC KEY-----
              ...
              -----END PUBLIC KEY-----

4.5 SBOM

# 用 syft 生成 SBOM
syft my-app:1.0.0 -o spdx-json=sbom.spdx.json
syft my-app:1.0.0 -o cyclonedx-json=sbom.cyclonedx.json

# 注入到镜像
cosign attach sbom --sbom sbom.spdx.json my-registry.example.com/my-app:1.0.0

# 验证
cosign verify-attestation --key cosign.pub my-registry.example.com/my-app:1.0.0

4.6 镜像仓库策略

  • 不可变 tag(:1.0.0 不允许覆盖成新的 :1.0.0,必须用新 tag)。
  • 漏洞阈值:HIGH/CRITICAL 数量超限就拒绝。
  • 仓库隔离:内部业务镜像、第三方镜像、公开镜像分仓库。
  • Harbor、Quay、distribution 都支持。

4.7 最小基础镜像

# distroless 示例
FROM gcr.io/distroless/java17-debian12:nonroot

COPY --chown=nonroot:nonroot target/app.jar /app/app.jar

USER 65532:65532

ENTRYPOINT [ "java", "-jar", "/app/app.jar" ]
# static distroless(go 二进制)
FROM gcr.io/distroless/static-debian12:nonroot
COPY --chown=nonroot:nonroot myapp /
USER 65532:65532
ENTRYPOINT [ "/myapp" ]

distroless 没有 shell、没有包管理、没有 libc,攻击面最小。

第 5 步:策略执行

5.1 OPA Gatekeeper

# 安装
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper \
  --namespace gatekeeper-system \
  --create-namespace

约束模板:

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8scontainterlimits
spec:
  crd:
    spec:
      names:
        kind: K8sContainerLimits
  validation:
    openAPIV3Schema:
      type: object
      properties:
        cpu:
          type: string
        memory:
          type: string
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8scontainterlimits
      violation[{"msg": msg}] {
        container := input.review.object.spec.containers[_]
        not container.resources.limits.cpu
        msg := sprintf("container %v 没有设置 CPU limit", [container.name])
      }
      violation[{"msg": msg}] {
        container := input.review.object.spec.containers[_]
        not container.resources.limits.memory
        msg := sprintf("container %v 没有设置 memory limit", [container.name])
      }

约束实例:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sContainerLimits
metadata:
  name: must-have-limits
spec:
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
  parameters:
    cpu: "1"
    memory: "1Gi"

5.2 Kyverno

# 安装
helm repo add kyverno https://kyverno.github.io/kyverno
helm install kyverno kyverno/kyverno \
  --namespace kyverno \
  --create-namespace

策略:禁止特权容器

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: deny-privileged
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "禁止特权容器,请改用 SecurityContext"
      pattern:
        spec:
          containers:
          - securityContext:
              privileged: "false|nil"

策略:要求资源限制

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resources
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: require-limits
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "必须设置 CPU/Memory limits"
      pattern:
        spec:
          containers:
          - resources:
              limits:
                memory: "?*"
                cpu: "?*"

策略:禁止挂载 docker.sock

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-docker-sock
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: deny-docker-sock
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "禁止挂载 docker.sock"
      pattern:
        spec:
          =(volumes):
          - X(hostPath):
              path: "!=/var/run/docker.sock"

策略:要求只读根文件系统

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-readonly-root
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: readonly-root
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "必须设置 readOnlyRootFilesystem"
      pattern:
        spec:
          containers:
          - securityContext:
              readOnlyRootFilesystem: true

策略:要求 runAsNonRoot

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: run-as-non-root
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "必须 runAsNonRoot"
      pattern:
        spec:
          =(containers):
          - securityContext:
              runAsNonRoot: true

策略:拒绝特定 registry

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: restrict-registries
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: allow-internal-only
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "只允许使用内部仓库"
      pattern:
        spec:
          containers:
          - image: "my-registry.example.com/*"

策略:自动注入 securityContext

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: inject-security-context
spec:
  rules:
  - name: add-security-context
    match:
      any:
      - resources:
          kinds:
          - Pod
    mutate:
      patchStrategicMerge:
        spec:
          securityContext:
            runAsNonRoot: true
            seccompProfile:
              type: RuntimeDefault
          containers:
          - (name): "*"
            securityContext:
              allowPrivilegeEscalation: false
              readOnlyRootFilesystem: true
              capabilities:
                drop:
                - ALL

第 6 步:运行时检测

6.1 Falco

Falco 是 CNCF 毕业项目,用 syscalls 监控容器行为,提供规则引擎。

# 安装 helm chart
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set falco.json_output=true \
  --set falco.http_output.enabled=true \
  --set falco.http_output.url="http://falcosidekick.falco.svc.cluster.local:2801"

Falco rule 例子(默认规则已经覆盖很多):

- rule: Terminal shell in container
  desc: Alert if a shell is spawned in a container
  condition: >
    and spawned_process
    and container
    and proc.name = bash
  output: >
    Shell spawned in container
    (user=%user.name container=%container.name image=%container.image.repository:%container.image.tag shell=%proc.name parent=%proc.pname cmdline=%proc.cmdline)
  priority: WARNING
  tags: [process, container]

- rule: Crypto mining detection
  desc: Detects known crypto mining processes
  condition: >
    and open_write
    and container
    and proc.name in (xmrig, minerd, minergate, stratum)
  output: >
    Crypto mining detected
    (user=%user.name command=%proc.cmdline container=%container.name image=%container.image.repository:%container.image.tag)
  priority: CRITICAL
  tags: [process, crypto]

- rule: Sensitive file read
  desc: Reading /etc/shadow inside a container
  condition: >
    and open_read
    and container
    and fd.name = /etc/shadow
  output: >
    Sensitive file read
    (user=%user.name file=%fd.name container=%container.name image=%container.image.repository:%container.image.tag)
  priority: CRITICAL
  tags: [file, container]

- rule: Outbound connection to metadata service
  desc: Connect to cloud metadata service
  condition: >
    and outbound_connection
    and container
    and fd.sip.name in (169.254.169.254, metadata.google.internal)
  output: >
    Metadata service connection
    (user=%user.name dest=%fd.sip.name:%fd.sport container=%container.name)
  priority: WARNING
  tags: [network, container]

- rule: Container drift detected
  desc: >
    New executable detected in a container's running image since startup
  condition: >
    and open_write
    and container
    and not proc.exe_line in (known_container_procs)
  output: >
    Drift detected
    (user=%user.name proc=%proc.name exe=%proc.exe container=%container.name)
  priority: WARNING
  tags: [process, drift]

6.2 Falco + Falcosidekick 输出链路

helm install falcosidekick falcosecurity/falcosidekick \
  --namespace falco \
  --set config.loki.endpoint="http://loki.logging.svc.cluster.local:3100/loki/api/v1/push" \
  --set config.alertmanager.endpoint="http://alertmanager.monitoring.svc.cluster.local:9093" \
  --set config.slack.webhookurl="https://hooks.slack.com/services/xxx/yyy/zzz" \
  --set config.elasticsearch.host="http://elasticsearch.logging:9200"

Falcosidekick 支持几十种输出:Loki、Elasticsearch、Alertmanager、Slack、飞书、钉钉、PagerDuty、OpsGenie、Kafka、AWS SNS、Google Pub/Sub 等。

6.3 Tetragon(eBPF 实时阻断)

Tetragon 是 Cilium 出品的 eBPF 运行时检测 + 阻断工具,精度比 Falco 高,能在内核层做 Sigkill

helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon \
  --namespace kube-system \
  --set tetragon.enableTracingPolicy=false

TracingPolicy CRD:

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-spawn-shell
spec:
  kprobes:
  - call: "security_bpf_prog"
    syscall: false
    args:
    - index: 0
      type: "nop"
  tracepoints:
  - subsystem: "sched"
    event: "sched_process_exec"
    args:
    - index: 0
      type: "nop"
    trace:
    - type: "signal"
      pid: 0
      sig: 9
  kprobeMaxActive: 1024
  filters:
  - type: "kprobe"
    name: "do_filp_open"
    args:
    - index: 0
      type: "nop"

简化版(用 Tracepoint 阻断 bash):

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: deny-bash-in-container
spec:
  tracepoints:
  - subsystem: "sched"
    event: "sched_process_exec"
    filters:
    - args:
      - index: 0
        type: "nop"
      - data:
        - field: "comm"
          value: "bash"
          type: "string"
          op: "Equal"
    trace:
    - type: "Signal"
      pid: 0
      sig: "SIGKILL"

配合 Tetragon Agent,可以在内核态直接 kill 违规进程,零延迟。

6.4 Tracee

Aqua Security 出的运行时检测工具,基于 eBPF。

# 部署
kubectl apply -f https://github.com/aquasecurity/tracee/releases/latest/download/tracee-daemonset.yaml
# 检测可疑 syscall
kubectl exec -n tracee -it daemonset/tracee -- \
  tracee --containers \
  --output json \
  --filter comm=bash

第 7 步:网络层

7.1 NetworkPolicy 默认拒绝

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

7.2 允许特定 Pod 通信

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

7.3 禁止访问云元数据

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32

7.4 Calico GlobalNetworkPolicy

apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-metadata-cluster-wide
spec:
  selector: all()
  types:
  - Egress
  egress:
  - action: Allow
    destination:
      notNets:
      - 169.254.169.254/32

7.5 Cilium L7 策略

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-policy
  namespace: prod
spec:
  endpointSelector:
    matchLabels:
      app: api
  ingress:
  - fromEndpoints:
    - matchLabels:
        app: gateway
    toPorts:
    - ports:
      - port: "8080"
        protocol: TCP
      rules:
        http:
        - method: "GET"
          path: "/api/v1/.*"

第 8 步:沙箱运行时(按需启用)

8.1 gVisor(runsc)

# 安装
apt-get install -y runsc

# 配置 containerd
cat > /etc/containerd/config.toml <<EOF
[plugins.cri.containerd.runtimes.runsc]
  runtime_type = "io.containerd.runsc.v1"
  pod_annotations = ["*.container.gvisor.security.beta.kubernetes.io"]
EOF

# 在 Pod 里启用
metadata:
  annotations:
    runtime.class: gvisor
    container.gvisor.security.beta.kubernetes.io/app: "true"
spec:
  runtimeClassName: gvisor

8.2 Kata Containers

# 安装 kata
yum install -y kata-containers

# 注册 RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata

Pod 中:

spec:
  runtimeClassName: kata

8.3 选型建议

| 场景 | 推荐 runtime |
| --- | --- |
| 普通业务 | runc + seccomp + apparmor |
| 不可信代码 | runsc (gVisor) |
| 多租户 / 金融 / 强隔离 | kata-qemu / kata-cloud |
| serverless | runsc / firecracker |
| 边缘 / IoT | crun + landlock |

第 9 步:应急响应

9.1 告警接收

  • 飞书 / 钉钉 / Slack 群告警。
  • PagerDuty / OpsGenie / 短信告警(Critical 级别)。
  • 工单系统自动开 ticket。

9.2 自动隔离

收到 Falco Critical 告警后,自动化处理:

# falcosidekick rule
- name: auto-quarantine-on-escape
  condition: rule in (Container Drift Detected, Sensitive File Read, Crypto Mining Detected)
  actions:
  - name: delete-pod
    action: delete
  - name: cordon-node
    action: set
    params:
      field: spec.unschedulable
      value: "true"
  - name: taint-node
    action: set
    params:
      key: security.breach
      value: "true"
      effect: NoExecute

或者用 Function:

# 用 Falco + Falcosidekick + 自定义 webhook
# webhook 处理逻辑:
# 1. 收到告警
# 2. 解析告警里的 namespace/pod/node
# 3. kubectl delete pod -n <ns> <pod>
# 4. kubectl cordon <node>
# 5. 触发取证脚本

9.3 取证

#!/usr/bin/env bash
# /usr/local/bin/incident-collect.sh
# 在被逃逸的节点上收集证据

NS="$1"
POD="$2"
INCIDENT_ID=$(date +%Y%m%d%H%M%S)
OUT=/var/log/incident/$INCIDENT_ID
mkdir -p "$OUT"

# 1. 节点信息
hostname > "$OUT/hostname.txt"
date -u > "$OUT/time.txt"
uname -a > "$OUT/uname.txt"
cat /etc/os-release > "$OUT/os.txt"

# 2. Pod 信息
kubectl -n "$NS" get pod "$POD" -o yaml > "$OUT/pod.yaml"
kubectl -n "$NS" describe pod "$POD" > "$OUT/pod-describe.txt"

# 3. 容器内进程
kubectl -n "$NS" exec "$POD" -- ps auxf > "$OUT/ps.txt" 2>/dev/null || true
kubectl -n "$NS" exec "$POD" -- cat /proc/1/status > "$OUT/proc1.txt" 2>/dev/null || true

# 4. 网络状态
ss -anp > "$OUT/ss.txt"
netstat -anp > "$OUT/netstat.txt" 2>/dev/null || true
ip a > "$OUT/ip.txt"
ip route > "$OUT/route.txt"
iptables-save > "$OUT/iptables.txt" 2>/dev/null || true

# 5. 系统调用
ls -la /proc/*/exe > "$OUT/exe-symlinks.txt"

# 6. 关键文件
cp -a /etc/passwd "$OUT/passwd"
cp -a /etc/shadow "$OUT/shadow" 2>/dev/null || true
cp -a /etc/crontab "$OUT/crontab" 2>/dev/null || true
ls -la /var/spool/cron > "$OUT/cron.d" 2>/dev/null || true

# 7. 容器内文件系统(如果可能)
kubectl -n "$NS" exec "$POD" -- find / -newer /tmp -type f > "$OUT/new-files.txt" 2>/dev/null || true

# 8. 打包
tar -czf /var/log/incident/incident-$INCIDENT_ID.tar.gz -C "$OUT" .
echo "Evidence collected at /var/log/incident/incident-$INCIDENT_ID.tar.gz"

9.4 恢复

  • 隔离节点:kubectl cordon
  • 排空:kubectl drain --ignore-daemonsets
  • 保留节点做取证,不要直接重装。
  • 重放 Pod 在另一个干净节点上。
  • 排查根因后再恢复节点。
  • 更新规则 / 加固 / 复盘。

第 10 步:CI/CD 集成

10.1 镜像构建阶段扫描

# GitLab CI 示例
build-and-scan:
  stage: build
  image: docker:24
  services:
  - docker:24-dind
  script:
  - docker build -t my-app:$CI_COMMIT_SHORT_SHA .
  - docker save my-app:$CI_COMMIT_SHORT_SHA | trivy image --input - --severity HIGH,CRITICAL
  allow_failure: false

10.2 准入控制拦截

  • Kyverno / Gatekeeper 在 API Server 准入阶段校验镜像签名、漏洞、namespace。
  • Trivy Operator 持续扫描集群内镜像。

10.3 流水线示例

stages:
  - build
  - scan
  - sign
  - push
  - deploy

build:
  stage: build
  script:
    - docker build -t my-app:$CI_COMMIT_SHORT_SHA .
    - docker tag my-app:$CI_COMMIT_SHORT_SHA my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA

scan:
  stage: scan
  script:
    - trivy image --severity HIGH,CRITICAL --exit-code 1 my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA

sign:
  stage: sign
  script:
    - cosign sign --key cosign.key my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA
    - cosign attach sbom --sbom sbom.spdx.json my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA

push:
  stage: push
  script:
    - docker push my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA

deploy:
  stage: deploy
  script:
    - kubectl set image deployment/my-app my-app=my-registry.example.com/my-app:$CI_COMMIT_SHORT_SHA

第 11 步:合规与基线

11.1 CIS Benchmark 自动化

# 周期跑 kube-bench
0 2 * * 0 /usr/local/bin/kube-bench run --json > /var/log/kube-bench-$(date +\%Y\%m\%d).json

# 上报到合规平台
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -d @/var/log/kube-bench-$(date +\%Y\%m\%d).json \
  https://compliance.example.com/api/v1/kube-bench

11.2 报告模板

报告编号:CB-2026-Q2-001
评估日期:2026-06-05
评估范围:生产 K8s 集群(prod-cluster-1,30 节点)
评估依据:CIS Kubernetes Benchmark v1.8.0

结果:
  - PASS:123 项
  - FAIL:17 项
  - WARN:5 项

高风险 FAIL:
  1.2.1 Ensure that the API server pod specification file permissions are set to 644 or more restrictive
  1.2.2 Ensure that the API server pod specification file ownership is set to root:root
  2.1.1 Ensure that the --anonymous-auth argument is set to false
  ...

整改计划:
  2026-06-12 前完成所有 FAIL 项修复
  2026-06-19 前复核

责任人:张三

11.3 持续监控

  • Trivy Operator 持续生成 VulnerabilityReport,CI 接入失败阻断。
  • kube-bench 周期跑。
  • falco 持续监控。
  • compliance-operator(OpenShift / RH)也可以。

第 12 步:ATT&CK 容器矩阵映射

将检测 / 防御能力对应到 MITRE ATT&CK 容器矩阵(Container Matrix):

| ATT&CK 编号 | 描述 | 检测 | 防御 |
| --- | --- | --- | --- |
| T1611 | Escape to Host | Falco / Tetragon | seccomp / apparmor / runsc |
| T1610 | Deploy Container | Trivy | PSA / Kyverno |
| T1612 | Build Image on Host | audit | image build 权限 |
| T1505 | Server Software Component | Trivy | 镜像签名 |
| T1552 | Unsecured Credentials | Trivy | secret 管理 |
| T1609 | Container Administration Command | Falco | capability drop |
| T1611 | Escape to Host | Falco | runtime 沙箱 |
| T1554 | Compromise Client Software Binary | Trivy / cosign | SBOM / 签名 |
| T1555 | Credentials from Password Stores | Trivy | secret 不进镜像 |
| T1530 | Data from Cloud Storage | Falco / NSP | 数据加密 |

第 13 步:常见逃逸利用思路与缓解

出于合规考虑,本文不提供任何可用利用代码,只描述攻击思路与缓解措施。

13.1 内核漏洞类

思路:容器内调用有 bug 的 syscall,拿到宿主机 root。

缓解:

  • 内核打补丁(订阅 distros 安全公告)。
  • 用 runsc / Kata 把 syscall 拦截在沙箱内。
  • Falco / Tetragon 监控敏感 syscall。
  • 节点定期重启到新内核(云上换镜像,物理机升级内核)。

13.2 特权容器

思路:privileged: true 容器能 mount device / 改 cgroup / 写 sysfs。

缓解:

  • 禁止 privileged 容器(Kyverno / Gatekeeper / PSA)。
  • 灰度收紧:先 audit,warn,再 enforce。
  • 必须用特权容器的(如 kube-system 里的某些 Pod)单独 namespace。

13.3 docker.sock 挂载

思路:挂载 docker.sock 的容器能 docker exec 任意容器 / 启动特权容器。

缓解:

  • Kyverno 拒绝 hostPath 包含 docker.sock
  • 改用 containerd CRI,缩小攻击面。
  • 容器编排改 Kubernetes 原生。

13.4 hostPath 挂载

思路:挂载宿主机目录的容器能读敏感文件(/etc/shadow、/root/.ssh/id_rsa、/var/lib/kubelet/pki)。

缓解:

  • Kyverno / Gatekeeper 拒绝 hostPath(白名单例外)。
  • 用 CSI / PV 替代。
  • 必须用 hostPath 的(如日志采集)限定路径。

13.5 CAP_SYS_ADMIN

思路:拿到 CAP_SYS_ADMIN 的容器能 mount、cgroup 操作、内核模块。

缓解:

  • capabilities.drop: ["ALL"] + 必要时 add 最小集。
  • 拒绝 SYS_ADMIN / SYS_MODULE / SYS_PTRACE(Kyverno)。
  • Audit 阶段先统计哪些 Pod 需要,加白名单。

13.6 共享 PID namespace

思路:hostPID: true 容器能看到宿主机所有进程,能 attach ptrace。

缓解:

  • 禁止 hostPID: true(Kyverno)。
  • 灰度收紧。

13.7 rwx Secret 滥用

思路:把 K8s Secret 挂成 rwx 卷,容器内进程能改 secret,被攻击后改 secret 让所有 Pod 拉起恶意镜像。

缓解:

  • secret 挂载成 ro。
  • 用 external secrets(Vault / Sealed Secrets)。
  • RBAC 收紧 secret 权限。

第 14 步:演练与复盘

14.1 红蓝演练

  • 内部蓝队模拟攻击。
  • 外部红队 / 顾问做渗透。
  • 利用 CVE 数据库 + ATT&CK 容器矩阵挑选测试场景。
  • 跑通后给防御方反馈,加固。

14.2 Tabletop 演练

  • 选 3-5 个典型逃逸场景做桌面演练。
  • 评估响应流程是否顺畅。
  • 评估值班 / 升级路径。

14.3 GameDay

  • 在生产集群做 GameDay 演练。
  • 制造一个告警,看响应链路是否触发。
  • 验证告警 → 取证 → 隔离 → 修复 → 复盘流程。

14.4 复盘

每次演练 / 真实事件后:

事件编号:SEC-2026-06-001
事件类型:模拟容器逃逸
发现时间:2026-06-05 14:30
响应时间:14:32 收到告警,14:35 完成隔离
影响范围:1 个 Pod,1 个节点
根因:测试 Pod 误用 privileged + hostPath
影响:测试环境,无业务影响
修复:
  - 删除特权 Pod
  - 应用 Kyverno 策略禁止 privileged
  - 增加 GameDay 演练频率

经验:
  - 告警链路正常
  - 应急脚本可执行
  - 值班响应迅速
  - 待优化:自动化隔离脚本

常用命令清单

Trivy

trivy image <image>
trivy image --severity HIGH,CRITICAL <image>
trivy image --format json -o report.json <image>
trivy image --compliance docker-cis <image>
trivy image --compliance k8s-cis <image>
trivy fs /path
trivy repo <repo>
trivy k8s cluster
trivy k8s cluster --report summary
trivy k8s cluster --severity HIGH,CRITICAL
trivy sbom <image>

kube-bench

kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs job/kube-bench

docker-bench

docker run --rm --net host --pid host --userns host \
  --cap-add audit_control \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /etc:/etc:ro \
  docker/docker-bench-security

cosign

cosign generate-key-pair
cosign sign --key cosign.key <image>
cosign verify --key cosign.pub <image>
cosign attach sbom --sbom sbom.spdx.json <image>
cosign attach attestation --predicate <file> <image>
cosign verify-attestation --key cosign.pub <image>
cosign copy --sign-key cosign.key <src> <dst>

Falco

# 检查 Falco 状态
kubectl -n falco get pods
kubectl -n falco logs -l app.kubernetes.io/name=falco

# 看规则
kubectl -n falco exec -it <falco-pod> -- cat /etc/falco/falco_rules.local.yaml

# 触发规则验证
kubectl -n falco exec -it <falco-pod> -- sh
# 在容器内跑
nslookup example.com
# 看 Falco 是否触发 Unexpected outbound DNS connection

Tetragon

kubectl -n kube-system get pods -l app.kubernetes.io/name=tetragon
kubectl -n kube-system logs -l app.kubernetes.io/name=tetragon
kubectl -n kube-system exec -it <tetragon-agent> -- tetra status
kubectl -n kube-system exec -it <tetragon-agent> -- tetra tracepoint list

Kyverno

kubectl get cpol
kubectl describe cpol disallow-privileged
kubectl get policyreport -A
kubectl get clusterpolicyreport

Gatekeeper

kubectl get constraints
kubectl get constrainttemplates
kubectl get k8scontainterlimits must-have-limits -o yaml

kubectl 安全检查

# 1. 特权容器
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | "\(.metadata.namespace)/\(.metadata.name)"'

# 2. hostPath
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.volumes[]?.hostPath != null) | "\(.metadata.namespace)/\(.metadata.name) -> \(.spec.volumes[].hostPath.path)"'

# 3. hostNetwork / hostPID / hostIPC
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.hostNetwork==true or .spec.hostPID==true or .spec.hostIPC==true) | "\(.metadata.namespace)/\(.metadata.name)"'

# 4. runAsUser == 0
kubectl get pods -A -o json | jq -r '.items[] | .spec.containers[]? | select((.securityContext.runAsUser // 0) == 0) | "\(.name)"'

# 5. 缺资源限制
kubectl get pods -A -o json | jq -r '.items[] | .spec.containers[]? | select((.resources.limits.cpu // null) == null or (.resources.limits.memory // null) == null) | "\(.name)"'

配置示例汇总

Kyverno 综合策略

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: pod-security-baseline
spec:
  validationFailureAction: Enforce
  background: true
  rules:
  - name: deny-privileged
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "禁止特权容器"
      pattern:
        spec:
          containers:
          - securityContext:
              privileged: "false|nil"

  - name: require-readonly
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "必须 readOnlyRootFilesystem"
      pattern:
        spec:
          containers:
          - securityContext:
              readOnlyRootFilesystem: true

  - name: no-hostpath
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "禁止 hostPath"
      pattern:
        spec:
          =(volumes):
          - X(hostPath): {}

  - name: require-limits
    match:
      any:
      - resources:
          kinds:
          - Pod
    validate:
      message: "必须设置资源限制"
      pattern:
        spec:
          containers:
          - resources:
              limits:
                memory: "?*"
                cpu: "?*"

Falco 自定义规则

customRules:
  custom-rules.yaml: |+
    - rule: Detected crypto miner
      desc: 检测容器内挖矿行为
      condition: >
        and open_write
        and container
        and proc.name in (xmrig, minerd, minergate, cpuminer, minr)
      output: >
        Crypto mining detected
        (user=%user.name command=%proc.cmdline container=%container.name image=%container.image.repository:%container.image.tag)
      priority: CRITICAL
      tags: [process, crypto]

    - rule: Detect shell in container
      desc: 容器内出现交互式 shell
      condition: >
        and spawned_process
        and container
        and proc.name in (bash, sh, zsh, fish, ash, csh, ksh)
        and proc.pname != docker-entrypoint
      output: >
        Shell spawned in container
        (user=%user.name shell=%proc.name container=%container.name image=%container.image.repository:%container.image.tag)
      priority: WARNING
      tags: [process, container]

    - rule: Detect /proc/kcore read
      desc: 读取 /proc/kcore
      condition: >
        and open_read
        and container
        and fd.name = /proc/kcore
      output: >
        /proc/kcore read attempt
        (user=%user.name container=%container.name image=%container.image.repository:%container.image.tag)
      priority: CRITICAL
      tags: [file, container]

    - rule: Detect capability manipulation
      desc: 修改 capability
      condition: >
        and open_write
        and container
        and fd.name = /proc/self/status
      output: >
        Capability manipulation
        (user=%user.name container=%container.name image=%container.image.repository:%container.image.tag)
      priority: WARNING
      tags: [process, container]

    - rule: Outbound to known malicious
      desc: 访问恶意 IP(示意,需结合威胁情报)
      condition: >
        and outbound_connection
        and container
        and fd.sip.name in (bad1.example.com, bad2.example.org)
      output: >
        Outbound to known bad
        (user=%user.name dest=%fd.sip.name container=%container.name)
      priority: CRITICAL
      tags: [network, container]

Tetragon TracingPolicy

apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-bash-spawn
spec:
  kprobes:
  - call: "sched_process_exec"
    syscall: false
    args:
    - index: 0
      type: "nop"
  tracepoints:
  - subsystem: "sched"
    event: "sched_process_exec"
    filters:
    - data:
      - field: "comm"
        value: "bash"
        type: "string"
        op: "Equal"
    trace:
    - type: "Signal"
      pid: 0
      sig: "SIGKILL"

NetworkPolicy 默认拒绝

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

NetworkPolicy 允许 DNS

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector: {}
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

NetworkPolicy 拒绝元数据

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32

Pod SecurityContext 模板

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10000
        runAsGroup: 10000
        fsGroup: 10000
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: api
        image: my-registry.example.com/api:1.0.0
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 10000
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
          seccompProfile:
            type: RuntimeDefault
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: data
          mountPath: /app/data
      volumes:
      - name: tmp
        emptyDir: {}
      - name: data
        emptyDir: {}
      automountServiceAccountToken: false

OPA Gatekeeper 限制 hostPath

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8spsphostfs
spec:
  crd:
    spec:
      names:
        kind: K8sPSPHostFS
  validation:
    openAPIV3Schema:
      type: object
  targets:
  - target: admission.k8s.gatekeeper.sh
    rego: |
      package k8spsphostfs
      violation[{"msg": msg}] {
        volume := input.review.object.spec.volumes[_]
        volume.hostPath
        not input.parameters.allowedHostPaths[_].pathPrefix
        msg := sprintf("hostPath 路径 %v 不在白名单", [volume.hostPath.path])
      }
      violation[{"msg": msg}] {
        volume := input.review.object.spec.volumes[_]
        volume.hostPath
        not path_matches_prefix(volume.hostPath.path, input.parameters.allowedHostPaths)
        msg := sprintf("hostPath 路径 %v 不在白名单", [volume.hostPath.path])
      }
      path_matches_prefix(path, allowed) {
        allowed_path := allowed[_].pathPrefix
        startswith(path, allowed_path)
      }

排查思路

排查表

| 现象 | 检查点 | 命令 | 关键指标 | 结论 |
| --- | --- | --- | --- | --- |
| 容器内看到宿主机进程 | hostPID | kubectl get pod -o yaml | hostPID | 配置错误 |
| 容器内 mount / 宿主目录 | hostPath | kubectl get pod -o yaml | volumes | 配置错误 |
| 容器内能 docker exec | docker.sock | ls /var/run/docker.sock | mount | 高危 |
| 容器内能改系统时间 | SYS_TIME | capsh --print | capabilities | 权限过大 |
| 容器内挖矿 | 进程名 | ps auxf | xmrig | 入侵 |
| Falco 大量告警 | rules | falco logs | trigger | 规则过严 |
| Tetragon 阻断失败 | 节点内核 | uname -r | < 4.19 | 升级内核 |
| 镜像扫描 100+ 高危 | base image | trivy image | CRITICAL | 换 base |
| 节点 /etc/passwd 改动 | inode | stat /etc/passwd | mtime | 入侵 |
| 节点 kubectl 出现异常 API 调用 | audit | audit log | action | 入侵 |

排查流程

  1. 看现象:什么告警?什么资源?
  2. 看 Pod / Node 配置kubectl get pod -o yaml
  3. 看 namespace 标签:是否启用了 PSA。
  4. 看运行时:是否 gVisor / Kata。
  5. 看镜像扫描报告:Trivy / Harbor 报告。
  6. 看检测系统日志:Falco / Tetragon / Tracee。
  7. 看审计日志:kube-apiserver audit log。
  8. 看节点日志:kubelet、containerd、内核 dmesg。
  9. 定位根因:CVE / 配置 / 误用 / 入侵。
  10. 修复 / 加固
  11. 验证
  12. 复盘

风险提醒

  1. privileged: true 是高危配置,生产严格禁止。
  2. hostPath 是高危配置,优先用 PV / CSI。
  3. capability 显式添加要谨慎,尤其 SYS_ADMIN / SYS_MODULE / SYS_PTRACE。
  4. seccomp / apparmor Unconfined 等于没保护。
  5. Kyverno / Gatekeeper Enforce 在生产前先在 Audit 模式跑一段时间。
  6. Tetragon Sigkill 会直接 kill 进程,谨慎使用,先在 staging 验证。
  7. Falco 规则过严 会产生告警风暴。
  8. Trivy 误报:某些库版本判断不准确,重要漏洞要人工复核。
  9. cosign 私钥 必须妥善保管,建议用 KMS。
  10. Harbor / 镜像仓库 要做镜像不可变 tag,否则签名验证会被绕过。
  11. gVisor 性能 不一定适合所有业务(IO 密集型尤其)。
  12. 演练 不能在生产做;要在 staging 或专门环境做。

验证方式

加固验证

# 1. 跑 kube-bench
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl wait --for=condition=complete job/kube-bench --timeout=300s
kubectl logs job/kube-bench

# 2. 跑 docker-bench
docker run --rm --net host --pid host --userns host \
  --cap-add audit_control \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  docker/docker-bench-security

# 3. 检查 PSA
kubectl get ns -L pod-security.kubernetes.io/enforce

# 4. 检查 Kyverno 策略
kubectl get cpol

# 5. 检查 privileged
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | "\(.metadata.namespace)/\(.metadata.name)"'

检测验证

# 1. 触发 Falco 规则
kubectl run test-shell --image=alpine --rm -it -- sh
# 在容器内跑 nslookup
# 看 Falco 是否告警

# 2. 触发 Tetragon
kubectl exec -it <pod> -- bash
# 看 Tetragon 是否阻断

# 3. Trivy 扫描
trivy image my-registry.example.com/my-app:1.0.0

镜像验证

# 签名验证
cosign verify --key cosign.pub my-registry.example.com/my-app:1.0.0

# SBOM
cosign verify-attestation --type spdxjson --key cosign.pub my-registry.example.com/my-app:1.0.0

应急验证

# 模拟特权容器
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: test-priv
spec:
  containers:
    - name: c
      image: alpine
      securityContext:
        privileged: true
EOF

# Kyverno / PSA 应该拒绝

回滚方案

Kyverno 策略回滚

# 1. 改 Audit 模式
kubectl patch cpol <policy> --type='json' -p='[{"op":"replace","path":"/spec/validationFailureAction","value":"Audit"}]'

# 2. 删除策略
kubectl delete cpol <policy>

# 3. 关 PSA
kubectl label namespace <ns> pod-security.kubernetes.io/enforce-

Tetragon TracingPolicy 回滚

kubectl delete tracingpolicy <policy>

Falco 规则回滚

kubectl -n falco edit cm falco-config
# 删掉自定义规则
kubectl -n falco rollout restart daemonset falco

加固过的 Pod 不能启动

# 临时关 Kyverno
kubectl scale deployment kyverno --replicas=0 -n kyverno

# 或者把策略改 Audit
kubectl patch cpol <policy> --type='json' -p='[{"op":"replace","path":"/spec/validationFailureAction","value":"Audit"}]'

# 排查 Pod
kubectl describe pod <pod>
# 看 Events 里的 Admission 拒绝信息

sandbox runtime 启动失败

# 临时回到 runc
kubectl -n kube-system edit cm kubelet-config
# 或节点上 /var/lib/kubelet/config.yaml
# containerRuntimeEndpoint 改回默认
systemctl restart kubelet

总结

容器逃逸不是单点问题,而是"内核 + 容器配置 + 软件漏洞 + 供应链 + 应用层"的综合防御问题。防守不能只靠某个工具,要做"层层加码":

  • 供应链层:Trivy 扫描 + cosign 签名 + Kyverno 验签。
  • 构建层:CI 集成扫描、门禁、最小基础镜像。
  • 部署层:PSA / Kyverno / Gatekeeper 策略执行。
  • 运行时层:seccomp / apparmor / 沙箱 runtime。
  • 检测层:Falco / Tetragon / Tracee 实时检测。
  • 应急层:自动隔离 + 取证 + 复盘。
  • 合规层:CIS Benchmark 自动化、ATT&CK 矩阵映射。

核心要点:

  • 不让特权容器 / hostPath / docker.sock 进入生产。
  • 用 PSA restricted 起步,逐步收紧。
  • 镜像签名 + 漏洞扫描 + SBOM。
  • 运行时检测必须有,至少 Falco 起步。
  • 应急响应流程要演练过,不是写在文档里。
  • 沙箱 runtime(gVisor / Kata)按需上,不要一刀切。
  • ATT&CK 矩阵映射出来能看出短板。

进阶方向:

  • Confidential Containers(CoCo)+ SEV-SNP / TDX。
  • Zero Trust 容器(SPIFFE / SPIRE + Service Mesh)。
  • 用 eBPF 做更细粒度运行时隔离(KubeArmor、Tetragon 进阶)。
  • 用 Confidential Computing 保护 secret。
  • 把检测规则、应急脚本、CIS 报告做成自动化平台。

如果你对容器安全的攻防实践有更多想法,欢迎到云栈社区与大伙儿一起交流。

附录

关键路径

  • Falco 规则:/etc/falco/falco_rules.local.yaml
  • Kyverno 策略:ClusterPolicy CRD。
  • Gatekeeper 模板:ConstraintTemplate CRD。
  • Tetragon 策略:TracingPolicy CRD。
  • K8s PSA 标签:namespace label。
  • 镜像仓库:Harbor / Quay / distribution。
  • 节点内核:/boot/vmlinuz-*
  • Falcosidekick 输出:webhook / Kafka / S3。

关键端口

  • Falco gRPC:5060。
  • Falco HTTP(health):8765。
  • Tetragon API:54321。
  • Trivy Operator:未暴露端口。
  • Kyverno:443(webhook)。

关键资源

常见 CVE 列表

- CVE-2019-5736 (runc)
- CVE-2020-15257 (containerd)
- CVE-2021-30465 (runc)
- CVE-2021-22555 (netfilter)
- CVE-2022-0185 (fsconfig)
- CVE-2022-0492 (cgroup v1)
- CVE-2022-0847 (Dirty Pipe)
- CVE-2022-23222 (nss)
- CVE-2022-2588 (route4)
- CVE-2023-0386 (FUSE overlayfs)
- CVE-2023-26489 / CVE-2023-32629 (GameOver(lay))
- CVE-2024-1086 (nf_tables)
- CVE-2024-21626 (runc)

具体 CVE 持续以 NVD 和发行版公告为准。

写给初中级读者的最后建议

  1. 不要让生产集群里有 privileged: true Pod(除了系统 Pod)。
  2. 镜像只允许从内部仓库拉,签名 + 扫描是标配。
  3. Falco / Kyverno 至少装一个,先在 Audit 模式跑。
  4. PSA restricted 是基线,业务不满足就改业务,不是改策略。
  5. 应急脚本提前写好,演练过,不要等出事了再写。
  6. 沙箱 runtime(gVisor / Kata)按业务重要程度引入,不要一刀切。
  7. ATT&CK 容器矩阵做出来就是安全能力地图,能看出短板。

容器安全防护层级示意图




上一篇:OpenAI误封ChatGPT账号陆续解封,掉订阅用户获1个月订阅补偿
下一篇:Claude API大面积宕机,疑似发生跨租户数据泄露
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-6-7 23:33 , Processed in 0.912025 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表