Docs header transparent bg

如何排查 RubyGems 和 Bundler TLS/SSL 问题

如果您遇到与 SSL 证书和/或 TLS 版本相关的问题,那么您来对地方了。在本指南中,我们将解释这两种问题是如何产生的以及如何解决它们。本指南中的许多说明可以帮助解决 SSL 证书问题或 TLS 版本问题。

如果您不想了解原因,只想尽快解决问题,可以直接跳到 SSL 问题的解决方案

目录

问题

为什么我看到 certificate verify failed

如果您在尝试从 RubyGems 获取更新时看到以下 SSL 错误:OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

此错误发生在您的计算机缺少验证 RubyGems.org 后面服务器是否正确所需的文件时。

最新版本的 RubyGems 应该可以解决此问题,因此我们建议您更新到当前版本。要告诉 RubyGems 将自身更新到最新版本,请运行 gem update --system。如果这不起作用,请尝试以下手动更新过程。

(我们所说的“应该可以解决此问题”是什么意思?请查看下面的 这些证书是什么?Ruby 如何使用 CA 证书 部分,以更好地了解潜在问题。)

这些证书是什么?

每当您的计算机使用 HTTPS 与服务器通信时,它都会使用SSL 证书作为连接的一部分。证书允许您的计算机知道它正在与域的真实服务器通信,并允许它确保您的计算机和该服务器可以完全私密地进行通信,而不会让任何其他计算机知道来回发送的内容。

为了知道 RubyGems.org 的证书是否正确,您的计算机会查询来自证书颁发机构 (CA) 的另一个证书。CA 证书捆绑包包含来自所有为服务器提供 SSL 证书的公司(如 Verisign、Globalsign 和许多其他公司)的证书。

每个 CA 都有一个“根”证书,用于验证其他证书。CA 证书被称为“根”,因为它们签署其他证书,而这些证书又签署其他证书,证书的图形看起来像一棵树,而“根”证书位于树的根部。您的计算机将使用其内置的包含许多根证书的 CA 捆绑包来确定是否信任特定网站(例如 RubyGems.org)提供的 SSL 证书。

偶尔,新的公司会被添加到 CA 捆绑包中,或者现有公司的证书到期,需要分发新的证书。对于大多数网站来说,这不是什么大问题,因为网络浏览器会定期更新其 CA 捆绑包,作为一般浏览器更新的一部分。

Ruby 如何使用 CA 证书

RubyGems.org 使用的 SSL 证书来自一个较新的根证书。Ruby(以及 RubyGems 和 Bundler)没有定期更新的 CA 捆绑包,用于在联系网站时使用。通常,Ruby 使用操作系统 (OS) 提供的 CA 捆绑包。在较旧的操作系统上,此 CA 捆绑包可能非常旧——可能已有十年之久。由于如此旧的 CA 捆绑包无法验证 RubyGems.org 的(较新的)证书,您可能会看到以下错误:certificate verify failed

更复杂的是,18-24 个月前的一次无关更改导致 RubyGems.org 发行了新的 SSL 证书。这意味着用于验证连接的“根”证书已更改。因此,即使您之前升级了 RubyGems/Bundler 以解决 SSL 问题,您也需要再次升级——这次升级到具有更新证书的更新版本。

排查证书错误

首先运行自动 SSL 检查,并按照说明操作。您可能需要更新 Bundler更新 RubyGems手动更新 RubyGems 证书,或者甚至安装新的操作系统证书

为什么我看到 read server hello A

此错误表示您的机器无法与 RubyGems.org 建立安全连接。造成此问题最常见的原因是 Ruby 使用了旧版本的 OpenSSL。从 2018 年 1 月 1 日起,连接到 RubyGems.org 所需的最低版本为 2012 年 3 月 12 日发布的 OpenSSL 1.0.1。

要了解为什么需要该版本,请继续阅读。要查看有关如何更新 OpenSSL 和/或 Ruby 以解决问题的说明,请跳至故障排除部分

