跳转到帖子

授权测试中如何低影响验证 Kubernetes RBAC 越权与跨命名空间权限?

精选回复

发布于

问题背景

在一次已授权的集群测试中,当前账号可以查看某个命名空间的 Pod 和 ConfigMap。怎样确认它是否还能跨命名空间访问 Secret、执行 Pod 命令或修改 RBAC,同时避免直接读取业务数据、创建持久资源?另外,为什么 kubectl auth can-i 返回 yes,实际请求仍可能得到 403?

这类问题建议分成身份、RBAC 授权、准入控制和实际对象四层排查。不要直接以读取 Secret 内容或进入业务 Pod 作为第一步,SelfSubjectAccessReview 和服务端 dry-run 通常足够完成低影响验证。

一、先固定测试身份和上下文

先记录当前集群、用户和命名空间,避免在错误环境执行命令。不要使用 kubectl config view --raw,因为输出可能包含客户端证书或令牌。

kubectl config current-context
kubectl config view --minify
kubectl auth whoami -o yaml
kubectl version --client

较旧版本的 kubectl 可能不支持 auth whoami,此时可从非 raw 的 minify 输出确认 context 和 user 名称。测试报告中还应记录时间、来源地址和授权工单编号,便于后续对应审计日志。

二、使用 can-i 建立权限基线

先从授权范围内的命名空间开始,不要一上来枚举整个集群。下面几项比单纯检查 get pods 更有价值:

# 查看 app-prod 命名空间内的有效权限
kubectl auth can-i --list -n app-prod

# Secret 的 list 通常等同于批量读取,因为返回对象可能包含 data 字段
kubectl auth can-i get secrets -n app-prod
kubectl auth can-i list secrets -n app-prod

# 子资源需要单独检查
kubectl auth can-i create pods/exec -n app-prod
kubectl auth can-i create pods/portforward -n app-prod
kubectl auth can-i get pods/log -n app-prod

# 检查是否能够修改授权关系
kubectl auth can-i create rolebindings.rbac.authorization.k8s.io -n app-prod
kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io
kubectl auth can-i escalate roles.rbac.authorization.k8s.io -n app-prod
kubectl auth can-i impersonate users

其中 bind、escalate 和 impersonate 属于需要重点关注的权限。账号即使当前不能读取 Secret,也可能通过绑定高权限 ClusterRole、创建更高权限 Role,或模拟其他身份绕过原有边界。

跨命名空间验证应逐个检查授权范围内的目标命名空间。例如:

for ns in app-dev app-test app-prod; do
  printf '%-12s ' "$ns"
  kubectl auth can-i get secrets -n "$ns"
done

不要把 --all-namespaces 的结果直接当成完整结论。不同命名空间可能存在独立 RoleBinding,而且某些外部授权器对 SelfSubjectAccessReview 的返回并不完整。

三、用 SelfSubjectAccessReview 精确验证单项权限

kubectl auth can-i 本质上也是在请求授权审查。需要保留原始证据时,可以显式提交 SelfSubjectAccessReview。该对象不会作为普通业务资源持久保存,也不会读取目标 Secret 的内容。

cat <<'EOF' | kubectl create -f - -o yaml
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: ""
    resource: secrets
    verb: get
    namespace: app-prod
    name: payment-api-token
EOF

重点查看返回结果中的 status.allowed、status.denied、status.reason 和 status.evaluationError。测试记录中建议保留完整输出,但不要记录真实令牌值或 Secret 数据。

如果要检查非资源 URL,例如健康检查或 metrics 接口,应改用 nonResourceAttributes:

cat <<'EOF' | kubectl create -f - -o yaml
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  nonResourceAttributes:
    path: /metrics
    verb: get
EOF

四、使用服务端 dry-run 验证写权限

对于 create、patch 等写操作,可以选择无敏感内容的 ConfigMap 作为探针,并使用服务端 dry-run。这样请求会经过 API Server、授权器和大部分准入链路,但不会持久化对象。

kubectl create configmap rbac-probe \
  --from-literal=marker=authorized-test \
  -n app-prod \
  --dry-run=server \
  -o yaml

