Dapr 1.4.2 修复解析:sidecar-injector 准入 Webhook 阻断 Pod 创建的根因与排查方案
【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr
导读
Dapr 1.4.2 是一个专注于修复 Sidecar 注入器(sidecar-injector)准入 Webhook 误伤问题的补丁版本。此前,当 Pod 由未在"允许的控制器账号"名单中的组件创建(例如 Tekton CICD 控制器创建 TaskRun Pod)时,sidecar-injector.dapr.io会直接拒绝该 Pod 的准入请求,导致 Pod 无法创建、CI 流水线整体失败。本文以 docs/release_notes/v1.4.2.md 为主线,结合仓库中注入器服务的源码实现(pkg/injector/service)与 Helm 配置(charts/dapr/charts/dapr_sidecar_injector/values.yaml),剖析该问题的根因、1.4.2 的修复策略,以及如何通过ALLOWED_SERVICE_ACCOUNTS配置自定义放行规则,帮助你在部署 Dapr 或接入 CI/CD 时快速定位同类准入错误。
背景:Sidecar 注入器与准入 Webhook
在 Kubernetes 上启用 Dapr 后,dapr-sidecar-injector服务以MutatingAdmissionWebhook的形式运行:任何带有dapr.io/enabled: "true"注解的 Pod 在创建时,其 AdmissionReview 请求都会被转发到注入器,由注入器判断是否需要注入 Dapr sidecar,并返回 JSON Patch 供 API Server 应用。这一入口逻辑位于 handler.go 的handleRequest中,处理链路依次为:
- 校验请求体的 Content-Type 与 AdmissionReview 解码;
- 仅处理
Kind == "Pod"的请求; - 通过 podHasDaprEnabled 判断 Pod 是否带有
dapr.io/enabled注解,非 Dapr Pod 直接放行(respondWithAllowed); - 对 Dapr Pod 执行请求方身份校验(
isAuthorizedUser),通过后调用getPodPatchOperations生成注入补丁。
也就是说,注入器既是"注入器",也是 Pod 准入链路上的一个"关卡"——这正是 1.4.1/1.4.2 修复风暴的舞台。
问题现象:Pod 创建被 Webhook 直接拒绝
v1.4.2 的发布说明中明确指出修复了问题 #3709:
Sidecar-injector.dapr.io was blocking Pod admissions if sidecar could not be injected.
当某个控制器(Controller)代表工作负载创建 Pod、而注入器因身份校验无法为其注入 Dapr sidecar 时,Webhook 不是"跳过注入"而是"整体拒绝准入",最终导致 Pod 根本无法创建。发布说明给出的真实场景是使用Tekton CICD部署任务时的报错:
- lastTransitionTime: '2021-09-24T07:48:19Z' message: >- failed to create task run pod "function-sample-builder-cgmn8-buildrun-nfkb8-nlbjs": admission webhook "sidecar-injector.dapr.io" denied the request: service account 'system:serviceaccount:tekton-pipelines:tekton-pipelines-controller' not on the list of allowed controller accounts. Maybe invalid TaskSpec reason: CouldntGetTask status: 'False' type: Succeeded这个报错的几个关键信息:
- 请求方身份:
system:serviceaccount:tekton-pipelines:tekton-pipelines-controller,即 Tekton Pipeline 控制器在集群内使用的 ServiceAccount(Kubernetes 的UserInfo.Username格式); - 拒绝原因:
not on the list of allowed controller accounts,即该 ServiceAccount 不在注入器内置的"允许的控制器账号"名单中; - 业务影响:TaskRun Pod 无法创建,Task 状态变为
CouldntGetTask(Succeeded=False),流水线失败。
问题的由来:1.4.1 修复 #3699 时引入了过度收紧
要理解 1.4.2 修复什么,必须先看 1.4.1。在 docs/release_notes/v1.4.1.md 中,1.4.1 修复了问题 #3699:使用kubectl debug调试节点(例如kubectl debug node/<node> -it --image=...)时,请求同样会触发注入器并报出admission webhook "sidecar-injector.dapr.io" denied the request: service account 'xxxxxxx' not on the list of allowed controller accounts。
1.4.1 的修复方式是引入"允许的控制器账号"白名单机制:注入器只信任来自白名单控制器(如 deployment-controller、statefulset-controller 等)或系统管理员组(system:masters)的准入请求。但白名单天然存在覆盖不全的问题:任何不在名单内的控制器(Tekton、Argo、GitHub Actions Runner 等 CI 控制器)创建的 Pod 都会被误拒,这就是 #3709 的由来。1.4.2 正是在 1.4.1 允许注入 Dapr sidecar(修复 #3699)的基础上,把误伤导致的 Pod 创建阻断一并解决。
根因剖析:allowed controller accounts 机制与身份校验链
允许名单的源码定义
当前仓库中,内置的允许名单定义在 injector.go:
var AllowedServiceAccountInfos = []string{ "kube-system:replicaset-controller", "kube-system:replication-controller", "kube-system:deployment-controller", "kube-system:cronjob-controller", "kube-system:job-controller", "kube-system:statefulset-controller", "kube-system:daemon-set-controller", "openshift-operator-lifecycle-manager:olm-operator-serviceaccount", "tekton-pipelines:tekton-pipelines-controller", //nolint:misspell "mirrord:mirrord-operator", }注意其中的tekton-pipelines:tekton-pipelines-controller——这正是 1.4.2 针对 #3709 场景补充的条目:Tekton 控制器创建的 Pod(TaskRun Pod)现在被视为合法请求方,不会再被拒绝。从中也可以看出 Dapr 后续版本持续在为各类控制器扩展这份名单(如 OpenShift OLM、mirrord)。
身份校验的完整调用链
请求方校验发生在handleRequest的这段逻辑(handler.go):
if !i.isAuthorizedUser(ar.Request) { log.Errorf("service account '%s' not on the list of allowed controller accounts", ar.Request.UserInfo.Username) diagAppID := getAppIDFromRequest(ar.Request) respondWithAllowed(w, ar, gvk) RecordFailedSidecarInjectionCount(diagAppID, "pod_patch") return }isAuthorizedUser(handler.go)采用三重放行策略:
func (i *injector) isAuthorizedUser(req *admissionv1.AdmissionRequest) bool { return i.allowServiceAccountUser(req.UserInfo.Username) || utils.Contains(i.authUIDs, req.UserInfo.UID) || utils.Contains(req.UserInfo.Groups, systemGroup) }- 按 ServiceAccount 名称匹配:
allowServiceAccountUser会先从Username中剥离system:serviceaccount:前缀(常量serviceAccountUserInfoPrefix,见 injector.go),取出namespace:name后交给namespaceNameMatcher匹配; - 按 UID 匹配:
authUIDs是启动时通过 AllowedControllersServiceAccountUID 从集群中查询白名单 ServiceAccount 的真实 UID 集合(getServiceAccount会调用 Kubernetes API 列出 ServiceAccount 并解析其 UID,见 injector.go),作为名称匹配之外的第二道防线(defense-in-depth); - 按用户组匹配:请求方若属于
system:masters(systemGroup)则直接放行,这是管理员操作的兜底通道。
关键点:拒绝与"放行但不注入"的差异
值得对比的是:当前仓库代码在身份校验失败时调用的是respondWithAllowed(返回Allowed: true但不附带任何 Patch),同时记录一条RecordFailedSidecarInjectionCount指标。也就是说,注入器演进后的行为是"放行 Pod、跳过注入"而非"拒绝准入"——这与 1.4.2 修复前的"denied the request"行为不同。这一点印证了 1.4.2 修复的核心方向:注入失败不应阻断业务 Pod 的生命周期,Dapr 应该选择性地放弃注入而不是成为准入链路上的硬故障点。
匹配机制演进:从精确名单到 Glob 模式
1.4.2 时代的问题本质是"精确白名单覆盖不全"。当前仓库中,该机制已演进为基于 Gopath.Match的 Glob 匹配器,实现位于 serviceaccountmatcher.go:
- 每个模式必须是
namespace:name形式,且恰好包含一个冒号(多于一个或缺少冒号都会报错); - 命名空间与名称两个部分都支持 Glob 语法:
*匹配任意字符序列、?匹配单个字符、[...]匹配字符集合; - 空字符串模式会被静默跳过;没有任何有效模式时返回一个恒为
false的匹配器; - 多个模式之间是"或"的关系,任一命中即放行。
该行为在 serviceaccountmatcher_test.go 中有大量用例覆盖,例如:
| 模式 | 请求方namespace:name | 结果 |
|---|---|---|
ns:sa | ns:sa | 放行(精确匹配) |
ns:sa | ns:sa-extra | 拒绝(不做子串匹配) |
ns-*:sa-* | ns-foo:sa-bar | 放行(前缀通配) |
ns-?:sa | ns-A:sa | 放行(?匹配单字符) |
ns-?:sa | ns-AB:sa | 拒绝(?不匹配多字符) |
ns:sa-[abc] | ns:sa-b | 放行(字符类) |
ns:sa-[abc] | ns:sa-d | 拒绝(字符类不命中) |
修复方案落地:如何配置自定义允许的控制器账号
1.4.2 修复的核心是把 Tekton 等已知 CI 控制器补进内置名单;而面对自建控制器或私有 CI,Dapr 提供了可配置的扩展点。
环境变量方式
注入器通过 config.go 中的envconfig读取环境变量:
ALLOWED_SERVICE_ACCOUNTS:以逗号分隔的额外允许 ServiceAccount 列表,格式为namespace:name,当前版本支持 Glob 通配;ALLOWED_SERVICE_ACCOUNTS_PREFIX_NAMES:已弃用(Deprecated),其"尾缀*前缀匹配"语义已被ALLOWED_SERVICE_ACCOUNTS的 Glob 能力取代;若仍配置,会打印ALLOWED_SERVICE_ACCOUNTS_PREFIX_NAMES is deprecated; use ALLOWED_SERVICE_ACCOUNTS instead的警告日志(见 injector.go)。
这两项环境变量与内置AllowedServiceAccountInfos会在NewInjector中被合并为同一批匹配模式(injector.go)。
Helm 方式
在 Helm 安装时,可通过dapr_sidecar_injector子 chart 的 values 配置(values.yaml):
dapr_sidecar_injector: allowedServiceAccounts: "" allowedServiceAccountsPrefixNames: ""对应模板会将值注入 Deployment 环境变量(dapr_sidecar_injector_deployment.yaml),chart 的 README.md 给出了三种典型写法:
my-ns:my-sa:精确匹配单个 ServiceAccount;my-ns:*:放行某命名空间下的全部 ServiceAccount;team-*:deploy-*:前缀通配,匹配多个团队/多个部署账号。
例如,为自建的 GitLab Runner(命名空间gitlab)放行:
dapr_sidecar_injector: allowedServiceAccounts: "gitlab:gitlab-runner"或放行某个命名空间下所有控制器账号:
dapr_sidecar_injector: allowedServiceAccounts: "ci-runners:*"匹配失败时的降级行为
结合 handler_test.go 中的用例(如TestSidecarInjectUserInfoNotMatchesServiceAccountPrefix、TestSidecarInjectDefaultServiceAccountNearMissDenied),可以确认以下行为细节:
- 精确名单不做子串匹配:
kube-system:deployment-controller放行,但kube-system:deployment-controller-extra会被视为未授权(见TestSidecarInjectDefaultServiceAccountNearMissDenied,handler_test.go); - 未授权请求虽然 HTTP 返回 200、Pod 被放行,但不会注入 sidecar,并计入失败注入指标(
RecordFailedSidecarInjectionCount),便于通过 Prometheus 监控发现"该注入而未注入"的 Pod(注入器指标定义见 metrics.go)。
测试与验证:如何在仓库中复现与确认
仓库内已为这套机制准备了完整的单元测试,可作为验证与回归依据:
- handler_test.go:通过
httptest直接向handleRequest发起 AdmissionReview 请求,覆盖成功注入、错误 Content-Type、非 Pod 类型、组不匹配、UID 匹配/不匹配、ServiceAccount 前缀匹配、内置名单精确匹配与"近似名拒绝"等十余个场景; - injector_test.go:
TestAllowedControllersServiceAccountUID使用 fake Kubernetes Client 创建tekton-pipelines:tekton-pipelines-controller等 ServiceAccount,验证 UID 白名单的查询与解析逻辑(含配置有效/无效账号的用例); - serviceaccountmatcher_test.go:针对 Glob 匹配器本身的纯函数测试,覆盖通配符、字符类、非法模式报错、空模式恒拒绝等边界情况。
若要亲手验证,可在仓库根目录执行:
go test ./pkg/injector/service/...升级与排障建议
围绕 #3709 这类"准入 Webhook 误伤",给出如下实操建议:
- 遇到
denied the request: service account '...' not on the list of allowed controller accounts时,先定位请求方身份:报错中的system:serviceaccount:<namespace>:<name>即为创建 Pod 的控制器账号,确认该控制器是否需要创建 Dapr 注入的 Pod; - 区分两类诉求:若该控制器创建的 Pod 根本不需要 Dapr(无
dapr.io/enabled注解),则注入器本应直接放行;若确实需要注入,则应将账号加入ALLOWED_SERVICE_ACCOUNTS; - 优先使用通配而非逐个枚举:对于 CI 这类控制器账号动态变化的场景,
ci-*:runner-*之类的 Glob 模式比维护精确名单更稳健; - 升级到包含本修复的版本:1.4.2 将
tekton-pipelines:tekton-pipelines-controller纳入内置允许名单,同时保持 1.4.1 对kubectl debug(#3699)的修复不回归;后续版本进一步将拒绝行为调整为"放行但不注入",从机制上消除了单点故障风险; - 监控注入失败指标:即使 Pod 被放行,也要通过
RecordFailedSidecarInjectionCount对应的注入失败指标关注"静默未注入"的 Pod,避免业务在无 sidecar 状态下运行。
总结
Dapr 1.4.2 通过将 Tekton Pipeline 控制器账号纳入 Sidecar 注入器允许名单,解决了 #3709 中"准入 Webhook 阻断 Pod 创建"的问题,是 1.4.1 引入白名单机制后的一次重要收敛。围绕这一修复,本文从 pkg/injector/service/injector.go、handler.go、serviceaccountmatcher.go 出发,还原了允许名单、身份校验链与 Glob 匹配机制的实现全貌,并给出了通过ALLOWED_SERVICE_ACCOUNTS/allowedServiceAccounts自定义放行规则的完整配置路径。理解这条准入链路的判定逻辑,是你在任何集群化 CI/CD 场景下排查 Dapr sidecar 注入问题的基础。
【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考