控制平面加固检查
目标
整理控制平面组件的危险设置清单,亲手编写 etcd 静态加密配置,通过 kubectl auth can-i 测量并确认命名空间隔离和匿名访问范围,最后编写汇总证据的加固报告。
为什么重要
安全检查真正获得信任的时刻,不是说“最好这样做”,而是展示**“当前集群就是这样”**。kubectl auth can-i --as= 是生成这类证据的工具。与其阅读 RBAC 清单并在脑中计算,不如直接询问 apiserver;即使存在绑定重叠或组继承等复杂情况,也能得到准确答案。
亲手编写 EncryptionConfiguration,是因为 providers 数组的顺序决定一切。若把 identity 放在前面,就会一边以为已启用加密,一边仍以明文存储。这个错误不会报错,因此很难察觉。
本实验环境无法修改 apiserver 进程的真实标志。因此第 2、3、4 步采用编写检查产物的形式。也请一并了解,实际应对 CIS 基准时正是这种形态——留下配置依据往往比修改设置更耗时。
步骤
- 创建目录
/root/kcsa-hard和命名空间kcsa-hard。 - 在
/root/kcsa-hard/risky-flags.txt中严格按以下形式写五行生产 apiserver 不应存在的设置:--anonymous-auth=true、--authorization-mode=AlwaysAllow、--insecure-port=8080、--profiling=true、--service-account-lookup=false。不要加入其他行。 - 在
/root/kcsa-hard/encryption-config.yaml中编写 EncryptionConfiguration:apiVersion 为apiserver.config.k8s.io/v1,目标资源为secrets,providers 正好 2 个,按正确顺序放置aescbc(键名key1,secret 是经过 base64 编码的真实值)和identity。 - 在
/root/kcsa-hard/kubelet-checklist.txt中严格按以下形式写四行 kubelet 加固项:authentication.anonymous.enabled=false、authentication.webhook.enabled=true、authorization.mode=Webhook、readOnlyPort=0。不要加入其他行。 - 创建命名空间
kcsa-team-a和kcsa-team-b,并分别创建 ServiceAccountapp。在kcsa-team-a中创建 Rolepod-reader(对核心组pods授予get、list)和 RoleBindingapp-pod-reader(目标是kcsa-team-a的 SAapp)。随后在两个命名空间分别运行kubectl auth can-i list pods,并使用--as=system:serviceaccount:kcsa-team-a:app,确认答案不同。 - 以匿名用户询问三项权限,将结果保存到
/root/kcsa-hard/anon.txt,内容为三行키=값:get-pods=(在命名空间kcsa-hard中 get Pod)、list-secrets=(在全部命名空间 list secrets)、healthz=(get 非资源路径/healthz)。匿名主体无法通过--as模拟(见下文),因此使用管理员权限创建SubjectAccessReview,读取.status.allowed,将 true 写为yes,false 写为no。 - 编写
/root/kcsa-hard/hardening-report.md。以下五行必须原样作为章节标题:## apiserver、## etcd、## kubelet、## namespace、## anonymous。正文还必须提及以下五项证据:--anonymous-auth=false、aescbc、readOnlyPort=0、kcsa-team-b、system:anonymous。
参考
- 如
kubectl auth can-i list pods -n kcsa-team-b --as=system:serviceaccount:kcsa-team-a:app所示,可用--as模拟其他主体。只有管理员权限才能模拟。 - 唯独匿名主体不能通过
--as查询。kubectl auth can-i会创建 SelfSubjectAccessReview,该权限属于 ClusterRolesystem:basic-user,且只绑定到system:authenticated组。模拟system:anonymous时,apiserver 会把组改成system:unauthenticated,因此请求本身以 Forbidden 结束。 - 所以第 6 步使用
SubjectAccessReview。这是管理员代问“该主体能否执行此操作”的对象,可在spec中直接写入user和groups。资源问题使用resourceAttributes,/healthz等非资源路径使用nonResourceAttributes,答案以 true/false 返回到.status.allowed。 - 可用
head -c 32 /dev/urandom | base64生成 base64 编码值。 - 常见错误 1:第 3 步把
identity放在 providers 数组第一位。这样即使启用了加密,仍会以明文存储。 - 常见错误 2:第 6 步凭记忆填写标准答案。评分会与直接询问当前集群的结果比较,必须转录实际执行得到的值。
准备工作区和命名空间
创建目录 /root/kcsa-hard 和命名空间 kcsa-hard。
创建一个用于集中存放产物的目录和一个实验命名空间。创建目录时有一个选项可一次建立所有中间路径。
编写 apiserver 危险标志清单
在 /root/kcsa-hard/risky-flags.txt 中严格按以下形式写五行生产 apiserver 不应存在的设置:--anonymous-auth=true、--authorization-mode=AlwaysAllow、--insecure-port=8080、--profiling=true、--service-account-lookup=false。不要加入其他行。
填写时思考每个标志破坏的是哪个阶段(认证、授权、信息暴露、令牌验证)。文件只能包含指定的五行,并须保留包含值的完整形式。
编写 EncryptionConfiguration
在 /root/kcsa-hard/encryption-config.yaml 中编写 EncryptionConfiguration:apiVersion 为 apiserver.config.k8s.io/v1,目标资源为 secrets,providers 正好 2 个,按正确顺序放置 aescbc(键名 key1,secret 是经过 base64 编码的真实值)和 identity。
providers 数组的顺序具有含义:第一项用于写入,所有项用于读取,据此确定两个 provider 的顺序。secret 必须是经过 base64 编码的真实字符串,保留占位符无法通过。
整理 kubelet 认证与授权检查项
在 /root/kcsa-hard/kubelet-checklist.txt 中严格按以下形式写四行 kubelet 加固项:authentication.anonymous.enabled=false、authentication.webhook.enabled=true、authorization.mode=Webhook、readOnlyPort=0。不要加入其他行。
用点号形式写 kubelet 配置文件(KubeletConfiguration)的字段路径,涵盖匿名访问、Webhook 认证、授权模式和只读端口。思考授权模式使用 AlwaysAllow 会让什么失去意义。
用 can-i 证明命名空间隔离
创建命名空间 kcsa-team-a 和 kcsa-team-b,并分别创建 ServiceAccount app。在 kcsa-team-a 中创建 Role pod-reader(对核心组 pods 授予 get、list)和 RoleBinding app-pod-reader(目标是 kcsa-team-a 的 SA app)。随后在两个命名空间分别运行 kubectl auth can-i list pods,并使用 --as=system:serviceaccount:kcsa-team-a:app,确认答案不同。
Role 只在自身命名空间有效。不能只做断言,要通过测量证明:以同一 ServiceAccount 分别询问两个命名空间,答案会不同。可用 --as 模拟其他主体。
观察匿名用户能做什么
以匿名用户询问三项权限,将结果保存到 /root/kcsa-hard/anon.txt,内容为三行 키=값:get-pods=(在命名空间 kcsa-hard 中 get Pod)、list-secrets=(在全部命名空间 list secrets)、healthz=(get 非资源路径 /healthz)。匿名主体无法通过 --as 模拟(见下文),因此使用管理员权限创建 SubjectAccessReview,读取 .status.allowed,将 true 写为 yes,false 写为 no。
本步骤不能背答案。必须直接询问当前集群并原样转录结果。但匿名主体不能用 kubectl auth can-i --as=system:anonymous 查询,因为匿名用户无权创建该命令使用的审查对象。另有一种由管理员代问的审查对象,也能查询非资源路径。
汇总成加固报告
编写 /root/kcsa-hard/hardening-report.md。以下五行必须原样作为章节标题:## apiserver、## etcd、## kubelet、## namespace、## anonymous。正文还必须提及以下五项证据:--anonymous-auth=false、aescbc、readOnlyPort=0、kcsa-team-b、system:anonymous。
根据前五步的产物编写文档。章节标题必须与指定形式完全一致,正文必须包含支持各节结论的具体值。