
很多团队的代码、CI、制品全在 GitLab 里,身份早就收口到 GitLab 了。可对象存储控制台还得单独开账号:新人入职要记两套密码,离职漏删存储侧的本地账号就是个敞口。与其再养一套身份,不如让存储直接认 GitLab 发下来的身份:GitLab 负责"你是谁",存储侧的策略负责"你能干什么"。
解决方案是 OpenID Connect(OIDC)单点登录:对象存储的官方文档明确把 GitLab 列为支持的提供商。接上之后,控制台登录页会出现一个 GitLab 按钮,点一下走 GitLab 的授权,回来就带着临时凭证进控制台。
OIDC 授权码流程怎么走
存储这边走的是带 PKCE(S256)的 OIDC 授权码流程,不依赖任何第三方的授权服务。官方文档把流程拆成五步:
- 浏览器在控制台点 GitLab 登录,RustFS 发出带 PKCE S256 质询的授权码请求。
- GitLab 验证你的身份,带着
code和state把浏览器重定向回 RustFS 的回调地址。 - RustFS 拿授权码去 GitLab 的令牌端点换 token。
- RustFS 校验 ID Token 的签名、签发者、受众、有效期和 nonce。
- RustFS 把配置的本地 IAM 策略挂给这次登录,给控制台会话签发临时凭证。
身份归 GitLab,授权归存储侧的 IAM 策略。GitLab 只回答"这人是不是本人",具体能列桶还是能删对象,由 RustFS 侧挂的 policy 决定。
回调地址格式固定为 https://<RustFS 公网源>/rustfs/admin/v3/oidc/callback/<provider-id>,本篇用默认的 provider id default。另外,回调与登录端点都要求 HTTPS:官方文档把"公共 HTTPS 控制台源"列为前置条件,GitLab 页的示例截图用了 HTTP 回调,旁边专门标注了"仅测试环境"。生产部署直接按 HTTP 配,授权流程会在此卡住。

GitLab 侧:建一个 OIDC 应用
以管理员登录 GitLab,打开 Admin Area → Applications → New application:
- Name:
RustFS OIDC - Redirect URI:
https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default - 勾选 Confidential 和 Trusted(后者让这个实例级应用跳过用户授权确认页)
- Scopes:选
openid、profile、email
保存后拿到 Application ID 和 Secret,这两个值接下来填进 RustFS 的环境变量。Redirect URI 必须和 RustFS 侧配的回调地址一字不差,否则授权会被拒。还有一点值得知道:Trusted 这个复选框只在 Admin Area 的实例级应用里出现,如果因为权限原因改在 group 下建应用,是看不到它的,用户首次登录时会多一个授权确认页,这属于正常现象,不是配置错误。
GitLab 侧配置完可以先自检一把,确认发现文档里该有的都有:
bash
curl -fsS "https://gitlab.example.com/.well-known/openid-configuration" | jq '{
issuer, authorization_endpoint, token_endpoint,
scopes_supported, code_challenge_methods_supported
}'
issuer 应该是 GitLab 的地址,code_challenge_methods_supported 里要有 S256,没有它 PKCE 流程走不通。
RustFS 侧:一组 OPENID 环境变量
RustFS 的 OIDC 完全由 RUSTFS_IDENTITY_OPENID_* 环境变量驱动,另外还要配一个公共浏览器源。把上一步拿到的 Application ID / Secret 填进去,重启生效:
bash
RUSTFS_BROWSER_REDIRECT_URL="https://rustfs.example.com"
RUSTFS_IDENTITY_OPENID_ENABLE=on
RUSTFS_IDENTITY_OPENID_CONFIG_URL=https://gitlab.example.com
RUSTFS_IDENTITY_OPENID_CLIENT_ID=<gitlab-application-id>
RUSTFS_IDENTITY_OPENID_CLIENT_SECRET=<gitlab-application-secret>
RUSTFS_IDENTITY_OPENID_SCOPES=openid,profile,email
RUSTFS_IDENTITY_OPENID_REDIRECT_URI=https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default
RUSTFS_IDENTITY_OPENID_DISPLAY_NAME=GitLab
RUSTFS_IDENTITY_OPENID_EMAIL_CLAIM=email
RUSTFS_IDENTITY_OPENID_USERNAME_CLAIM=preferred_username
RUSTFS_IDENTITY_OPENID_ROLE_POLICY=consoleAdmin
重启后可以先验证提供商是否就绪,再走登录:
bash
curl -fsS "https://rustfs.example.com/rustfs/admin/v3/oidc/providers" | jq
一个关键的安全点,也是一个要想清楚的预期差:ROLE_POLICY 决定登录后能做什么,而且会给经该提供商登录的所有用户分配同一个策略 。官方文档这句话意味着,这个变量里做不了按 GitLab 用户或组的区分,A 组的人和 B 组的人登录进来拿到的权限一模一样。想按组区分权限,官方 OIDC 概述给的路子是声明映射:身份提供商带一个扁平的组或角色声明,其值对应 RustFS 侧已有的策略名,而不是在 ROLE_POLICY 里配一张映射表。这里有个现实约束要先认清:GitLab 官方文档的 OIDC 声明表里,确实输出了 groups、groups_direct,以及 owner / maintainer / developer 三种角色对应的那几组 group claims,但表里同时标注了它们不进 ID Token,只在 /oauth/userinfo 端点返回 。也就是说,走声明映射之前得先确认这套部署里的声明是从 ID Token 取还是从 userinfo 取,不能假定 GitLab 给的声明一定够用;要自定义索赔名,还得在 GitLab 侧额外配置自定义声明,不是 OAuth 应用里勾几下就有。GitLab 侧没有现成的扁平声明可用时,就退而求其次:为这类登录专门建一个只含所需权限的策略(比如只读加特定桶的读写),示例的 consoleAdmin 是控制台完整管理员,包含桶管理和 IAM 用户管理在内的全部能力,OIDC 登录入口不该用它。

