发布于7小时前7小时 问题背景在一次已授权的 Kubernetes 内网渗透测试中,发现 dev 命名空间内的 ServiceAccount ci-runner 无权直接读取 Secret,但拥有创建 Pod 的权限。此时是否应判定为 Secret 读取或权限提升?如何在不接触生产敏感数据的前提下完成验证,并给出可落地的修复建议?这个问题不能只看 get secrets 的结果。Kubernetes 的权限边界由 RBAC、Pod 准入策略、ServiceAccount、节点挂载能力和网络出口共同决定。主体只要能创建 Pod,通常就可能让 kubelet 代为挂载命名空间内的 Secret,或者让新 Pod 使用权限更高的 ServiceAccount。因此,单独执行一次 kubectl auth can-i get secrets 不足以证明边界安全。问题定位建议先确认测试身份、目标命名空间和授权范围。以下操作默认已经获得集群所有者授权,优先使用只读查询和服务端 dry-run。若 impersonation 返回 403,不应自行申请或绕过权限,应由集群管理员代为导出结果。CTX=prod-cluster NS=dev SUBJECT=system:serviceaccount:dev:ci-runner # 查看 RBAC 计算后的权限;该结果不包含准入控制和网络策略影响 kubectl --context="$CTX" auth can-i \ --as="$SUBJECT" --list -n "$NS" # 对关键动作逐项确认 for check in \ "create pods" \ "get secrets" \ "list secrets" \ "create deployments.apps" \ "create jobs.batch" \ "create pods/exec" \ "create pods/attach" \ "create serviceaccounts" \ "create rolebindings.rbac.authorization.k8s.io"; do kubectl --context="$CTX" auth can-i $check \ --as="$SUBJECT" -n "$NS" done # 导出命名空间内的绑定关系 kubectl --context="$CTX" get rolebinding -n "$NS" -o yaml kubectl --context="$CTX" get role -n "$NS" -o yaml # 检查该主体是否被 ClusterRoleBinding 直接绑定 kubectl --context="$CTX" get clusterrolebinding -o json | \ jq --arg s "$SUBJECT" ' .items[] | select(any(.subjects[]?; ((.kind + ":" + (.namespace // "") + ":" + .name) == ("ServiceAccount:dev:ci-runner")))) | {name: .metadata.name, roleRef: .roleRef}'kubectl auth can-i --list 只反映 RBAC 授权结果,不会告诉你 Pod Security Admission、Kyverno、Gatekeeper、镜像策略或 NetworkPolicy 是否会阻止后续动作,因此必须继续检查准入边界。技术分析需要重点判断以下几条链路:Secret 间接访问:创建 Pod 时引用 Secret,API Server 通常不会要求 Pod 创建者同时具备 get secrets。Pod 被准入并调度后,由 kubelet获取并挂载 Secret。ServiceAccount 继承:能够创建 Pod 的主体通常可以在 PodSpec 中指定同命名空间内的其他 ServiceAccount。Kubernetes RBAC 默认没有一个通用的“use serviceaccount”检查来阻止这一点,实际限制往往依赖准入策略。节点边界突破:如果准入策略允许 privileged、hostPath、hostPID 或 hostNetwork,新 Pod 的影响可能从命名空间扩大到节点。控制器等价权限:创建 Deployment、DaemonSet、Job 或 CronJob,最终同样能够创建 Pod,不能因为没有直接的 create pods 就排除风险。已有工作负载修改:对 Deployment 的 patch/update 权限也可能被用于替换镜像、命令或 ServiceAccount。因此,若 ci-runner 可以创建任意 Pod,而命名空间内存在高权限 ServiceAccount 或敏感 Secret,应至少按“命名空间内权限提升”记录。若同时允许高风险宿主机能力,则应进一步评估节点或集群级影响。安全验证步骤第一步是盘点命名空间内可被 Pod 使用的身份及其绑定。测试账号没有读取权限时,可让管理员提供脱敏导出。# 查看命名空间中的 ServiceAccount,以及是否显式关闭令牌自动挂载 kubectl --context="$CTX" get serviceaccount -n "$NS" \ -o custom-columns='NAME:.metadata.name,AUTOMOUNT:.automountServiceAccountToken' # 查看工作负载实际使用的 ServiceAccount kubectl --context="$CTX" get pods -n "$NS" \ -o custom-columns='POD:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName' # 查看 Pod Security Admission 标签 kubectl --context="$CTX" get namespace "$NS" --show-labels # 补充检查命名空间隔离配置 kubectl --context="$CTX" get networkpolicy,resourcequota,limitrange -n "$NS"第二步使用服务端 dry-run 验证普通 Pod 是否能通过授权和准入。该请求会进入 API Server 的授权与准入流程,但不会持久化对象或启动容器。kubectl --context="$CTX" apply \ --as="$SUBJECT" \ --server-side --dry-run=server -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: rbac-boundary-probe namespace: dev labels: security-test: authorized-dry-run spec: serviceAccountName: ci-runner automountServiceAccountToken: false restartPolicy: Never securityContext: seccompProfile: type: RuntimeDefault containers: - name: pause image: registry.k8s.io/pause:3.9 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true capabilities: drop: ["ALL"] EOF第三步验证高风险 PodSpec 是否被准入策略拒绝。仍然只使用服务端 dry-run,不实际创建 Pod。预期结果应是被 Pod Security Admission 或其他策略拒绝。kubectl --context="$CTX" apply \ --as="$SUBJECT" \ --server-side --dry-run=server -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: privileged-boundary-probe namespace: dev labels: security-test: authorized-dry-run spec: restartPolicy: Never hostPID: true containers: - name: pause image: registry.k8s.io/pause:3.9 securityContext: privileged: true volumeMounts: - name: host-root mountPath: /host volumes: - name: host-root hostPath: path: / type: Directory EOF如果第二个请求返回 dry-run 创建成功,说明集群没有有效阻断 privileged、hostPID 或根目录 hostPath。此时无需实际启动容器,服务端接受结果、RBAC 配置和审计日志已经足以证明控制缺失。对于 Secret 间接访问,不建议引用真实 Secret 做验证。更稳妥的方式是在隔离测试命名空间中,由资产方创建仅包含随机值的 canary Secret,再让测试 Pod 挂载该对象。验证完成后立即删除测试资源。生产命名空间内如果已经存在敏感 Secret,通常结合“可创建任意 Pod”和“缺少限制 Secret 引用的准入策略”即可形成有效风险结论。日志留痕与证据固定建议保留测试时间、集群上下文、主体、命名空间、完整命令、服务端返回码和 Kubernetes Audit ID。不要只保存终端截图,因为截图通常缺少请求身份和对象信息。如果能够访问控制平面审计日志,可按测试主体过滤:jq -r ' select(.user.username == "system:serviceaccount:dev:ci-runner") | select(.objectRef.namespace == "dev") | [ .requestReceivedTimestamp, .auditID, .verb, .objectRef.resource, (.objectRef.subresource // "-"), (.objectRef.name // "-"), (.responseStatus.code // 0) ] | @tsv ' /var/log/kubernetes/audit.log审计策略中,Pod 创建和 RBAC 变更可以记录 RequestResponse,但 Secret 资源应避免记录请求体和响应体,否则审计日志本身会成为敏感数据源。可使用如下原则配置:apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata resources: - group: "" resources: ["secrets"] - level: RequestResponse verbs: ["create", "update", "patch", "delete"] resources: - group: "" resources: ["pods", "serviceaccounts"] - group: "apps" resources: ["deployments", "daemonsets", "statefulsets"] - group: "batch" resources: ["jobs", "cronjobs"] - group: "rbac.authorization.k8s.io" resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"] - level: Metadata风险定级参考可创建 Pod,但只能使用低权限 ServiceAccount,且 enforced 级别为 restricted,同时禁止 hostPath、特权模式和危险网络能力:通常属于命名空间工作负载控制风险,仍需结合 Secret 和业务数据评估。可创建 Pod,并可指定绑定了更高权限的 ServiceAccount:属于明确的权限提升路径。可创建 Pod,且能引用命名空间内生产 Secret:可按敏感信息间接访问处理,即使主体自身没有 get secrets。可创建 privileged、hostPID 或根目录 hostPath Pod:可能扩大到节点权限,应按高危处理。可创建 DaemonSet 且缺少有效准入限制:影响可能覆盖多个节点,通常比单 Pod 权限更严重。修复建议不要把通用的 create pods 授予 CI 身份。优先让 CI 更新受控 Deployment 的指定字段,或通过独立部署控制器完成发布。需要注意,RBAC 的 resourceNames 不能可靠约束 create 请求,因为对象授权检查阶段无法按既有资源名限制任意新建对象。不要用它解决“只能创建固定名称 Pod”的问题。为业务、部署系统和运维任务拆分 ServiceAccount,避免多个工作负载共用高权限身份。对不需要访问 Kubernetes API 的工作负载设置 automountServiceAccountToken: false。这只能减少令牌暴露,不能替代 RBAC 和准入控制。在生产命名空间启用并强制执行 Pod Security Admission 的 restricted 策略,同时设置 audit 和 warn 标签观察兼容性。使用 Kyverno 或 Gatekeeper 限制可选的 serviceAccountName,并拒绝 privileged、hostPath、hostPID、hostIPC、危险 capabilities 和不受控 hostNetwork。通过 NetworkPolicy 限制工作负载访问 API Server、云元数据地址和非必要外部网络。注意 NetworkPolicy 不是身份授权的替代品。如果确认已经存在可利用窗口,应检查该主体创建或修改过的 Pod、Job、Deployment 和 DaemonSet,并轮换可能被挂载的业务凭据。仅删除 RoleBinding 不能消除已泄露凭据的影响。# 一个常见的命名空间基线示例,应用前应先评估现有工作负载兼容性 kubectl label namespace dev \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/enforce-version=latest \ pod-security.kubernetes.io/audit=restricted \ pod-security.kubernetes.io/warn=restricted \ --overwrite总结在 Kubernetes 权限验证中,“不能直接读取 Secret”不等于无法获得 Secret。只要主体能够创建或修改 Pod,就必须继续检查 Secret 引用、ServiceAccount 选择、控制器权限和宿主机能力。实际测试应优先采用 impersonation、只读查询和服务端 dry-run,并通过审计日志固定证据。修复重点不是简单删除一个 verb,而是同时收紧 RBAC、ServiceAccount 使用范围、Pod 准入策略和网络出口。 服务器、云资产与权限边界梳理示意 网络请求、日志与边界流量分析示意
5小时前5小时 是否构成权限提升,关键不是“能创建 Pod”本身,而是这个 Pod 能否获得当前 ServiceAccount 原本没有的身份、数据或节点权限。建议按下面几条验证。 1. 先确认实际授权范围和所在 namespace NS=test SA=app-sa kubectl auth can-i create pods \ --as=system:serviceaccount:${NS}:${SA} -n ${NS} kubectl auth can-i --list \ --as=system:serviceaccount:${NS}:${SA} -n ${NS} kubectl get rolebinding,clusterrolebinding -A -o wide | grep -E "${SA}|system:serviceaccounts:${NS}" 注意是否同时具有 pods/exec、pods/attach、pods/log、secrets、rolebindings 等权限。单纯能创建但不能 exec,并不代表无法利用,Pod 仍可主动向外发送数据或访问 API。 2. 检查能否让 Pod 使用 namespace 内更高权限的 ServiceAccount Pod 创建者通常可以在 spec.serviceAccountName 中指定同 namespace 的其他 ServiceAccount,并不要求其本身具备 impersonate 权限。如果目标 SA 权限更高,且没有准入策略限制,这通常可以认定为权限提升。 kubectl get sa -n ${NS} kubectl auth can-i --list \ --as=system:serviceaccount:${NS}:target-sa -n ${NS} 可先用服务端 dry-run 验证准入链是否允许,不会实际落地 Pod: kubectl create --dry-run=server -f - \ --as=system:serviceaccount:${NS}:${SA} <<'EOF' apiVersion: v1 kind: Pod metadata: name: sa-escalation-check namespace: test spec: serviceAccountName: target-sa restartPolicy: Never containers: - name: check image: registry.k8s.io/pause:3.10 EOF 如果允许创建,并且 target-sa 比原 SA 权限更高,例如可读取 Secret、修改工作负载或访问其他 namespace,就已经存在清晰的提权路径。实际验证时不必读取敏感数据,证明目标身份具有额外的 can-i 权限即可。 3. 检查 namespace 内的 Secret、ConfigMap 和 PVC 暴露 即使原 SA 没有 get secrets,Pod 创建权限也可能允许其把同 namespace 的 Secret 作为 volume 或环境变量挂载,再通过容器读取。PVC、ConfigMap 同理。因此要检查准入策略是否禁止任意引用: kubectl get secrets,configmaps,pvc -n ${NS} kubectl get validatingadmissionpolicies,validatingwebhookconfigurations kubectl get clusterpolicy,policy -A 2>/dev/null 一个无敏感内容的 dry-run 示例: kubectl create --dry-run=server -f - \ --as=system:serviceaccount:${NS}:${SA} <<'EOF' apiVersion: v1 kind: Pod metadata: name: secret-mount-check namespace: test spec: restartPolicy: Never containers: - name: check image: registry.k8s.io/pause:3.10 volumeMounts: - name: candidate mountPath: /mnt/check readOnly: true volumes: - name: candidate secret: secretName: test-secret EOF 4. 检查是否可以扩大到节点级权限 重点看准入控制是否允许以下字段: privileged: true 或危险 capabilities; hostPath,尤其是 /、容器运行时 socket、kubelet 目录; hostPID、hostNetwork、hostIPC; 可控的 nodeName、nodeSelector、tolerations,能否调度到控制平面或敏感节点; 可写 PVC 是否被其他高权限工作负载使用。 kubectl get ns ${NS} --show-labels kubectl get ns ${NS} -o jsonpath='{.metadata.labels}' | jq 重点确认 Pod Security Admission 标签是否为 restricted,以及是否有 Gatekeeper/Kyverno 等额外策略。RBAC 显示可以创建 Pod,只说明通过了授权层;最终能否提交危险 Pod,还取决于准入控制。 结论上可以这样定级: 只能创建受限 Pod,不能选择其他 SA、挂载敏感对象或使用危险主机配置:更偏向“工作负载创建/资源滥用权限”,不一定构成权限提升。 能使用权限更高的 SA,或读取原身份无权访问的 Secret/PVC:属于 namespace 范围的权限提升。 能创建 privileged、hostPath、hostPID 等 Pod 并影响节点:通常可上升为节点权限,影响同节点其他工作负载,风险明显更高。 修复时不能只移除 pods/exec。更有效的是收紧 create pods,为可创建的 Pod 限定 ServiceAccount、Secret/PVC 引用和安全上下文,同时在 namespace 启用 restricted 级别的 Pod Security Admission,并避免同一 namespace 混放高权限 SA 与低信任 Pod 创建者。
创建帐户或登录后发表意见