news 2026/9/10 10:38:39

Backstage Kubernetes 后端代理端点(Proxy Endpoint)完全指南:从 REST API 透传到权限管控

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Backstage Kubernetes 后端代理端点(Proxy Endpoint)完全指南:从 REST API 透传到权限管控

Backstage Kubernetes 后端代理端点(Proxy Endpoint)完全指南:从 REST API 透传到权限管控

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

导读

Backstage 的 Kubernetes 插件默认提供了 Pod、Service、Deployment 等资源的展示能力,但当你需要基于 Kubernetes 数据构建更丰富的开发者门户体验时(例如读取并操作 Custom Resources、调用任意 Kubernetes REST API),就需要借助 Kubernetes 后端插件的Proxy 端点。本文将基于docs/features/kubernetes/proxy.md文档并结合仓库源码,系统讲解代理端点的工作原理、认证机制、如何通过权限框架禁用该端点,以及已知的局限性与规避思路。读完本文,你将掌握如何在前端插件中通过KubernetesBackendClient向目标集群发起任意 REST 请求,并能用 PermissionPolicy 精确管控该端点的访问。

一、Proxy 端点是什么:为插件提供任意 Kubernetes API 访问

Backstage 的 Kubernetes 插件(plugins/kubernetes)面向 Catalog 实体提供了开箱即用的资源浏览能力。但贡献者(Contributors)如果希望基于 Kubernetes 数据创建自定义的开发者门户体验——典型场景是与默认 Kubernetes 插件行为之外的 Custom Resources 进行交互——就可以利用 Kubernetes 后端插件的 Proxy 端点,向 Kubernetes REST API 发起任意请求。

简单来说,Proxy 端点充当了一个"中转站":前端插件把请求发给 Backstage 后端,后端根据请求头解析出目标集群,再把请求转发到真实的 Kubernetes API Server,并把响应原样返回给前端。这样做的核心价值在于:

  • 前端无需直接暴露或持有集群凭据,所有认证逻辑收敛在 Backstage 后端;
  • 插件开发者可以访问 Kubernetes 的完整 REST API 表面(包括自定义资源),而不局限于插件内置的kubernetes资源列表;
  • 后端可以在转发前统一执行权限校验(见第四节)。

最小可运行示例:获取命名空间列表

文档给出了一段使用KubernetesBackendClient获取命名空间的示例,这是使用 Proxy 端点最直接的入口:

import { useApi } from '@backstage/core-plugin-api'; import { kubernetesApiRef } from '@backstage/plugin-kubernetes'; const CLUSTER_NAME = ''; // use a known cluster name const kubernetesApi = useApi(kubernetesApiRef); await kubernetesApi.proxy(CLUSTER_NAME, '/api/v1/namespaces');

其中:

  • CLUSTER_NAME需要替换为你在集群配置中定义的实际集群name字段(关于集群命名见 configuration.md);
  • 第二个参数/api/v1/namespaces是 Kubernetes REST API 的路径,会被拼接到 Proxy 端点之后转发给目标集群。

前端客户端的底层实现

kubernetesApiRef对应的实现是KubernetesBackendClient,其proxy方法在 KubernetesBackendClient.ts 中定义。从源码可以看到它做了三件事:

  1. 根据集群名解析出该集群的authProvideroidcTokenProvider
  2. 通过getCredentials获取该认证提供方的凭据;
  3. 把请求发往${discoveryApi.getBaseUrl('kubernetes')}/proxy${options.path},并附上两个关键请求头:
return { ...options.init?.headers, [`Backstage-Kubernetes-Cluster`]: options.clusterName, ...(k8sToken && { [kubernetesAuthHeader]: k8sToken, }), };

注意这里的认证头并非固定名:getKubernetesAuthHeaderByAuthProvider会根据authProvider动态拼接,例如authProvidergoogle时生成Backstage-Kubernetes-Authorization-google,若还配置了oidcTokenProvider则进一步拼为Backstage-Kubernetes-Authorization-google-<provider>。这个细节说明:Proxy 端点在请求头层面就支持多认证提供方的区分

二、工作原理:从请求头到集群转发的完整链路

2.1 集群解析:Backstage-Kubernetes-Cluster

