一、问题背景
GitLab 版本:
| GitLab | 18.11.3 FOSS |
| Ruby | 3.3.10 |
| PostgreSQL | 17.8 |
| GitLab Shell | 14.49.0 |
部署方式:
- Omnibus GitLab
- 外层 Nginx 反向代理
- HTTPS 终止于 Nginx
初始目标:
禁止匿名用户访问
/explore
在后台关闭:
css
Admin → Settings → General → Visibility and access controls
时,点击 Save 出现:
vbscript
500 Internal Server Error
但:
- GitLab Web 正常
- Clone 正常
- Push 正常
- CI 基本正常
只有后台保存配置时 500。
二、现象与初步判断
浏览器 F12:
bash
POST /admin/application_settings/general 500
同时发现:
css
Mixed Content favicon warning
但后续确认:Mixed Content 不是根因。
三、日志分析
Rails 日志出现:
arduino
OpenSSL::Cipher::CipherError
以及:
bash
lib/authn/token_field/encryption_helper.rb
token_authenticatable
ensure_token
decrypt_token
说明:GitLab 在读取某个 encrypted token 时解密失败。
四、排查过程
1. 初步怀疑 Project visibility
测试:
less
p = Project.find_by(name: "gaotong")
p.save!
结果:
ruby
=> true
说明:
- Project Model 正常
- 普通 save 正常
2. 批量扫描 Project
执行:
ruby
Project.find_each do |p|
begin
p.save!
rescue => e
puts p.id
puts e
end
end
发现:
大量项目出现:
arduino
OpenSSL::Cipher::CipherError
初步确认:
GitLab 存在大量历史损坏 token。
3. 使用 secrets doctor
执行:
gitlab-rake gitlab:doctor:secrets
结果:
css
Total: 185 row(s) affected
包括:
| DeployToken | 148 |
| RemoteMirror | 19 |
| ProjectImportData | 10 |
| User | 3 |
| Ci::Build | 3 |
| ApplicationSetting | 1 |
说明:实例存在大规模 encrypted state 损坏。
五、核心问题定位
最终定位:
ApplicationSetting.current.error_tracking_access_token
触发:
arduino
OpenSSL::Cipher::CipherError
说明:后台 Settings 页面在保存时会读取 error_tracking_access_token。而该 token 已无法解密。
六、数据库验证
PostgreSQL 中确认字段:
sql
SELECT column_name
FROM information_schema.columns
WHERE table_name='application_settings'
AND column_name LIKE '%error_tracking%';
结果:
error_tracking_access_token_encrypted
执行:
ini
UPDATE application_settings
SET error_tracking_access_token_encrypted = NULL;
验证:
sql
SELECT
error_tracking_access_token_encrypted IS NULL
FROM application_settings;
结果:
t
说明:数据库字段已经成功清空。
七、异常现象
即使数据库字段已 NULL:
s.error_tracking_access_token
仍继续触发:
decrypt_token
ensure_token
token_set?
并报:
arduino
OpenSSL::Cipher::CipherError
说明:GitLab 18.11 token_authenticatable 内部状态已异常。
疑似:
- token framework bug
- cached token state
- 历史 encrypted metadata 残留
- secrets 与 token state 不一致
八、临时验证
通过 monkey patch:
ruby
ApplicationSetting.class_eval do
define_method(:error_tracking_access_token) do
nil
end
end
成功绕过 getter。
说明:500 的核心触发点就是该 token getter。
九、最终工程决策
由于:
- GitLab 主功能正常
- 问题深入 token framework 内核
- 修复成本远高于收益
- 真实目标只是"禁止匿名访问"
最终决定:在 Nginx 层直接 deny。
配置:
bash
location ~ ^/(explore|public|users/sign_up) {
return 302 /users/sign_in;
}
避免继续深入 GitLab 内部 token 状态修复。
十、结论
本次问题本质属于:
GitLab 历史 encrypted state / secrets 不一致导致的 token framework 异常。
特点:
- 系统大部分功能正常
- 后台部分设置页 500
- 仅在触发 token getter 时异常
- 普通运维层难以彻底修复
十一、经验总结
1. GitLab 后台 500 首先检查:
gitlab-rake gitlab:doctor:secrets
2. GitLab 17/18 后:
token_authenticatable
是高风险区域。
3. secrets.json 不一致会导致:
- token 解密失败
- 后台 save 500
- 部分 API 异常
但:系统表面仍可能正常运行。
4. 对于"匿名访问控制"类需求:
Nginx / WAF / Reverse Proxy 收口通常比深入修 GitLab 更稳。