【初阶·融合】容器安全基础:镜像扫描、最小权限与安全上下文深度解析

【初阶·融合】容器安全基础:镜像扫描、最小权限与安全上下文深度解析

专栏:《AI 工程与安全深度实战》· 第1轮·第3篇(融合篇)

阅读本文前,建议先完成:【初阶·云原生】AI 应用容器化交付深度解析【初阶·安全】AI 安全威胁面全景概述。本文作为本轮次的"知识缝合点",将容器基础设施知识与安全威胁认知融合,帮助你在同一难度层级内完成从"会用容器"到"会安全地用容器"的关键跃迁。

目录

  1. 前言
  2. 技术背景与演进逻辑
    • 2.1 容器安全问题的诞生:从"它能跑"到"它安全吗"
    • 2.2 传统安全模型的失效:边界防御在容器时代的困境
    • 2.3 容器安全的四层模型
    • 2.4 与 AI 工作负载的关联:为什么 GPU 容器更需要安全加固
  3. 核心原理深度解析
    • 3.1 镜像扫描原理:从静态分析到漏洞图谱构建
    • 3.2 最小权限原则的容器化表达
    • 3.3 Kubernetes SecurityContext 机制详解
    • 3.4 Linux 内核安全机制与容器的交互
  4. 核心模块/流程/机制详解
    • 4.1 镜像安全扫描全链路拆解
    • 4.2 SecurityContext 配置决策树
    • 4.3 运行时安全策略执行流程
    • 4.4 多层防御:镜像扫描 + 准入控制 + 运行时策略的协同
  5. 技术优缺点与适用场景
    • 5.1 镜像扫描的优势与局限
    • 5.2 SecurityContext 最小权限的优势与局限
    • 5.3 生产适用场景
    • 5.4 禁忌场景
  6. 实战落地
    • 6.1 环境准备与工具链
    • 6.2 Trivy 镜像扫描实战
    • 6.3 Dockerfile 安全加固实战
    • 6.4 Kubernetes SecurityContext 完整配置
    • 6.5 Pod Security Admission 准入控制
    • 6.6 CI/CD 集成:扫描门禁
    • 6.7 生产避坑经验
  7. 全文总结
  8. 免责声明
  9. 本期专栏更新说明
  10. 专栏推荐
  11. 参考资料

前言

核心痛点 :在上一篇文章中,我们梳理了 AI 安全威胁面的全景------从 Prompt Injection 到数据投毒,从模型逆向到供应链攻击。但有一个关键问题悬而未决:这些攻击一旦突破应用层,进入容器运行时环境之后,我们拿什么来兜底? 容器安全正是在这一层提供纵深防御。本文将容器基础设施知识与安全威胁认知融合,系统讲解容器安全的三大核心支柱------镜像扫描、最小权限、安全上下文------从原理到落地,帮助你在同一难度层级内建立"会用容器且会安全地用容器"的完整能力。

适配人群

  • 已掌握 Docker/K8S 基础操作,希望系统学习容器安全加固的开发者
  • AI 基础设施工程师,需要为 GPU 推理/训练容器配置安全策略
  • 安全研究人员,正在建立容器安全评估的方法论框架

收获能力:读完本文你将掌握:

  1. 容器镜像漏洞扫描的完整原理与 Trivy/Grype 实操能力
  2. 最小权限原则在容器场景中的落地方法(非 root 用户、能力裁剪、只读文件系统)
  3. Kubernetes SecurityContext 的逐字段解析与生产级配置模板
  4. Pod Security Admission(PSA)准入控制的配置与 enforcement 策略
  5. 镜像扫描门禁接入 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 的 libssl3libc6 等系统包中的 CVE
  • Layer 1(apt 安装)libgl1-mesa-glx 等图形库的历史漏洞
  • Layer 2(pip 安装)torchtransformers 及其传递依赖中的已知漏洞
  • 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 bomb
  • io:限制磁盘 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 镜像扫描的优势与局限

技术优势

  1. 自动化程度高:Trivy/Grype 可零人工介入完成全镜像漏洞扫描,输出结构化 JSON 结果,直接接入 CI/CD 流程
  2. 数据库覆盖广:聚合 NVD、GHSA、各发行版 Security Tracker 等 20+ 数据源,覆盖主流 OS 和语言生态
  3. 成本极低:开源免费,单次扫描通常在 30 秒至 2 分钟内完成(取决于镜像大小)
  4. 合规支撑强:扫描结果可直接映射到 PCI DSS(Req 6)、CIS Benchmark、NIST SP 800-190 等合规标准的控制项
  5. SBOM 联动:与 Syft 等 SBOM 工具配合,满足美国行政令 14028 对软件供应链透明度的要求

