必须在pod创建阶段拦截root容器:启用psa强制restricted策略(标签pod-security.kubernetes.io/enforce=restricted),配置securitycontext中runasnonroot:true及runasuser,构建镜像时移除root依赖并验证用户。

当恶意镜像在Kubernetes中以root身份启动时,攻击者可能利用容器逃逸获取宿主机root权限,直接读取节点上的Secrets、挂载宿主机目录或执行特权系统调用。必须在Pod创建阶段就拦截这类高危行为,不能依赖运行时检测。
启用Pod Security Admission(PSA)强制执行非root策略
从Kubernetes v1.25起,Pod Security Admission(PSA)已作为内置准入控制器替代废弃的PodSecurityPolicy(PSP)。它通过命名空间标签控制安全级别,无需安装额外组件。
第一步:为目标命名空间打上强制执行标签:kubectl label ns default pod-security.kubernetes.io/enforce=restricted
第二步:该标签会自动启用restricted策略集,其中包含runAsNonRoot:true硬性要求——任何未显式声明非root用户或镜像默认以root启动的Pod都会被API Server直接拒绝,返回Forbidden: violates PodSecurity "restricted:latest"错误。
注意:标签值必须是enforce而非warn或audit,后两者仅记录不拦截。
在SecurityContext中显式声明runAsNonRoot与runAsUser
即使启用了PSA,仍需在Pod或Container层级配置SecurityContext,否则部分场景下拦截可能失效。
方法一:基础写法(适用于已内置非root用户的镜像)
securityContext: runAsNonRoot: true
方法二:精确控制UID(推荐,避免镜像内用户映射混乱)
securityContext: runAsNonRoot: true runAsUser: 1001 runAsGroup: 1001
【关键前提】 镜像中必须存在UID 1001对应的有效用户(如Dockerfile中含adduser -u1001 -D appuser),否则容器启动失败且不会回退到root。
配合镜像构建阶段彻底移除root依赖
单纯靠K8s拦截是被动防御,必须同步切断root运行路径。
① 修改Dockerfile,删除所有root依赖项:用USER 1001替换USER root或省略USER指令;监听端口改为非特权端口(如8080而非80);移除SETUID二进制文件:RUN find / -xdev -perm /6000 -type f -exec chmod a-s {} \; || true
② 使用官方非root镜像源:例如Nginx改用nginxinc/nginx-unprivileged,而非nginx:alpine;Alpine基础镜像本身不带root用户风险,但需确认应用层未重新提权。
③ 构建后验证镜像用户:运行docker inspect <image> | jq '.[0].Config.User'</image>,输出应为"1001"或空字符串(表示无显式USER,但PSA会拦截)。
通过ValidatingAdmissionPolicy拦截未声明SecurityContext的Pod
针对开发人员可能遗漏SecurityContext字段的情况,需用K8s v1.26+的动态策略做兜底。
创建策略对象,匹配所有未设置spec.securityContext.runAsNonRoot的Pod:
matchConstraints: resourceRules: - apiGroups: [""] resources: ["pods"] namespaces: ["default"] validation: expression: "object.spec.securityContext.runAsNonRoot == true"
该策略会在API Server接收请求时实时校验,不满足即拒绝,比PSA标签更细粒度且可跨命名空间统一管控。











