ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

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

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 中定义。从源码可以看到它做了三件事根据集群名解析出该集群的authProvider与oidcTokenProvider通过getCredentials获取该认证提供方的凭据把请求发往${discoveryApi.getBaseUrl(kubernetes)}/proxy${options.path}并附上两个关键请求头return { ...options.init?.headers, [Backstage-Kubernetes-Cluster]: options.clusterName, ...(k8sToken { [kubernetesAuthHeader]: k8sToken, }), };注意这里的认证头并非固定名getKubernetesAuthHeaderByAuthProvider会根据authProvider动态拼接例如authProvider为google时生成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 请求转发仅有的两处修改集群确定后请求会被转发到目标集群。整体上代理对每个请求只做两处修改剥离端点的基础 URL 前缀即把/proxy前缀从请求路径中移除剩下的路径如/api/v1/namespaces才是转发到 API Server 的路径。实现上prepareProxyTarget使用正则把req.baseUrl替换为集群的url.pathname见 KubernetesProxy.ts认证头改写请求中的Backstage-Kubernetes-Authorization头会变成转发请求时使用的Authorization头详见第三节。2.3 中间件与 WebSocket 支持从实现细节看转发基于http-proxy-middleware的createProxyMiddleware完成KubernetesProxy.ts并且有几个值得插件作者了解的工程细节每个集群一个中间件实例代理为每个远端集群创建并缓存一个中间件因为secure是否跳过 TLS 校验无法在单个实例上按请求动态决定条目会在 TTL 之后或集群详情变化时刷新支持 WebSocketdispatchToProxy会检测Connection: upgrade与Upgrade: websocket头走middleware.upgrade路径这意味着 Proxy 端点同样可用于kubectl式的 WebSocket 交互场景中间件缓存可配置在 KubernetesRouter.ts 中代理会读取kubernetes.proxy.middlewareCache配置ttl.milliseconds与maxSize来控制缓存行为审计事件代理通过ProxyAuditSession记录每次代理请求的审计事件包含集群名、方法、路径等失败时会把错误序列化进响应体并在开发环境下附带堆栈NODE_ENV development。三、认证机制Bearer Token 与不支持的 mTLS3.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定义了getCredential、validateCluster、presentAuthMetadata三个方法具体的策略实现在plugins/kubernetes-backend/src/auth/目录下例如ServiceAccountStrategy会读取clusterDetails.authMetadata.serviceAccountTokenServiceAccountStrategy.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 tokenproxy端点也可以返回 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权限名并返回DENYimport { 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, ): PromisePolicyDecision { 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字段getClusterForRequest中clusters.find(c c.name clusterName)只会命中第一个匹配项代理可能把请求转发到错误的集群。规避建议在生产环境中为每个集群配置全局唯一的name参见 configuration.md 中clusters.*.name的说明避免同名冲突。六、实战小结与最佳实践基于本文的文档说明与源码分析使用 Proxy 端点时的最佳实践可以归纳为确认目标集群已配置通过 cluster locatorconfig、gke、eks等注册集群并保证每个集群的name全局唯一在前端使用kubernetesApiRef优先复用KubernetesBackendClient.proxy()它会自动处理集群解析与认证头拼接无需手写请求头认证选型Proxy 端点面向 Bearer token 场景mTLS/x509 客户端证书不在支持范围内这类集群应走其他认证通道权限管控如需收紧访问使用PermissionPolicy对kubernetes.proxy权限进行 DENY/ALLOW 裁决——即使集群侧会放行Backstage 侧的 403 依然生效关注已知 bug避免同名集群配置规避 issue #15901 的误转发风险善用审计能力代理每次转发都会产生审计事件包含解析后的目标集群名可结合 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),仅供参考
返回列表