现存局限

  1. 无法检测未知漏洞(0day):扫描只能匹配已知 CVE,对尚未披露的漏洞无能为力
  2. 可达性盲区:传统扫描列出所有理论上的漏洞,但无法判断漏洞对应功能在容器内是否实际被使用
  3. 配置错误检测有限:虽然 Trivy 能扫描 Dockerfile 和 K8S manifest 的错误配置,但覆盖面不如专用工具(如 kube-bench、checkov)
  4. 误报与噪声:发行版的 backport 修复(版本号不变但补丁已打)会产生大量误报,需人工过滤
  5. 对自定义/内部依赖无覆盖:企业内部自研库不在公共 CVE 数据库中,无法被扫描
  6. 先有镜像再有扫描:扫描发生在构建之后,如果漏洞阻止发布则需要重建------这比在 Dockerfile 阶段就阻止引入漏洞组件要滞后

5.2 SecurityContext 最小权限的优势与局限

技术优势

  1. 缩小攻击面最直接:将容器的 capabilities 从约 40 项裁剪到 1-2 项,攻击者即使获得代码执行能力也"什么也做不了"
  2. 零性能开销:capabilities drop、readOnlyRootFilesystem、no_new_privs 都是内核原生机制,不增加任何运行时开销
  3. 不可逆性allowPrivilegeEscalation: false 设置的 no_new_privs 标志对所有子进程不可逆,提供了硬性安全保障
  4. 合规即开即用:PSA restricted 模式直接映射到 CIS Benchmark v2.0.1 的核心安全要求,无需额外配置
  5. 配置标准化:YAML 声明式配置,易于版本控制和审计,与 GitOps 流程天然契合

现存局限

  1. 改造存量应用有成本:为 root 运行的旧应用适配非 root 用户,可能需要修改文件权限、端口绑定逻辑等
  2. 调试困难capabilities drop: ALL 后应用启动失败时,往往只能通过 strace 逐系统调用排查缺少的能力
  3. 容器层设置有限:SecurityContext 能限制的是容器内的行为,但对于内核级别的漏洞利用(直接 syscall 攻击),仍需 seccomp/AppArmor 等更深层机制配合
  4. Pod/Container 两级覆盖的潜在混淆:容器级别覆盖 Pod 级别设置可能导致"以为配置了,实际被覆盖了"的情况
  5. 生态碎片化: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

处理策略

  1. 确认 HIGH 级别的 OS 包漏洞是否可达(本容器是否使用 TLS)
  2. Python urllib3/certifi 的 HIGH 漏洞通常影响 HTTPS 连接验证 → 如果推理服务对外暴露 HTTPS → 立即升级
  3. 错误配置项 → 立即修复 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"]

关键加固点解释

  1. 精确 Digest@sha256:... 确保拉取的是经过验证的精确镜像版本,防止上游镜像被篡改(标签漂移攻击)
  2. 非 root 用户 :创建专用用户 inference,UID 1000,不属于 root 组
  3. --chown:COPY 时直接设置文件所有权,避免后续 chown 产生额外层
  4. 清理缓存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 容器的特殊性(大体积、多依赖、高权限需求),安全加固的价值更加突出。

本文建立的三道防线构成了容器安全的最小可行安全基线:

  1. 镜像扫描(Trivy/Grype):在镜像进入 Registry 之前发现并阻断包含已知漏洞的镜像,是供应链安全的第一道门禁
  2. SecurityContext 最小权限:通过非 root 用户运行、能力裁剪、只读文件系统、禁止提权四重控制,将容器攻击面从"全开"缩小到"仅必需"
  3. PSA 准入控制:在集群层面通过 restricted 策略强制执行安全基线,使个人配置错误无法绕过组织级的安全策略

三道防线协同工作,形成"构建时检查 → 部署时阻断 → 运行时限制"的完整安全链路。每个控制单独来看都有局限,但组合使用可实现大幅的安全提升。对于 AI 工程团队而言,从最容易落地的镜像扫描和 SecurityContext 配置开始,逐步扩展到 PSA 强制准入和运行时异常检测,是一条低成本、高回报的安全能力建设路径。

关键原则回顾

  • 默认拒绝,按需放行(Default Deny)
  • 纵深防御,不依赖单一控制(Defense in Depth)
  • 最小权限,只给必需的(Least Privilege)
  • 安全左移,在构建阶段就消除风险(Shift Left)

免责声明

本文所有技术内容仅供安全研究与教学目的使用。文中涉及的攻击技术均已做无害化处理,仅保留教学所需的最小核心代码。文中引用的 CVE 编号均为已公开披露且已有修复方案的已知漏洞。严禁将文中技术用于非法用途。实际部署安全方案前请结合自身业务场景进行充分测试,安全配置可能因集群版本、容器运行时和底层操作系统差异而需要调整。

本期专栏更新说明

本文为《AI 工程与安全深度实战》订阅专栏持续迭代内容。本文为第 1 轮(初阶)· 第 3 篇(融合篇) ,至此初阶三篇文章(云原生篇 → 安全篇 → 融合篇)已全部完成。下一篇将进入第 2 轮(中阶)· 云原生篇:GPU 感知调度与设备插件机制深度解析。

专栏按初/中/高阶递进规划,长期更新 AI 云原生架构、GPU 算力工程、LLMOps 运维智能化、模型安全攻防、供应链安全、安全治理与合规实践,一次订阅,永久持续更新。

专栏推荐

参考资料