Backstage Scaffolder 的scaffolder.requireScmUserCredentials:强制用户凭证执行 SCM 操作的安全开关
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
在 Backstage 的软件模板(Software Templates)体系中,Scaffolder 插件默认会借助集成(Integration)配置中保存的服务级凭证(例如app-config.yaml里为 GitHub、GitLab 配置的 Token)来执行诸如publish:github、fetch:template这类 SCM 操作。这种做法在自动化场景下十分方便,却也意味着任何一个能触发模板任务的用户,都可能间接借用服务级凭证的权限。为了收紧这一权限边界,Backstage 引入了scaffolder.requireScmUserCredentials配置项,本文将以当前仓库(Backstage monorepo)中的实现为准,完整讲解这一配置的作用范围、底层实现、配置方法、例外情况与适用建议。
一句话读懂这个配置
在 config.d.ts 中,该配置的官方注释是:
Requires supported SCM actions to only operate with credentials explicitly provided by the signed-in user. Defaults to false.
即:开启后,受支持的 SCM 操作只能使用登录用户显式提供的凭证来执行;默认值为false(关闭)。它本质上是 Scaffolder 后端插件(@backstage/plugin-scaffolder-backend)的一项安全策略开关,通过限制"谁的身份被用来执行 SCM 操作"来降低越权风险。
配置方法
该配置属于后端配置,位于scaffolder节点下,类型为布尔值。在app-config.yaml(或后端环境对应的配置文件)中启用:
scaffolder: requireScmUserCredentials: true更完整的示例可以参考官方文档 configuration.md 中的 "Requiring SCM user credentials" 一节。
从配置解析的角度看,各个动作模块通过config.getOptionalBoolean('scaffolder.requireScmUserCredentials') ?? false读取该值。例如 GitHub 模块在 module.ts 中读取后,将其作为参数批量注入到所有 GitHub 相关动作工厂中:
const requireScmUserCredentials = config.getOptionalBoolean('scaffolder.requireScmUserCredentials') ?? false;getOptionalBoolean意味着只要不配置该键,就等价于false(关闭),行为与 Backstage 其他布尔配置项的约定一致。
作用范围:哪些操作会被约束
1. SCM 变更类(Mutation)动作
文档明确指出:开启后,受支持的GitHub、GitLab、Bitbucket Cloud、Bitbucket Server、Azure DevOps的变更类动作会拒绝"未携带用户 Token"的请求。所谓变更类动作,指的是会在远端仓库、Issue、CI 上产生写操作的 action,例如:
publish:github/github:repo:create/github:repo:push/github:pull-request/github:environment/github:webhook/github:deploy-key等 GitHub 动作;publish:gitlab/gitlab:merge-request/gitlab:repo:push/gitlab:issue:create/gitlab:project-access-token:create等 GitLab 动作;- Bitbucket Cloud / Bitbucket Server / Azure DevOps 对应的发布与变更动作。
这些动作在创建时都会接收requireScmUserCredentials选项。以 GitHub 模块为例,module.ts 将同一个布尔值注入给githubActionsDispatch、githubAutolinks、githubDeployKey、githubEnvironment、githubIssuesLabel、githubIssuesCreate、githubRepoCreate、githubRepoPush、githubWebhook等一整套动作工厂。
在动作内部,校验逻辑表现为"有用户 Token 就放行,没有则报错"。例如github:pull-request动作在 githubPullRequest.ts 中:
if (requireScmUserCredentials && !providedToken) { throw new InputError( `No user credentials provided for host ${host}, but scaffolder.requireScmUserCredentials is enabled`, ); }当用户提供的模板参数中没有显式给出 token(即token输入项)时,动作会直接抛出InputError,任务失败并显示明确原因。
2. 拉取类(Fetch)动作
文档进一步说明:内置的fetch:plain、fetch:plain:file、fetch:template、fetch:template:file四个 fetch 动作,在GitHub 与 GitLab的读取场景下同样强制要求用户 Token。这些动作的源码位于 fetch 目录,例如fetch:plain在 plain.ts 中调用了统一的校验函数:
assertScmUserCredentials({ integrations, requireScmUserCredentials, url: ctx.input.url, baseUrl: ctx.templateInfo?.baseUrl, token: ctx.input.token, });这四个 fetch 动作都支持token输入项(schema 中声明为 "An optional token to use for authentication when reading the resources"),因此模板作者可以让 fetch 动作使用用户凭证读取仓库内容。
底层实现:assertScmUserCredentials校验逻辑
fetch 类动作共用同一个校验函数 assertScmUserCredentials.ts,其核心逻辑是:
const USER_TOKEN_SUPPORTED_INTEGRATION_TYPES = new Set(['github', 'gitlab']); if (!requireScmUserCredentials || token) { return; // 未开启,或用户已提供 token,直接放行 } // 从 url 或 baseUrl 解析出主机 const integration = integrations.byUrl(sourceUrl); if (integration && USER_TOKEN_SUPPORTED_INTEGRATION_TYPES.has(integration.type)) { throw new InputError( `No user credentials provided for host ${sourceUrl.host}, but scaffolder.requireScmUserCredentials is enabled`, ); }几个值得注意的细节:
- 双重判断:只有当
requireScmUserCredentials为true且用户没有提供 token 时才可能报错;两者缺一即放行。 - 只针对 GitHub 与 GitLab 的 fetch 读取:
USER_TOKEN_SUPPORTED_INTEGRATION_TYPES只包含'github'和'gitlab'两种集成类型,这是由ScmIntegrations.byUrl()按 URL 解析出的集成类型决定的。 - URL 解析兜底:当
url本身解析失败时,会回退尝试baseUrl(即模板自身的地址),仍解析失败则由后续 fetch 逻辑报错,不会在此处误伤。 - 错误信息明确:报错信息中包含
host与配置项名称,便于模板作者定位是哪个 host、哪个开关导致失败。
对应的测试用例见 assertScmUserCredentials.test.ts,其中明确验证了:
- 对
github.example.com与gitlab.example.com的读取,未带 token 时抛出InputError,且错误信息包含 "No user credentials provided for host ... but scaffolder.requireScmUserCredentials is enabled"; - 提供
token: 'user-token'后则正常放行。
这些测试共同保证了"开关开启 → 无用户凭证 → 拒绝"这一行为是可验证、可回归的。
例外情况:哪些操作不受影响
按文档说明,以下两类不受该配置约束:
- 自定义动作(Custom Actions):只有官方内置的受支持动作才执行该校验;用户自建的动作若未接入该开关,则不受影响。
- 不接收用户 Token 输入的 SCM 读取器:某些 SCM 读取路径(如只走服务级集成的读取器)不接受
token输入,因此不在此开关的强制范围内。
此外还有一个特殊限制:GitLab 的publish:gitlab动作中的setUserAsOwner与ownerUsername输入项无法与该配置同时使用。因为这两个输入项本身需要 GitLab 集成的特权凭证(用于将用户设置为仓库所有者),这与"只使用用户凭证"的语义冲突。开启该配置后,若模板仍使用这些输入项,将无法正常工作,需要在模板中移除或改写相关逻辑。
与凭证提供器的关系:服务级凭证仍可用作兜底
需要澄清一个容易混淆的点:开启该配置并不会禁用集成配置中的服务级凭证。从实现上看,GitHub 模块的动作仍会使用DefaultGithubCredentialsProvider.fromIntegrations(integrations)(见 module.ts)构建凭证提供器,用于解析集成配置中的凭证。该校验只是在动作入口处增加了"用户是否显式提供 token"的前置门槛——如果模板通过token输入项传入了用户凭证,则后续执行仍可依赖凭证提供器做 URL → 凭证的解析与兜底。换句话说,该配置的作用是强制模板显式携带用户凭证,而不是彻底移除服务级凭证机制。
何时开启:安全场景与落地建议
该配置适合以下场景:
- 多租户/多人共用的 Backstage 实例:希望模板执行 SCM 操作时,权限与"谁在跑这个任务"强绑定,避免借用全局 Token 的权限放大风险;
- 审计敏感:需要确保每次 SCM 写操作都能追溯到具体用户的凭证;
- 模板发布前的安全基线:团队希望通过强制用户凭证,倒逼模板作者在模板参数中显式声明并传递
token输入。
落地时建议分三步走:
- 先在测试/预发环境的
app-config.yaml开启scaffolder.requireScmUserCredentials: true; - 运行一批覆盖主要模板的任务(含 fetch 与 publish 类动作),观察失败日志中的
No user credentials provided for host ...报错,逐一为模板补充token输入(如${{ parameters.token }}或由前端传入的登录态 Token); - 确认所有常用模板均能携带用户凭证执行后,再在生产环境开启。
小结
scaffolder.requireScmUserCredentials是 Scaffolder 在"便利"与"安全"之间提供的一个明确开关:开启后,GitHub、GitLab、Bitbucket Cloud、Bitbucket Server、Azure DevOps 的受支持变更动作以及 GitHub/GitLab 场景下的内置 fetch 动作,都要求用户显式提供凭证;未提供时任务以InputError失败并给出可定位的错误信息。其实现由各动作模块的requireScmUserCredentials选项与统一的assertScmUserCredentials校验函数共同支撑(见 ScaffolderPlugin.ts、assertScmUserCredentials.ts),配合模板中的token输入项即可在保留自动化能力的同时,把 SCM 操作的身份牢牢锁定在登录用户身上。
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考