Proxy 会把请求中的Backstage-Kubernetes-Cluster请求头解释为目标集群的名称。这个名称会被逐一与所有已配置的 cluster locators 返回的集群进行比对——第一个name字段与请求头值匹配的集群即为转发目标。

在后端实现中,这个逻辑位于 KubernetesProxy.ts 的getClusterForRequest方法:

const clusterName = req.headers[HEADER_KUBERNETES_CLUSTER.toLowerCase()]; const clusters = await this.clusterSupplier.getClusters({ ... }); if (hasClusterNameHeader) { cluster = clusters.find(c => c.name === clusterName); } else if (clusters.length === 1) { cluster = clusters.at(0); } if (!cluster) { throw new NotFoundError(`Cluster '${clusterName}' not found`); }

从源码还可以看到两个值得注意的行为:

  • 当没有提供集群头时,如果 locator 恰好只返回一个集群,则默认使用该唯一集群(clusters.at(0));
  • 没有任何已配置集群时,直接抛出NotFoundErrorNo Clusters configured);
  • 请求头的常量定义在 KubernetesProxy.ts 中:HEADER_KUBERNETES_CLUSTER = 'Backstage-Kubernetes-Cluster'

2.2 请求转发:仅有的两处修改

集群确定后,请求会被转发到目标集群。整体上,代理对每个请求只做两处修改

  1. 剥离端点的基础 URL 前缀:即把/proxy前缀从请求路径中移除,剩下的路径(如/api/v1/namespaces)才是转发到 API Server 的路径。实现上,prepareProxyTarget使用正则把req.baseUrl替换为集群的url.pathname,见 KubernetesProxy.ts;
  2. 认证头改写:请求中的Backstage-Kubernetes-Authorization头会变成转发请求时使用的Authorization头(详见第三节)。

2.3 中间件与 WebSocket 支持

从实现细节看,转发基于http-proxy-middlewarecreateProxyMiddleware完成(KubernetesProxy.ts),并且有几个值得插件作者了解的工程细节:

  • 每个集群一个中间件实例:代理为每个远端集群创建并缓存一个中间件,因为secure(是否跳过 TLS 校验)无法在单个实例上按请求动态决定;条目会在 TTL 之后或集群详情变化时刷新;
  • 支持 WebSocketdispatchToProxy会检测Connection: upgradeUpgrade: websocket头,走middleware.upgrade路径,这意味着 Proxy 端点同样可用于kubectl式的 WebSocket 交互场景;
  • 中间件缓存可配置:在 KubernetesRouter.ts 中,代理会读取kubernetes.proxy.middlewareCache配置(ttl.millisecondsmaxSize)来控制缓存行为;
  • 审计事件:代理通过ProxyAuditSession记录每次代理请求的审计事件(包含集群名、方法、路径等),失败时会把错误序列化进响应体,并在开发环境下附带堆栈(NODE_ENV === 'development')。

三、认证机制:Bearer Token 与不支持的 mTLS

3.1 当前实现期望什么

文档明确指出:Proxy 没有任何 mTLS 支持,因此它不能用于连接使用 x509 Client Certs 认证策略的集群。当前的/proxy实现期望调用方通过Backstage-Kubernetes-Authorization头提供一个Bearer token,这个 token 在转发请求时会被用作Authorization头的值。

这一逻辑对应 KubernetesProxy.ts 中的处理:

