Backstage v1.26.0 发布详解:安全默认的 Auth 架构落地、应用后端公开入口与后端系统关键变更
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
本篇技术指南围绕 Backstage v1.26.0 官方发布说明展开,深度剖析本次版本中落地并完成的 BEP-0003 Auth 架构演进(用户身份证明uip声明、零配置插件签名密钥、OBO 令牌与外部访问静态令牌)、app-backend实验性公开应用入口、新后端系统的模块加载与服务初始化变更、OpenAPI 工具链新增的 lint 与 fuzz 命令,以及事件系统、Catalog 错误事件、Kubernetes 代理等配套改动。读者读完可掌握 v1.26.0 的核心能力变化、升级路径与源码级实现原理。
一、版本概览与升级总览
Backstage v1.26.0 是继 v1.25.0 之后的一次重要功能版本,核心主题是"安全默认"(secure by default):BEP-0003(Auth 架构演进,见仓库内 beps/0003-auth-architecture-evolution/README.md)中关于后端安全的主要部分在本版本全部落地。此外,本次发布还涉及应用后端(app-backend)、新后端系统(New Backend System)、认证模块迁移、OpenAPI 工具链、事件系统与 Catalog 错误事件等多个方面。
与以往版本一样,官方强烈建议将你的 Backstage 项目保持在此最新版本之上。详细的升级指引可参考仓库文档 docs/getting-started/keeping-backstage-updated.md。本版本不包含任何安全修复(Security Fixes),属于纯粹的功能与架构演进版本。
值得注意的是,本次发布涉及若干BREAKING(破坏性)变更,主要集中在新后端系统相关包(如@backstage/backend-app-api@0.7.0)与 Kubernetes 后端插件,下文将逐一说明。
二、Auth 改进:BEP-0003 全面落地,后端安全默认化
v1.26.0 最核心的亮点是 BEP-0003(Auth 架构演进)的主要部分全部就位,从"后端需要自行加保护"转变为"后端默认受保护"。BEP 全文可在仓库 beps/0003-auth-architecture-evolution/README.md 查阅,其设计要点包括:引入新的AuthService与HttpAuthService接口、默认访问控制策略(Default Auth Policy)、受限用户令牌(Limited User Token)、以及基于 On-Behalf-Of(OBO)的服务间通信。v1.26.0 将这些设计从提案变为实现。
2.1 用户令牌新增uip身份证明声明
由auth后端签发的用户令牌(Backstage user token)现在包含一个新的uip(user identity proof,用户身份证明)声明。该声明是一段独立的身份证明,用于 Cookie、service on-behalf-of 令牌等"有限的用户识别"场景,无需传递真实用户令牌及其完整授权能力。
从源码实现看,uip声明由 plugins/auth-backend/src/identity/issueUserToken.ts 中的createUserIdentityClaim()生成:它仅以sub、iat、exp三个字段构造 payload,并用与用户令牌相同的签名密钥生成一段JWS 签名片段(而非完整的 JWT),从而大幅压缩嵌入主令牌的体积:
// plugins/auth-backend/src/identity/issueUserToken.ts(节选) const uip = await createUserIdentityClaim({ header: { typ: tokenTypes.limitedUser.typParam, alg: key.alg, kid: key.kid, }, payload: { sub, iat, exp }, key: signingKey, }); const claims: BackstageTokenPayload = { ...additionalClaims, iss: issuer, sub, ent, aud, iat, exp, uip, // 新增的用户身份证明声明 };同时,签发逻辑对令牌体积设置了MAX_TOKEN_LENGTH = 32768的硬性上限(约 500 个实体引用的余量),超过即抛出明确错误,防止因 ownership 声明或自定义声明过大导致令牌膨胀。这一机制也呼应了 BEP-0003 中"将用户令牌中的所有权信息分离出去"的设计目标——完整的所有权信息(ent声明)将逐步迁移到由auth-backend提供的/v1/userinfo端点(该端点在本版本已有bf4d71a提交的初始实现,可解析并返回用户令牌中的sub与ent声明)。
2.2 插件间认证:零配置公钥签名 + OBO 令牌
插件到插件(plugin-to-plugin)的认证在本版本被正式限定作用域(properly scoped),并基于一套零配置(zero-configuration)公钥签名方案:
- 每个插件现在可以生成自己的签名密钥用于令牌签发,生成的公钥存储在插件数据库中;
- 公钥通过新端点
/.backstage/auth/v1/jwks.json对外暴露(见@backstage/backend-app-api的 changelog 说明); AuthService新增getPluginRequestToken方法,可签发作用域受限的令牌,使目标插件能够识别请求来自哪个插件;- 借助前述
uip身份证明机制,新增on-behalf-of(OBO)令牌,用于在上游请求中安全携带用户身份。
OBO 令牌的核心实现位于 packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts 的getPluginRequestToken:
case 'user': { const { token } = internalForward; const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(token); return this.pluginTokenHandler.issueToken({ pluginId: this.pluginId, targetPluginId, onBehalfOf: { limitedUserToken: onBehalfOf.token, expiresAt: onBehalfOf.expiresAt, }, }); }可以看到,当一个用户请求需要转发给上游插件时,当前插件会先把用户的真实令牌转换为受限用户令牌(createLimitedUserToken),再将其封装进面向targetPluginId的插件令牌中。这样 OBO 令牌的受众(audience)被限定为目标插件,无法被滥用于其他后端插件;目标插件既能识别最近的调用方(subject),也能按原始调用者的身份应用权限。BEP 文档中的两张时序图(token-sequence-cookie.drawio.svg与token-sequence-obo.drawio.svg)分别描述了 Cookie 令牌流与 OBO 令牌流的完整交互。
基于同一身份证明机制,TechDocs 等需要暴露 Cookie 端点的插件现在会签发插件私有作用域的 Cookie,从而杜绝"同域共部署的其他插件被恶意复用 Cookie"的滥用场景。
2.3 外部调用方:backend.auth.externalAccess静态令牌
从外部调用方请求你的后端插件(service-to-service auth)在本版本变得更加简单:新增了一个配置段backend.auth.externalAccess,在传统的 legacy 服务令牌之外,支持配置静态令牌(static tokens)。该系统被设计为可扩展的——未来可以继续添加更多认证方法。
其底层实现位于 packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts,默认注册了三种令牌处理器(static、legacy、jwks),并支持通过externalTokenHandlersServiceRef(multiton service ref)注册自定义处理器:
const defaultHandlers: Record<string, ExternalTokenHandler<unknown>> = { static: staticTokenHandler, legacy: legacyTokenHandler, jwks: jwksTokenHandler, };配置解析逻辑支持accessRestrictions(访问限制)字段,用于将外部令牌的访问限定到特定插件、特定权限名或权限动作(create/read/update/delete),详见 packages/backend-defaults/src/entrypoints/auth/external/helpers.ts 中的readAccessRestrictionsFromConfig()。一个典型的externalAccess配置如下:
backend: auth: externalAccess: - type: static options: token: ${MY_STATIC_TOKEN} subject: 'external:my-service' # 该令牌代表的服务主体 accessRestrictions: - plugin: catalog # 仅允许访问 catalog 插件 permission: catalog-entity-read - plugin: scaffolder permissionAttribute: action: ['read', 'create']同时,旧的backend.auth.keys配置仍会被读取(用于兼容 legacy 服务令牌),但会输出弃用警告日志:The backend.auth.keys config has been replaced by backend.auth.externalAccess。另外,ServerTokenManager现在也会读取新的backend.auth.externalAccess设置(见@backstage/backend-common@0.21.7的变更00fca28)。
2.4 升级注意事项:多后端部署的更新顺序与数据库依赖
多后端部署更新顺序:如果你的 Backstage 部署拆分为 3 个或更多独立后端,必须先更新"出站调用最少"的核心后端。新的自动化插件服务认证虽然使用特性检测(feature detection)来确定认证方式,但只能处理一跳(single hop)。实践中这意味着:如果使用了 permission backend 插件,应先更新 permission backend 实例,再更新 catalog。
数据库依赖:插件服务认证系统使用插件数据库存储非对称密钥。因此,任何需要向其他插件发起请求的插件现在都必须拥有数据库(并且需要在 JWKS 端点上接受入站 HTTP 请求)。对大多数采用默认配置的部署而言无需任何操作(默认设置会按需自动创建逻辑数据库),但如果你使用的是自定义或锁定的数据库设置,则可能需要为插件创建额外的数据库。
2.5 新增 Auth 模块:Cloudflare Access、Azure Easy Auth、Bitbucket
更多认证提供方被迁移为独立的后端模块实现,从而支持新后端系统。本版本新增的模块包括:
- Cloudflare Access:拆分为独立模块
@backstage/plugin-auth-backend-module-cloudflare-access-provider(v0.1.0,PR #24287 之前的 c26218d 提交),原 Cloudflare Access 类型被标记为弃用; - Azure Easy Auth:新模块
@backstage/plugin-auth-backend-module-azure-easyauth-provider(v0.1.0)。注意破坏性变更:默认 provider ID 从easyAuth改为azureEasyAuth,切换模块后需要同步更新 app config 以及前端ProxiedSignInPage的providerprop; - Bitbucket:新模块
@backstage/plugin-auth-backend-module-bitbucket-provider(v0.1.0),将 Bitbucket auth provider 迁移到独立模块包。
这三个模块对应的源码目录均可在仓库 plugins 下找到:plugins/auth-backend-module-cloudflare-access-provider/、plugins/auth-backend-module-azure-easyauth-provider/、plugins/auth-backend-module-bitbucket-provider/。
三、App Backend:实验性公开应用入口(public entry point)
app-backend插件现在能够保护主应用包(main application bundle),只向未认证用户提供有限的公开包(public bundle)。启用方式:在你的 app 包中添加一个src/index-public-experimental.tsx入口点,该入口点仅在生产构建中使用。这仍然是一个实验性功能。
与该特性配套的变更包括:
@backstage/plugin-auth-react@0.1.0:移除了CookieAuthRefreshProvider与useCookieAuthRefresh的path选项(BREAKING);新增CookieAuthRedirect组件,用于在使用app-backend的独立公开入口时,将公开包重定向到受保护包;@backstage/core-app-api@1.12.4与@backstage/frontend-app-api@0.6.4:应用现在能感知自己是否由app-backend以"公开包 + 受保护包"分离模式提供;处于受保护模式时,应用会持续刷新会话 Cookie,并在用户登出时清除 Cookie;@backstage/plugin-app-backend:在启用公开入口并与新 auth 服务配合时,实现基于 Cookie 的认证,并在缓存存储中跟踪资源命名空间。
四、新后端系统(New Backend System)关键变更
4.1 模块仅在关联插件存在时加载(BREAKING)
@backstage/backend-app-api@0.7.0:模块(modules)不再被加载,除非其扩展的插件(plugin)存在。这是本版本新后端系统最重要的行为变更,直接影响依赖注入与启动行为。与之配套,@backstage/backend-test-utils@0.3.7的startTestBackend会在"提供了模块但没有父插件"时自动添加占位插件(placeholder plugins),保证测试环境的正常启动。
4.2createServiceFactory新增initialization选项
服务工厂新增初始化选项initialization,允许服务创建者覆盖服务的初始化时机(懒加载 lazy 或 饿加载 eager)。默认策略保持与当前行为一致:插件作用域(plugin scoped)服务默认懒加载,根作用域(root scoped)服务默认饿加载。该选项同时被加入到@backstage/backend-plugin-api@0.6.17的类型定义中。
4.3/api/:pluginId路径保留(BREAKING)
/api/:pluginId路径现在保留给插件流量专用,不再允许在 http router 服务中将其配置为其他用途。具体而言(见@backstage/backend-app-apichangelog 的10327fb变更):
httpRouterServiceFactory的getPath选项被弃用;- 更一般地,插件 API 路径被限定为
/api/:pluginId/形式; - 指向
/api/*但不匹配任何已注册插件的请求,不再由 index router 处理,而是返回 404。
4.4 全插件 LoggerService 迁移
本次发布在全仓库范围内完成了将所有插件从旧 Winston logger 迁移到LoggerService的"大扫除"变更(PR #24224,由 @drodil 贡献)。对大多数用户透明,但旧后端系统的部分用户会注意到差异,需要移除 winston 兼容包装器(winston compatibility wrapper)。受影响的包包括@backstage/backend-tasks@0.5.22、@backstage/plugin-kubernetes-backend@0.17.0(BREAKING)、@backstage/plugin-tech-insights-node@0.6.0(BREAKING)、@backstage/plugin-badges-backend@0.4.0、@backstage/plugin-adr-backend等。
五、OpenAPI 工具链:新 lint 规则与 fuzz 模糊测试
@backstage/repo-tools@0.8.0为 OpenAPI 工具链新增两项能力:
5.1allowReservedlint 规则
repo schema openapi lint命令新增一条 lint 规则,强制所有 URL 参数设置allowReserved: true。原因:默认的 URL 编码过于严格,例如不允许将+作为空格的编码。修复方式是为参数补充allowReserved: true:
/v1/todos: get: operationId: ListTodos # ... parameters: - name: entity in: query + allowReserved: true schema: type: string该规则的实现位于 packages/repo-tools/src/commands/repo/schema/openapi/lint.ts,错误信息为Query parameters must specify allowReserved (true or false)。
5.2 新增 fuzz 模糊测试命令
新增两个 fuzz 命令,用于对插件进行模糊测试(fuzzing),通过自动生成符合 schema 的输入帮助发现应用代码中的 bug:
# 对单个包进行 OpenAPI fuzz backstage-cli package schema openapi fuzz # 对仓库中声明了 fuzz 脚本的所有包进行 fuzz(仅测试相对指定 ref 有变更的包) backstage-cli repo schema openapi fuzz命令注册位于 packages/repo-tools/src/commands/index.ts(repo schema openapi fuzz与package schema openapi fuzz),repo级命令会筛选package.json中声明了fuzz脚本的包并执行yarn fuzz(见 packages/repo-tools/src/commands/repo/schema/openapi/fuzz.ts)。底层依赖 Schemathesis 测试库。
六、事件系统改进与新后端系统的接入
6.1 eventsServiceRef 取代 EventBroker / EventSubscriber
在新后端系统中,事件系统应改为通过依赖导出的eventsServiceRef访问,而非旧的EventBroker和EventSubscriber模式。因此,仍然使用旧后端系统的用户会注意到 GitHub catalog providers 的构造方式发生了一些变化(详见@backstage/plugin-catalog-backend-module-github@0.6.0的 BREAKING 变更):
GithubOrgEntityProvider.onEvent变为私有;GithubOrgEntityProvider.supportsEventTopics被移除;GithubMultiOrgEntityProvider.fromConfig的eventBroker选项被移除(supportsEventTopics同样移除);- 受影响用户请将
EventsService实例作为events选项传入这些 provider。
6.2 Catalog 错误事件(Catalog Error Events)
Catalog 中的实体处理错误(entity processing errors)过去被发送到本地 logger,这在生产环境中往往造成大量日志噪音且难以排查。本版本改为发送到事件系统(events system)。如果你希望保留原有的日志行为,可以订阅该 topic实现(PR #23022,由 @punkle 贡献)。
七、Kubernetes 代理破坏性变更
@backstage/plugin-kubernetes-backend@0.17.0中,KubernetesProxy现在要求向其构造函数传入DiscoveryService(如果你仍在使用旧后端系统)。这是本版本的一个明确 BREAKING 变更(变更号6c19c14)。该版本同时完成了 Winston logger 到LoggerService的替换(5dd8177,BREAKING),并修复了BackstageCredentials转发、代理 handler 缺失 header 处理、credentials为undefined时崩溃等问题。
八、其他值得关注的变更
除上述主题外,v1.26.0 还包含以下要点(详见完整 changelog docs/releases/v1.26.0-changelog.md):
配置加载器
@backstage/config-loader@1.8.0:默认环境变量替换函数现在会裁剪替换值两端的空白字符,避免环境变量误含空白引发的 bug;若依赖旧行为,可自行覆盖substitutionFunc:ConfigSources.default({ substitutionFunc: async name => process.env[name], });同时新增了对环境变量的参数替换支持。
集成层
@backstage/integration@1.10.0:新增 AWS CodeCommit URL Reader/Integration。Catalog 插件
@backstage/plugin-catalog@1.19.0:EntitySwitch路由函数新增isApiType();<AboutCard>新增"create something similar"按钮,若实体带backstage.io/source-template注解则链接到对应 scaffolder 模板。通知系统:
notifications-backendURL 查询参数由minimal_severity改为minimumSeverity;通知页面支持对多条选中的通知批量触发"Save"或"Mark as read";processor 函数更名为preProcess/postProcess,并新增processOptions处理能力。搜索
@backstage/plugin-search-backend-module-elasticsearch@1.4.0:使用新后端系统时,Elasticsearch provider仅在存在search.elasticsearch配置段时才被添加。CLI
@backstage/cli@0.26.3:新增versions:migrate命令,帮助将包迁移到新的@backstage-community命名空间;默认 linter 设置中加入 deprecation 插件(默认关闭)。
九、升级路径与后续行动清单
基于以上变更,v1.26.0 的升级行动清单可归纳为:
- 保持项目更新:参考 docs/getting-started/keeping-backstage-updated.md 中的升级指引,将依赖升级到 v1.26.0 对应版本。
- 多后端部署:按"出站调用最少优先"的顺序更新后端(先 permission backend,再 catalog);确认插件具备数据库且 JWKS 端点可达。
- 旧后端系统用户:移除 winston 兼容包装器;将 GitHub org provider 的
eventBroker选项替换为events;为KubernetesProxy构造函数补充DiscoveryService。 - 新后端系统用户:确认模块加载行为变更(模块仅在插件存在时加载);如需自定义服务初始化时机,使用
createServiceFactory的initialization选项。 - 认证配置:如需开放外部调用,配置
backend.auth.externalAccess(static/legacy/jwks + accessRestrictions);如需整体关闭内置保护,可配置backend.dangerouslyDisableServiceAuth: true(详见 BEP 文档)。 - OpenAPI 使用者:为所有 URL 参数补充
allowReserved: true以通过新 lint 规则,并可尝试新的 fuzz 命令进行 schema 驱动的模糊测试。
总而言之,v1.26.0 标志着 Backstage 认证体系从"可选加固"走向"安全默认"的转折点:用户身份证明、插件签名密钥、OBO 令牌与外部访问配置共同构成了更清晰、更可扩展的安全边界;而新后端系统的模块加载与服务初始化变更则进一步夯实了后端架构的稳定性。对于正在升级或规划多后端部署的团队,本文列出的破坏性变更与升级顺序将是落地过程中最值得关注的部分。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考