撤销权限不等于终止连接
一句话总结
撤销权限是阻止新 API 请求的控制措施,而终止已经在运行的命令,则是需要另行确认的响应动作。
为什么需要它
运维人员临时开放了终端权限。工作结束后,他从 Role 中删除了 exec 权限,并确认新终端无法再打开。这时,已经打开的旧窗口还能执行命令吗?即使在同一个界面上看到的都是终端,创建新连接的请求,与通过已经打开的连接发送的输入,走的是不同的路径。如果过早宣布响应完成,就可能漏掉正在运行的会话。
在 LabHub 专用的 k3s v1.36.4 探针中,新的 exec 被拒绝之后,已有的流依然会响应此后新生成的输入。这并不是说之前的输出还留在屏幕上。我们是通过权限撤销之后生成并发送的各不相同的随机数和 ACK 对上了,才观察到真实的执行。这是对已确认的版本和路径得出的结果,并不保证所有代理或会话管理器的行为。
工作原理
Kubernetes RBAC 的 Role 在命名空间范围内允许请求。权限是叠加的,所以即使收窄了某一个 Role,只要其他绑定授予了同样的权限,请求仍然可能被允许。因此不能只看 YAML,还必须同时确认实际请求的成功与拒绝。
查询 Pod 与在容器中执行命令也是不同的权限。Pod 用 pods 指定,执行用名为 pods/exec 的 subresource 指定。与一般的资源创建不同,像 exec 这种目标名称出现在 URL 中的 subresource,可以用 resourceNames 限定到特定的 Pod。本实验允许对 target 和 control 执行 GET,而只允许对 target 执行 exec。如果使用通配符,连对照组也会被放开,也就无法在狭窄的范围内重现问题。在 exec 上同时写明 get 和 create,是为了兼顾客户端连接方式的差异,并不表示所有普通的 GET 都会执行进程。
终端在建立连接之后,会长时间收发 stdin、stdout 和 stderr。官方的流式传输文档介绍了把 HTTP 连接升级为双向通道来使用的结构。因此,不要把新请求被拒绝与已有通道被终止当作同一件事。auth can-i 是用来询问权限的工具,既不是断开已有连接的命令,也不能代替 exec 真实成功的证据。
本实验中的 --as 是 impersonation,即把以管理员证书登录的请求代理为 ServiceAccount 身份。API 服务器会认证原始请求者,确认其代理权限,然后以被代理的身份进行授权。用这种方式可以验证 RBAC,但并没有验证 ServiceAccount bearer 令牌的过期和撤销。这个主题会在 KCSA 的真实令牌实验中讲到。
在现场相遇的样子
在事件响应中,阻断与保全会发生冲突。如果先删除 Pod,可能会丢失执行痕迹和临时文件;如果只顾着调查而干等,危险的执行可能会持续下去。必须先决定把哪些证据保留在哪里,以及愿意承受哪些业务影响。实验中的 containment.json 是一份小型响应计划,记录目标 UID、要保留的对照组 UID、终止前的证据哈希以及处置范围。这个哈希是用来发现文件因意外而被改动的手段,并不是能够防御 root 用户恶意重新生成全部数据的电子签名。
只凭名称就去删除同样很危险,因为在删除和重新创建之间,同名的另一个 Pod 可能会顶替进来。UID 前置条件会把调查的对象与实际要变更的对象绑定在一起。实验给出正常终止的宽限期,并同时观察 Pod 的消失以及已有 exec 进程的终止。不能仅凭 API 对象删除成功,就断定节点上的实际进程已经结束。
在生产环境的响应中,所属的 Deployment 可能会重新创建 Pod,GitOps 也可能把权限恢复原样。本实验只使用健康的单个 VM 内的独立 Pod,所以不会模拟这种自动恢复。在真实服务中,还需要一并修改持有期望状态的控制器和 Git 配置。保留对照组,是为了区分请求被阻断是否由整个服务故障造成,并确认处置范围没有扩大。
即使恢复完成,也不要把权限全部恢复原样。要确认新 target 的 UID 和 Ready 以及 GET 成功,并检查 exec 是否仍被拒绝。服务恢复与重新授予临时管理权限,是两种不同的审批。响应报告中要分别写明新连接被拒绝、已有连接的额外响应、终止以及恢复的结果,不能用一盏绿灯代替其余所有内容。
下一项实验要做什么
先调查 scope 并编写窄范围的 Role,然后打开真实的 exec 流。撤销权限后,对比新请求的被拒绝与已有连接的新响应。保全证据之后,只终止指定的 UID,并进一步确认对照组和新 Pod 的权限边界。helper 只运行无害的 ACK 程序。检查文件读取的是永久保存的观测记录,所以在最后一个步骤之后重新评分,也不会丢失过去的成功记录。如果中途的执行不确定地中断,不会自动重启,而是引导你下载数据并在新会话中重现。