部署细节里最容易低估的是官方文档里这句轻描淡写的要求:用了反向代理或负载均衡时,要保留回调查询字符串,并把授权请求和回调请求路由到同一个 RustFS 节点,因为进行中的 OIDC state 存在该节点本地。单节点部署对这句话无感;MNMD 多节点集群里,负载均衡把回调打到另一个节点,那次登录就会失败,而且请求是按负载分配的,表现就是时好时坏的偶现登录失败,排查方向很容易跑偏到证书、时钟这类不相关的地方。解决办法是在负载均衡上做会话保持(sticky session),或用其他方式保证这两类请求始终落在同一节点。会话粘性不用全局开:登录只发生在浏览器这几步,S3 业务 API 流量跟它没关系,负载均衡只对 /rustfs/admin/v3/oidc/* 这个前缀开启粘性,其余接口照常轮询,登录成功率保住了,业务流量也不会被绑到少数节点上。伴生的还有一个反代通用事项:TLS 终止在代理上时,后端拿到的请求是 HTTP,记得按反代惯例转发 X-Forwarded-Proto 这类转发头,这是所有挂在反向代理后面的 Web 服务的通配要求。最后,GitLab 那边如果报重定向 URI 不匹配,先核对 GitLab 应用里注册的 URI 与 RUSTFS_IDENTITY_OPENID_REDIRECT_URI 是否逐字符一致。
顺手把 GitLab 的存储也迁过来
身份打通之后,存储后端也可以一并收口。GitLab 的 artifacts、LFS、uploads、packages、registry 等都能指向 S3 兼容端点,RustFS 100% 兼容 S3,直接改 gitlab.rb 即可:
ruby
gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['connection'] = {
'provider' => 'AWS',
'endpoint' => 'https://rustfs.example.com',
'region' => 'us-east-1',
'path_style' => true,
'aws_access_key_id' => '<rustfs-ak>',
'aws_secret_access_key' => '<rustfs-sk>'
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gitlab-lfs'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gitlab-uploads'
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages'
path_style 这行别漏:GitLab 的 S3 连接默认走虚拟主机寻址(bucket.host/object),自建 S3 兼容存储一般没有配套的通配域名解析,缺了它上传阶段就会报错。GitLab 官方文档对 S3 兼容服务的建议就是设为 true,让请求改用 host/bucket/object 的路径寻址。
每个 bucket 要先用 rc mb rustfs/gitlab-artifacts 之类提前建好,再让 GitLab 写入。另外要清楚一点:开启对象存储后,旧的本地文件不会自动挪过去。官方按 artifacts、LFS、uploads、packages 等对象类型分别给了迁移指南,需要逐类执行对应的 gitlab-rake 迁移任务;迁移全部确认完成之前,本地目录一个都别删。
把 GitLab 接成 OIDC IdP,等于让对象存储复用团队已有的身份体系:少一套账号要管,再把 GitLab 的制品存储也指过来,存储和身份两头都收口到自托管平面。有一类收尾动作容易省略:人员离职时,只停在 GitLab 侧停用账号是不够的。控制台会话用的是登录时签发的临时凭证,在过期之前依然有效,GitLab 侧的停用不会立刻掐断它,得把"等会话自然过期"也算进离职流程;这段窗口期涉及敏感桶的话,再把存储侧的关联访问密钥一并处理。至于这个窗口具体有多长,别指望有现成答案:官方文档里没有列出控制台 OIDC 会话的有效期时长,也没有对应的配置项,只能按你部署的版本去核对控制台文档,心里才有个量化的数。生产上线前还有三件小事值得做:把 ROLE_POLICY 换成最小权限自定义策略、给控制台配受信 TLS 证书让 OIDC 回调走 HTTPS、固定镜像版本标签,升级前先备份再切。
RustFS 已于 2026 年 9 月 16 日发布 1.0.0 正式版,本文涉及的 OIDC 配置均出自官方文档,代码在 GitHub 的 rustfs/rustfs 仓库,遇到配置对不上的问题可以去 issue 区翻一翻。