当集群规模扩大、团队增多之后,仅靠 Kubernetes 自带的 RBAC 往往满足不了需求。比如公司希望复用已有的 IAM 平台统一管理权限,或者需要根据业务标签、时间窗口、审批状态等动态因素决定一次 API 请求是否放行,这些场景都超出了 RBAC 的表达能力。Kubernetes 提供的 Webhook 授权模式正好解决这类问题:apiserver 在处理每个请求时,把请求的上下文信息 POST 给一个外部 HTTP 服务,由这个服务给出允许或拒绝的结论。本文完整演示这套机制的搭建过程。

外部授权 Webhook 的工作原理
要先理解 apiserver 的认证与授权是两个独立阶段。认证回答的是“你是谁”,授权回答的是“你能不能做这件事”。Kubernetes 支持多种授权模式,包括 Node、RBAC、ABAC 和 Webhook,可以在 --authorization-mode 参数中用逗号组合多个模式,apiserver 会按顺序逐个尝试,只要有一个模式放行,请求就通过。
当轮到 Webhook 模式时,apiserver 会构造一个 SubjectAccessReview 对象,以 JSON 形式 POST 到配置文件中指定的 URL。这个对象包含了用户名、用户组、请求动词、资源名、命名空间、非资源路径等关键信息。外部服务只需要返回一个同样结构的响应,其中 allowed 字段为 true 表示允许。需要特别注意的是,这个请求走的不是集群内部的 Service 网络路径,而是 apiserver 直接发起的 HTTPS 调用,因此对证书、超时、可用性的要求都要认真对待。
还有一个容易被忽视的点:Webhook 服务不可用时的表现取决于失败策略。默认情况下,如果调用外部服务失败或超时,apiserver 会拒绝该请求并返回错误,这是一种 fail-closed 的安全设计。生产环境中必须保证授权服务的高可用,否则它一旦故障,整个集群的 API 访问都会受影响。
动手实现一个授权 Webhook 服务
下面用 Go 写一个最小可用的授权服务。它监听 8443 端口,接收 SubjectAccessReview 请求,并按照自定义规则返回结论。示例中的规则很简单:允许所有对 pods 的 get 和 list 请求,其余拒绝。
package main
import (
"encoding/json"
"log"
"net/http"
)
// 对应 SubjectAccessReview 的核心字段
type reviewRequest struct {
Spec struct {
User string `json:"user"`
Groups []string `json:"groups"`
Verb string `json:"verb"`
Resource string `json:"resource"`
Namespace string `json:"namespace"`
} `json:"spec"`
}
type reviewResponse struct {
APIType string `json:"apiVersion"`
Kind string `json:"kind"`
Status struct {
Allowed bool `json:"allowed"`
Reason string `json:"reason,omitempty"`
} `json:"status"`
}
func handler(w http.ResponseWriter, r *http.Request) {
var req reviewRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
resp := reviewResponse{APIType: "authorization.k8s.io/v1beta1", Kind: "SubjectAccessReview"}
// 自定义授权逻辑:仅放行 pods 的读取类操作
if req.Spec.Resource == "pods" && (req.Spec.Verb == "get" || req.Spec.Verb == "list") {
resp.Status.Allowed = true
resp.Status.Reason = "read pods is permitted"
} else {
resp.Status.Allowed = false
resp.Status.Reason = "denied by custom policy"
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/authorize", handler)
// 要求 TLS,apiserver 默认使用 HTTPS 调用
log.Fatal(http.ListenAndServeTLS(":8443", "server-cert.pem", "server-key.pem", mux))
}证书方面建议直接用 kubectl 生成一套客户端 CA,让 apiserver 用客户端证书访问授权服务,服务端证书也由同一 CA 签发,实现双向认证。可以用 kubectl create secret tls 把证书存进集群,再以 Deployment 方式部署这个服务,前置一个 Service 供 apiserver 访问。这里有个细节:apiserver 是以 Pod 网络之外的视角访问 URL 的,所以授权服务的地址必须写 apiserver 能解析到的地址,比如集群内 Service 的 DNS 名称或一个稳定的负载均衡地址。
接着编写授权配置文件 authorization-webhook.yaml,格式如下:
apiVersion: v1
kind: Config
clusters:
- name: authz-webhook
cluster:
server: https://authz-webhook.default.svc:8443/authorize
certificate-authority: /etc/kubernetes/pki/webhook-ca.pem # 校验服务端证书
users:
- name: kube-apiserver
user:
client-certificate: /etc/kubernetes/pki/webhook-client.pem # 客户端证书
client-key: /etc/kubernetes/pki/webhook-client-key.pem
current-context: authz-webhook
contexts:
- name: authz-webhook
context:
cluster: authz-webhook
user: kube-apiserver最后修改 kube-apiserver 的启动参数,把 Webhook 加入授权模式,并指向上述配置文件:--authorization-mode=Node,RBAC,Webhook 以及 --authorization-webhook-config-file=/etc/kubernetes/authorization-webhook.yaml。对于 kubeadm 部署的集群,这两个参数写在 /etc/kubernetes/manifests/kube-apiserver.yaml 中,保存后 apiserver 静态 Pod 会自动重启生效。
联调排查与生产注意事项
配置完成后先用普通用户执行 kubectl get pods 验证。如果返回 403 且原因是 denied by custom policy,说明链路已经通了,只是策略拒绝了请求;如果返回 500 或者提示 connection refused、timeout 之类错误,多半是网络不通或证书问题,此时 Webhook 还没被正确调用。区分这两种失败是排障的第一步。
证书错误是最常见的坑。检查 CA 证书路径是否挂载进了 apiserver 容器、证书的 SAN 字段是否包含授权服务的域名、文件权限是否可读。可以借助 kubectl auth can-i get pods --as=alice 命令模拟指定用户的授权判断,快速验证策略结果而不必真的切换用户,这是联调阶段非常好用的工具。
性能层面要关注两个参数:--authorization-webhook-cache-authorized-ttl 和 --authorization-webhook-cache-unauthorized-ttl,分别控制允许和拒绝结果的缓存时间,默认都是 5 分钟。合理调大缓存能显著降低外部服务的压力,但要注意策略变更后生效会有延迟。此外建议授权服务自身做多副本部署并配就绪探针,同时把处理逻辑控制在毫秒级,避免拖慢每一次 API 调用。日志中记录被拒绝的用户与资源信息也很重要,方便审计和策略迭代。
整体来看,Webhook 授权把权限判断从集群内部抽离出来,代价是引入了一个强依赖组件。只要做好高可用、缓存与证书管理,它就能稳定支撑企业级统一权限管控的需求,与 RBAC 形成互补而非替代的关系。
KubernetesWebhook外部授权修改时间:2026-09-10 19:38:35