:基于 backend.auth.externalAccess 的 REST API 外部访问控制指南)
Backstage 外部服务认证BEP-0007基于 backend.auth.externalAccess 的 REST API 外部访问控制指南【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage导读本文围绕 Backstage 官方 BEPBackstage Enhancement Proposal0007Authentication of External Services见 beps/0007-auth-external-services/README.md系统讲解如何让外部服务安全地访问 Backstage 后端插件暴露的 REST API。你将掌握backend.auth.externalAccess配置区的完整语法、static/legacy/jwks三种外部访问类型的用法与差异、访问限制access restrictions的精细控制方式以及这些机制在backstage/backend-defaults中的底层实现原理最终能够为 CI/CD 流水线、外部异步服务、本地开发脚本等场景配置一套安全可控的 API 调用凭据。背景为什么需要外部服务认证在 Backstage 的新后端系统中插件之间的通信通过coreServices.auth与coreServices.httpAuth两个核心认证服务完成插件默认会生成自签名令牌并自动验证无需任何配置。然而这套插件间认证机制仅限 Backstage 生态内部使用——外部调用者无法参与其中导致以下问题外部异步服务如需要向用户发送通知的后台任务无法方便地调用 Backstage 插件 API外部集成系统需要与软件目录catalog等插件交互时缺乏清晰的认证路径本地开发阶段希望用临时令牌直接curl调用 API却没有轻量方案。在旧的 service-to-service-auth--old.md 教程中虽然提供了共享密钥签名令牌的方案但教程对如何正确构造调用缺乏清晰说明使用体验笨重。BEP-0007 正是在新认证服务coreServices.auth/coreServices.httpAuth语境下对旧方案的演进与替代——其前置基础是 BEP-0003: Auth Architecture Evolution。目标Goals让异构外部服务以最低复杂度访问 Backstage REST API迁移到新认证方案完全可选旧的共享密钥签名令牌继续可用通过配置指定外部实体可以访问哪些 API 端点通过限制 API 上下文、为不同外部方授予独立访问权限来降低安全风险——即使某个外部服务被攻破也只能访问受限的 API 子集提供更清晰、更新的示例让方案易于理解和使用。非目标Non-Goals不替换现有的 Backstage 服务到服务认证或令牌机制不提供让插件绕过现有服务到服务认证的出口——即使技术上可行也被视为反模式。核心方案backend.auth.externalAccess 配置区BEP-0007 引入了一个新的应用配置区段backend.auth.externalAccess作为声明外部服务访问方式的推荐途径。同时旧的backend.auth.keys配置区段仍然受支持但被视为**遗留legacy**形式——新认证代码读取它仅出于向后兼容目的使用它会触发一条日志警告提示迁移到新格式。关键设计externalAccess是一个数组每个元素带有一个type字段以支持未来扩展。使用数组意味着配置系统不会在不同配置文件之间对访问方法做合并merge从而避免因意外合并而造成的安全漏洞。BEP 文档给出的最小配置示例backend: auth: externalAccess: - type: static options: token: ${SERVICE_API_TOKEN} scope: plugins: catalog其中type任意字符串框架内置处理若干类型初始为固定集合未来可能做成可扩展options通用对象具体字段因类型而异上例中static类型只有token从环境变量读取scope可选控制该访问方法可执行的操作范围超出范围的调用会被 403 拒绝作用域可包含插件 ID 和/或权限详细说明见下文作用域与访问限制一节。需要说明的是BEP 提案阶段将限制字段命名为scope而当前仓库实现中该字段已演进为accessRestrictions本文后续章节会按实现现状详细展开。三种外部访问类型详解BEP-0007 明确覆盖legacy与static两种类型而jwks类型在 BEP 中仅作为未来方向的示意在当前的仓库实现中已经落地见 packages/backend-defaults/config.d.ts 与 jwks.ts。当前实现内置的三种类型如下。1. static静态令牌API Keystatic类型允许你指定任意静态字符串作为 API Key调用方将其原样放入请求头Authorization: Bearer token配置示例backend: auth: externalAccess: - type: static options: token: ${SERVICE_API_TOKEN} subject: cicd-system-completion-events # accessRestrictions: ...实现要点来自 static.tstoken可以是任意不含空白字符的字符串但出于安全考虑应足够长、难以暴力猜测initialize阶段会强制校验令牌必须匹配^\S$非空白字符且长度至少为 8 个字符否则直接抛出配置错误subject主题同样必须是非空白字符串用于标识每个调用方并成为接收方插件拿到的 credentials 对象的一部分推荐在命令行生成令牌node -p require(crypto).randomBytes(24).toString(base64)由于令牌可以是任意字符串你还可以为其添加辨识前缀例如freben-local-dev-方便调试追踪。2. legacy旧共享密钥签名令牌legacy类型与旧的共享密钥签名方法完全对应任何在此输入的密钥都会与backend.auth.keys中指定的密钥合并使用。配置示例backend: auth: externalAccess: - type: legacy options: secret: ${EXTERNAL_ACCESS_SIGNATURE_SECRET} subject: my-external-service # accessRestrictions: ...实现要点来自 legacy.tssecret是 base64 编码的随机字节同时用于签名与验证对称密钥必须足够长以防暴力猜测配置校验要求其为合法 base64 字符串从源码可推断其验证逻辑调用方需用 HS256 算法、以 base64url 解码后的密钥签署 JWTJWT 负载要求sub为backstage-server、不带aud声明令牌通过Authorization: Bearer jwt传递验证流程会先做鸭子类型预检检查alg是否为 HS256、sub/aud是否符合预期再执行jwtVerify只有签名验证失败ERR_JWS_SIGNATURE_VERIFICATION_FAILED才返回未匹配其他错误会继续抛出当通过旧的backend.auth.keys配置加载时subject 会被固定为external:backstage-plugin。3. jwks基于 JWKS 的 JWT 验证jwks类型允许通过配置的 JSON Web Key SetJWKS端点验证外部调用者的 JWT 令牌适合使用第三方身份提供方如 Auth0签发的令牌做认证的外部调用方。BEP-0007 中将其列为未来方向示例当前仓库已实现backend: auth: externalAccess: - type: jwks options: url: https://other-service.acme.org/.well-known/jwks.json issuer: https://example.com algorithm: RS256 audience: example, other-example subjectPrefix: custom-prefix # accessRestrictions: ...各选项含义见 packages/backend-defaults/config.d.tsurl必填JWKS 端点完整 URL必须指向一个无需认证即可返回 JWKS 的端点algorithm可选用于验证 JWT 的算法可多个传入的 JWT 必须使用其中之一签名issuer可选JWT 的签发者传入的 JWT 的iss声明必须匹配其中之一audience可选JWT 的目标受众传入的 JWT 的aud声明必须匹配其中之一或完全没有 audiencesubjectPrefix可选主体前缀。所有验证通过后的 subject 都会带external:前缀若配置了subjectPrefix则拼接为external:subjectPrefix:sub形式例如external:custom-prefix:sub。BEP 文档中还以示意形式给出了未来可能出现的更多类型不属于本 BEP 范围仅用于说明扩展方向backend: auth: externalAccess: - type: certificate options: publicCert: $file: ./service-cert.pem作用域与访问限制精细控制外部调用权限BEP 提案中的 scope 概念BEP-0007 提出使用可选的scope字段控制访问方法的操作范围不指定任何 scope 时该访问方法拥有无限作用域可执行所有类型的操作一旦指定超出范围的请求将返回403拒绝。提案阶段支持三种限制方式scope下可给字符串或字符串列表任一规则匹配即允许按目标插件 ID 限制scope: plugin: catalog按请求的权限类型限制scope: permission: catalog.entity.read按权限属性限制scope: permissionAttributes: { action: read }需要特别提醒如果设置了插件规则那么再为该插件添加权限规则将不会生效——因为插件规则已经匹配直接放行。实现现状accessRestrictions在最终实现中BEP 的scope概念被落地为accessRestrictions字段数组形式见 docs/auth/service-to-service-auth.md 与 helpers.ts。每个externalAccess条目可携带可选的accessRestrictions数组中的每项包含plugin必填插件 ID 字符串例如catalog。允许访问该插件可用下面的字段进一步细化permission可选权限名称集合逗号/空格分隔的字符串或字符串数组。给定后该访问方法在对应插件中仅能执行这些具名权限permissionAttribute可选权限属性键值对象每个值同样是集合。常用于限制action属性取值限定为create、read、update、deletereadAccessRestrictionsFromConfig会校验非法值见 helpers.ts。注意permission与permissionAttribute仅对启用了权限系统检查的端点生效未受权限系统保护的端点不受这些设置影响。完整示例backend: auth: externalAccess: - type: static options: token: ${CICD_TOKEN} subject: cicd-system-completion-events accessRestrictions: - plugin: events - type: static options: token: ${ADMIN_CURL_TOKEN} subject: admin-curl-access上例中使用CICD_TOKEN的调用者只能访问events后端插件访问其他插件会被拒绝而ADMIN_CURL_TOKEN未加限制拥有全部插件、全部功能的无限制访问权——官方文档建议尽可能显式声明访问限制以降低风险。helpers.ts中的解析逻辑还包含若干防御性校验accessRestrictions中只允许plugin、permission、permissionAttribute三个键同一个插件 ID 不允许声明两次permissionAttribute下只允许action键。任何违规都会在启动阶段抛出配置错误而非运行期才暴露。源码级实现原理令牌验证入口ExternalAuthTokenHandler外部令牌的验证完全落在coreServices.auth服务实现中核心是 ExternalAuthTokenHandler.ts配置读取ExternalAuthTokenHandler.create通过config.getOptionalConfigArray(backend.auth.externalAccess)读取新配置用config.getOptionalConfigArray(backend.auth.keys)读取旧配置类型注册默认处理器表defaultHandlers内置static、legacy、jwks三种defaultHandlers见同文件 ExternalAuthTokenHandler.ts未知的type会在启动时直接抛错并列出合法取值旧配置兼容一旦检测到backend.auth.keys存在就会输出DEPRECATION WARNING日志提示该配置已被backend.auth.externalAccess取代并将旧密钥按legacy处理器逐一并入上下文验证流程verifyToken(token)依次尝试所有上下文context每个上下文由一个type处理器与其对应的allAccessRestrictions组成首个成功返回结果的处理器即命中若存在访问限制且当前插件 ID 不在允许列表中则抛出NotAllowedError403 语义This tokens access is restricted to plugin(s) ...。可扩展的处理器接口处理器遵循统一接口见 types.tsexport interface ExternalTokenHandlerTContext { type: string; initialize(ctx: { options: Config }): TContext; verifyToken( token: string, ctx: TContext, ): Promise{ subject: string } | undefined; }同时框架提供了createExternalTokenHandler辅助函数见 helpers.ts以及externalTokenHandlersServiceRef服务引用multiton 类型允许开发者注册自定义外部令牌处理器——这正是 BEP 中未来可能把访问类型做成可扩展的方向通过在 ExternalAuthTokenHandler.ts 中声明的core.auth.externalTokenHandlers服务引用注入自定义 handler即可支持新的type各 handler 的type必须唯一重复会抛错。访问限制如何生效配置中的accessRestrictions会被解析为AccessRestrictionsMapMappluginId, BackstagePrincipalAccessRestrictions验证通过后若存在限制映射会按当前请求目标插件 ID 取出对应的限制条件随验证结果一并返回服务主体service principal类型上会带有可选的访问限制字段自配置携带而来ServerPermissionClient据此与允许的操作列表比对同时 auth 服务可基于插件 ID 规则做早期拒绝。整体而言外部令牌验证的职责全部收敛在coreServices.auth的authenticate方法内部无需新增 API返回的是带 service principal 的常规 credentials——接收方插件无需任何改动即可正常处理外部调用者的身份。实战外部调用方如何使用引入新的外部调用者及其专属密钥需要更新 app-config 文件并重启后端——该机制面向选定服务的集成因此暂不提供运行期动态添加调用者的能力。配置完成后外部调用方通过Authorization: Bearer请求头发送令牌即可# 使用 static 令牌 curl -H Authorization: Bearer ${SERVICE_API_TOKEN} \ https://backstage.example.com/api/catalog/entities # 使用 legacy 共享密钥签名的 JWT curl -H Authorization: Bearer eyJhbGciOiJIUzI... \ https://backstage.example.com/api/events典型使用场景包括需要向用户发送通知的外部异步服务配合 notifications 插件与软件目录catalog交互的外部集成服务本地开发中临时可curl的令牌——用static类型配置一个带freben-local-dev-前缀的短期令牌即可。发布计划与演进legacy与static类型的初始试点实现及对应配置项已合并进当前仓库访问限制scope/accessRestrictions的校验能力仍在持续完善但由于默认作用域为全部后续增量添加限制不会造成破坏性变更新增更多访问类型如jwks已在实现中落地未来可能需要框架层面的服务扩展点service extension points目前尚未作为优先级。备选方案对比BEP-0007 在设计过程中评估了以下备选方案各自的取舍如下方案思路权衡数据库中动态持久化共享密钥密钥存库增删无需改app-config.yaml、无需重启应用灵活性强但复杂度高且未必需要这种弹性维持现状调用方自行组装适用于现有实现的 JWT外部调用方类型与运行环境差异大组装难度高按插件逐个做访问控制保留各插件独立的访问控制参考 PR #23441用例高度重复建立通用机制是更优选择共享令牌申请器shared token requester引入统一令牌申请服务参考 PR #23465可简化令牌管理与提升可访问性但需深入考虑其对现有系统的集成影响总结BEP-0007 为 Backstage 的外部服务认证建立了一套配置驱动、默认安全、可精细限权的机制以backend.auth.externalAccess数组取代旧的backend.auth.keys通过type区分static/legacy/jwks三种且未来可扩展访问方式配合accessRestrictions实现按插件、按权限、按权限属性的精细化访问控制。其实现完全内聚于coreServices.auth服务见 packages/backend-defaults/src/entrypoints/auth对接收方插件透明无侵入。在实际部署中官方建议即便配置了外部访问也应尽量将 Backstage 实例屏蔽在公网之外仅在确有需要时开放访问并将外部令牌的访问限制声明到最小必要范围。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考