恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Git LFS push反复要密码?Gerrit+nginx+lfs-test-server认证配置详解
首页
资讯中心
/
Git LFS push反复要密码?Gerrit+nginx+lfs-test-server认证配置详解
Git LFS push反复要密码?Gerrit+nginx+lfs-test-server认证配置详解
发布时间:2026/9/7 17:15:03
我先把结论放在前面git lfs push 反复提示输入密码十有八九不是密码错了而是 LFS 请求根本没有走到 lfs-test-server或者 Authorization 头在 nginx 转发过程中被吞了。我从第一台机器踩坑到现在把完整的配置链路理清楚之后这个问题基本五分钟内就能定位。这篇博文就按“背景 - 原理 - 实操 - 排查”的顺序把 Gerrit 里接 Git LFS 的完整配置讲透。如果你正在用 Gerrit 做代码评审又希望仓库里能存放二进制大文件比如安装包、模型文件、设计稿Git LFS 几乎是必经之路。但 Gerrit 原生不支持 LFS 协议所以主流方案是单独部署一个 lfs-test-server再通过 nginx 做统一入口。这个方案本身不复杂复杂的是 LFS 的认证机制和 nginx 的请求转发细节两者一旦没对齐就会反复出现标题里的问题。1. 问题背景与整体架构1.1 为什么 Gerrit 项目要用 Git LFS先聊聊背景。普通 Git 仓库的核心是保存文本文件的内容快照它会把每个文件压缩成对象存进.git/objects。当仓库里混入几百 MB 甚至几个 GB 的二进制文件时问题就来了每次提交都会生成一份新的对象哪怕是只改了一个字节整个文件也会被复制一份仓库体积迅速膨胀clone 一次慢到怀疑人生。Git LFSLarge File Storage的解决方案是用一个“指针文件”替代真正的大文件。这个指针文件只有几百字节里面记录了大文件对象的 SHA-256、大小、版本等信息。真实的大文件内容被上传到独立的 LFS 存储服务端而 Git 仓库本身只保存轻量指针。这样 clone 仓库时拉取的是指针文件只有当 checkout 需要真实文件时Git LFS 客户端才会从 LFS 服务器按需下载。Gerrit 是代码评审系统它不直接参与文件的传输和存储只做 refs 的接收与评审流程控制。因此 Git LFS 介入后大文件会绕过 Gerrit 走到 lfs-test-servergerrit 只需要看到 refs 更新和指针文件即可。这既保证了评审流程不变又避免了仓库膨胀是很多公司内部研发环境的标准做法。1.2 我实际部署的架构nginx Gerrit lfs-test-server在我自己的环境里架构是这样的所有 HTTP/HTTPS 流量先进入 nginxnginx 根据路径把请求分发到 Gerrit 和 lfs-test-server。仓库访问走 Gerrit 的 Web 和 SSH 端口比如https://code.example.com/gerrit/。LFS 请求走独立的路径比如https://code.example.com/lfs/nginx 将该路径反向代理到 lfs-test-server 默认监听的9999端口。之所以在中间加一层 nginx是为了统一对外域名和端口让团队内的所有开发者只需要记住一个服务地址。Gerrit 本身也可以直接配一个 Base URL但把 LFS 的独立服务也暴露在同一个域名下配置会更干净。这种架构的一个隐藏陷阱是nginx 对/lfs/路径的处理必须把 LFS 请求的 URI 原样转发给 lfs-test-server不能重写路径否则 LFS API 会 404。另一个陷阱就是后面要重点说的 Authorization 头。1.3 “提示输入密码”的现象和排查起点标题里的问题现象是这样的在本地执行git lfs push --all origin或者正常git push时发现大文件没有被推送然后终端弹出了 HTTPS 的用户名密码输入框。更烦人的是即使你输入了 Gerrit 的账号密码它依然会重新提示反复多次最后失败。这个现象的直观感受是“认证不过”但排查方向别一开始就扎进 Gerrit 的账号体系里。正确的排查起点是把 LFS 请求链路拆开看git lfs 是否把请求发到了预期的 lfs-test-server 地址nginx 把请求转给 lfs-test-server 时是否丢失了 HTTP Basic Auth 的请求头lfs-test-server 里是否存在你输入的账号本地的 git 是否愿意缓存这些凭证避免每次 push 都重新询问后面第 3 章会按这个顺序逐步验证。2. 核心原理Git LFS 的认证链路2.1 LFS 的 Batch API 和 Authorization 头要理解为什么 LFS 会提示输入密码先得知道 LFS 在哪里发起认证。当git push时LFS 客户端会先调用服务端的 Batch API默认路径是{LFS_URL}/info/lfs/objects/batch。这个接口是一个 POST 请求请求体里携带待上传的对象 ID 列表响应体里返回每个对象的上传地址、过期时间等信息。Batch API 在服务端需要进行认证而认证方式基于 HTTP Basic Auth。也就是说客户端会在请求头里添加Authorization: Basic base64(username:password)如果服务端校验失败会返回 401并携带一个LFS-Authenticate响应头。此时 Git LFS 客户端就会向用户询问用户名和密码。问题往往出在“服务端校验失败”这一步。如果你确认密码没打错那大概率是服务端压根没收到 Authorization 头或者收到了但不知道去哪校验。2.2 lfs-test-server 的用户体系lfs-test-server 是 Git LFS 的官方参考实现之一主要用途是测试 LFS 客户端和协议。它能跑起来但它的用户体系和 Gerrit 完全是两套。它不读取 Gerrit 的账号数据库也不接入 LDAP而是使用自己独立配置的用户名和密码。lfs-test-server 默认的启动方式支持两个环境变量LFS_USER和LFS_PASS分别表示初始用户名和密码。它还有初始化的方式可以创建一个用户用于后续上传和下载。没有这个用户任何请求都会 401自然就会弹出密码输入框。理解了这一点你就会明白一个关键认知不能用 Gerrit 的账号密码来应对 LFS 的 HTTP Basic Auth。Gerrit 账号只负责 Git 仓库的 fetch/receive 操作LFS 传输则只认 lfs-test-server 自己的账号体系。很多人反复输入 Gerrit 密码验的却永远是这个独立账号当然不可能成功。2.3 nginx 在中间转发时容易丢的东西nginx 反向代理的默认行为里请求头并不是全部透传的。比如Authorization头你需要显式告诉 nginx 把它转发给上游。如果不做配置nginx 可能把该头吞掉或者只转发了部分信息。一个典型的配置片段是这样的location /lfs/ { proxy_pass http://127.0.0.1:9999; 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 Authorization $http_authorization; }其中proxy_set_header Authorization $http_authorization;是解决标题问题的关键一行。$http_authorization就是 nginx 从原始请求中读取的 Authorization 头的变量。另外如果 LFS 服务跑在 HTTPS 后面X-Forwarded-Proto也需要转发否则 LFS 返回的下载地址可能变成 http外部访问就会出问题。完整的配置我放在 3.3 节。2.4 为什么 Gerrit 账号救不了 LFS 认证再深入解释一下为什么 Gerrit 账号救不了 LFS 认证。在 nginx Gerrit lfs-test-server 的架构中LFS 请求的路径与 Gerrit 的git路径是分开的/gerrit/路径 → nginx 转发到 Gerrit 的 HTTP 端口比如 8080。/lfs/路径 → nginx 转发到 lfs-test-server 的 9999 端口。这两条路径在 nginx 层就是不同的 location请求被分发到完全不同的后端服务。Gerrit 账号只在第一条路径上有效第二条路径上 lfs-test-server 只认自己数据库里的用户名和密码。你拿 Gerrit 账号去验 lfs-test-server等于是拿 A 公司的工牌去刷 B 公司的门禁密码输一百遍也解不开。所以配置的第一步就是明确你的 LFS 服务到底由哪个后端提供用户体系在哪里然后在客户端里使用对应账号。3. 实操解决从配置到 push 全流程3.1 第一步检查 git lfs env确认 LFS 走的是哪条路很多问题其实在第一步就能看出端倪。进入你的仓库目录执行git lfs env输出里会列出当前仓库的 LFS 配置比如Endpointhttps://code.example.com/lfs/ (authbasic)重点看Endpoint和auth。Endpoint决定了你的 LFS 请求实际发往哪里。如果它显示的是 Gerrit 的仓库地址比如https://code.example.com/gerrit/foo.git/info/lfs那说明你的 LFS URL 没有配置独立地址请求会打到 Gerrit而 Gerrit 不实现 LFS API自然会出现各种异常包括认证问题。如果你的 LFS 服务是独立部署的建议在仓库里显式配置 LFS URLgit config lfs.url http://code.example.com/lfs/也可以在~/.gitconfig中配置全局的 LFS URL。显式配置的好处是不管仓库 remote 地址怎么变LFS 请求始终指向唯一正确的服务端。另一个需要确认的是 remote URL。如果 remote 地址设置成了带用户名密码的形式比如https://user:passcode.example.com/gerrit/foo.gitLFS 可能会拿这个用户名密码去访问 LFS 服务但 lfs-test-server 可能并没有这个账号也会导致认证错误。这种场景建议把 URL 里的明文账号去掉统一走 git 的凭证存储。3.2 第二步配置 lfs-test-server 的用户和启动参数先看 lfs-test-server 的启动方式。假设你已经在服务器上装好了 lfs-test-server常见启动命令是lfs-test-server -l :9999 -d /data/lfs其中-l指定监听地址-d指定数据存储目录。它接受环境变量LFS_USER和LFS_PASS在首次初始化时创建初始用户。在多数版本里你还可以通过-u和-p参数直接设置lfs-test-server -l :9999 -d /data/lfs -u lfsuser -p lfspass这里创建的lfsuser就是 LFS 服务的用户名lfspass是对应密码。切记这组账号是 LFS 专用账号不要和 Gerrit 账号混在一起。如果公司有安全规范要求定期换密码记得同步更新客户端使用的凭证否则又会触发“提示输入密码”的循环。启动之后可以先用 curl 验证一下 lfs-test-server 是否活着以及认证是否生效curl -u lfsuser:lfspass http://127.0.0.1:9999/info/lfs/objects/batch -X POST -d {}如果返回401表示需要认证返回200或者其他非 401 状态码说明请求已经进入到应用层。这一步能帮我们把问题定位在认证配置还是网络层。3.3 第三步nginx 关键配置与 header 透传nginx 的 location 配置是核心。假设你的对外服务域名是https://code.example.comLFS 对外路径是/lfs/那么配置写location /lfs/ { proxy_pass http://127.0.0.1:9999; proxy_http_version 1.1; 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 https; proxy_set_header Authorization $http_authorization; client_max_body_size 0; }这里有几个细节proxy_pass http://127.0.0.1:9999;后面不带路径这样 URI 会被原样转发LFS 的/info/lfs/objects/batch路径才能正确落到 lfs-test-server。client_max_body_size 0;表示不限制上传大小避免大文件上传被 nginx 的默认 1MB 限制拦下。proxy_set_header Authorization $http_authorization;透传认证头。如果这一步配错了排查时你会看到 curl 命令里带认证访问后端正常但 git lfs push 却一直卡在认证环节。这就是 nginx 中间把 Authorization 头丢了。如果你希望 LFS 路径不出现在对外的 URL 中也可以基于子域名路由比如 lfs 服务用lfs.example.comnginx 配置对应一个 server_name 即可原理相同。3.4 第四步git 客户端凭证存储配置服务器和 nginx 都就位了客户端还需要处理好凭证缓存。Git 默认可能会在每次请求时都询问用户名密码尤其是首次使用 LFS 的时候。为了减少“提示输入密码”的频次可以配置凭证存储。在用户全局配置里启用 store 模式git config --global credential.helper store这样第一次输入的用户名密码会被保存在~/.git-credentials文件里后续请求自动携带。文件内容形如http://lfsuser:lfspasscode.example.com需要注意的是store 模式是明文存储如果是在个人开发机上问题不大但在共享机器上要谨慎。如果公司安全要求高可以改用cache模式只缓存一段时间git config --global credential.helper cache --timeout3600另外LFS 的认证信息不仅用于 pushclone 时也需要。所以建议在所有开发者的客户端里统一配置好 credential helper否则每个新人 clone 大仓库时都会遇到密码提示。3.5 完整配置示例一次 push 成功把前面所有步骤串起来完整的操作流程大概是这样的。服务器端# 1. 创建 lfs-test-server 数据目录 mkdir -p /data/lfs # 2. 启动服务创建 LFS 专用账号 lfs-test-server -l :9999 -d /data/lfs -u lfsuser -p lfspassnginx 端server { listen 443 ssl; server_name code.example.com; # SSL 证书配置省略 location /lfs/ { proxy_pass http://127.0.0.1:9999; 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 https; proxy_set_header Authorization $http_authorization; client_max_body_size 0; } location /gerrit/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; } }客户端# 1. 指定 LFS URL在仓库内执行 git config lfs.url https://code.example.com/lfs/ # 2. 配置凭证缓存 git config --global credential.helper store # 3. 推送 git lfs push --all origin第一次推送时输入lfsuser/lfspass后续就不会再反复询问了。4. 常见问题与排查技巧实录4.1 密码明明输对了还是反复提示这里的最常见原因就是第 2 章说的nginx 没转发 Authorization 头或者你输的账号不在 lfs-test-server 的用户体系里。排查方法在服务器上直接 curl 带用户名密码访问 lfs-test-server确认账号有效。在客户端抓包或者看 nginx access log确认请求是否带有 Authorization 头。在 nginx 配置里加上proxy_set_header Authorization $http_authorization;reload 后再试。如果你用的是某些带自定义认证模块的 nginx还要注意有没有可能覆盖掉原始 Authorization 头。比如有的配置会写死proxy_set_header Authorization ;来清空这种写法会把 LFS 认证直接打断。4.2 push 时返回 401 Unauthorized如果返回的是 401说明服务端收到了请求但认证失败。这时优先检查 lfs-test-server 的账号密码是否拼写正确以及 URL 中是否夹带了错误的明文账号。还要注意有些旧版本 lfs-test-server 在创建用户时大小写敏感而且密码不能包含符号否则在 URL 中会被解析成分隔符。如果你在~/.git-credentials里存的密码带特殊字符记得做 URL 编码或者改用 credential helper 而不是 URL 内嵌账号。另外如果你在 Git 全局配置里设置了http.extraheader比如带了某种固定认证头那它可能会覆盖 LFS 请求里的 Authorization导致 Basic Auth 校验失败。这个坑比较隐蔽遇到的话先检查git config --global --list | grep http。4.3 LFS 文件没传到 lfs-test-server而是推到 Gerrit 了这个现象是git push 成功但 LFS 文件实际没有上传到独立服务端仓库里只剩指针文件别人 clone 后无法下载真实大文件。排查思路在仓库里执行git lfs env确认 Endpoint 是不是指向 lfs-test-server。如果 Endpoint 显示的是 Gerrit 的仓库地址说明仓库没有配置lfs.url或者配置被仓库级的 .gitconfig 覆盖了。检查.gitattributes文件确认哪些文件模式被匹配为 LFS。比如.gitattributes里写了*.zip filterlfs difflfs mergelfs -text那么git add的时候*.zip文件才会被转换成指针文件真正的内容才会在 push 时走 LFS。如果你没有匹配到对应模式二进制文件会被当作普通内容提交到 Git 仓库LFS 完全不参与自然也就不会上传 LFS。4.4 clone 新机器时报认证失败很多人配置好 push 流程却发现换一台机器 clone 时又提示密码。这和第 3.4 节的凭证存储直接相关。新机器上 Git 的 credential helper 默认可能是空的第一次请求 LFS 时就会询问用户名密码。在一台共享构建机上建议配置git config --global credential.helper store或者使用credential.helper cache配合较长的 timeout。如果是 CI 环境可以在构建脚本里用环境变量方式提供凭证或者生成一个~/.git-credentials文件。另外git lfs clone 和 git clone 的区别要清楚。git lfs clone在 clone 时会额外运行git lfs pull如果 LFS 认证失败会在最后一步报错。建议在新机器上先跑git lfs env确认认证信息和 endpoint 都正确再执行 clone。4.5 问题排查速查表现象可能原因解决方向git lfs push 反复提示输入密码nginx 未透传 Authorization 头添加proxy_set_header Authorization $http_authorization;输入正确账号仍提示LFS URL 指向错误的地址检查git lfs env里的 Endpointpush 返回 401lfs-test-server 账号错误或不存在在 lfs-test-server 侧创建用户并用 curl 验证文件没上传 LFS 但 push 成功.gitattributes 未匹配文件模式确认文件类型与 LFS 匹配规则clone 成功但 checkout 失败LFS 认证未缓存或凭证过期配置 credential helper更新密码大文件上传直接 413nginx 默认 body 大小限制配置client_max_body_size 0;这些坑我都踩过一遍尤其是 nginx 丢 Authorization 头那一次排查最耗时。当时我把 lfs-test-server 的日志翻了个底朝天发现根本没收到请求头才意识到问题出在反代层。所以如果遇到认证问题优先从网络链路的请求头开始查别一头扎进代码配置里。最后再分享一个排查小技巧先说结论当你配置完所有内容后可以用一条命令快速验证 LFS 认证是否真正打通git lfs push --dry-run origin master它不会真的上传但会触发 LFS 的 Batch API 请求。如果这一步不再询问密码说明认证链路已经通了接下来推送大文件一般不会再出问题。我在实际使用中的另一个体会是LFS 密码的维护最好集中管理。如果团队成员多与其每个人在本地手动配置证书不如在团队文档里统一写明 LFS 专用账号的获取方式并且把仓库级的lfs.url直接写进.gitconfig或.lfsconfig文件提交到仓库里。这样后面的人 clone 下来不需要手动猜 endpoint减少很多“为什么我 push 不了”的咨询量。如果你后续还想扩展可以考虑在 lfs-test-server 前面加一层简单的访问控制或者通过 nginx 的auth_basic做一个统一入口验证。不过那已经超出标题问题的范围了先把“git lfs push 提示输入 https 密码”这个问题解决掉你这一步就算真正走通了。