为什么重新创建账户后权限仍然存在?
目标
使用真实 bearer 令牌区分 audience、认证和授权,并比较删除后同名重建 SA 时旧令牌与基于名称的绑定如何工作。
为什么重要
撤销权限和吊销凭据不是同一件事。若只看账户名称就判断响应完成,现有权限可能重新作用于新身份。 本实验不使用管理员代理请求,而是通过 TokenRequest、TokenReview 和验证 CA 的真实 HTTPS GET 进行确认。 只使用个人 k3s VM 内的合成 ConfigMap。不要带入生产 kubeconfig 或真实秘密。 环境准备可能需要数分钟。实验时长 55 分钟,如需更多时间,请在到期前延长。VM、文件和令牌会在会话结束时回收。 请提前下载所需观测,但不要下载或共享令牌原文。
已准备的环境与工具
- 自己的命名空间 kcsa-identity,以及另一团队的 kcsa-identity-other。两者都有 reader SA 和 lesson-policy ConfigMap。
- Role 和 RoleBinding 均名为 named-config-reader,只允许 get 自己的一个 ConfigMap。
- identity-client Pod 使用固定镜像 digest、non-root、cap drop ALL、只读根目录,并将自动令牌挂载设为 false。
- API audience 为 labhub-kcsa-api。labhub-kcsa-report 是用于比较的受众,并未部署单独的报告服务器。
- 辅助工具:在 python3 /opt/fixtures/kcsa_identity_lab.py 后附加 act、capture、observe、grade 和步骤编号。
只需直接编写输入 JSON。令牌签发结果由辅助工具以 0600 权限保存在 /opt/fixtures/kcsa-identity-tokens 中,不输出原文。 这些文件只是个人实验内部材料,不应编辑或粘贴进报告。手动签发令牌的请求寿命为 3 小时,请根据服务器响应确认实际过期时间。 内部基线 /opt/fixtures/kcsa-identity-context.json 和 /opt/fixtures/kcsa-identity-lab-context.json 由辅助工具管理,请勿删除或编辑。 act 只执行指定变更。capture 确认观测条件后,将 JSON 保存到 /root/kcsa-identity。 observe 只输出当前观测,grade 不修改资源或学生文件。TokenReview 是令牌验证 API,不会创建持久化对象。
步骤
- 用 capture 1 保存 /root/kcsa-identity/baseline.json。调查 kcsa-identity 中 reader SA、identity-client Pod、named-config-reader Role·RoleBinding 和 lesson-policy ConfigMap 的 UID。Pod 和 SA 的自动令牌挂载均为 false,并须保持另一团队 kcsa-identity-other 的正常访问。
- 在 /root/kcsa-identity/old-request.json 中编写 authentication.k8s.io/v1 TokenRequest。spec.audiences 为 ["labhub-kcsa-api"],expirationSeconds 为 10800,boundObjectRef 为 apiVersion=v1、kind=Pod、name=identity-client,以及第 1 步观测到的真实 Pod UID。用 act 2 签发,capture 2 将哈希、声明和验证身份记录到 bound-token.json。不要输出令牌原文。
- 用 capture 3 保存 /root/kcsa-identity/scoped-request.json。确认原 bearer 令牌查询自己的 lesson-policy 返回 200,查询另一团队返回 403。不能用管理员证书或 --as 请求代替此证据。
- 在 /root/kcsa-identity/report-request.json 中编写与第 2 步相同的 TokenRequest,但把 audiences 改为 ["labhub-kcsa-report"]。用 act 4 和 capture 4 保存 audience.json。该令牌的 API GET 应为 401,默认 TokenReview 为 false,明确指定报告 audience 的 TokenReview 为 true。
- 在 /root/kcsa-identity/deny-role.json 中编写 rbac.authorization.k8s.io/v1 Role。名称为 named-config-reader,命名空间为 kcsa-identity,rules 是空数组。用 act 5 和 capture 5 保存 rbac-revoked.json。绑定和 SA UID 保持不变,原令牌认证为 true,查询为 403。
- 在 /root/kcsa-identity/restore-role.json 中编写同一 Role,但 rules 中放一条 apiGroups=[""]、resources=["configmaps"]、resourceNames=["lesson-policy"]、verbs=["get"] 的规则。用 act 6 恢复 Role,并以 UID 前置条件删除原 SA 后同名重建。用 capture 6 保存 uid-revoked.json。原 Pod 和绑定 UID 保持不变,SA UID 改变;旧令牌在过期前就应认证 false 并返回 401。
- 在 /root/kcsa-identity/new-request.json 中编写与第 2 步具有相同 API audience、寿命和原 Pod UID 的 TokenRequest。用 act 7 和 capture 7 保存 new-identity.json。新令牌的 sub 相同但 SA UID 不同,通过现有绑定查询返回 200,旧令牌仍为 401。
- 用 act 8 仅删除已调查的原 RoleBinding,并使用 UID·resourceVersion 前置条件。用 capture 8 保存 /root/kcsa-identity/closed.json。同时确认新令牌认证 true、查询 403;旧令牌 401;另一团队身份和 ConfigMap UID 保持不变,正常查询返回 200。
参考
令牌解码不等于签名验证,管理员 --as 请求也不验证该 bearer 令牌的受众或过期时间。 保留已经完成步骤的文件和配置。不要删除历史记录后,用当前状态重新制造相同的成功记录。 进入后续步骤后,过去步骤按记录时的令牌寿命验证,同时另行确认当前步骤的真实状态。 观测新步骤前,请保留紧邻前一步的记录和输入文件。另一团队的 SA、Role、绑定、ConfigMap,以及自己的 Pod、Role、ConfigMap UID 都必须保留到最后。 步骤准备只创建缺失的先前输入与观测,不覆盖当前答案或未完成输入。 若辅助工具等待超时,请检查同一 VM 上的 observe 和状态。不要反复安装或扩大权限来解决。 删除 SA 可能影响其他令牌,不要把本实验当作生产环境无条件适用的响应顺序。 401、403 应结合本次认证请求的上下文理解,并非所有匿名请求都会返回 401。 观测记录是学习材料,不是控制 root 的远程证明或防作弊机制。 官方文档:ServiceAccount 令牌 · 基于名称与命名空间的 RBAC。
调查基线身份与安全设置
用 capture 1 保存 /root/kcsa-identity/baseline.json。调查 kcsa-identity 中 reader SA、identity-client Pod、named-config-reader Role·RoleBinding 和 lesson-policy ConfigMap 的 UID。Pod 和 SA 的自动令牌挂载均为 false,并须保持另一团队 kcsa-identity-other 的正常访问。
不要只看名称,也要查看 metadata.uid。分别确认 Pod 和 SA 的自动挂载设置。
签发绑定到真实 Pod UID 的令牌
在 /root/kcsa-identity/old-request.json 中编写 authentication.k8s.io/v1 TokenRequest。spec.audiences 为 ["labhub-kcsa-api"],expirationSeconds 为 10800,boundObjectRef 为 apiVersion=v1、kind=Pod、name=identity-client,以及第 1 步观测到的真实 Pod UID。用 act 2 签发,capture 2 将哈希、声明和验证身份记录到 bound-token.json。不要输出令牌原文。
TokenRequest 的绑定 UID 不能猜测,应从 baseline.json 的 snapshot.pod.metadata.uid 读取。spec 中不需要令牌原文。
真实请求的命名空间边界
用 capture 3 保存 /root/kcsa-identity/scoped-request.json。确认原 bearer 令牌查询自己的 lesson-policy 返回 200,查询另一团队返回 403。不能用管理员证书或 --as 请求代替此证据。
认证 true 不表示本次请求已获允许,请检查实际 GET 的资源和命名空间。
比较受众不同的令牌
在 /root/kcsa-identity/report-request.json 中编写与第 2 步相同的 TokenRequest,但把 audiences 改为 ["labhub-kcsa-report"]。用 act 4 和 capture 4 保存 audience.json。该令牌的 API GET 应为 401,默认 TokenReview 为 false,明确指定报告 audience 的 TokenReview 为 true。
令牌即使尚未过期,受众也可能不同。不部署报告服务器,而是比较明确指定受众的验证。
撤销权限并保留认证
在 /root/kcsa-identity/deny-role.json 中编写 rbac.authorization.k8s.io/v1 Role。名称为 named-config-reader,命名空间为 kcsa-identity,rules 是空数组。用 act 5 和 capture 5 保存 rbac-revoked.json。绑定和 SA UID 保持不变,原令牌认证为 true,查询为 403。
不要删除 Role,只清空 rules。同时观察账户和绑定 UID 相同以及返回 403。
同名的新 ServiceAccount
在 /root/kcsa-identity/restore-role.json 中编写同一 Role,但 rules 中放一条 apiGroups=[""]、resources=["configmaps"]、resourceNames=["lesson-policy"]、verbs=["get"] 的规则。用 act 6 恢复 Role,并以 UID 前置条件删除原 SA 后同名重建。用 capture 6 保存 uid-revoked.json。原 Pod 和绑定 UID 保持不变,SA UID 改变;旧令牌在过期前就应认证 false 并返回 401。
不要以无限权限恢复 Role。请比较同名新账户和原账户的 UID。
连接新身份与现有绑定
在 /root/kcsa-identity/new-request.json 中编写与第 2 步具有相同 API audience、寿命和原 Pod UID 的 TokenRequest。用 act 7 和 capture 7 保存 new-identity.json。新令牌的 sub 相同但 SA UID 不同,通过现有绑定查询返回 200,旧令牌仍为 401。
RoleBinding 中的 SA 主体基于名称和命名空间。请同时确认新令牌成功与旧令牌失效。
精确撤销权限并确认影响
用 act 8 仅删除已调查的原 RoleBinding,并使用 UID·resourceVersion 前置条件。用 capture 8 保存 /root/kcsa-identity/closed.json。同时确认新令牌认证 true、查询 403;旧令牌 401;另一团队身份和 ConfigMap UID 保持不变,正常查询返回 200。
区分认证 false 与认证 true 但授权失败。另一团队的正常请求是撤销范围的对照基线。