如何排查 GitLab CI 中 Node.js 下载的 SSL 握手错误?
结论
仅凭 Remote host terminated the handshake: SSL peer shut down incorrectly,还不能判断问题是否出在证书信任上。这个错误说明下载连接在 TLS 握手期间被对端或网络路径关闭。原因可能是 GitLab Runner 的代理或出口网络、TLS 检查设备,也可能与 Java 信任库配置有关。由于本地运行正常、CI 失败,建议先比较两边的网络路径和代理配置。
问题中的 Maven 命令通过 -Djavax.net.ssl.trustStore=cacerts-copied.jks,为 Maven 所在的 Java 进程指定了信任库。frontend-maven-plugin 下载 Node.js 时由 Java 发起请求,因此这个设置可能影响下载连接的证书验证。不过,日志无法证明信任库就是问题所在;导入的 my-artifactory.crt 也未必是访问 nodejs.org 所需的证书。
排查步骤
1. 在失败的 Runner 上检查下载地址
在同一个 GitLab job、同一个镜像和相近的网络条件下,检查 Node.js 文件地址:
curl -Iv https://nodejs.org/dist/v16.20.2/node-v16.20.2-linux-x64.tar.gz
如果这里也无法建立 TLS 连接,问题可能出在 Runner 的网络、代理或出口路径。如果 curl 成功而 Maven 仍然失败,继续检查 Java 的信任库和代理设置。两者使用的 TLS 实现可能不同,因此 curl 成功并不能证明 Java 连接一定正常。
2. 对比本地与 GitLab CI 的代理设置
检查两边是否设置了 HTTPS_PROXY、HTTP_PROXY、NO_PROXY,以及 Maven settings.xml 中是否配置了代理。如果 CI 需要通过代理访问外网,确认 Maven 所在的 Java 进程也能使用该代理。交互式 shell 或 curl 中生效的代理设置,不一定会自动用于 Java 请求。
如果网络路径中有 TLS 检查代理,需要确认 Java 信任库信任该代理签发证书所需的 CA。请按组织提供的证书进行配置,不要关闭证书校验来绕过问题。
3. 检查实际使用的 Java 信任库
确认 Maven 使用的 Java、信任库文件及其中的证书:
java -version
keytool -list -keystore cacerts-copied.jks -storepass changeit
确认 cacerts-copied.jks 是当前 job 创建的,并且包含访问下载连接所需的受信任 CA。当前脚本导入的 my-artifactory.crt 是 Artifactory 相关证书。只有下载链路确实由它或相关中间设备签发时,这张证书才可能解决 nodejs.org 的连接问题。
4. 获取 Java TLS 握手日志
可以临时为 Maven JVM 启用 TLS 调试日志,查看握手在哪一步中断:
mvn -Djavax.net.debug=ssl,handshake \
-s setup_ci/settings.xml \
-Djavax.net.ssl.trustStore=cacerts-copied.jks \
-Djavax.net.ssl.trustStorePassword=changeit \
compile
日志可能包含证书和连接信息,分享前请检查并移除敏感内容。也可以使用 Maven 的 -X 查看插件执行细节。
可选处理方式
如果确认 Runner 无法稳定访问 nodejs.org,可以将所需的 Node.js 发行包放到组织允许访问的内部制品库,并根据当前 frontend-maven-plugin 版本支持的下载地址配置使用内部镜像。如果问题确实来自 Java 信任库,应将实际签发下载链路证书的受信任 CA 加入正确的信任库,并确保 CI 中的 Maven 使用该文件。
修复后,在 GitLab CI 中重新运行 job 验证。即使本地运行成功,Runner 的网络路径和 Java 配置也可能不同。
备注:内容仅供参考。