dry-run 并不等同于绝对零影响。准入 Webhook 只有在声明 sideEffects 为 None 或 NoneOnDryRun 时才应处理 dry-run 请求,但错误实现的外部 Webhook 仍可能调用第三方系统。因此,生产环境执行前应确认测试窗口和准入链配置。

不建议为了证明 create pods 权限而直接创建高权限 Pod。Pod 创建权限的风险应结合 Pod Security、准入策略、可挂载的 ServiceAccount、hostPath、hostNetwork 和 privileged 等配置评估。通常 RBAC 结果加准入 dry-run 已足够形成证据。

五、定位权限来源

如果当前身份有查看 RBAC 对象的权限,可以沿 Subject、RoleBinding、RoleRef 逐层确认来源:

# 查看命名空间级绑定
kubectl get rolebinding -n app-prod -o yaml
kubectl get role -n app-prod -o yaml

# 仅在授权范围允许时查看集群级绑定
kubectl get clusterrolebinding -o yaml
kubectl get clusterrole -o yaml

排查时注意以下规则:

  • RoleBinding 可以引用 ClusterRole,但权限只在该 RoleBinding 所在命名空间生效。
  • ClusterRoleBinding 会把 ClusterRole 权限授予集群范围内的主体,影响通常更大。
  • subjects 中的 ServiceAccount 必须同时核对 name 和 namespace;同名 ServiceAccount 并不是同一个主体。
  • 通配符 apiGroups、resources 或 verbs 容易在集群升级、安装 CRD 后自动扩大权限。
  • resourceNames 不能可靠限制 create 和 deletecollection。对 list、watch 的限制还依赖客户端携带 metadata.name 字段选择器,不能把它当作通用隔离手段。
  • 聚合 ClusterRole 可能通过 rbac.authorization.k8s.io/aggregate-to-* 标签动态合并规则,不能只检查单个角色文件。

六、can-i 显示 yes,但实际请求返回 403 的原因

常见原因不是 RBAC 判断错误,而是测试的请求并不完全相同:

  • 命名空间不同:can-i 未指定 -n 时可能使用当前 context 的默认命名空间。
  • 资源与子资源不同:get pods 不代表可以 create pods/exec,也不代表可以 get pods/log。
  • 准入控制拒绝:RBAC 已允许,但 ValidatingAdmissionWebhook、Pod Security 或策略引擎仍可返回 Forbidden。
  • 资源名称不同:角色规则使用了 resourceNames,而实际请求访问的是另一个对象。
  • 外部授权器参与:Webhook authorizer 可能根据额外上下文作出不同判断,SelfSubjectAccessReview 结果也可能带 evaluationError。
  • 凭据发生变化:短期令牌刷新、ServiceAccount Token 轮换或切换 context 后,实际身份可能与基线记录不一致。

此时应比较实际请求的 verb、API group、resource、subresource、namespace 和 name。可以临时提高 kubectl 客户端日志级别观察请求路径,但日志中仍需检查并脱敏 Authorization 等头部信息。

kubectl -v=7 get pod example-pod -n app-prod 2>&1

如果响应由准入 Webhook 拒绝,错误信息通常会包含 webhook 名称或策略名称。不要通过删除 Webhook、修改 failurePolicy 等方式绕过,应由集群管理员结合控制面日志定位。

七、日志留痕建议

API Server 审计策略至少应记录 Secret 访问、Pod 交互子资源和授权审查。对 Secret 建议使用 Metadata 级别,避免审计日志再次保存敏感数据。

apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
  - RequestReceived
rules:
  - level: Metadata
    resources:
      - group: ""
        resources:
          - secrets
          - pods/exec
          - pods/portforward
          - pods/log
  - level: Request
    resources:
      - group: authorization.k8s.io
        resources:
          - selfsubjectaccessreviews
  - level: Metadata

自建控制面需要通过 kube-apiserver 的 --audit-policy-file 和 --audit-log-path 等参数启用;托管集群通常需要在云平台控制面日志选项中开启。修改前应评估日志量、保存周期和敏感字段。

审计日志中重点关联 user.username、user.groups、sourceIPs、verb、objectRef、responseStatus.code 和 requestReceivedTimestamp:

