故障排查报告:GitLab 18.11.3 后台 Settings 500

36 阅读3分钟

一、问题背景

GitLab 版本:

GitLab18.11.3 FOSS
Ruby3.3.10
PostgreSQL17.8
GitLab Shell14.49.0

部署方式:

  • Omnibus GitLab
  • 外层 Nginx 反向代理
  • HTTPS 终止于 Nginx

初始目标:

禁止匿名用户访问 /explore

在后台关闭:

Admin → Settings → General → Visibility and access controls

时,点击 Save 出现:

500 Internal Server Error

但:

  • GitLab Web 正常
  • Clone 正常
  • Push 正常
  • CI 基本正常

只有后台保存配置时 500。

二、现象与初步判断

浏览器 F12:

POST /admin/application_settings/general 500

同时发现:

Mixed Content favicon warning

但后续确认:Mixed Content 不是根因。

三、日志分析

Rails 日志出现:

OpenSSL::Cipher::CipherError

以及:

lib/authn/token_field/encryption_helper.rb
token_authenticatable
ensure_token
decrypt_token

说明:GitLab 在读取某个 encrypted token 时解密失败。

四、排查过程

1. 初步怀疑 Project visibility

测试:

p = Project.find_by(name: "gaotong")
p.save!

结果:

=> true

说明:

  • Project Model 正常
  • 普通 save 正常

2. 批量扫描 Project

执行:

Project.find_each do |p|
  begin
    p.save!
  rescue => e
    puts p.id
    puts e
  end
end

发现:

大量项目出现:

OpenSSL::Cipher::CipherError

初步确认:

GitLab 存在大量历史损坏 token。

3. 使用 secrets doctor

执行:

gitlab-rake gitlab:doctor:secrets

结果:

Total: 185 row(s) affected

包括:

DeployToken148
RemoteMirror19
ProjectImportData10
User3
Ci::Build3
ApplicationSetting1

说明:实例存在大规模 encrypted state 损坏。

五、核心问题定位

最终定位:

ApplicationSetting.current.error_tracking_access_token

触发:

OpenSSL::Cipher::CipherError

说明:后台 Settings 页面在保存时会读取 error_tracking_access_token。而该 token 已无法解密。

六、数据库验证

PostgreSQL 中确认字段:

SELECT column_name
FROM information_schema.columns
WHERE table_name='application_settings'
AND column_name LIKE '%error_tracking%';

结果:

error_tracking_access_token_encrypted

执行:

UPDATE application_settings
SET error_tracking_access_token_encrypted = NULL;

验证:

SELECT
  error_tracking_access_token_encrypted IS NULL
FROM application_settings;

结果:

t

说明:数据库字段已经成功清空。

七、异常现象

即使数据库字段已 NULL:

s.error_tracking_access_token

仍继续触发:

decrypt_token
ensure_token
token_set?

并报:

OpenSSL::Cipher::CipherError

说明:GitLab 18.11 token_authenticatable 内部状态已异常。

疑似:

  • token framework bug
  • cached token state
  • 历史 encrypted metadata 残留
  • secrets 与 token state 不一致

八、临时验证

通过 monkey patch:

ApplicationSetting.class_eval do
  define_method(:error_tracking_access_token) do
    nil
  end
end

成功绕过 getter。

说明:500 的核心触发点就是该 token getter。

九、最终工程决策

由于:

  • GitLab 主功能正常
  • 问题深入 token framework 内核
  • 修复成本远高于收益
  • 真实目标只是“禁止匿名访问”

最终决定:在 Nginx 层直接 deny。

配置:

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 更稳。