SSL 和 TLS 协议版本

互联网上的安全连接使用HTTPS,这是 HTTP 的安全版本。该安全性最初由 SSL 提供,SSL 是安全套接字层 (Secure Sockets Layer) 的缩写。随着时间的推移,研究人员发现了 SSL 中的缺陷,网络开发人员做出了相应的更改和修复。在 SSL 3.0 之后,它被 TLS 或传输层安全性 (Transport Layer Security)取代。

随着时间的推移,TLS 也进行了修订。TLS 版本 1.2 最初定义于 2011 年,并从 2012 年开始由 OpenSSL 支持,是当前的标准。2017 年,发现所有早于 TLS 1.2 的 SSL 和 TLS 版本都存在严重缺陷,这些缺陷可能会被有决心或有知识的攻击者利用。因此,安全最佳实践建议主动阻止所有版本的 SSL,以及 TLS 版本 1.0 和 1.1。

TLS 1.0 和 1.1 已被弃用

RubyGems.org 使用名为Fastly 的第三方 CDN 提供商,该提供商允许世界各地的用户快速下载 gem。

去年,Fastly 宣布将弃用 TLS 版本 1.0 和 1.1,这是由于 PCI 安全标准委员会发布的强制性规定。(在 Fastly 的博客文章中详细了解此信息。

因此,从 2018 年 1 月开始,RubyGems.org 将至少需要 TLSv1.2。这意味着 RubyGems.org 和 gem 命令将不再支持不支持 TLS 1.2 的 Ruby 和 OpenSSL 版本。

排查协议错误

要排查协议连接错误,首先请 运行自动 SSL 检查 并按照说明操作。您可能需要 更新 Bundler更新 RubyGems,甚至重新安装 Ruby(您可以通过 版本管理器包管理器 找到重新安装说明)。

解决方案

自动 SSL 检查

首先,运行此脚本 检查您的错误是否由 SSL 证书问题或 TLS 版本问题引起。

您可以使用以下命令立即运行脚本(Windows 10 也适用):

$ curl -Lks 'https://git.io/rg-ssl' | ruby

如果输出显示“Your Ruby can’t connect to rubygems.org because you are missing the certificate”,则表示您存在证书验证错误,需要更新您的证书。

如果显示“Your Ruby can’t connect to rubygems.org because your version of OpenSSL is too old”,则表示您的 OpenSSL 版本过旧,与 TLSv1.2 不兼容,您需要升级 OpenSSL 和/或重新编译 Ruby 以使用更新版本的 SSL。

本指南中的说明可以帮助您解决这两个问题。

更新 Bundler

通过运行以下命令更新到最新版本的 Bundler:

gem install bundler

更新 RubyGems

您可以尝试使用自更新命令升级 RubyGems:

gem update --system

如果该命令失败,您可以尝试自行下载最新版本的 RubyGems 并安装它,请按照以下步骤操作。在本例中,我们将下载并安装 RubyGems 2.7.6。如果您阅读本文时最新版本的 RubyGems 已更改,则需要将所有出现 2.7.6 的地方更改为已下载的 RubyGems 版本。

  1. 使用您的网络浏览器访问 下载 RubyGems 页面,并下载最新版本的 rubygems 的 gem 版本。
  2. 下载 gem 后,在 macOS 上打开 Terminal.app,或在 Windows 上打开带有 Ruby 的命令提示符。
  3. 将目录更改为您的下载文件夹。在 macOS 上,命令为 cd ~/Downloads。在 Windows 上,命令为 cd C:\Users\%USERNAME%\Downloads
  4. 通过运行 gem install --local rubygems-update-2.7.6.gem 安装下载的 RubyGems 升级 gem。
  5. 运行升级命令 update_rubygems

完成!运行 gem --version 验证您是否正在使用最新版本的 RubyGems。

更新系统时钟

如果您的系统时钟设置为过去或将来的时间,您的机器将无法与 RubyGems.org 建立安全连接。要解决此问题,您需要将系统时钟设置为当前时间。在 Linux 中,您可以通过运行 sudo ntpdate ntp.ubuntu.com 来更新系统时钟。

以下是更新系统时钟的其他可能解决方案

更新 CA 证书

安装新的 RubyGems 证书

如果您无法更新 RubyGems,您可以手动添加 RubyGems 所需的证书。如果您拥有足够新的 RubyGems 版本(版本 2.1.x 及更高版本)可以使用这些“内置”证书,并且您成功安装了证书,它将无需升级 RubyGems 版本即可工作。

警告:这些说明只会添加新的证书;Ruby 将保持不变。要确保您的 Ruby 版本可以使用 TLSv1.2,请再次运行代码段。如果不是,请按照本指南中的另一组说明来升级 Ruby。

步骤 1:获取新的信任证书

从以下链接下载 .pem 文件:GlobalSignRootCA.pem

然后,找到下载的文件,并检查以确保文件名以 .pem 结尾。(注意:某些浏览器会将扩展名更改为 .txt,这将阻止它工作。因此,务必确保您下载的文件以 .pem 扩展名结尾。)

步骤 2:在您的安装中找到 RubyGems 证书目录

接下来,您需要找到安装 Ruby 的目录,以便将 .pem 文件添加到其中。

在 Windows 上:

打开您的命令行并输入

C:\>gem which rubygems

您将看到类似这样的输出

C:/Ruby21/lib/ruby/2.1.0/rubygems.rb

要打开一个显示我们需要找到的目录的窗口,请在同一个窗口中输入文件扩展名之前的路径部分(但使用反斜杠)。例如,根据上面的输出,您将运行以下命令

C:\>start C:\Ruby21\lib\ruby\2.1.0\rubygems

这将打开一个资源管理器窗口,显示 RubyGems 安装到的目录。

在 macOS 上:

打开终端并运行以下命令

$ gem which rubygems

您将看到类似这样的输出

/opt/rubies/2.4.1/lib/ruby/2.4.0/rubygems.rb

要打开一个显示我们要查找目录的窗口,请在该输出上使用open命令,但不要在末尾添加“.rb”,如下所示

$ open /opt/rubies/2.4.1/lib/ruby/2.4.0/rubygems

将打开一个 Finder 窗口,显示 RubyGems 安装到的目录。

步骤 3:复制新的信任证书

在窗口中,打开ssl_certs目录。找到其他.pem文件,例如AddTrustExternalCARoot.pem(可能位于rubygems.org等子目录中),并将您的文件拖到它们旁边。

完成此操作后,您应该能够按照最顶部的说明自动更新 RubyGems。请访问更新 RubyGems部分以获取分步说明。如果这不起作用,请继续按照本指南进行操作。

安装新的操作系统证书

当与 Homebrew 一起安装的 OpenSSL 版本干扰 Ruby 查找正确证书的能力时,此解决方案可能有效。有时,卸载所有内容并从头开始就足以解决问题。

首先,您需要删除 RVM。您可以通过运行以下命令来完成此操作

$ rvm implode

接下来,您需要从 Homebrew 中删除 OpenSSL。(使用--force可确保您删除可能拥有的所有版本的 OpenSSL)

$ brew uninstall openssl --force

现在,您可以按照上一步中的说明重新安装 RVM。

从版本管理器重新安装 Ruby

使用 rvm 安装

注意:如果使用 RVM 更新 SSL 证书不起作用,请尝试此解决方案。如果使用 RVM 安装的 Ruby 即使在更新后也无法找到正确的证书,您可能可以通过重新安装 RVM 然后重新安装您的 Ruby 版本来解决它。

运行以下命令以删除 RVM 并重新安装它

$ rvm implode $ \curl -sSL https://get.rvm.io | bash -s stable

然后,重新安装 Ruby,同时告诉 RVM 您不想使用预编译的二进制文件。(不幸的是,这将花费更长时间,但它有望解决 SSL 问题。)

此命令将安装 Ruby 2.2.3。调整命令以安装您需要的 Ruby 版本

$ rvm install 2.2.3 --disable-binary

使用 ruby-buildrbenv install 安装

按照 rbenv 的更新和故障排除 ruby-build 指南中概述的说明进行操作。

从操作系统包管理器重新安装 Ruby

macOS:内置 Ruby

macOS 10.13 High Sierra 附带与 TLSv1.2 兼容的默认 Ruby。

要检查您当前的 macOS 版本,请转到 Apple 菜单并选择“关于本机”。如果您看到“macOS High Sierra”以外的任何内容,则需要升级到最新的 macOS(否则,请按照下一组说明在 Homebrew 中安装更新版本的 Ruby)。

升级到 High Sierra

    1. 打开 App Store 应用程序
    1. 选择“更新”选项卡
    1. 点击“macOS High Sierra”的“安装”按钮

macOS:使用 Homebrew 安装

注意:要使用 Homebrew 安装较新版本的 Ruby,请先确保 Homebrew 已安装。如果brew命令不存在,请按照http://brew.sh.cn上的安装说明进行操作,然后返回这些步骤。

    1. 运行brew update
    1. 运行brew install ruby
    1. 如果 Ruby 已经安装,运行brew upgrade ruby升级到最新版本。

Debian 或 Ubuntu 16.04:使用 apt-get 安装

注意:要使用apt删除 Ruby,您需要检查已安装的 Ruby 版本。apt安装 Ruby v2.3.1。

要卸载,请按照此处列出的说明进行操作。(这些说明适用于 Ubuntu 和 Debian。)

成功卸载 Ruby 后,通过运行以下命令重新安装它

$ sudo apt-get install ruby

Fedora:使用 dnf 安装

注意:最新版本的 Fedora 使用dnf作为其包管理器,但旧版本使用yum。如果您看到错误消息dnf: command not found,请将这些说明中的dnf替换为yum

首先,通过运行以下命令卸载 Ruby

$ dnf remove ruby

然后重新安装(此命令将安装 Ruby 2.3)

$ dnf install ruby

RHEL 或 CentOS:使用 yum 安装

按照在 CentOS 上升级 Ruby的说明进行操作。(它们还包括 OpenSSL 故障排除说明。)

Windows:使用 Ruby 安装程序安装

在控制面板中,在“程序”中找到 Ruby 安装程序。点击文件夹,然后再次点击“卸载 Ruby”。通过在 RubyInstaller 上下载Ruby 和 Ruby DevKit来重新安装。

其他帮助

运行另一个自动 SSL 检查

重新运行自动SSL 检查以验证问题是 SSL 问题还是 TLS 问题。如果您已经按照上述故障排除步骤操作,但仍然遇到问题,请转到下面的创建问题部分以获取下一步操作。

创建问题

如果这些说明都没有解决问题,下一步是打开一个问题。

(如果您遇到的错误来自gem install,请在RubyGems 问题跟踪器中创建一个问题。如果它来自bundle install,请在Bundler 问题跟踪器中创建一个问题。)

请包括

  • 运行 gem env 的输出
  • 运行 bundle env 的输出
  • 运行 ruby -ropenssl -e 'puts OpenSSL::OPENSSL_LIBRARY_VERSION' 的输出
  • 您的 Ruby 版本管理器(如果有)
  • 您的操作系统和操作系统版本
  • 您的包管理器名称和版本(如果适用)

为本指南贡献

如果您找到了此处未列出的解决方案,请提交 PR 将您的解决方案添加到 本指南 中!

如果您发现错误或注意到某些内容缺失,请 在 GitHub 上编辑此文档