jq -c '
  select(
    .objectRef.namespace == "app-prod" and
    (.objectRef.resource == "secrets" or
     .objectRef.subresource == "exec" or
     .objectRef.subresource == "portforward")
  ) |
  {
    time: .requestReceivedTimestamp,
    user: .user.username,
    source: .sourceIPs,
    verb: .verb,
    object: .objectRef,
    code: .responseStatus.code
  }
' /var/log/kubernetes/audit/audit.log

八、风险判断与修复建议

  • 将 list secrets 按高风险处理,因为 Kubernetes API 返回的 Secret 列表可能包含完整 data 字段,而不只是名称。
  • 将 create pods 与工作负载安全配置联合评估。若允许挂载其他 ServiceAccount、使用 hostPath、特权容器或访问节点网络,风险可能接近权限提升。
  • 删除不必要的通配符规则,明确 apiGroups、resources、verbs 和 namespaces。
  • 优先使用命名空间级 RoleBinding;只有确有集群级需求时才使用 ClusterRoleBinding。
  • 严格限制 bind、escalate、impersonate,以及对 Role、RoleBinding、ClusterRoleBinding 的写权限。
  • 不需要调用 Kubernetes API 的工作负载,应设置 automountServiceAccountToken: false。
  • 发现 ServiceAccount 令牌可能暴露后,应先移除权限绑定,再滚动工作负载或轮换关联凭据,避免只删 Pod 而保留原有授权关系。
  • 通过 Pod Security Admission 或等价策略限制 privileged、hostPID、hostNetwork、hostPath 和高危 capabilities,避免“RBAC 不高但 Pod 创建能力过强”。

例如,确认业务不依赖集群 API 后,可以在变更窗口关闭默认 ServiceAccount 的自动令牌挂载:

kubectl patch serviceaccount default -n app-prod \
  -p '{"automountServiceAccountToken":false}'

这项修改只影响后续创建或重建的 Pod,已有 Pod 通常需要滚动更新。执行前应检查 Deployment、Job 和控制器是否显式依赖该令牌,并准备回滚方案。

总结

低影响验证 Kubernetes RBAC 的核心顺序是:固定身份和上下文、使用 can-i 建立基线、通过 SelfSubjectAccessReview 验证敏感权限、用服务端 dry-run 检查写操作、追溯 RoleBinding 或 ClusterRoleBinding 来源,最后结合审计日志确认实际请求。对于 Secret、pods/exec、Pod 创建及 RBAC 写权限,不需要通过读取真实数据或进入业务容器来证明风险;权限审查结果、准入响应和绑定关系通常已经能够形成完整证据链。

网络流量与边界分析示意图
网络请求、日志与边界流量分析示意
安全事件影响分析示意图
安全动态、影响面和处置建议示意

低影响验证建议分成“当前身份的授权判断”和“潜在提权链检查”两部分,尽量只调用 Authorization API,不实际创建工作负载、绑定角色或读取 Secret 内容。

1. 先固定测试身份和集群,避免串上下文

kubectl config current-context
kubectl auth whoami
kubectl version --short

保留上述输出以及测试时间,后续方便和审计日志对应。

2. 用 SelfSubjectAccessReview 验证具体权限

kubectl auth can-i --list适合初筛,但不要只看列表结果,尤其是带 resourceNames、聚合 ClusterRole 或自定义 authorizer 时,应针对具体动作检查:

# 当前命名空间
kubectl auth can-i get pods -n app-a
kubectl auth can-i list secrets -n app-a
kubectl auth can-i get secret/example -n app-a

# 跨命名空间对比
kubectl auth can-i get pods -n app-b
kubectl auth can-i list secrets -n app-b
kubectl auth can-i create deployments.apps -n app-b

# 集群范围资源
kubectl auth can-i get nodes
kubectl auth can-i list clusterroles.rbac.authorization.k8s.io

需要留下结构化证据时,可以直接提交 SelfSubjectAccessReview。该对象不会持久化,也不会访问目标资源:

cat >/tmp/ssar.json <<'EOF'
{
  "apiVersion": "authorization.k8s.io/v1",
  "kind": "SelfSubjectAccessReview",
  "spec": {
    "resourceAttributes": {
      "namespace": "app-b",
      "verb": "get",
      "group": "",
      "resource": "secrets",
      "name": "example"
    }
  }
}
EOF

kubectl create --raw \
  /apis/authorization.k8s.io/v1/selfsubjectaccessreviews \
  -f /tmp/ssar.json | jq .

