在项目中执行 git fetch 时报错:
fatal: unable to access 'https://git.example.com/rxx/demo_project.git/':
gnutls_handshake() failed: Error in protocol version
2. 表现
git fetch/git pull均失败,无法访问远程仓库。- 报错信息固定为
gnutls_handshake() failed: Error in protocol version。 - 系统其他工具(如
curl)访问同一域名却正常。
3. 根因分析
3.1 服务器只支持 TLS 1.3
用 openssl s_client 实测远端服务器:
bash
echo | openssl s_client -connect git.example.com:443 -tls1_2 # 失败
# error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version
echo | openssl s_client -connect git.example.com:443 -tls1_3 # 成功
服务器对 TLS 1.2 握手直接拒绝,只放行 TLS 1.3。
3.2 git 客户端使用的 TLS 库不支持 TLS 1.3
- 旧版系统 git(Ubuntu 18.04 自带 2.17.1)使用系统 gnutls 3.5.18,不支持 TLS 1.3。
- 升级到 PPA 版 git 2.50.1 后依旧失败,因为 PPA 把 gnutls 版 libcurl 静态链接 进了 git 二进制:
ldd /usr/bin/git看不到 libcurl 动态依赖(被静态打入)。git -c http.sslBackend=openssl报Could not set SSL backend to 'openssl': already set,说明编译时已固定为 gnutls。
- 系统
curl之所以正常,是因为它链接的是 openssl 版 libcurl(libcurl.so.4)。
3.3 关键点
| 组件 | TLS 后端 | TLS 1.3 支持 | 结果 |
|---|---|---|---|
| 系统 curl | OpenSSL 1.1.1 | 支持 | 正常 |
| 系统 git 2.17.1 | gnutls 3.5.18 | 不支持 | 失败 |
| PPA git 2.50.1 | 静态 gnutls libcurl | 不支持 | 失败 |
4. 解决办法:从源码编译 git,链接 openssl 版 libcurl
核心思路:让 git 使用系统已有的 openssl 版 libcurl.so.4,而不是默认的 -lcurl(它会解析到 gnutls 版的 libcurl.so -> libcurl-gnutls.so 符号链接)。
4.1 下载源码
bash
cd /tmp/opencode
curl -sL -o git.tar.gz https://github.com/git/git/archive/refs/tags/v2.50.1.tar.gz
tar xzf git.tar.gz
cd git-2.50.1
4.2 生成 configure 并配置
bash
make configure
./configure --prefix=$HOME/git-local --without-tcltk CURL_LDFLAGS="-Wl,-l:libcurl.so.4"
注意:configure 生成的
config.mak.autogen会覆盖为CURL_LDFLAGS=-lcurl,需要手动改回:
bash
sed -i 's|^CURL_LDFLAGS=-lcurl$|CURL_LDFLAGS=-Wl,-l:libcurl.so.4|' config.mak.autogen
4.3 编译并安装
bash
make -j$(nproc)
make install
4.4 验证
bash
# 确认 git-remote-http 链接 openssl 版 libcurl
ldd $HOME/git-local/libexec/git-core/git-remote-http
# libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4
# libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1
# 测试 fetch
$HOME/git-local/bin/git fetch origin # 成功,exit=0
4.5 设为默认
bash
echo 'export PATH="$HOME/git-local/bin:$PATH"' >> ~/.zshrc
# 新开终端生效
5. 总结
- 服务器只接受 TLS 1.3,客户端 TLS 库不支持则握手失败。
- PPA 版 git 静态绑定了 gnutls 版 libcurl,无法通过配置切换后端。
- 从源码编译 git 并强制链接 openssl 版
libcurl.so.4即可解决。