问题背景
容器逃逸(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_CHOWN、CAP_DAC_OVERRIDE、CAP_FOWNER、CAP_FSETID、CAP_KILL、CAP_SETGID、CAP_SETUID、CAP_SETPCAP、CAP_NET_BIND_SERVICE、CAP_NET_RAW、CAP_SYS_CHROOT、CAP_MKNOD、CAP_AUDIT_WRITE、CAP_SETFCAP)。特权容器(privileged: true)会获得全部 capability。
4. Seccomp / AppArmor / SELinux / Landlock
- seccomp:限制容器可用的 syscall。Kubernetes 提供
RuntimeDefault、Localhost、Unconfined 三种 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_ADMIN、NET_ADMIN、SYS_PTRACE、SYS_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 被攻陷后中间层加恶意代码。
- 镜像仓库被攻击者替换镜像。
- 镜像拉取过程中被中间人。
- 基础镜像里就有的漏洞(
log4j、xz-utils、libcrypto)。
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-security、kube-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 | 入侵 |
排查流程
- 看现象:什么告警?什么资源?
- 看 Pod / Node 配置:
kubectl get pod -o yaml。
- 看 namespace 标签:是否启用了 PSA。
- 看运行时:是否 gVisor / Kata。
- 看镜像扫描报告:Trivy / Harbor 报告。
- 看检测系统日志:Falco / Tetragon / Tracee。
- 看审计日志:kube-apiserver audit log。
- 看节点日志:kubelet、containerd、内核 dmesg。
- 定位根因:CVE / 配置 / 误用 / 入侵。
- 修复 / 加固。
- 验证。
- 复盘。
风险提醒
- privileged: true 是高危配置,生产严格禁止。
- hostPath 是高危配置,优先用 PV / CSI。
- capability 显式添加要谨慎,尤其 SYS_ADMIN / SYS_MODULE / SYS_PTRACE。
- seccomp / apparmor Unconfined 等于没保护。
- Kyverno / Gatekeeper Enforce 在生产前先在 Audit 模式跑一段时间。
- Tetragon Sigkill 会直接 kill 进程,谨慎使用,先在 staging 验证。
- Falco 规则过严 会产生告警风暴。
- Trivy 误报:某些库版本判断不准确,重要漏洞要人工复核。
- cosign 私钥 必须妥善保管,建议用 KMS。
- Harbor / 镜像仓库 要做镜像不可变 tag,否则签名验证会被绕过。
- gVisor 性能 不一定适合所有业务(IO 密集型尤其)。
- 演练 不能在生产做;要在 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 和发行版公告为准。
写给初中级读者的最后建议
- 不要让生产集群里有
privileged: true Pod(除了系统 Pod)。
- 镜像只允许从内部仓库拉,签名 + 扫描是标配。
- Falco / Kyverno 至少装一个,先在 Audit 模式跑。
- PSA restricted 是基线,业务不满足就改业务,不是改策略。
- 应急脚本提前写好,演练过,不要等出事了再写。
- 沙箱 runtime(gVisor / Kata)按业务重要程度引入,不要一刀切。
- ATT&CK 容器矩阵做出来就是安全能力地图,能看出短板。
