Lesson 15:Pod 安全策略与容器加固

admin 发布于 7 天前 9 次阅读


学习目标

  • 理解容器逃逸的攻击路径
  • 掌握 Security Context 的核心配置
  • 理解 Pod Security Admission(PSA)三级标准
  • 能为 Pod 配置安全基线

1. 容器逃逸风险

容器本质是进程(Lesson 01),隔离靠内核特性而非硬件虚拟化。如果容器拥有过高权限,攻击者可能:

攻击路径条件
内核漏洞利用(如 DirtyCow)容器以 root 运行
挂载宿主机文件系统Pod 挂载了 hostPath /
特权容器逃逸privileged: true
Linux Capabilities 滥用拥有 SYS_ADMIN 等危险能力

核心认知:容器不是虚拟机。虚拟机有独立的内核,容器共享宿主机内核。一旦攻击者在容器内获得 root 权限并利用内核漏洞,就有可能"逃逸"到宿主机,控制整个节点乃至整个集群。


2. Security Context 加固配置

yamlspec:
  securityContext:                    # Pod 级别安全上下文
    runAsNonRoot: true                # 强制以非 root 用户运行
    runAsUser: 1000                   # 指定用户 ID
    fsGroup: 2000                     # 文件系统组 ID
  containers:
  - name: app
    image: my-app:v1
    securityContext:                  # 容器级别安全上下文
      allowPrivilegeEscalation: false # 禁止提权
      readOnlyRootFilesystem: true    # 根文件系统只读
      capabilities:
        drop: ["ALL"]                 # 移除所有 Linux Capabilities
        add: ["NET_BIND_SERVICE"]     # 仅保留绑定低端口的能力

每项配置的防御效果

配置防御效果
runAsNonRoot: true即使镜像默认用 root,K8s 也会拒绝启动
runAsUser: 1000强制容器进程以 UID 1000 运行,降低权限
readOnlyRootFilesystem: true攻击者无法写入恶意文件到容器文件系统
allowPrivilegeEscalation: false阻断 SUID 类提权攻击(如 sudo
capabilities.drop: ["ALL"]移除所有特殊内核能力,最小权限原则
fsGroup: 2000所有挂载的 Volume 文件归属 GID 2000,控制文件访问权限

最佳实践:生产环境的 Pod YAML 应该默认包含以上所有安全配置。只在明确需要时才放开特定的 Capability(如 NET_BIND_SERVICE 用于绑定 80 端口)。


3. Pod Security Admission(PSA)

PSA 是 K8s 内置的 Pod 安全准入控制器,在 Namespace 级别强制执行安全策略。

三个安全级别

级别限制适用场景
Privileged无限制系统组件(kube-system)
Baseline禁止特权容器、禁止 hostNetwork一般业务应用
Restricted必须非 root、只读 rootfs、移除所有 capabilities安全敏感应用

设计意图:PSA 解决了一个管理难题——即使你在文档中要求开发者配置 Security Context,也无法保证每个人都会遵守。PSA 在 Namespace 级别强制拦截不合规的 Pod 创建请求,从制度上杜绝"裸奔"容器。

为 Namespace 启用 PSA

bash# 对命名空间强制执行 restricted 级别
kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

参数解读

  • enforce=restricted:强制拦截——不合规的 Pod 直接拒绝创建
  • warn=restricted:警告模式——不合规的 Pod 仍然可以创建,但会输出警告信息

动手实验

实验 15.1:验证 PSA 拦截不合规 Pod

bash# === 在 Master 节点 (172.16.11.104) 上执行 ===

# 创建受限命名空间
kubectl create namespace secure-ns
kubectl label namespace secure-ns pod-security.kubernetes.io/enforce=restricted

预期输出

bashnamespace/secure-ns created
namespace/secure-ns labeled
bash# === 在 Master 节点 (172.16.11.104) 上执行 ===

# 尝试创建一个不合规的 Pod(Nginx 默认以 root 运行)
kubectl run root-pod --image=docker.m.daocloud.io/library/nginx -n secure-ns

预期输出

bashError from server (Forbidden): pods "root-pod" is forbidden: violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "root-pod" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "root-pod" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "root-pod" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "root-pod" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")

输出解读:PSA 一次性列出了这个 Pod 违反的所有 restricted 策略条款:

  1. allowPrivilegeEscalation != false:没有显式禁止提权
  2. unrestricted capabilities:没有 drop 所有 capabilities
  3. runAsNonRoot != true:没有强制非 root 运行
  4. seccompProfile:没有设置 Seccomp 安全计算模式

Pod 被直接拒绝创建,不会有任何容器启动。这就是 enforce 模式的硬拦截效果。

bash# === 在 Master 节点 (172.16.11.104) 上执行 ===

# 创建一个完全合规的 Pod
kubectl apply -n secure-ns -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: docker.m.daocloud.io/library/busybox
    command: ['sleep', '3600']
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
EOF

预期输出

bashpod/secure-pod created

输出解读:合规 Pod 成功创建!每一项安全配置都满足了 restricted 级别的要求。

bash# === 在 Master 节点 (172.16.11.104) 上执行 ===

# 验证 Pod 正常运行
kubectl get pod -n secure-ns

预期输出

bashNAME         READY   STATUS    RESTARTS   AGE
secure-pod   1/1     Running   0          3s

输出解读:Pod 以 UID 1000(非 root)正常运行,所有安全策略满足。这就是"安全+可用"的正确姿势。

bash# === 在 Master 节点 (172.16.11.104) 上执行 ===

# 清理
kubectl delete namespace secure-ns

预期输出

bashnamespace "secure-ns" deleted

为什么不需要先手动删除 secure-pod

在 Kubernetes 中,Namespace 是资源的隔离边界。当你删除一个 Namespace 时,K8s 会级联删除(Cascading Delete)该命名空间下的所有资源——包括 Pod、Service、ConfigMap、Secret、NetworkPolicy 等。也就是说,kubectl delete namespace secure-ns 执行后,之前创建的 pod/secure-pod 会被自动清理,无需单独删除。

你可以在删除前通过 kubectl get all -n secure-ns 查看命名空间内的全部资源,删除后再执行同样的命令验证——此时会提示命名空间不存在。


检查点

  •  能说出至少 3 种容器逃逸攻击路径
  •  能编写带 Security Context 的安全 Pod YAML
  •  理解 PSA 三个级别的区别
  •  能为 Namespace 启用 PSA 强制执行

此作者没有提供个人介绍。
最后更新于 2026-04-28