📝 背景与问题现象

最近入职了一家正在成长期的公司,代码评审和CICD流程机制很不完善,于是我决定搭建一套 Gerrit+Jenkins 流水线,在搭建和配置 Gerrit 代码评审系统时,为了实现网页端(Web UI)的免密自动登录,我们通常会采用 Nginx Basic Auth + Gerrit HTTP Auth 的托管认证架构。
然而,在架构搭建完成后,团队成员在本地使用 Git 命令行通过 HTTP/HTTPS 协议克隆(Clone)或推送(Push)代码时,却遭遇了失败:报错现象:命令行提示 401 Unauthorized403 Forbidden,即使输入了 Gerrit 网页端生成的 HTTP CredentialsHTTP 专用密码) 也依然无法通过验证。
诡异的是:如果绕过 Nginx,直接使用 Gerrit 自身的 8080 端口进行克隆(如 git clone http://172.16.31),认证和克隆完全正常。
在这里插入图片描述

🔍 深度排查与根因分析

通过对比 8080 端口与 Nginx 代理端口的克隆表现,问题被精准锁定在 Nginx 反向代理层。

  1. Gerrit/a/ 路径机制
    Gerrit 的路由规则中,带有 /a/URL(例如 /a/p/project_name)代表强制认证的 Git 操作路径。当 Git 客户端请求该路径时,会携带包含 Gerrit HTTP 专用密码的 Authorization 请求头。Gerrit 收到后会自行解析并校验。

  2. NginxGit 凭证的"两重认证冲突"
    在初始的 Nginx 配置中,由于没有对 Git 的特定路径做分流,所有的请求都会落入 location / 规则中。这就导致了致命的冲突:

    • 凭证误判location / 开启了 Nginx 自身的 auth_basic(读取 .htpasswd)。当 Git 客户端带着 Gerrit 的专用密码请求过来时,Nginx 误以为这是给自己的验证凭证。
    • 双重拦截:因为 Gerrit 专用密码与 Nginx.htpasswd 密码不一致,Nginx 直接在最外层拦截并返回了 401 错误,导致请求根本没有到达 Gerrit 服务端。

🛠️ 解决方案

要解决这个问题,核心思路是:将网页端请求与 Git 命令行请求在 Nginx 层进行分流。对 Git 命令行及公开克隆路径关闭 Nginx 层的认证,将其原封不动地透传给 Gerrit 自身去控权。

同时,为了防止大项目克隆时出现 HTTP 缓冲区溢出、中断或超时,还需要在 Nginx 中加入针对 Git 大文件流的优化参数。

🚀 终极 Nginx 配置文件

以下是经过优化和整合后的完整 nginx.conf 核心配置:

server {
    listen 80;
    server_name 172.16.31.59;
    
    # 1. 清除网页端认证缓存的特殊路径
    location = /logout {
        auth_basic off;
        return 401;
    }
    
    # 限制上传文件的最大体积(支持大单品、大仓库推送)
    client_max_body_size 20480M; 

    # 2. 【核心修复】合并所有 Git 命令行、工具链以及匿名/公开克隆路径
    # 正则匹配说明:
    # /a/ 认证克隆、/tools/ 客户端钩子工具
    # /p/ /info/refs /git- 开头的通用 Git 智能 HTTP 协议标准路径
    location ~* ^/(a|tools|p|info/refs|git-upload-pack|git-receive-pack) {
        # 关键:关闭 Nginx 层认证,让 Git 客户端的专用密码直接透传给 Gerrit 校验
        auth_basic off; 

        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 针对 Git 大文件传输流(Stream)的优化
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        
        # 防止大项目克隆/推送时,由于网络慢或数据量大导致 Nginx 触发 504 超时
        proxy_connect_timeout 600s;
        proxy_read_timeout    600s;
        proxy_send_timeout    600s;
    }

    # 3. 网页端(Web UI)与 Gerrit REST API 请求
    location / {
        # 仅对网页登录和常规浏览开启 Nginx Basic Auth
        auth_basic "Gerrit Code Review";
        auth_basic_user_file /etc/nginx/.htpasswd;

        # 认证成功后,将用户名通过请求头传递给 Gerrit 实现免密登录
        proxy_set_header X-Remote-User $remote_user;

        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

💡 总结与避坑心得

路由优先级:在 Nginx 中,正则匹配 location ~* 的优先级高于通用前缀匹配 location /。利用这一特性,我们可以完美剥离 Git 流量。

不仅仅是 /a/:除了带认证的 /a/ 和工具路径 /tools/,还要把不带 /a/ 的匿名克隆路径(如 /p//info/refs)也一并对 Nginx 释放。否则,团队成员在拉取公开项目或未登录克隆时,依然会被 Nginx 的弹窗拦截。

协议优化:由于 Git 传输大文件是流式数据,配置 proxy_http_version 1.1 并清除 Connection 头,能有效维持持久连接,规避 RPC failed; curl 56 Recv failure 错误。

如果这篇博客对你有帮助,欢迎点赞、收藏并分享给正在填坑的小伙伴!如果有更优雅的配置方案,欢迎在评论区交流。 🚀流。 🚀

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