第 12 章 准入控制与容器安全¶
配套实验手册:《Kubernetes 实验手册》(manual/ 目录)实验 09「认证与授权」 Lab 7/8(SecurityContext/PSA)。本章讲安全模型的第三道门(准入控制)与容器级加固——前两章管"谁能用集群"(认证/授权),本章管"创建请求合不合法、容器跑得安不安全"。
学习目标¶
学完本章,你应该能够:
- 解释准入控制(Admission)的时机与角色,区分 Mutating 与 Validating 两类
- 列举常见的准入控制器(LimitRange/ResourceQuota/PSA)并说出它们各自拦什么
- 解释 Pod Security Admission 的三个级别(privileged/baseline/restricted)与实施方式(命名空间标签)
- 解释 SecurityContext 的关键字段(runAsUser/runAsNonRoot/readOnlyRootFilesystem/capabilities)
- 区分 Pod 级与容器级 securityContext 的生效范围
- 解释"SecurityContext 是自觉、PSA 是强制"的配合关系
- 解释 imagePullSecrets 的机制(私有仓库凭据怎么注入)
12.1 准入控制:第三道门¶
12.1.1 时机与角色¶
第 11 章的三道门流程里,准入(Admission)发生在认证、授权之后,对象写入 etcd 之前:
flowchart LR
A["认证\n(你是谁)"] --> B["授权\n(你能干啥)"]
B --> C["准入控制\n(请求合法吗?)"]
C -->|"通过"| D[("写入 etcd")]
C -.->|"拒绝/修改"| E["Forbidden / 补默认值"]
style A fill:#E8F4FD,stroke:#4A90D9
style B fill:#E8F4FD,stroke:#4A90D9
style C fill:#FFF3E0,stroke:#E08A3C
style D fill:#E8F8E8,stroke:#5BA85B
style E fill:#FDECEA,stroke:#D94F4F
读图要点:准入是"资源落地前的最后一道闸"——认证授权决定"能不能来",准入决定"来的是不是合格";它既能拒绝(Validating)也能修改(Mutating,如补默认值)。
角色:对象创建/更新/删除时,一组准入控制器(Admission Controllers)按顺序检查(和修改)请求——这是"规则能拦在资源落地之前"的机制(LimitRange/ResourceQuota/PSA 都靠它,第 7 章的两层防线在这里执行)。
12.1.2 两类控制器¶
| 类型 | 行为 | 例子 |
|---|---|---|
| Mutating(修改型) | 修改请求内容(如自动填默认值) | LimitRange 给没写 requests 的 Pod 补默认值 |
| Validating(校验型) | 校验请求,不合法拒绝 | LimitRange 超限拒绝、PSA 违规拒绝 |
流程顺序:先 Mutating(补默认)→ 再 Validating(按补完的值校验)——所以 LimitRange"先填默认值、再校验是否超限"是同一步里的两个阶段。
认知:你在第 7 章看到的"自动填 requests"和"Forbidden 拒绝"都是准入控制器的功劳——资源被拒绝时的报错(Forbidden/exceeded quota/violates PodSecurity)都来自这一关。
12.1.3 常见准入控制器(本章前后知识点的"汇聚点")¶
| 控制器 | 拦截/修改什么 | 对应章节 |
|---|---|---|
| LimitRange | 单 Pod 资源上下限(填默认 + 校验) | 第 7 章 |
| ResourceQuota | 命名空间总量配额 | 第 7 章 |
| PodSecurity | Pod 安全标准(§12.2) | 本章 |
| ServiceAccount | 自动挂 SA token | 第 11 章 |
| NamespaceLifecycle | 阻止在删除中的命名空间建资源 | — |
12.2 Pod Security Admission(PSA):安全标准的强制执行¶
⚠️ PSP 已废弃:PSA 的前身是 PSP(PodSecurityPolicy)——K8s v1.21 弃用、v1.25 彻底移除。网上旧教程里的
PodSecurityPolicy对象在 v1.36 已不存在,一律使用 PSA(命名空间标签方式)。
12.2.1 为什么需要¶
§12.3 会讲 SecurityContext(容器自己声明安全要求)——但靠自觉不够:谁能保证集群里每个 Pod 都写了非 root?PSA 把安全标准变成"命名空间级的强制规则":违规的 Pod 创建即被拒绝(准入校验)。
12.2.2 三个安全级别(由松到严)¶
| 级别 | 含义 | 典型限制 |
|---|---|---|
| privileged | 无限制(默认,相当于没有 PSA) | 无 |
| baseline | 最小限制(默认建议) | 禁止 privileged、hostPath、hostNetwork/hostPID/hostIPC、特权端口等 |
| restricted | 最严格(生产核心) | baseline 全部 + 要求非 root(runAsNonRoot)、只读根文件系统、drop ALL 能力、seccompProfile: RuntimeDefault 等 |
级别选择:baseline 是生产默认(挡住最常见的高危配置);restricted 给核心/多租户场景(要求苛刻,可能影响正常应用——需要应用配合加固)。
Seccomp Profile(restricted 的强制项):seccomp 限制容器能发起的系统调用(攻击面收敛——即使容器被攻破,危险 syscall 也被拦):
securityContext:
seccompProfile:
type: RuntimeDefault # 使用运行时的默认 seccomp 配置(containerd 内置)
# type: Localhost + localhostProfile: 自定义 profile(进阶)
RuntimeDefault:containerd 内置的默认策略(阻断了 mount/未授权 ptrace 等危险调用)——restricted 级别要求它- 不配置 =
Unconfined(无限制)——v1.27+ 的新 Pod 默认带 RuntimeDefault 注释(行为向安全靠拢)
12.2.3 实施方式:命名空间标签¶
PSA 通过在命名空间上打标签实施(不是 Pod 上)——按命名空间定标准:
三个动作标签:
| 标签动作 | 行为 |
|---|---|
enforce |
强制:违规 Pod 创建被拒(最常用) |
audit |
允许创建,但记录审计日志(先观察再强制) |
warn |
允许创建,但给用户警告 |
渐进式落地建议:先
warn/warn观察哪些应用会违规 → 修好后再切warn——避免一上来就强制把现有应用全拒了。
12.2.4 违规的后果(报错解读)¶
kubectl -n psa-demo run bad --image=busybox --privileged
Error from server (Forbidden): pods "bad" is forbidden: violates PodSecurity "baseline:latest":
privileged (container "bad" must not set securityContext.privileged=true)
报错三要素:违反了哪个级别(baseline)、违反哪条规则(privileged)、怎么修(must not set...)——实验 09 Lab 8 亲手验证。
12.3 SecurityContext:容器加固¶
12.3.1 默认风险¶
第 1 章讲过:容器内 whoami 是 root(实验 09 Lab 7 实测)——容器内 root 与宿主机共享内核权限,容器被攻破 = 拿到宿主机 root 级别能力(有逃逸风险)。
12.3.2 关键字段(四个必须懂)¶
spec:
securityContext: # Pod 级:对 Pod 内所有容器生效
runAsNonRoot: true # 禁止以 root(UID 0)运行,否则拒绝启动
runAsUser: 1000 # 指定运行用户(UID)
fsGroup: 1000 # 卷文件的属组
containers:
- name: app
securityContext: # 容器级:只对本容器生效
readOnlyRootFilesystem: true # 根文件系统只读(防写入恶意文件)
capabilities:
drop: ["ALL"] # 丢弃全部 Linux 能力
add: ["NET_BIND_SERVICE"] # 按需加回(绑定低端口)
allowPrivilegeEscalation: false # 禁止提权(setuid 等)
| 字段 | 作用 | 防什么 |
|---|---|---|
runAsNonRoot + runAsNonRoot |
非 root 运行 | 容器逃逸、root 权限滥用 |
readOnlyRootFilesystem |
根文件系统只读 | 恶意文件写入(挂载卷仍可写) |
capabilities.drop: ["ALL"] |
丢弃能力 | 危险内核能力(SYS_ADMIN 等) |
allowPrivilegeEscalation: false |
禁止提权 | 子进程提权 |
生产常用组合:
drop: ["ALL"]+drop: ["ALL"](只留绑低端口的能力)+drop: ["ALL"]——最小能力原则。
12.3.3 Pod 级 vs 容器级¶
| Pod 级(spec.securityContext) | 容器级(containers[].securityContext) | |
|---|---|---|
| 生效范围 | Pod 内所有容器 | 本容器 |
| 典型字段 | runAsUser/runAsNonRoot/fsGroup | readOnlyRootFilesystem/capabilities |
| 覆盖关系 | 容器级可以覆盖 Pod 级 | — |
12.3.4 与 PSA 的关系:自觉 vs 强制¶
SecurityContext:Pod 自己声明安全要求("我自觉")——不写就没人管
PSA:命名空间强制标准("你必须安全")——不达标创建即拒
生产配合:PSA 定红线(baseline/restricted 标签)+ SecurityContext 落实细节(非 root/只读/丢能力)
注意一个循环关系:restricted 级别要求的正是 SecurityContext 那套字段(runAsNonRoot/readOnlyRootFilesystem/drop ALL)——先学 SecurityContext 才知道 restricted 要求什么。
12.4 镜像安全¶
12.4.1 私有仓库凭据:imagePullSecrets¶
拉取私有仓库镜像时,kubelet 需要凭据——用第 8 章的 dockerconfigjson 类型 Secret:
① 创建凭据 Secret(存 .dockerconfigjson)
kubectl create secret docker-registry regcred \
--docker-server=<仓库> --docker-username=<用户> --docker-password=<密码>
② Pod 声明使用
spec:
imagePullSecrets:
- name: regcred
③ kubelet 拉取时用该凭据认证
注意:imagePullSecrets 按命名空间生效——每个命名空间都要创建自己的凭据;Pod 必须显式引用(不会自动用)。
12.4.2 最小镜像与签名(概念)¶
- 最小镜像:用精简基础镜像(如 distroless/alpine)——攻击面小、体积小(第 3 章安装时感受过镜像体积的影响)
- 镜像签名(cosign 等):发布时签名、部署时校验——防供应链攻击(进阶概念,知道存在即可)
12.5 策略即代码:OPA Gatekeeper / Kyverno(第三方准入)¶
问题:PSA 只覆盖"Pod 安全"这一维度——生产还要约束"镜像来自可信仓库""必须带资源限制""禁止特定标签"等自定义策略。PSA 做不了,交给策略引擎:
| 引擎 | 特点 | 策略写法 |
|---|---|---|
| OPA Gatekeeper | 通用策略引擎(OPA/Rego) | Rego 语言 + ConstraintTemplate(学习曲线陡) |
| Kyverno | 专为 K8s 设计 | YAML 声明式(match + match/match),上手快 |
Kyverno 策略示例(概念):要求所有 Pod 必须带资源限制
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resources
spec:
rules:
- name: require-limits
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Pod 必须声明 resources"
pattern:
spec:
containers:
- resources:
limits: {}
两者与 PSA 的关系:
- PSA:内置的"Pod 安全标准"(三级别)
- Gatekeeper/Kyverno:可编程的任意准入规则(超越 Pod 安全,管所有资源)
- 生产组合:PSA 定基线 + Kyverno/Gatekeeper 定组织级策略(镜像仓库白名单、必带标签、资源要求等)
决策逻辑:只需要 Pod 安全标准 → PSA 够用;需要自定义/组织级策略 → 引入 Kyverno(YAML 友好)或 OPA Gatekeeper(表达力最强)。
12.6 实验演练指引¶
本章机制对应实验 09「认证与授权」 Lab 7/8:
- Lab 7 SecurityContext:非 root 运行、只读根文件系统、drop 能力——
whoami从 root 变 1000 的实测对比(§12.3) - Lab 8 Pod Security Admission:命名空间标签 enforce=baseline → 违规 Pod 创建被拒(privileged/hostPath)→ 合规 Pod 正常创建(§12.2)
教学建议:Lab 7 先看"默认 root"的基线,再加固对比;Lab 8 多试几种违规(privileged/hostPath)摸清 baseline 的完整禁令清单。
本章小结¶
- 准入控制(第三道门):认证授权之后、写入 etcd 之前——Mutating 改请求(补默认值)、Validating 拒请求;LimitRange/ResourceQuota/PSA 都靠它(第 7 章的"拒绝"机制在这里)
- PSA:三个级别(privileged/baseline/restricted)+ 三个动作(enforce/audit/warn)+ 命名空间标签实施——baseline 是生产默认,渐进式落地(warn → enforce)
- SecurityContext:runAsNonRoot/runAsUser(非 root)、readOnlyRootFilesystem(只读根)、capabilities drop/add(最小能力)、Pod 级 vs 容器级
- 自觉 vs 强制:SecurityContext 是声明、PSA 是执行——PSA 定红线、SC 落实细节;restricted 要求的正是 SC 那套字段
- 镜像安全:imagePullSecrets(私有仓库凭据,命名空间级)、最小镜像、签名(概念)
衔接:第 13 章讲集群级安全(证书体系/etcd 加密/kubelet 安全)——从"Pod 安不安全"上升到"集群信任链安不安全"。
思考题¶
- 准入控制在请求处理流程的哪个位置?"补默认值"和"拒绝请求"分别是哪类控制器?
- 第 7 章的"exceeded quota"报错,是哪道门拦下的?(提示:不是认证也不是授权)
- baseline 和 restricted 的核心区别是什么?生产默认推荐哪个?
- 一个应用必须以 root 跑(老镜像改不了),PSA enforce=restricted 会发生什么?怎么处理(提示:audit/warn 或专门命名空间)?
- SecurityContext 的
runAsNonRoot: true与runAsNonRoot: true各自防什么?只写其中一个够吗? - 私有仓库的 Pod 拉镜像报 ImagePullBackOff(ErrImagePull),可能是什么原因?(提示:imagePullSecrets)
CKA 考点标注(对应域 1/2/3): - 必考操作:
kubectl label ns xxx pod-security.kubernetes.io/enforce=baseline、kubectl label ns xxx pod-security.kubernetes.io/enforce=baseline- 必考机制:PSA 三级别与三动作、SecurityContext 关键字段(runAsNonRoot/readOnlyRootFilesystem/capabilities)、imagePullSecrets - 高频场景题:加固 Pod(SC 字段组合)、命名空间强制安全标准、私有镜像拉取配置 - 排障关联(域 5):violates PodSecurity(PSA 拦截)、violates PodSecurity(私有仓库凭据/镜像名)