const authHeader = req.headers[HEADER_KUBERNETES_AUTH.toLocaleLowerCase('en-US')]; if (typeof authHeader === 'string') { req.headers.authorization = authHeader; } else { // 通过 authStrategy 获取凭据 const credential = await this.authStrategy.getCredential(cluster, authObj); if (credential.type === 'bearer token') { req.headers.authorization = `Bearer ${credential.token}`; } else if (credential.type === 'x509 client certificate') { target.key = credential.key; target.cert = credential.cert; } }

有趣的是,虽然文档说明 x509 客户端证书不被代理支持,但源码中AuthenticationStrategy的凭据类型定义(types.ts)仍然包含x509 client certificate分支。从源码结构看,这是为AuthenticationStrategy接口预留的能力,而 Proxy 的对外契约依然是Bearer token——因此在文档语境下,连接 mTLS/x509 集群的请求不应走/proxy端点。

3.2 默认认证装饰:KubernetesAuthTranslator 的作用

文档进一步说明:Proxy 期望提供一个KubernetesAuthTranslator,用于默认给所有请求装饰认证信息。它的做法是:根据clusterDetails中定义的authProvider,向clusterDetails填充一个serviceAccountToken字段。

对应到源码:

  • AuthenticationStrategy接口(types.ts)定义了getCredentialvalidateClusterpresentAuthMetadata三个方法;
  • 具体的策略实现在plugins/kubernetes-backend/src/auth/目录下,例如ServiceAccountStrategy会读取clusterDetails.authMetadata.serviceAccountToken(ServiceAccountStrategy.ts);
  • 如果请求头中没有提供认证信息,prepareProxyTarget会调用authStrategy.getCredential(cluster, authObj)获取凭据并注入Authorization头。这也与文档"The proxy expects a KubernetesAuthTranslator to be provided that is used to decorate all requests with Auth by default"的表述一致。

换句话说,Proxy 的认证策略是"调用方显式提供 token 优先,否则回退到集群配置中的默认认证方式"。对应的测试用例在 KubernetesProxy.test.ts 中均有覆盖,例如"应在未提供backstage-kubernetes-auth字段时默认使用策略提供的 bearer token 作为授权头"、"应把Backstage-Kubernetes-Auth字段追加到请求的授权头"等(见 L932、L1034 附近)。

四、通过 PermissionPolicy 禁用 Proxy 端点

Kubernetes 插件可以借助权限框架禁用proxy端点的使用。这种集成允许管理员使用明确定义的 PermissionPolicy 来整体限制该端点的访问。关键点在于:即使请求携带了集群会授权的有效 ID token,proxy端点也可以返回 403 错误——这给了集成方信心:Backstage 不会在未授权的场景下替不受欢迎的一方访问 Kubernetes 集群。

4.1 前置条件

该功能假设你的 Backstage 实例已经启用了 权限框架。权限框架是 Backstage 提供的细粒度访问控制体系,PermissionPolicy在其中负责裁决每个权限请求。

4.2 权限点的定义

Proxy 端点对应的权限点在 permissions.ts 中定义:

export const kubernetesProxyPermission = createPermission({ name: 'kubernetes.proxy', attributes: {}, });

它与另外两个权限一起被导出:

  • kubernetes.resources.read/resources/services/:serviceId端点)
  • kubernetes.clusters.read/clusters端点)

4.3 示例策略:拒绝一切 proxy 请求

文档给出了一个可直接落地的示例策略,在handle中匹配kubernetes.proxy权限名并返回DENY

import { AuthorizeResult, PolicyDecision, } from '@backstage/plugin-permission-common'; import { PermissionPolicy, PolicyQuery, PolicyQueryUser, } from '@backstage/plugin-permission-node'; class KubernetesDenyAllProxyEndpointPolicy implements PermissionPolicy { async handle( request: PolicyQuery, user?: PolicyQueryUser, ): Promise<PolicyDecision> { if (request.permission.name === 'kubernetes.proxy') { return { result: AuthorizeResult.DENY, }; } return { result: AuthorizeResult.ALLOW }; } }

4.4 后端如何强制执行

从后端路由源码(KubernetesRouter.ts)可以看到,/proxy路由挂载时就把permissionApi传给了代理:

router.use('/proxy', proxy.createRequestHandler({ permissionApi }));

在 KubernetesProxy.ts 的authorizeAndDispatch中,代理会在解析集群、转发请求之前先执行权限校验:

const authorizeResponse = await permissionApi.authorize( [{ permission: kubernetesProxyPermission }], { credentials: await this.httpAuth.credentials(req) }, ); if (authorizeResponse[0].result === AuthorizeResult.DENY) { ... res.status(403).json({ error: serializeError(new NotAllowedError('Unauthorized')), }); return false; }

因此,即使请求附带了集群会授权的有效 ID token,只要权限策略拒绝,代理也会直接返回 403。文档明确给出了被拒绝时的响应体示例:

{ "error": { "name": "NotAllowedError" } }

这与源码中serializeError(new NotAllowedError('Unauthorized'))的序列化结果一致(NotAllowedError来自@backstage/errors)。