重点记录 status.allowed、status.denied 和 status.reason。不要为了证明权限而直接执行 kubectl get secret -o yaml,授权判断已经足够证明可达性。

3. 排查可形成 RBAC 提权的权限组合

# 创建或修改授权对象
kubectl auth can-i create roles.rbac.authorization.k8s.io -n app-b
kubectl auth can-i update roles.rbac.authorization.k8s.io -n app-b
kubectl auth can-i create rolebindings.rbac.authorization.k8s.io -n app-b
kubectl auth can-i patch rolebindings.rbac.authorization.k8s.io -n app-b

# 绕过权限升级保护的特殊 verb
kubectl auth can-i escalate roles.rbac.authorization.k8s.io -n app-b
kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io
kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io

# 身份模拟及服务账号令牌
kubectl auth can-i impersonate serviceaccount/build -n app-b
kubectl auth can-i impersonate users
kubectl auth can-i impersonate groups
kubectl auth can-i create serviceaccounts/token -n app-b

其中 create rolebindings 不一定等于能绑定任意高权限角色,还要结合 bind 权限以及当前身份本身已拥有的权限判断;同理,能创建 Role 也不一定能写入超出自身权限的规则,除非具备 escalate。

4. 只读核对实际绑定关系

kubectl get rolebindings.rbac.authorization.k8s.io \
  -A -o json >/tmp/rolebindings.json

kubectl get clusterrolebindings.rbac.authorization.k8s.io \
  -o json >/tmp/clusterrolebindings.json

kubectl get roles.rbac.authorization.k8s.io \
  -A -o json >/tmp/roles.json

kubectl get clusterroles.rbac.authorization.k8s.io \
  -o json >/tmp/clusterroles.json

重点检查:

  • RoleBinding 的 roleRef 是否引用 ClusterRole。它只在该 RoleBinding 所在命名空间生效,但可能带来该命名空间内的高权限。
  • ClusterRoleBinding 是否绑定到普通用户、业务组或服务账号。
  • ClusterRole 是否包含通配符 *、secrets、pods/exec、nodes/proxy、RBAC 对象或 webhook 配置。
  • 带 aggregationRule 的 ClusterRole 要查看聚合后的实际规则,不能只看原始标签。

可以用 jq 快速找出跨命名空间服务账号绑定:

jq -r '
  .items[] as $rb |
  $rb.subjects[]? |
  select(.kind=="ServiceAccount") |
  [
    $rb.metadata.namespace,
    (.namespace // $rb.metadata.namespace),
    .name,
    $rb.roleRef.kind,
    $rb.roleRef.name
  ] | @tsv
' /tmp/rolebindings.json

5. 验证“创建 Pod 即可借用高权限 ServiceAccount”时要单独判断

仅有 create pods 并不能完全证明能指定任意 ServiceAccount,还可能受准入策略限制。经授权后可使用服务端 dry-run,提交一个不落库、不调度的最小对象:

cat <<'EOF' | kubectl apply --dry-run=server -f -
apiVersion: v1
kind: Pod
metadata:
  name: rbac-validation
  namespace: app-b
spec:
  serviceAccountName: privileged-sa
  automountServiceAccountToken: false
  restartPolicy: Never
  containers:
    - name: check
      image: registry.k8s.io/pause:3.9
EOF

服务端 dry-run 会经过授权和准入,但不会持久化或调度。它仍可能触发声明支持 dry-run 的 admission webhook,因此应确认测试窗口和审计要求。这个结果只能证明对象可通过创建与准入,不能证明 ServiceAccount 的令牌已实际挂载;不要为了补齐证据启动 Pod 或导出令牌。

6. 关于 --as

不要默认用 kubectl auth can-i --as=... 模拟其他用户。该请求本身需要当前身份拥有 impersonate 权限,失败并不代表目标身份没有权限,只代表当前身份不能模拟它。测试其他身份时,优先使用授权方提供的独立测试凭据,或由集群管理员执行 SubjectAccessReview。

最终报告建议按“身份、命名空间、verb、resource、resourceName、allowed、判定依据”逐条列出。这样既能证明跨命名空间授权,也不会实际读取业务数据或修改 RBAC。

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。