Kubernetes如同容器世界里的交通警察,当你在集群里同时运行银行系统和休闲小游戏时,得确保不会出现"特权容器越狱后给游戏充值"的尴尬场面。今天咱们就深入聊聊Pod安全策略(PSP)和SecurityContext这对安全组合拳(技术栈:Kubernetes 1.25 + containerd运行时)。
一、Pod安全策略PSP:集群级的入场安检
虽然PSP在1.25版本已废弃(由PSA替代),但仍有超过30%的生产集群仍在使用。我们先看经典场景:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: baseline-restrictive
spec:
  privileged: false                  # 关掉特权模式的口子
  allowPrivilegeEscalation: false    # 防止权限升级
  runAsUser:
    rule: MustRunAsNonRoot           # 掐灭root用户的火苗
  seLinux:
    rule: RunAsAny                   # 允许不同团队的SELinux策略
  supplementalGroups:
    rule: RunAsAny
  volumes:
  - configMap                        # 允许的业务配置类型
  - secret
  - emptyDir
绑定到特定服务账号后,这个策略会像机场安检员一样检查所有Pod:
# 创建名为game-server的服务账号
kubectl create serviceaccount game-sa
# 通过ClusterRoleBinding绑定策略
kubectl create clusterrolebinding psp-game \
  --clusterrole=psp:baseline-restrictive \
  --serviceaccount=default:game-sa
这时如果用户用此账号启动特权Pod:
apiVersion: v1
kind: Pod
metadata:
  name: test-breaker
spec:
  serviceAccountName: game-sa
  containers:
  - name: hacker
    image: nginx
    securityContext:
      privileged: true  # 触发死亡红线
等待你的将是类似这样的报错:Pod "test-breaker" is forbidden: violates PodSecurityPolicy "baseline-restrictive": privileged
二、PodSecurityContext:给每个Pod上的电子镣铐
如果说PSP是集群级防御,那么SecurityContext就是每个Pod自带的智能手铐。看这个三层防护配置:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-gateway
spec:
  replicas: 3
  template:
    spec:
      securityContext:               # 第一层:Pod全局设定
        runAsUser: 1000              
        runAsGroup: 3000
        fsGroup: 2000                # 文件系统组权限
      containers:
      - name: payment-processor
        image: alipay:v2.3
        securityContext:             # 第二层:容器级加强
          allowPrivilegeEscalation: false
          capabilities:
            add: ["NET_ADMIN"]       # 仅开放必要网络权限
            drop: ["ALL"]
        volumeMounts:
        - name: tmp-vol
          mountPath: /tmp
      initContainers:
      - name: vault-init
        image: vault-configurator
        securityContext:             # 第三层:初始化容器特殊处理
          runAsUser: 0               # 允许临时使用root初始化
          readOnlyRootFilesystem: true
这个配置的巧妙之处在于:
- 用fsGroup=2000确保日志文件可写入
- 主容器丢弃所有Linux能力仅保留网络管理
- init容器虽短暂获取root但限制文件系统只读
三、必须知道的关联技术
Seccomp配置实例(Kubernetes 1.19+默认启用):
securityContext:
  seccompProfile:
    type: RuntimeDefault            # 使用运行时默认配置
配合PSP的安全策略使用,能拦截99%的非常规系统调用
AppArmor实战步骤:
- 在节点加载配置文件:
apparmor_parser -q <<EOF
#include <tunables/global>
profile payment-app flags=(attach_disconnected) {
  # 禁止二进制文件修改
  deny /usr/bin/** wl,
}
EOF
- Pod配置注解激活:
metadata:
  annotations:
    container.apparmor.security.beta.kubernetes.io/main: localhost/payment-app
四、六大应用场景实战分析
场景1:金融系统的"金库"防护
# 财务处理服务配置
securityContext:
  runAsNonRoot: true
  capabilities:
    drop: ["SETUID", "SETGID"]    # 防止提权操作
  seccompProfile:
    type: Localhost
    localhostProfile: finance-seccomp.json  # 定制财务专用策略
场景2:AI训练集群的GPU控制
securityContext:
  runAsUser: 1001
  capabilities:
    add: ["SYS_RAWIO"]            # GPU直通需要裸I/O权限
  privileged: false
  allowPrivilegeEscalation: false
五、技术选型的双刃剑
优势对比表:
| 维度 | PSP优势 | SecurityContext优势 | 
|---|---|---|
| 生效粒度 | 集群级统一管控 | Pod级灵活调整 | 
| 维护成本 | 集中管理更方便 | 无需额外RBAC配置 | 
| 防御纵深 | 拦截恶意部署动作 | 防止容器运行时突破 | 
| 兼容性 | 支持旧版本集群 | 适配所有Kubernetes版本 | 
典型缺陷警报:
- PSP启用后如果忘记设置默认策略,会导致所有Pod创建失败(报错Forbidden)
- SecurityContext的fsGroup改动可能导致已有文件权限紊乱
- 启用readOnlyRootFilesystem后未挂载emptyDir会导致日志写入失败
六、采坑指南与最佳实践
致命陷阱一:用户ID冲突 某电商曾因多个服务都使用runAsUser=1000导致共享存储的权限混乱。正确做法是通过类似下面的自动化分配:
# 基于namespace生成用户ID范围
USER_ID=$((20000 + $(kubectl get ns $NAMESPACE -o jsonpath='{.metadata.uid}' | tr -dc '0-9' | cut -c1-4)))
审计技巧:
# 快速定位安全隐患Pod
kubectl get pods --all-namespaces -o json | \
  jq '.items[] | select(.spec.securityContext.privileged == true) | .metadata.name'
七、总结
通过PSP和SecurityContext的组合拳,我们可以实现从集群入口到容器内部的立体防御。但安全就像洋葱,永远需要叠加其他措施(网络策略、镜像扫描等)。在即将全面转向PSA的过渡期,建议同时配置:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
spec:
  paramKind:
    group: apps.example.com
    kind: PodSecurityDefaults
  rules:
  - condition: "!object.metadata.namespace.matchLabels['security-level'] == 'high'"
    expression: "!object.spec.containers.securityContext.privileged"
评论