【初阶·融合】容器安全基础:镜像扫描、最小权限与安全上下文深度解析
专栏:《AI 工程与安全深度实战》· 第1轮·第3篇(融合篇)
阅读本文前,建议先完成:【初阶·云原生】AI 应用容器化交付深度解析 和 【初阶·安全】AI 安全威胁面全景概述。本文作为本轮次的"知识缝合点",将容器基础设施知识与安全威胁认知融合,帮助你在同一难度层级内完成从"会用容器"到"会安全地用容器"的关键跃迁。
目录
- 前言
- 技术背景与演进逻辑
- 2.1 容器安全问题的诞生:从"它能跑"到"它安全吗"
- 2.2 传统安全模型的失效:边界防御在容器时代的困境
- 2.3 容器安全的四层模型
- 2.4 与 AI 工作负载的关联:为什么 GPU 容器更需要安全加固
- 核心原理深度解析
- 3.1 镜像扫描原理:从静态分析到漏洞图谱构建
- 3.2 最小权限原则的容器化表达
- 3.3 Kubernetes SecurityContext 机制详解
- 3.4 Linux 内核安全机制与容器的交互
- 核心模块/流程/机制详解
- 4.1 镜像安全扫描全链路拆解
- 4.2 SecurityContext 配置决策树
- 4.3 运行时安全策略执行流程
- 4.4 多层防御:镜像扫描 + 准入控制 + 运行时策略的协同
- 技术优缺点与适用场景
- 5.1 镜像扫描的优势与局限
- 5.2 SecurityContext 最小权限的优势与局限
- 5.3 生产适用场景
- 5.4 禁忌场景
- 实战落地
- 6.1 环境准备与工具链
- 6.2 Trivy 镜像扫描实战
- 6.3 Dockerfile 安全加固实战
- 6.4 Kubernetes SecurityContext 完整配置
- 6.5 Pod Security Admission 准入控制
- 6.6 CI/CD 集成:扫描门禁
- 6.7 生产避坑经验
- 全文总结
- 免责声明
- 本期专栏更新说明
- 专栏推荐
- 参考资料
前言
核心痛点 :在上一篇文章中,我们梳理了 AI 安全威胁面的全景------从 Prompt Injection 到数据投毒,从模型逆向到供应链攻击。但有一个关键问题悬而未决:这些攻击一旦突破应用层,进入容器运行时环境之后,我们拿什么来兜底? 容器安全正是在这一层提供纵深防御。本文将容器基础设施知识与安全威胁认知融合,系统讲解容器安全的三大核心支柱------镜像扫描、最小权限、安全上下文------从原理到落地,帮助你在同一难度层级内建立"会用容器且会安全地用容器"的完整能力。
适配人群:
- 已掌握 Docker/K8S 基础操作,希望系统学习容器安全加固的开发者
- AI 基础设施工程师,需要为 GPU 推理/训练容器配置安全策略
- 安全研究人员,正在建立容器安全评估的方法论框架
收获能力:读完本文你将掌握:
- 容器镜像漏洞扫描的完整原理与 Trivy/Grype 实操能力
- 最小权限原则在容器场景中的落地方法(非 root 用户、能力裁剪、只读文件系统)
- Kubernetes SecurityContext 的逐字段解析与生产级配置模板
- Pod Security Admission(PSA)准入控制的配置与 enforcement 策略
- 镜像扫描门禁接入 CI/CD 流水线的完整方案
技术背景与演进逻辑
2.1 容器安全问题的诞生:从"它能跑"到"它安全吗"
容器技术自 Docker 在 2013 年爆发以来,经历了从"新奇玩具"到"生产标配"的快速演进。2026 年的今天,根据 CNCF 年度调查报告,超过 96% 的组织已经在生产环境使用 Kubernetes,而容器镜像的数量以每年 40% 的速度增长。在这种规模下,安全不再是一个可选项。
容器安全问题的根源可以追溯到容器技术的核心设计哲学:共享内核(Shared Kernel)。与虚拟机不同,同一宿主机上的所有容器共享同一个 Linux 内核。这意味着:
[宿主机 Linux Kernel]
│
├──→ [容器 A] ─── 应用进程(隔离弱)
│ 共享内核
├──→ [容器 B] ─── 应用进程(隔离弱)
│ 共享内核
└──→ [容器 C] ─── 应用进程(隔离弱)
共享内核
对比:虚拟机架构(强隔离)
[Hypervisor]
├──→ [VM A] ─── 独立 Guest Kernel ─── 应用(隔离强)
├──→ [VM B] ─── 独立 Guest Kernel ─── 应用(隔离强)
└──→ [VM C] ─── 独立 Guest Kernel ─── 应用(隔离强)
这种共享内核架构的直接影响是:内核漏洞(CVE)可能同时影响宿主机和所有容器。一个容器逃逸(Container Escape)漏洞,攻击者可从容器内突破到宿主机,进而控制所有容器。近两年的关键容器逃逸漏洞------CVE-2024-21626(runc 文件描述符泄漏)和 CVE-2023-2640/CVE-2023-32629(OverlayFS 提权链)------都是这一架构风险的现实案例。
2.2 传统安全模型的失效:边界防御在容器时代的困境
传统安全模型建立在"城堡-护城河"范式之上:
传统边界防御模型(已过时)
[外部网络] ──→ [防火墙] ──→ [DMZ] ──→ [内网应用服务器]
│
假设:内网是安全的
现实:容器之间没有"内网"概念
这个模型在容器时代彻底失效,原因有三:
| 传统假设 | 容器时代的现实 | 安全后果 |
|---|---|---|
| 服务器是长期存在的 | 容器生命周期以分钟/小时计,Pod IP 频繁变化 | 基于 IP 的防火墙规则不可维护 |
| 应用之间通过网络边界隔离 | 同一节点上的 Pod 共享内核和网络命名空间(除非显式配置) | 东西向流量不受传统防火墙检查 |
| 安全补丁按季度/月度节奏 | 基础镜像可能包含数月前的 CVE,镜像构建后漏洞仍在累积 | 从构建到部署的时间窗口内,新 CVE 持续出现 |
因此,容器安全需要一套全新的方法论:将安全控制内嵌到容器生命周期的每一个阶段------从镜像构建、到注册中心存储、到部署准入、再到运行时监控。这就是所谓的"安全左移 + 持续右移"(Shift Left + Shield Right)。
2.3 容器安全的四层模型
容器安全可以按攻击面分为四个层次,每一层对应不同的安全控制:
容器安全四层模型
[第一层:镜像安全] ← 本文重点一:镜像扫描
│
├── 基础镜像选择(Distroless / Alpine / Slim)
├── 依赖扫描(OS 包 + 语言依赖 + IaC 文件)
├── 镜像签名与验证(Cosign + Sigstore)
└── Secrets 防泄漏(.gitignore + .dockerignore + 扫描)
│
↓ 镜像进入 Registry
│
[第二层:Registry 安全] ← 本文涉及:扫描门禁
│
├── 认证与授权(RBAC on Registry)
├── 推送时自动扫描(Trivy / Grype on push)
└── 签名强制验证(只允许签名镜像部署)
│
↓ 镜像被拉取部署
│
[第三层:编排平台安全] ← 本文重点二:SecurityContext + PSA
│
├── Pod Security Admission(PSA)准入控制
├── SecurityContext(runAsNonRoot / readOnlyRootFS / capabilities)
├── RBAC(最小权限 ServiceAccount)
├── NetworkPolicy(Pod 间网络隔离)
└── seccomp / AppArmor / SELinux 策略
│
↓ 容器运行中
│
[第四层:运行时安全] ← 本文涉及:基础检测思路
│
├── 异常行为检测(Falco / Tetragon - eBPF)
├── 文件完整性监控
├── 进程白名单
└── 容器逃逸检测
本文聚焦第一层(镜像扫描)和第三层(SecurityContext + 准入控制),这是容器安全的两道最重要防线,也是 AI 工程团队最容易落地且收益最高的安全投入。
2.4 与 AI 工作负载的关联:为什么 GPU 容器更需要安全加固
AI 工作负载的容器化有自己的特殊性,这使得安全加固更加迫切:
| AI 工作负载特征 | 安全风险 | 容器安全对策 |
|---|---|---|
| 镜像体积巨大(10GB+)含 CUDA/cuDNN/PyTorch 全家桶 | 大量系统包 → CVE 面积极大 | 分层扫描,优先关注 OS 层 CUDA 运行时 |
GPU 设备挂载(nvidia.com/gpu) |
容器对 GPU 的直接访问增加内核交互面 | 严格限制 capabilities,禁用 SYS_ADMIN |
| 训练任务需要挂载大量数据卷 | HostPath/PVC 权限过大可能泄漏训练数据 | readOnlyRootFilesystem + 精确卷权限 |
| 推理服务暴露 HTTP/gRPC 端点 | 模型服务是攻击入口,一旦突破即进入容器 | SecurityContext 完整加固 + NetworkPolicy |
| 大量开源模型和依赖(HuggingFace/PyPI) | 供应链攻击面极大 | 依赖扫描 + SBOM 生成 + 签名验证 |
以典型的 GPU 推理服务为例,其安全风险链路如下:
[外部请求]
↓
[推理 API Gateway]
↓
[Triton/vLLM 推理容器] ← 如果被突破...
│
├── 有 GPU 设备访问 ──→ 可能触发内核驱动漏洞 → 提权
├── 挂载了模型存储卷 ──→ 可读写模型文件 → 模型投毒
└── 默认 root 运行 ──→ 容器内完全控制 → 横向移动
核心原理深度解析
3.1 镜像扫描原理:从静态分析到漏洞图谱构建
3.1.1 容器镜像的分层结构与扫描挑战
容器镜像由多个只读层(Layer)叠加而成,每一层对应 Dockerfile 中的一条指令。以典型的 AI 推理镜像为例:
Dockerfile 层 → 镜像层
FROM nvidia/cuda:12.6.3-runtime-ubuntu22.04 → Layer 0: Ubuntu 22.04 + CUDA runtime
RUN apt-get update && apt-get install -y → Layer 1: 系统工具和库
libgl1-mesa-glx libglib2.0-0
RUN pip install torch==2.7.1 → Layer 2: Python 深度学习框架
transformers==4.52.1
COPY ./model /app/model → Layer 3: 模型文件
COPY ./server.py /app/ → Layer 4: 推理服务代码
CMD ["python", "/app/server.py"] → Layer 5: 启动命令(元数据层)
漏洞可能藏在任意一层中:
- Layer 0(基础镜像) :Ubuntu 22.04 的
libssl3、libc6等系统包中的 CVE - Layer 1(apt 安装) :
libgl1-mesa-glx等图形库的历史漏洞 - Layer 2(pip 安装) :
torch、transformers及其传递依赖中的已知漏洞 - Layer 3-4(COPY):COPY 指令本身不引入传统 CVE,但可能引入错误配置或被篡改的模型文件
3.1.2 漏洞扫描引擎的工作流程
以当前最主流的两款开源扫描器 Trivy(v0.60.x,Aqua Security)和 Grype(v0.90.x,Anchore)为例,扫描流程如下:
[镜像扫描全流程]
Step 1: 镜像获取与解包
[容器镜像] ──→ [tar.gz 解包] ──→ [manifest.json + 各层 tar]
│
Step 2: 文件系统重建 │
[各层 tar 叠加] ──→ [Union 文件系统视图] ──→ [完整的 rootfs]
│
Step 3: 包管理器数据库解析 │
[rootfs/var/lib/dpkg/status] ──→ [已安装的 deb 包列表]
[rootfs/var/lib/rpm/Packages] ──→ [已安装的 rpm 包列表]
[rootfs/usr/local/lib/python3.11/] ──→ [pip 包列表]
[rootfs/node_modules/] ──→ [npm 包列表]
│
Step 4: 版本匹配与漏洞查询 │
[包名 + 版本] ──→ [漏洞数据库查询]
│
├── OS 漏洞:各发行版 OVAL/Security Tracker
├── 语言漏洞:GitHub Advisory Database (GHSA)
├── 通用漏洞:NVD (National Vulnerability Database)
└── 社区情报:OSV (Open Source Vulnerabilities)
│
Step 5: 结果输出与评分 ↓
[漏洞列表 + CVSS 评分 + 修复版本 + 影响层级]
Trivy 和 Grype 的核心差异:
| 对比维度 | Trivy (v0.60.x) | Grype (v0.90.x) |
|---|---|---|
| 扫描范围 | OS 包 + 语言依赖 + IaC 文件 + K8S 资源 | OS 包 + 语言依赖 + SBOM |
| IaC 扫描 | 支持(Dockerfile/K8S/Terraform 等) | 不支持(专注于漏洞扫描) |
| SBOM 生成 | 支持 CycloneDX/SPDX 输出 | 支持 Syft 格式(调用 Syft) |
| 数据库更新 | GitHub Advisory + 多源聚合 | 基于 Grype DB(每日更新) |
| 忽略机制 | .trivyignore 文件 |
.grype.yaml 配置文件 |
| 性能 | 略快(Go 实现,内存优化好) | 略慢(需加载完整 DB) |
| K8S 集成 | Operator 模式 + 内置 CIS Benchmark | 需配合其他工具 |
3.1.3 漏洞评分与优先级:为什么 CVSS 不是唯一的判断标准
仅看 CVSS 评分来判断修复优先级是不够的。以两个假设场景为例:
| CVE 示例 | CVSS 分数 | 攻击向量 | 是否可达 | 修复优先级 |
|---|---|---|---|---|
CVE-2024-XXXX:libssl 漏洞 |
9.8 (Critical) | 网络可达,需 TLS 握手 | 容器不对外暴露 TLS 端口 → 不可达 | 低 |
CVE-2024-YYYY:Python pickle 反序列化 |
7.5 (High) | 本地可利用 | 推理服务接收外部输入 → 可达 | 高 |
这就是为什么现代容器安全强调 "可达性分析"(Reachability Analysis)------不仅要扫描出 CVE 列表,还要判断 CVE 对应的功能在容器中是否真的被使用。CISA Known Exploited Vulnerabilities (KEV) 目录也是关键参考:凡是被列入 KEV 的漏洞,说明已在野外被实际利用,无论 CVSS 分数如何都应优先修复。
3.2 最小权限原则的容器化表达
3.2.1 最小权限原则的起源与容器适配
最小权限原则(Principle of Least Privilege,PoLP)由 Saltzer 和 Schroeder 在 1975 年提出,原始表述是:"Every program and every privileged user of the system should operate using the least amount of privilege necessary to complete the job."
在容器场景中,这个原则被映射为四个具体的技术控制:
最小权限原则 ──容器化映射──→
│
├── 1. 非 root 用户运行(runAsNonRoot)
│
├── 2. 能力裁剪(capabilities drop ALL, add specific)
│
├── 3. 只读根文件系统(readOnlyRootFilesystem)
│
└── 4. 禁止特权提升(allowPrivilegeEscalation: false)
3.2.2 Linux Capabilities:从"全有或全无"到细粒度权限
传统 Unix 权限模型有一个根本性缺陷:root 用户拥有所有权限,非 root 用户几乎没有任何特权操作权限。Linux Capabilities(自内核 2.2 引入)将超级用户的权限分解为约 40 个独立的位(bit)。
与容器安全最相关的能力分类:
高危能力(生产容器必须移除):
| Capability | 功能 | 在容器中的滥用风险 |
|---|---|---|
CAP_SYS_ADMIN |
大量系统管理操作(挂载文件系统、设置 hostname 等) | 最危险的能力,几乎等于 root。可用于挂载 cgroup、修改内核参数、容器逃逸 |
CAP_SYS_PTRACE |
跟踪任意进程、读写其内存 | 可读取其他容器的进程内存 → 数据窃取 |
CAP_SYS_MODULE |
加载/卸载内核模块 | 直接向内核注入代码 → 完全控制宿主机 |
CAP_NET_ADMIN |
网络配置(防火墙、路由、接口) | 重定向流量、ARP 欺骗、中间人攻击 |
CAP_SYS_RAWIO |
原始 I/O 操作(直接磁盘/内存访问) | 绕过文件系统权限读写任意数据 |
CAP_SYS_BOOT |
重启系统 | DoS 攻击 |
常见安全能力(按需添加):
| Capability | 功能 | 典型使用场景 |
|---|---|---|
CAP_NET_BIND_SERVICE |
绑定 1024 以下端口 | Web 服务绑定 80/443 端口 |
CAP_CHOWN |
修改文件所有者 | 初始化脚本需要调整文件权限 |
CAP_SETUID / CAP_SETGID |
设置 UID/GID | 某些初始化进程需要切换用户 |
CAP_DAC_OVERRIDE |
绕过文件读写执行权限检查 | 某些监控 Agent 需要读取系统文件 |
最佳实践:从 ALL DROP 开始
[默认容器(危险)] [安全容器(推荐)]
拥有全部 capabilities capabilities.drop: ["ALL"]
SYS_ADMIN: enable capabilities.add: ["NET_BIND_SERVICE"] ← 仅此一项
SYS_PTRACE: enable
NET_ADMIN: enable
NET_BIND_SERVICE: enable
...(约 40 项全开)
3.3 Kubernetes SecurityContext 机制详解
SecurityContext 是 Kubernetes 中实现最小权限的核心机制。它可以在 Pod 级别和容器级别两个粒度进行配置,容器级别的设置会覆盖 Pod 级别的同名设置。
3.3.1 SecurityContext 字段全解析
Pod 级别字段(spec.securityContext):
| 字段 | 类型 | 默认值 | 安全影响 |
|---|---|---|---|
runAsUser |
int64 | 镜像默认用户(通常为 0) | 指定容器内进程的 UID。必须设为非 0 值 |
runAsGroup |
int64 | 0 | 指定容器内进程的 GID |
runAsNonRoot |
bool | false | true 时强制拒绝以 root 运行的容器,即使镜像默认用户是 root 也会被 kubelet 拒绝启动 |
fsGroup |
int64 | - | 挂载卷的文件系统组所有权,确保同一 Pod 内多容器可共享文件 |
fsGroupChangePolicy |
string | Always |
控制卷权限变更策略:OnRootMismatch 更高效 |
seccompProfile |
object | Unconfined |
seccomp 系统调用过滤策略(RuntimeDefault 推荐) |
supplementalGroups |
\[\]int64 | - | 附加组 ID,容器进程同时属于这些组 |
sysctls |
\[\]Sysctl | - | 内核参数调整(安全相关 sysctl 需特殊权限) |
容器级别字段(spec.containers[].securityContext):
| 字段 | 类型 | 默认值 | 安全影响 |
|---|---|---|---|
runAsUser |
int64 | 继承 Pod 级别 | 覆盖 Pod 级别的运行用户 |
runAsGroup |
int64 | 继承 Pod 级别 | 覆盖 Pod 级别的运行组 |
runAsNonRoot |
bool | 继承 Pod 级别 | 覆盖 Pod 级别的非 root 强制 |
readOnlyRootFilesystem |
bool | false | 关键安全字段。为 true 时容器根文件系统只读,防止攻击者修改系统文件或植入恶意程序 |
allowPrivilegeEscalation |
bool | true | 关键安全字段 。为 false 时禁止进程通过 setuid/setgid 提权(内核 no_new_privs 标志) |
privileged |
bool | false | 极度危险。为 true 时容器获得几乎所有 capabilities 和宿主机设备访问权。任何生产环境都应禁用 |
capabilities.add |
\[\]Capability | - | 显式添加的 Linux capabilities |
capabilities.drop |
\[\]Capability | - | 移除的 Linux capabilities。至少应包含 ALL |
procMount |
string | Default |
/proc 挂载类型。Unmasked 会暴露宿主机 /proc 信息 |
3.3.2 allowPrivilegeEscalation 的底层实现
allowPrivilegeEscalation: false 在内核层面设置了 no_new_privs 标志(prctl(PR_SET_NO_NEW_PRIVS, 1))。这个标志一旦设置,进程及其所有子进程将永久无法通过以下方式获取新权限:
- 执行 setuid/setgid 程序
- 通过 capabilities 继承获得新能力
- 通过
seccomp过滤器更改安全策略
这一机制的不可逆性是关键------即使攻击者在容器中获得了代码执行能力,也无法进一步提权。它实际上建立了一道"提权防火墙"。
3.4 Linux 内核安全机制与容器的交互
容器安全不是 Docker/Kubernetes 的"自研安全层",而是对 Linux 内核多年安全基础设施的编排和暴露。
3.4.1 Namespace 隔离机制
Linux Namespace 是容器隔离的基石。每种 Namespace 隔离一种系统资源视图:
| Namespace 类型 | 隔离内容 | 安全意义 |
|---|---|---|
PID |
进程 ID 空间 | 容器内只能看到自己的进程(ps aux 看不到宿主机进程) |
NET |
网络栈(接口、路由、防火墙规则) | 每个容器有独立的网络环境和 IP |
MNT |
文件系统挂载点 | 容器内的 / 不等于宿主机的 / |
UTS |
主机名和域名 | 容器可以有独立的 hostname |
IPC |
进程间通信(信号量、消息队列、共享内存) | 防止容器间 IPC 干扰 |
USER |
用户和组 ID 映射 | 容器内的 root (UID 0) 可映射为宿主机的普通用户(User Namespace Remapping) |
Namespace 提供的是"视图隔离",而不是"安全边界"。它们阻止的是"看到",而不是"做到"。内核漏洞可能绕过这些隔离。
3.4.2 Cgroups 资源限制机制
Cgroups(Control Groups,v2 为当前标准)限制容器可使用的系统资源。从安全角度看,Cgroups 防止的是 DoS 攻击------一个被攻破的容器不能通过资源耗尽影响宿主机或其他容器。
关键 Cgroup 控制器:
cpu/cpu.max:防止 CPU 垄断memory/memory.max:防止内存耗尽(OOM)pids/pids.max:防止 fork bombio:限制磁盘 IO
3.4.3 seccomp(Secure Computing Mode)
seccomp 是 Linux 内核的系统调用过滤器。它允许进程声明"我只使用以下系统调用",任何超出范围的系统调用都会被内核拦截(可选择 kill 进程或记录日志)。
Kubernetes 提供了内置的 RuntimeDefault seccomp 配置文件,在 Kubernetes v1.31+ 中,这个 profile 默认阻止了约 60+ 个高危系统调用,包括:
reboot--- 防止容器重启宿主机kexec_load--- 防止加载新内核mount--- 防止未授权挂载umount2--- 防止卸载关键文件系统ptrace--- 防止进程调试/注入add_key/request_key--- 防止内核密钥环操作
3.4.4 AppArmor 和 SELinux
AppArmor(Debian/Ubuntu 默认)和 SELinux(RHEL/CentOS 默认)是 Linux 的强制访问控制(MAC)框架。它们通过策略文件控制进程可访问的文件、网络和 capabilities------即使进程以 root 身份运行,也无法绕过 MAC 策略的限制。
Kubernetes 支持通过注解将 AppArmor 或 SELinux 规则应用到 Pod:
- AppArmor:
container.apparmor.security.beta.kubernetes.io/<container_name>: localhost/<profile_name> - SELinux:Pod spec 中的
seLinuxOptions字段
核心模块/流程/机制详解
4.1 镜像安全扫描全链路拆解
将镜像扫描集成到完整的开发和部署流程中,形成如下全链路:
[开发者 push 代码]
↓
[CI Pipeline ── Git 触发]
│
├── Step 1: Build Image (docker build / kaniko)
│ ↓
│ [生成镜像含 tag: git-sha256]
│
├── Step 2: 镜像扫描 (Trivy / Grype)
│ │
│ ├── 扫描结果:0 CRITICAL / 2 HIGH / 8 MEDIUM / 15 LOW
│ │
│ ├── 判断门禁条件:
│ │ ├── CRITICAL > 0 → ❌ 阻断构建(BLOCK)
│ │ ├── HIGH > 5 → ❌ 阻断构建(BLOCK)
│ │ ├── MEDIUM > 20 → ⚠️ 警告但放行(WARN)
│ │ └── 条件通过 → ✅ 继续
│ │
│ ↓
├── Step 3: Push to Registry (带扫描结果元数据)
│ ↓
│ [Harbor / ACR / ECR / GCR]
│
├── Step 4: 签名 (Cosign) ← Keyless 签名(通过 OIDC)
│ ↓
│ [Sigstore Rekor 透明度日志]
│
└── Step 5: 生成 SBOM (Syft / Trivy)
↓
[SBOM 存入 Registry 或专用数据库]
---部署阶段---
[Kubernetes Admission Controller]
│
├── 检查项 1:镜像签名是否有效?(Kyverno / OPA)
│ └── 签名无效 → ❌ 拒绝部署
│
├── 检查项 2:是否有未解决的 CRITICAL 漏洞?
│ └── 有 → ❌ 拒绝部署
│
└── 检查项 3:SBOM 是否完整?
└── 缺失 → ⚠️ 允许但标记为不合规
---运行时阶段---
[运行时监控── Falco / Tetragon]
│
├── 检测到异常系统调用 → 告警
├── 非预期网络连接 → 告警
└── 文件完整性变化 → 告警
4.2 SecurityContext 配置决策树
配置 SecurityContext 不是死记硬背字段列表,而是一个基于工作负载特征的决策过程:
[开始配置 SecurityContext]
│
↓
问题 1: 容器需要以 root 运行吗?
├── 是 → 检查是否真的不可避免?
│ ├── 不可避免 → runAsNonRoot: false, 高风险,需审批 + 运行时监控加强
│ └── 可改造 → runAsNonRoot: true, runAsUser: 1000+
│
└── 否 → runAsNonRoot: true, runAsUser: 1000+
│
↓
问题 2: 容器进程需要写根文件系统吗?
├── 是 → 具体哪些路径需要写?
│ ├── /tmp → 挂载 emptyDir 卷
│ ├── /var/log → 挂载 emptyDir 卷(或 sidecar 日志收集)
│ └── 其他 → 精确挂载卷
│ readOnlyRootFilesystem: true(+ 挂载需要写的卷)
│
└── 否 → readOnlyRootFilesystem: true
│
↓
问题 3: 容器需要哪些 Linux Capabilities?
├── 不确定 → capabilities.drop: ["ALL"]
│ 逐步测试,按需添加(add)
│ 每次添加一个,验证应用功能
│
└── 明确知道 → capabilities.drop: ["ALL"]
capabilities.add: ["NET_BIND_SERVICE"](示例)
│
↓
问题 4: 子进程可以通过 setuid/setgid 提权吗?
├── 需要(极少见,如某些特权初始化工具)→ allowPrivilegeEscalation: true
└── 不需要(99% 场景)→ allowPrivilegeEscalation: false
│
↓
问题 5: 容器需要宿主机设备或完整特权访问吗?
├── 是 → 几乎肯定是设计错误,重新评估架构
└── 否 → privileged: false
│
↓
问题 6: 需要应用系统调用过滤吗?
└── seccompProfile.type: RuntimeDefault(最低要求)
如需更严格的限制,自定义 seccomp profile
4.3 运行时安全策略执行流程
Pod 创建过程中,SecurityContext 和准入控制的执行顺序决定了"安全策略什么时候生效":
[kubectl apply -f pod.yaml]
│
↓
[1. API Server 接收请求]
│
├── 认证(Authentication):验证请求者身份
└── 授权(Authorization ── RBAC):验证请求者是否有权限执行此操作
│
↓
[2. Admission Controllers 链 ── 准入控制阶段]
│
├── Mutating Admission Webhooks 一一 修改 Pod spec
│ │
│ └── 例如:自动注入 sidecar、自动添加 tolerations
│
└── Validating Admission Webhooks 一一 验证 Pod spec
│
├── Pod Security Admission (PSA) → 检查 SecurityContext
│ ├── privileged 模式:不检查
│ ├── baseline 模式:检查基础项(禁用 hostPath/hostNetwork 等)
│ └── restricted 模式:最严格(强制非 root + 禁止提权 + capabilities 限制)
│
├── OPA Gatekeeper / Kyverno → 自定义策略
│ └── 例如:拒绝未签名镜像、强制 readOnlyRootFilesystem
│ ├── 检查通过 → 继续
│ └── 检查失败 → ❌ 拒绝创建(返回错误给用户)
│
↓
[3. 写入 etcd ── Pod 对象持久化]
│
↓
[4. Scheduler 调度 Pod 到节点]
│
↓
[5. Kubelet 创建容器]
│
├── 通过 CRI(Container Runtime Interface)调用容器运行时
│ └── 传递 SecurityContext 配置给 OCI runtime spec
│
└── containerd / CRI-O 创建容器
│
├── 应用 seccomp profile
├── 应用 AppArmor/SELinux 标签
├── 设置 capabilities
├── 设置 UID/GID
├── 设置 no_new_privs
└── 挂载根文件系统(read-only 或 read-write)
│
↓
[6. 容器启动 ── 安全策略生效]
4.4 多层防御:镜像扫描 + 准入控制 + 运行时策略的协同
容器安全的核心理念是纵深防御(Defense in Depth)------单一控制点被绕过时,后续控制层仍能提供保护。
攻击者视角下的多层防御矩阵
攻击方式 第一道防线 第二道防线 第三道防线
镜像扫描 准入控制 运行时策略
───────────────────────────────────────────────────────────────────────────────
使用含 CVE 的镜像 扫描发现CVE→阻断 签名验证→拒绝 (CVE无法直接触发检测
构建/推送 部署 除非被利用)
镜像被篡改/替换 (构建时未被篡改 签名验证→拒绝 文件完整性监控→
则扫描不报) 部署 检测到变更→告警
运行 root 容器 Dockerfile 扫描→ PSA restricted→ Falco 检测到 root
发现USER是root→告警 拒绝创建 进程→告警
挂载宿主机敏感路径 Dockerfile 扫描 PSA baseline→ Falco 检测到非预期
无法检测 拒绝 hostPath 文件访问→告警
运行时容器逃逸 无法防御 无法防御 eBPF 检测到非预期
(已过构建阶段) (已过准入阶段) 内核调用→告警
恶意进程注入 COPY 指令扫描 无法防御 Falco 检测到非预期
无法检测恶意代码 (合法镜像内的 进程 spawn→告警
恶意代码扫描无果)
此矩阵的核心启示是:没有任何单一防线是完美的,三者的组合才构成真正的安全体系。
技术优缺点与适用场景
5.1 镜像扫描的优势与局限
技术优势:
- 自动化程度高:Trivy/Grype 可零人工介入完成全镜像漏洞扫描,输出结构化 JSON 结果,直接接入 CI/CD 流程
- 数据库覆盖广:聚合 NVD、GHSA、各发行版 Security Tracker 等 20+ 数据源,覆盖主流 OS 和语言生态
- 成本极低:开源免费,单次扫描通常在 30 秒至 2 分钟内完成(取决于镜像大小)
- 合规支撑强:扫描结果可直接映射到 PCI DSS(Req 6)、CIS Benchmark、NIST SP 800-190 等合规标准的控制项
- SBOM 联动:与 Syft 等 SBOM 工具配合,满足美国行政令 14028 对软件供应链透明度的要求
现存局限:
- 无法检测未知漏洞(0day):扫描只能匹配已知 CVE,对尚未披露的漏洞无能为力
- 可达性盲区:传统扫描列出所有理论上的漏洞,但无法判断漏洞对应功能在容器内是否实际被使用
- 配置错误检测有限:虽然 Trivy 能扫描 Dockerfile 和 K8S manifest 的错误配置,但覆盖面不如专用工具(如 kube-bench、checkov)
- 误报与噪声:发行版的 backport 修复(版本号不变但补丁已打)会产生大量误报,需人工过滤
- 对自定义/内部依赖无覆盖:企业内部自研库不在公共 CVE 数据库中,无法被扫描
- 先有镜像再有扫描:扫描发生在构建之后,如果漏洞阻止发布则需要重建------这比在 Dockerfile 阶段就阻止引入漏洞组件要滞后
5.2 SecurityContext 最小权限的优势与局限
技术优势:
- 缩小攻击面最直接:将容器的 capabilities 从约 40 项裁剪到 1-2 项,攻击者即使获得代码执行能力也"什么也做不了"
- 零性能开销:capabilities drop、readOnlyRootFilesystem、no_new_privs 都是内核原生机制,不增加任何运行时开销
- 不可逆性 :
allowPrivilegeEscalation: false设置的no_new_privs标志对所有子进程不可逆,提供了硬性安全保障 - 合规即开即用:PSA restricted 模式直接映射到 CIS Benchmark v2.0.1 的核心安全要求,无需额外配置
- 配置标准化:YAML 声明式配置,易于版本控制和审计,与 GitOps 流程天然契合
现存局限:
- 改造存量应用有成本:为 root 运行的旧应用适配非 root 用户,可能需要修改文件权限、端口绑定逻辑等
- 调试困难 :
capabilities drop: ALL后应用启动失败时,往往只能通过strace逐系统调用排查缺少的能力 - 容器层设置有限:SecurityContext 能限制的是容器内的行为,但对于内核级别的漏洞利用(直接 syscall 攻击),仍需 seccomp/AppArmor 等更深层机制配合
- Pod/Container 两级覆盖的潜在混淆:容器级别覆盖 Pod 级别设置可能导致"以为配置了,实际被覆盖了"的情况
- 生态碎片化:PSA 是 K8S 内置的,但 OPA Gatekeeper、Kyverno 各有自己的策略语言,增加了学习成本
5.3 生产适用场景
场景一:面向公网的 AI 推理服务
AI 推理服务通常作为 API 暴露在公网,是最容易成为攻击入口的容器。必须应用完整加固:
- Trivy 扫描 gated in CI(阻断 CRITICAL)
- SecurityContext:
runAsNonRoot+readOnlyRootFilesystem+capabilities.drop: ALL+allowPrivilegeEscalation: false - PSA restricted 模式强制
- 模型文件通过 ReadOnlyMany PVC 挂载
场景二:多租户 GPU 集群中的训练任务
在多用户(不同团队)共享的 GPU 集群中,必须防止一个用户的容器越权访问其他用户的数据或干扰其他任务:
- SecurityContext 确保每个用户的训练容器使用不同 UID
- NetworkPolicy 限制训练 Pod 只能访问指定的数据服务和模型仓库
- GPU 设备通过 Device Plugin 隔离,配合 MIG(Multi-Instance GPU)硬件隔离
场景三:CI/CD 构建流水线中的构建容器
构建容器(如 Jenkins Agent、Tekton Task)通常需要较高的权限(挂载 Docker socket、构建镜像),是供应链攻击的高危目标:
- 必须使用专用的构建 ServiceAccount,与生产集群 RBAC 完全隔离
- 构建容器虽需部分特权,但
capabilities仍应精细裁剪(只加必需的,而非 ALL) - 构建过程在临时 Namespace 中进行,构建完成即销毁
5.4 禁忌场景
禁忌一:在安全要求极高的合规环境仅靠镜像扫描
镜像扫描是必要的,但不充分。在 PCI DSS(支付卡数据)、HIPAA(医疗数据)等合规环境中,仅做扫描而不配置 SecurityContext 和运行时监控,会留下可审计的安全缺口。
禁忌二:对传统单体应用一刀切开启 readOnlyRootFilesystem
直接在 readOnlyRootFilesystem 上运行未适配的遗留应用,会导致应用在初始化日志文件、临时文件时崩溃。必须先分析应用的写路径,通过卷挂载满足其写入需求,再开启只读文件系统。
禁忌三:在不需要 CAP_NET_BIND_SERVICE 时保留 NET_RAW
NET_RAW 能力允许容器创建原始套接字,这是 ARP 欺骗、ICMP 隧道等攻击的基础。除非容器明确需要(如实现自定义网络协议),否则应始终移除。
实战落地
6.1 环境准备与工具链
本文实战基于以下工具版本(2026年6月当前最新稳定版):
| 工具 | 版本 | 用途 |
|---|---|---|
| Docker | 27.x | 容器运行时(开发环境) |
| Kubernetes | v1.32.x | 容器编排平台 |
| containerd | 2.1.x | K8S 默认容器运行时 |
| Trivy | v0.60.1 | 镜像漏洞扫描 |
| Grype | v0.90.0 | 镜像漏洞扫描(备选) |
| Cosign | v2.5.x | 镜像签名 |
| Syft | v1.21.x | SBOM 生成 |
| Kubectl | v1.32.x | K8S 命令行工具 |
安装核心扫描工具:
bash
# 安装 Trivy(推荐首选)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# 安装 Grype(备选/交叉验证)
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# 安装 Cosign(镜像签名)
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign
chmod +x /usr/local/bin/cosign
# 安装 Syft(SBOM 生成)
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
6.2 Trivy 镜像扫描实战
基础扫描
bash
# 扫描本地 Docker 镜像
trivy image myapp:latest
# 扫描远程镜像(如公共仓库的 PyTorch 镜像)
trivy image pytorch/pytorch:2.7.1-cuda12.6-cudnn9-runtime
# 以 JSON 格式输出(便于 CI/CD 解析)
trivy image --format json --output scan-result.json myapp:latest
# 仅显示 CRITICAL 和 HIGH 级别的漏洞
trivy image --severity CRITICAL,HIGH myapp:latest
# 扫描镜像中的 IaC 错误配置(Dockerfile/K8S manifest 等)
trivy config ./k8s-manifests/
高级过滤与忽略
bash
# 使用 .trivyignore 文件忽略特定 CVE(如已知的误报或不适用场景)
cat > .trivyignore << 'EOF'
# 格式: CVE-ID
# 示例:基础镜像 openssl 漏洞,但应用不使用 TLS
CVE-2024-12345
# 过期日期支持(过期后 Trivy 重新报告)
CVE-2024-67890 # 忽略至 2026-12-31
EOF
trivy image --ignorefile .trivyignore myapp:latest
典型 AI 推理镜像的扫描结果分析
以 PyTorch 推理镜像为例,扫描结果通常呈现如下结构:
[镜像扫描结果分布(PyTorch v2.7.1 + CUDA 12.6)]
my-inference-app:latest (ubuntu 22.04)
├── OS Packages (dpkg)
│ ├── CRITICAL: 0
│ ├── HIGH: 3 ← libssl3, libcrypto3, libncurses6
│ ├── MEDIUM: 12
│ └── LOW: 8
│
├── Python Packages (pip)
│ ├── CRITICAL: 0
│ ├── HIGH: 2 ← urllib3, certifi
│ ├── MEDIUM: 7
│ └── LOW: 3
│
└── Misconfigurations (Kubernetes)
├── CRITICAL: 0
├── HIGH: 1 ← 未设置 runAsNonRoot
├── MEDIUM: 2 ← 未设置 readOnlyRootFilesystem, 未设置 capabilities drop
└── LOW: 1
处理策略:
- 确认 HIGH 级别的 OS 包漏洞是否可达(本容器是否使用 TLS)
- Python urllib3/certifi 的 HIGH 漏洞通常影响 HTTPS 连接验证 → 如果推理服务对外暴露 HTTPS → 立即升级
- 错误配置项 → 立即修复 SecurityContext
CI/CD 门禁脚本
bash
#!/bin/bash
# ci-trivy-gate.sh --- 镜像扫描门禁
# 在 CI pipeline 的 build stage 之后、push stage 之前执行
set -euo pipefail
IMAGE="$1"
THRESHOLD_CRITICAL="${2:-0}" # 默认:不允许任何 CRITICAL
THRESHOLD_HIGH="${3:-3}" # 默认:HIGH 不超过 3 个
echo "=== Trivy 扫描: $IMAGE ==="
# 执行扫描并输出 JSON
trivy image \n --format json \n --severity CRITICAL,HIGH \n --ignorefile .trivyignore \n --output trivy-report.json \n "$IMAGE"
# 解析漏洞数量
CRITICAL_COUNT=$(jq '[.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL")] | length' trivy-report.json)
HIGH_COUNT=$(jq '[.Results[].Vulnerabilities[] | select(.Severity == "HIGH")] | length' trivy-report.json)
echo "CRITICAL: $CRITICAL_COUNT / HIGH: $HIGH_COUNT"
# 门禁判断
PASS=true
if [ "$CRITICAL_COUNT" -gt "$THRESHOLD_CRITICAL" ]; then
echo "FAIL: CRITICAL 漏洞数 ($CRITICAL_COUNT) 超过阈值 ($THRESHOLD_CRITICAL)"
PASS=false
fi
if [ "$HIGH_COUNT" -gt "$THRESHOLD_HIGH" ]; then
echo "FAIL: HIGH 漏洞数 ($HIGH_COUNT) 超过阈值 ($THRESHOLD_HIGH)"
PASS=false
fi
if [ "$PASS" = false ]; then
echo "=== 漏洞详情 ==="
jq -r '.Results[].Vulnerabilities[] | select(.Severity == "CRITICAL" or .Severity == "HIGH") | " [(.Severity)] (.VulnerabilityID) --- (.Title) --- 修复版本: (.FixedVersion // "N/A")"' trivy-report.json
echo "=== 扫描门禁失败,阻断推送 ==="
exit 1
fi
echo "=== 扫描门禁通过 ==="
6.3 Dockerfile 安全加固实战
以下是一个典型的 AI 推理服务 Dockerfile,从"不安全"到"安全"的改造过程:
dockerfile
# ============================================
# 加固前:典型的"不够安全"的 AI 推理 Dockerfile
# ============================================
# FROM pytorch/pytorch:2.7.1-cuda12.6-cudnn9-runtime
#
# WORKDIR /app
# COPY requirements.txt .
# RUN pip install --no-cache-dir -r requirements.txt
# COPY . .
# EXPOSE 8000
# CMD ["python", "server.py"]
#
# 问题清单:
# 1. 默认 root 用户运行
# 2. 根文件系统可写
# 3. 未指定特定版本的基础镜像 Digest
# 4. 镜像层过多,COPY . . 包含不必要文件
# ============================================
# 加固后:生产级安全的 AI 推理 Dockerfile
# ============================================
# Step 1: 使用精确 Digest 的基础镜像(防止标签漂移)
FROM pytorch/pytorch:2.7.1-cuda12.6-cudnn9-runtime@sha256:a1b2c3d4e5f6...
# Step 2: 创建非 root 用户
ARG USERNAME=inference
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupadd --gid $USER_GID $USERNAME \n && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \n && mkdir -p /app /data/models /data/logs \n && chown -R $USERNAME:$USERNAME /app /data
# Step 3: 安装依赖(使用 --no-cache-dir 减少镜像体积)
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r /app/requirements.txt \n && rm -rf /root/.cache/pip
# Step 4: 仅复制应用代码(不包含测试、文档等非必要文件)
COPY --chown=$USERNAME:$USERNAME server.py /app/
COPY --chown=$USERNAME:$USERNAME src/ /app/src/
# Step 5: 切换到非 root 用户
USER $USERNAME
# Step 6: 声明暴露端口(文档目的,实际网络策略由 K8S 控制)
EXPOSE 8000
# Step 7: 健康检查(不依赖 root 权限)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \n CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"
# Step 8: 启动命令
CMD ["python", "server.py"]
关键加固点解释:
- 精确 Digest :
@sha256:...确保拉取的是经过验证的精确镜像版本,防止上游镜像被篡改(标签漂移攻击) - 非 root 用户 :创建专用用户
inference,UID 1000,不属于 root 组 --chown:COPY 时直接设置文件所有权,避免后续 chown 产生额外层- 清理缓存 :
rm -rf /root/.cache/pip减少攻击面
6.4 Kubernetes SecurityContext 完整配置
生产级安全 Pod 模板
yaml
apiVersion: v1
kind: Pod
metadata:
name: secure-inference-pod
labels:
app: inference
security-tier: restricted
spec:
# ===== Pod 级别 SecurityContext =====
securityContext:
# 指定非 root 用户和组(所有容器默认继承)
runAsUser: 1000
runAsGroup: 2000
runAsNonRoot: true
# 卷文件系统组(确保同一 Pod 内容器可共享卷文件)
fsGroup: 2000
fsGroupChangePolicy: "OnRootMismatch"
# seccomp 系统调用过滤(使用 K8S 内置的安全 profile)
seccompProfile:
type: RuntimeDefault
# ===== 卷定义 =====
volumes:
# 临时存储(日志、临时文件)--- 不挂载宿主机路径
- name: tmp-volume
emptyDir:
sizeLimit: 500Mi
- name: log-volume
emptyDir:
sizeLimit: 200Mi
# 模型存储(只读挂载 --- 来自 PVC 或 ConfigMap)
- name: model-volume
persistentVolumeClaim:
claimName: model-storage-pvc
readOnly: true
# ===== 容器定义 =====
containers:
# --- 主容器:推理服务 ---
- name: inference-server
image: myregistry.azurecr.io/inference-app:v1.2.3@sha256:abc123...
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
protocol: TCP
name: http
# 容器级别 SecurityContext
securityContext:
# 继承并强化 Pod 级别设定
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
# 能力裁剪 --- 从零开始,只加必需的
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # 绑定 8000 端口(< 1024 需要此能力,> 1024 可省略)
# 卷挂载
volumeMounts:
- name: tmp-volume
mountPath: /tmp
- name: log-volume
mountPath: /app/logs
- name: model-volume
mountPath: /data/models
readOnly: true
# 资源限制(防止 DoS)
resources:
limits:
cpu: "4"
memory: "16Gi"
nvidia.com/gpu: 1
requests:
cpu: "2"
memory: "8Gi"
nvidia.com/gpu: 1
# 探活
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
# --- Sidecar 容器:日志收集 ---
- name: log-collector
image: fluent/fluent-bit:3.2.7
securityContext:
allowPrivilegeEscalation: false
privileged: false
readOnlyRootFilesystem: true
runAsUser: 1001
runAsGroup: 2000
capabilities:
drop:
- ALL
# 日志收集容器不需要 NET_BIND_SERVICE
volumeMounts:
- name: log-volume
mountPath: /var/log/app
readOnly: true # sidecar 只读日志
- name: tmp-volume
mountPath: /tmp
上述 YAML 的安全控制矩阵:
控制项 Pod 级别 容器(推理) 容器(日志) 安全效果
─────────────────────────────────────────────────────────────────────────
runAsNonRoot: true ✓ ✓(继承) ✓(继承) 防止 root 运行
runAsUser: 1000 ✓ ✓(继承) ✓(继承) 统一 UID,便于审计
readOnlyRootFilesystem N/A true true 防止文件篡改
allowPrivilegeEscalation N/A false false 禁止 setuid 提权
privileged: false N/A ✓(默认) ✓(默认) 禁止特权容器
capabilities drop: ALL N/A ✓ ✓ 移除所有能力
capabilities add N/A NET_BIND (none) 仅绑定端口需要
seccompProfile ✓ 继承 继承 系统调用过滤
fsGroup: 2000 ✓ 继承 继承 卷文件权限统一
fsGroupChangePolicy ✓ 继承 继承 仅在必要时改权限
GPU 资源限制 N/A 1 GPU 0 GPU 防止 GPU 资源抢占
CPU/Memory 限制 N/A 限制 限制 防止 DoS
6.5 Pod Security Admission 准入控制
PSA 是 Kubernetes v1.25 起内置的 Pod 安全策略执行机制(替代了已废弃的 PodSecurityPolicy)。
命名空间级别配置
yaml
# 为命名空间设置 PSA 标签
---
apiVersion: v1
kind: Namespace
metadata:
name: ai-inference-production
labels:
# enforce: 强制拒绝不合规 Pod
# audit: 仅记录日志(不拒绝),用于测试阶段
# warn: 向用户发出警告(不拒绝),用于过渡阶段
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.32
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.32
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.32
---
apiVersion: v1
kind: Namespace
metadata:
name: ai-training-internal
labels:
# 训练命名空间使用 baseline(允许一些特权但禁止 hostPath/hostNetwork)
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.32
三个安全策略级别的对比
| 策略字段 | Privileged | Baseline | Restricted |
|---|---|---|---|
hostNetwork |
允许 | 禁止 | 禁止 |
hostPID/hostIPC |
允许 | 禁止 | 禁止 |
hostPorts |
允许 | 允许(无限制) | 禁止(或限制范围) |
privileged 容器 |
允许 | 禁止 | 禁止 |
CAP_SYS_ADMIN 等危险能力 |
允许 | 禁止 | 禁止 |
hostPath 卷 |
允许 | 禁止 | 禁止 |
procMount: Unmasked |
允许 | 禁止 | 禁止 |
runAsNonRoot |
不强制 | 不强制 | 强制 true |
allowPrivilegeEscalation |
不强制 | 不强制 | 强制 false |
| seccomp 类型 | 不强制 | 不强制 | 强制 RuntimeDefault 或 Localhost |
| capabilities drop | 不强制 | 不强制 | 强制 drop ALL(可 add NET_BIND_SERVICE) |
渐进式部署策略:
[阶段一:审计模式] [阶段二:警告模式] [阶段三:强制模式]
audit: restricted audit: restricted audit: restricted
warn: restricted warn: restricted warn: restricted
enforce: privileged enforce: baseline enforce: restricted
│ │
1 周观察,修复 baseline 1 周观察,修复 restricted
违规项 违规项
│ │
确认无意外阻断 全面强制
6.6 CI/CD 集成:扫描门禁
将镜像扫描和 SecurityContext 验证集成到 GitHub Actions 流水线中:
yaml
# .github/workflows/container-security.yml
name: Container Security Gates
on:
pull_request:
branches: [main, release/*]
push:
branches: [main]
env:
REGISTRY: myregistry.azurecr.io
IMAGE_NAME: inference-app
jobs:
# ===== Job 1: 镜像扫描 =====
trivy-scan:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@v0.25.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
exit-code: '1' # 发现漏洞时退出码为 1(阻断流水线)
ignore-unfixed: false # 有修复版本才算可操作
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
- name: Push image (only if scan passes)
run: |
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# ===== Job 2: K8S Manifest 安全校验 =====
k8s-security-check:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Kubeconform
run: |
curl -sSfL https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz | tar xz
sudo mv kubeconform /usr/local/bin/
- name: Install Trivy (for config scanning)
run: |
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
- name: Validate K8S manifests
run: |
kubeconform -summary -strict k8s/
- name: Scan K8S manifests for security misconfigurations
run: |
trivy config \n --severity CRITICAL,HIGH \n --exit-code 1 \n --format table \n k8s/
# ===== Job 3: SBOM 生成 =====
generate-sbom:
runs-on: ubuntu-24.04
needs: trivy-scan
steps:
- name: Generate SBOM with Syft
run: |
syft ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \n --output spdx-json \n --file sbom.spdx.json
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
6.7 生产避坑经验
坑 1:runAsNonRoot: true 后容器 CrashLoopBackOff
现象 :配置 runAsNonRoot: true 后,容器反复重启,日志显示 "Permission denied"。
根因:镜像默认以 root(UID 0)启动,或者容器内某些路径/文件的权限不属于指定的 runAsUser。
排查步骤:
bash
# 1. 查看镜像的默认用户
docker inspect <image> | jq '.[0].Config.User'
# 2. 如果为空字符串,说明镜像没有指定用户 → 默认 root
# 解决方案:修改 Dockerfile,添加非 root 用户
# 3. 检查文件的挂载权限
kubectl exec -it <pod> -- ls -la /app
kubectl exec -it <pod> -- whoami
# 4. 如果是卷权限问题
# 在 Pod securityContext 中配置 fsGroup
# securityContext:
# fsGroup: 2000
坑 2:readOnlyRootFilesystem: true 导致应用启动失败
现象:应用启动时报错 "Read-only file system",日志无法写入、临时文件无法创建。
根因 :应用尝试写入根文件系统的某个路径。常见路径包括 /tmp、/var/log、/run,或应用内硬编码的 /app/data 目录。
修复方案:
yaml
volumes:
- name: ephemeral-tmp
emptyDir:
sizeLimit: 1Gi
- name: ephemeral-var-run
emptyDir:
sizeLimit: 100Mi
volumeMounts:
- name: ephemeral-tmp
mountPath: /tmp
- name: ephemeral-var-run
mountPath: /var/run
识别需要写的路径可以通过 strace 快速定位:
bash
# 在容器内运行 strace,观察哪些 write/open 调用失败
strace -f -e trace=write,openat,creat -o strace.log python server.py 2>&1
grep "EROFS|Read-only" strace.log
坑 3:PSA restricted 模式下合法镜像被误拒绝
现象:PSA 配置为 enforce: restricted,某些合规镜像(如 Prometheus node-exporter)被拒绝创建。
根因:某些基础架构组件(如监控 Agent、日志收集器、CNI 插件)确实需要更高的权限来完成其功能。这些组件应当部署在独立的命名空间中,使用宽松的 PSA 策略。
最佳实践:按命名空间分层管理 PSA:
yaml
# 基础设施命名空间 --- baseline
kube-system: pod-security.../enforce: baseline
monitoring: pod-security.../enforce: baseline
ingress-nginx: pod-security.../enforce: baseline
# 生产应用命名空间 --- restricted
ai-inference-prod: pod-security.../enforce: restricted
ai-training-prod: pod-security.../enforce: restricted
# 开发/测试 --- audit only(不阻断,仅记录)
ai-inference-dev: pod-security.../enforce: privileged
pod-security.../audit: restricted
坑 4:capabilities 在容器级别覆盖 Pod 级别的 drop
现象 :在 Pod 级别设置了 capabilities.drop: ["ALL"],但在容器级别设置了 capabilities.add: ["SYS_ADMIN"]。由于容器级别的 add 会覆盖 Pod 级别的 drop,实际上容器拥有了 SYS_ADMIN 权限。
教训:始终在容器级别显式设置完整的 capabilities 配置,不要依赖 Pod 级别的隐式继承和补充语义。
yaml
# 错误示例 --- 以为容器会继承 Pod 的 drop ALL
spec:
securityContext:
capabilities:
drop: ["ALL"] # ← Pod 级别
containers:
- name: app
securityContext:
capabilities:
add: ["NET_ADMIN"] # ← 这会覆盖 Pod 级别的 drop!
# 正确示例 --- 在容器级别也显式 drop ALL
spec:
securityContext:
capabilities:
drop: ["ALL"] # Pod 级别 baseline
containers:
- name: app
securityContext:
capabilities:
drop: ["ALL"] # ← 显式重复,确保安全
add: ["NET_BIND_SERVICE"]
全文总结
容器安全不是一项单一技术,而是一套贯穿镜像构建、部署准入和运行时监控的纵深防御体系。在 AI 工程场景中,由于 GPU 容器的特殊性(大体积、多依赖、高权限需求),安全加固的价值更加突出。
本文建立的三道防线构成了容器安全的最小可行安全基线:
- 镜像扫描(Trivy/Grype):在镜像进入 Registry 之前发现并阻断包含已知漏洞的镜像,是供应链安全的第一道门禁
- SecurityContext 最小权限:通过非 root 用户运行、能力裁剪、只读文件系统、禁止提权四重控制,将容器攻击面从"全开"缩小到"仅必需"
- PSA 准入控制:在集群层面通过 restricted 策略强制执行安全基线,使个人配置错误无法绕过组织级的安全策略
三道防线协同工作,形成"构建时检查 → 部署时阻断 → 运行时限制"的完整安全链路。每个控制单独来看都有局限,但组合使用可实现大幅的安全提升。对于 AI 工程团队而言,从最容易落地的镜像扫描和 SecurityContext 配置开始,逐步扩展到 PSA 强制准入和运行时异常检测,是一条低成本、高回报的安全能力建设路径。
关键原则回顾:
- 默认拒绝,按需放行(Default Deny)
- 纵深防御,不依赖单一控制(Defense in Depth)
- 最小权限,只给必需的(Least Privilege)
- 安全左移,在构建阶段就消除风险(Shift Left)
免责声明
本文所有技术内容仅供安全研究与教学目的使用。文中涉及的攻击技术均已做无害化处理,仅保留教学所需的最小核心代码。文中引用的 CVE 编号均为已公开披露且已有修复方案的已知漏洞。严禁将文中技术用于非法用途。实际部署安全方案前请结合自身业务场景进行充分测试,安全配置可能因集群版本、容器运行时和底层操作系统差异而需要调整。
本期专栏更新说明
本文为《AI 工程与安全深度实战》订阅专栏持续迭代内容。本文为第 1 轮(初阶)· 第 3 篇(融合篇) ,至此初阶三篇文章(云原生篇 → 安全篇 → 融合篇)已全部完成。下一篇将进入第 2 轮(中阶)· 云原生篇:GPU 感知调度与设备插件机制深度解析。
专栏按初/中/高阶递进规划,长期更新 AI 云原生架构、GPU 算力工程、LLMOps 运维智能化、模型安全攻防、供应链安全、安全治理与合规实践,一次订阅,永久持续更新。
专栏推荐
参考资料
- Kubernetes Pod Security Standards
- Kubernetes Security Context Documentation
- Trivy Documentation - Aqua Security
- Grype - Anchore
- Orca Security: 8 Container Security Best Practices for 2026
- Wiz: Kubernetes Security Context Best Practices
- CIS Kubernetes Benchmark v2.0.1
- CISA Known Exploited Vulnerabilities Catalog
- CVE-2024-21626: runc Container Breakout
- CVE-2023-2640/CVE-2023-32629: Ubuntu OverlayFS Privilege Escalation
- NIST SP 800-190: Application Container Security Guide
- Kubernetes Seccomp Tutorial