审计员来了,却拿不出“已启用”的证据
目标
在集群里 亲自确认 若干安全控制项(阻止匿名访问、强制 Pod Security、关闭 ServiceAccount 令牌自动挂载、资源配额),并把结果以按控制 ID 分列的 pass/fail 报告和缺陷清单的形式留下来。
为什么重要
CIS Kubernetes Benchmark、NSA/CISA 加固指南、SOC 2 和 ISO 27001 这类框架,说到底都是在问“这项控制有没有打开”的问题清单。在审计中成为问题的,比起没有控制,更多是 写着打开了,实际上却是关着的。所以报告里的每一行,都应该来自集群现在给出的答案(auth can-i、对象字段、标签),而不是来自配置文件或记忆。
还有,不能只看加固过的地方就结束。找出像没人动过的 default 命名空间这样 留在控制之外的位置,并记为缺陷,是合规检查的一半。
步骤
- 创建检查对象命名空间
kcsa-comp。 - 询问
system:anonymous能不能 list Secret,并记录到anon.txt。 - 给
kcsa-comp加上pod-security.kubernetes.io/enforce=restricted标签。 - 创建满足全部 restricted 要求的 Pod
hardened。 - 关闭
defaultServiceAccount 的令牌自动挂载。 - 用 ResourceQuota
quota设置命名空间资源上限。 - 把四个控制项的实际结果写成
compliance.csv报告。 - 找出没有 enforce 标签的命名空间,作为缺陷记录到
gaps.txt。
参考
- 产出都放在
/root/kcsa-comp/下。评分器会把文件里的值与集群的实际状态重新核对。 kubectl auth can-i ... --as=<사용자>(占位符为用户)只有在该主体能创建 SelfSubjectAccessReview 时才会回答。连这个都做不到的主体,比如匿名用户,要由管理员用 SubjectAccessReview 代为询问。- 用
kubectl get ns -L <라벨키>(占位符为标签键)可以把各命名空间的标签放在一张表里看。 - 官方文档:Pod Security Standards · 审计(Auditing) · 授权与 SubjectAccessReview.
建立审计对象命名空间
创建将作为合规检查对象的命名空间 kcsa-comp。
审计从划定范围开始。命名空间用 create 配合 --dry-run=client 生成再 apply,运行多次也安全。
没有登录的人能不能看到 Secret
直接向集群询问未认证的用户(system:anonymous)在命名空间 kcsa-comp 中能不能 list Secret,并把答案以 anonymous-list-secrets=<yes|no> 一行写入 /root/kcsa-comp/anon.txt。
先给 kubectl auth can-i 加上 --as=system:anonymous 试试。得到的不是答案,而是拒绝——can-i 是以该用户自己的身份创建 SelfSubjectAccessReview,而匿名用户连这个都做不到。所以要由管理员代为询问,创建 SubjectAccessReview(authorization.k8s.io/v1),填好 spec.user、groups(system:unauthenticated)、resourceAttributes,读取 status.allowed。如果是 false,就是 no。
给命名空间立一个 restricted 门卫
给命名空间 kcsa-comp 加上 Pod Security Admission 标签 pod-security.kubernetes.io/enforce=restricted(想要的话,也可以同时加 warn、audit 标签)。
Pod Security Standards 靠一个命名空间标签就能打开。标签键是 pod-security.kubernetes.io/<모드>(占位符为模式),值是 privileged、baseline、restricted 之一。在 kubectl label 上加 --overwrite,再次运行也安全。
放进一个通过 restricted 标准的 Pod
在命名空间 kcsa-comp 中创建 Pod hardened(镜像 nginx:1.27-alpine)。在 Pod 级别声明 runAsNonRoot: true 和 seccompProfile.type: RuntimeDefault,在容器级别声明 allowPrivilegeEscalation: false、capabilities.drop: [ALL]、runAsNonRoot: true,满足 restricted 配置档的全部要求。
restricted 配置档要求不以 root 运行、阻止权限提升、丢弃所有 capability、指定 seccomp 配置档。securityContext 有 Pod 级别(spec.securityContext)和容器级别(containers[].securityContext)两处,每个字段能放的位置不同。只要有一项要求缺失,就可能因为第 3 步的标签而被拒绝创建。
没用到的令牌被插进了所有 Pod
给命名空间 kcsa-comp 的 default ServiceAccount 设置 automountServiceAccountToken: false,让没有另外请求的 Pod 不会自动挂载 API 令牌。
命名空间一创建,控制器就会帮你创建 default ServiceAccount。如果还没有,请稍等一会儿再确认。这个字段是 ServiceAccount 对象的顶层字段,可以用 patch 来改。
别让一个团队把集群吃光
在命名空间 kcsa-comp 中创建 ResourceQuota quota,把 pods=10、requests.cpu=2、requests.memory=2Gi 设为上限。
ResourceQuota 是限制整个命名空间总量的对象,在 spec.hard 下写出资源名称和上限。可以在 kubectl create quota 上用 --hard,把多个条目用逗号连起来。
交给审计员的控制项报告
创建 /root/kcsa-comp/compliance.csv。第一行是表头 control,status,下面把四个控制项 anonymous-access、psa-enforce、sa-token-automount、resource-quota 各写一行,status 里写实际确认的结果(pass 或 fail)。四个项目都必须是通过状态。
报告中的值不是声明,而是证据。评分器会对每一行在集群里重新确认对应的控制,如果写下的 status 与实际状态不同,就判为失败。想一想第 2、3、5、6 步里看到的结果,各自对应哪个控制项,写之前再确认一遍。
默认命名空间由谁在守护
没有 PSA enforce 标签的命名空间属于合规缺陷(gap)。确认集群的 default 命名空间是否有 enforce 标签,把有缺陷的命名空间名称以 psa-missing-namespace=<네임스페이스>(占位符为命名空间)一行写入 /root/kcsa-comp/gaps.txt。(不要加标签去修复——这一步是记录发现的步骤。)
想一次看到所有命名空间的标签,就在 kubectl get ns 上使用增加标签列的选项。把刚刚加固过的命名空间和没人动过的命名空间并排放在一起比较,缺陷就看出来了。