阶段一 · 容器与集群安全

Pod 安全与运行时防护

一句话总结

镜像安全解决的是"构建时不带坏东西进来". Pod 安全解决的是"运行时容器能做什么".
即使应用被攻破, 通过 SecurityContext, Capabilities 收缩, Seccomp 和 AppArmor 限制, 攻击者能造成的伤害也被限制在最小范围内.

运行时威胁模型

假设攻击者已经获得了容器内的代码执行能力(通过应用漏洞如 RCE), 接下来想做什么?

攻击动作 需要的权限 防御手段
读取 /etc/shadow root UID runAsNonRoot
安装后门程序 文件系统写入 readOnlyRootFilesystem
提权为 root setuid/setgid allowPrivilegeEscalation: false
逃逸到宿主机 SYS_ADMIN / privileged 移除 Capabilities + privileged: false
嗅探网络流量 NET_RAW 移除 NET_RAW Capability
挂载宿主机设备 SYS_ADMIN 移除 SYS_ADMIN + Seccomp
加载内核模块 SYS_MODULE 移除 SYS_MODULE + Seccomp

SecurityContext = 监狱的安保层级.
普通犯人只有一间封闭牢房(最小权限容器), 不能出门, 不能打电话, 不能接触工具.
privileged 容器 = 给了犯人监狱的万能钥匙.

SecurityContext 详解

SecurityContext 可以在 Pod 级别和 Container 级别设置. Container 级别覆盖 Pod 级别.

最佳实践配置

apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
securityContext: # Pod 级别
runAsNonRoot: true # 禁止 root 运行
runAsUser: 65532 # 指定 UID
runAsGroup: 65532
fsGroup: 65532 # volume 的 group ownership
seccompProfile:
type: RuntimeDefault # 启用默认 seccomp
containers:
- name: app
image: myapp:v1.0@sha256:abc123
securityContext: # Container 级别
allowPrivilegeEscalation: false # 禁止提权
readOnlyRootFilesystem: true # 只读文件系统
privileged: false # 非特权模式
capabilities:
drop: ["ALL"] # 移除所有 Capabilities
# add: ["NET_BIND_SERVICE"] # 如需绑定 <1024 端口才加
volumeMounts:
- name: tmp
mountPath: /tmp # 只有 /tmp 可写
volumes:
- name: tmp
emptyDir: {}

关键字段解释

字段 效果 为什么重要
runAsNonRoot: true 进程 UID ≠ 0 才允许启动 防止以 root 身份运行
readOnlyRootFilesystem 容器根文件系统只读 攻击者无法写入后门程序
allowPrivilegeEscalation 禁止 setuid/setgid 提权 阻止权限升级攻击
privileged: false 非特权模式 特权容器 ≈ 宿主机 root
capabilities.drop: ALL 移除所有 Linux Capabilities 最小权限原则

readOnlyRootFilesystem 需要配合可写 volume

很多应用需要写临时文件(日志, 缓存, PID 文件). 开启只读后要为 /tmp, /var/run 等目录挂载 emptyDir volume. 否则应用会因为写文件失败而崩溃.

Linux Capabilities

传统 Linux 权限模型是二元的: root(全能)vs 非 root(受限). Capabilities 将 root 的权限拆分为约 40 个细粒度能力:

Capability 权限 风险
NET_BIND_SERVICE 绑定 < 1024 端口 低(通常安全)
NET_RAW 使用原始套接字(ping/tcpdump) 中(可以嗅探网络)
SYS_PTRACE 追踪其他进程 高(可以注入代码到其他进程)
SYS_ADMIN 挂载, 修改 namespace 等 极高(几乎等于 root)
SYS_MODULE 加载内核模块 极高(可以修改内核行为)
DAC_OVERRIDE 绕过文件权限检查 高(可以读写任何文件)

正确做法: Drop ALL + Add 需要的

默认容器有约 14 个 Capabilities(包括 NET_RAW 等高风险的). 正确做法是先 drop: ["ALL"] 移除全部, 再逐个添加应用真正需要的.

大多数 Go HTTP 服务不需要任何额外 Capability, 直接全部移除即可.

Seccomp

Seccomp(Secure Computing)限制容器可以使用的 Linux 系统调用(syscall). 容器只需要约 40-50 个 syscall, 但 Linux 有 300+ 个, 没用到的全部禁止.

Profile 类型

类型 含义 使用方式
RuntimeDefault 容器运行时的默认 profile(containerd 约禁用 50 个危险 syscall) seccompProfile: {type: RuntimeDefault}
Localhost 自定义 profile(JSON 文件) seccompProfile: {type: Localhost, localhostProfile: "profiles/my.json"}
Unconfined 不限制(默认行为, 不推荐) 不设置 seccompProfile 时的状态
# 推荐: 至少启用 RuntimeDefault
securityContext:
seccompProfile:
type: RuntimeDefault