4.5 为什么先鉴权再转发很重要

从调用顺序看,权限校验发生在prepareProxyTarget(集群解析与凭据注入)之前。这意味着被拒绝的请求根本不会触发集群 locator 查询与认证凭据的获取,既避免了不必要的后端开销,也杜绝了"凭据先被解析、后被发现无权访问"的潜在信息泄露窗口。这也正是文档所说的"allowing integrators the confidence that Backstage is not accessing kubernetes clusters on behalf of undesired parties"。

五、其他已知限制

文档同时披露了 Proxy 端点的一个已知缺陷。该代理随 Backstage 1.9 发布,存在以下已知 bug:

  • 无法可靠地定位与其他已定位集群共享相同名称的集群(对应上游 issue #15901)。

换句话说,如果两个不同集群配置了相同的name字段,getClusterForRequestclusters.find(c => c.name === clusterName)只会命中第一个匹配项,代理可能把请求转发到错误的集群。规避建议:在生产环境中为每个集群配置全局唯一的name(参见 configuration.md 中clusters.*.name的说明),避免同名冲突。

六、实战小结与最佳实践

基于本文的文档说明与源码分析,使用 Proxy 端点时的最佳实践可以归纳为:

  1. 确认目标集群已配置:通过 cluster locator(configgkeeks等)注册集群,并保证每个集群的name全局唯一;
  2. 在前端使用kubernetesApiRef:优先复用KubernetesBackendClient.proxy(),它会自动处理集群解析与认证头拼接,无需手写请求头;
  3. 认证选型:Proxy 端点面向 Bearer token 场景,mTLS/x509 客户端证书不在支持范围内,这类集群应走其他认证通道;
  4. 权限管控:如需收紧访问,使用PermissionPolicykubernetes.proxy权限进行 DENY/ALLOW 裁决——即使集群侧会放行,Backstage 侧的 403 依然生效;
  5. 关注已知 bug:避免同名集群配置,规避 issue #15901 的误转发风险;
  6. 善用审计能力:代理每次转发都会产生审计事件(包含解析后的目标集群名),可结合 audit-events.md 对代理流量进行追踪与合规审计。

参考资源

  • 文档原文:docs/features/kubernetes/proxy.md
  • 后端代理实现:plugins/kubernetes-backend/src/service/KubernetesProxy.ts
  • 后端路由挂载:plugins/kubernetes-backend/src/service/KubernetesRouter.ts
  • 前端客户端实现:plugins/kubernetes-react/src/api/KubernetesBackendClient.ts
  • 权限点定义:plugins/kubernetes-common/src/permissions.ts
  • 认证策略接口:plugins/kubernetes-node/src/types/types.ts
  • 集群配置说明:docs/features/kubernetes/configuration.md
  • 相关测试:plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts、plugins/kubernetes-backend/src/service/KubernetesRouter.test.ts

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 10:34:10

告别tf树乱麻:用超图统一坐标框架,解决多传感器回环难题

如果你搞过多传感器机器人定位&#xff0c;大概率有过被 tf 树逼疯的瞬间。之前在做一个室内机器人项目&#xff0c;车上同时有轮式里程计、IMU 和激光雷达&#xff0c;走一圈回来想用闭环把轨迹校准一下&#xff0c;一广播新变换&#xff0c;整个 tf 树直接乱成一团。后来我把…

作者头像 李华
网站建设 2026/9/10 10:33:47

CANN/GE常量值匹配配置

EnableConstValueMatch 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Ten…

作者头像 李华
网站建设 2026/9/10 10:33:28

GE动态输入端口索引获取

GetDynamicInputIndexesByName 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTor…

作者头像 李华
网站建设 2026/9/10 10:31:38

WorkBuddy:面向学术认知过程的AI协作者

1. 这不是又一个“教授用AI写论文”的故事“一位教授与WorkBuddy的几个月&#xff0c;看看擦出了什么样的火花”——这个标题刚出现在我邮箱里时&#xff0c;我下意识点开又关掉三次。不是因为不感兴趣&#xff0c;而是太熟悉了&#xff1a;高校教师AI工具自动批改作业、生成PP…

作者头像 李华