RuntimeDefault 足够大多数场景

containerd 的默认 seccomp profile 禁用了 mount, reboot, kexec_load 等危险 syscall, 同时保留了正常应用所需的全部 syscall.

除非应用需要做非常特殊的操作(如直接操作设备文件), 否则 RuntimeDefault 就足够了.

AppArmor

AppArmor 是 Linux 的强制访问控制(MAC)框架, 限制进程可以访问的文件路径, 网络操作和 Capabilities. 与 Seccomp 互补: Seccomp 限制"能调哪些 syscall", AppArmor 限制"能访问哪些资源".

# K8s 1.30+ 使用 securityContext 字段
securityContext:
appArmorProfile:
type: RuntimeDefault # 使用容器运行时默认 AppArmor profile

自定义 AppArmor profile 示例:

# /etc/apparmor.d/myapp
profile myapp flags=(attach_disconnected) {
# 只允许读取 /app 目录
/app/** r,
# 只允许写入 /tmp
/tmp/** rw,
# 禁止网络监听(除 8080 端口)
network tcp,
# 禁止执行任何其他程序
deny /bin/** x,
deny /usr/bin/** x,
}

Pod Security Standards(PSS)

K8s 内置的 Pod 安全准入控制器, 按三个安全级别强制约束 Namespace 内的 Pod:

级别 约束 适用场景
Privileged 无限制(全部允许) 系统组件(kube-system)
Baseline 禁止已知的危险配置(hostNetwork, privileged, hostPID 等) 通用业务工作负载
Restricted 最严格(必须非 root, drop ALL capabilities, 只读 FS 等) 安全敏感的生产服务

启用方式

# 给 Namespace 打 label 启用 PSS
# mode: enforce(拒绝违规 Pod)/ warn(允许但告警)/ audit(只记录日志)
$ kubectl label namespace production \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted

渐进式实施策略

不要一上来就在所有 Namespace 开 enforce=restricted, 会导致大量 Pod 无法创建. 推荐路径:

  1. 先 audit 模式收集违规数据
  2. 逐个修复不合规的 Deployment
  3. 切换到 warn 模式验证
  4. 最终切到 enforce

运行时威胁检测

上面的机制是预防. 但如果攻击者绕过了预防怎么办? 需要检测:

Falco: 运行时安全监控

Falco(CNCF 孵化项目)通过监听 Linux 内核事件, 实时检测容器中的可疑行为:

  • 容器内启动了 shell(/bin/sh, /bin/bash)
  • 读取了敏感文件(/etc/shadow, /etc/passwd)
  • 进程尝试修改系统目录
  • 容器建立了对外的网络连接
  • 使用了不在白名单中的 syscall
# Falco 规则示例: 检测容器中启动 shell
- rule: Terminal shell in container
desc: Detect shell spawned in a container
condition: >
spawned_process and container and
proc.name in (bash, sh, zsh)
output: >
Shell spawned in container
(user=%user.name container=%container.name
shell=%proc.name parent=%proc.pname)
priority: WARNING

Defense in Depth 落地

预防(SecurityContext + Capabilities + Seccomp)+ 检测(Falco)+ 响应(告警 → 自动隔离 Pod / 人工处理)= 完整的运行时安全闭环.

快速回顾

  • SecurityContext: runAsNonRoot + readOnlyRootFilesystem + allowPrivilegeEscalation=false 是最基本的三件套
  • Capabilities: drop ALL + 按需 add, 大多数 Go 服务不需要任何额外 Capability
  • Seccomp: 至少启用 RuntimeDefault, 限制可用的系统调用
  • AppArmor: 限制进程可访问的文件路径和网络操作
  • Pod Security Standards: Namespace 级别的安全基线, 渐进式从 audit → warn → enforce
  • Falco: 运行时行为监控, 检测+告警形成安全闭环

动手练习

  1. 完整 SecurityContext 配置: 给一个现有 Deployment 添加完整的安全 SecurityContext(非 root + 只读 FS + drop ALL + seccomp RuntimeDefault), 验证应用仍然正常运行
  2. 只读文件系统验证: 在配置了 readOnlyRootFilesystem: true 的容器中执行 touch /test, 观察报错
  3. PSS enforce 测试: 给 Namespace 添加 pod-security.kubernetes.io/enforce=restricted label, 尝试部署一个 privileged Pod, 观察被拒绝
  4. Falco 告警触发: 安装 Falco(Helm chart), 触发一条告警规则(如在容器中执行 kubectl exec -- sh), 在 Falco 日志中找到对应告警