Skip to content

Commit ad9f1c7

Browse files
committed
Set Bundler.settings[:ssl_ca_cert] to download gems
When `bundle install` connects with a certification (CA) to a private RubyGems HTTPS server emulated by WEBrick, the connection to the https://localhost:18443/versions succeeded, but the connection to download gems failed with the following error. ``` + /home/jaruga/var/git/ruby/rubygems/bin/bundle config set --local ssl_ca_cert /home/jaruga/git/report-bundler-bundle-config-set-ssl_ca_cert/tmp/client/ssl/ca.crt ... + /home/jaruga/var/git/ruby/rubygems/bin/bundle install -V Running `bundle install --verbose` with bundler 4.1.0.dev Resolving dependencies because there's no lockfile HTTP GET https://localhost:18443/versions HTTP 206 Partial Content https://localhost:18443/versions HTTP GET https://localhost:18443/versions HTTP 200 OK https://localhost:18443/versions Fetching gem metadata from https://localhost:18443/ Looking up gems ["hello"] Resolving dependencies... Using bundler 4.1.0.dev 1: bundler (4.1.0.dev) from /home/jaruga/var/git/ruby/rubygems/bundler/bundler.gemspec Fetching hello 0.1.0 Retrying download gem from https://localhost:18443/ due to error (2/4): Gem::RemoteFetcher::FetchError SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:18443 state=error: certificate verify failed (unable to get local issuer certificate) (https://localhost:18443/gems/hello-0.1.0.gem) Sleeping for 1.22 seconds before retry Retrying download gem from https://localhost:18443/ due to error (3/4): Gem::RemoteFetcher::FetchError SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:18443 state=error: certificate verify failed (unable to get local issuer certificate) (https://localhost:18443/gems/hello-0.1.0.gem) Sleeping for 2.26 seconds before retry Retrying download gem from https://localhost:18443/ due to error (4/4): Gem::RemoteFetcher::FetchError SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:18443 state=error: certificate verify failed (unable to get local issuer certificate) (https://localhost:18443/gems/hello-0.1.0.gem) Sleeping for 4.02 seconds before retry Bundler::InstallError: Bundler::HTTPError: Could not download gem from https://localhost:18443/ due to underlying error <SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:18443 state=error: certificate verify failed (unable to get local issuer certificate) (https://localhost:18443/gems/hello-0.1.0.gem)> /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/rubygems_integration.rb:406:in 'Bundler::RubygemsIntegration#download_gem' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:531:in 'block in Bundler::Source::Rubygems#download_gem' /home/jaruga/.local/ruby-4.1.0-debug-3ef48ef9c8-openssl-4.1.0-7194354488/lib/ruby/4.1.0+1/rubygems.rb:1068:in 'Gem.time' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:530:in 'Bundler::Source::Rubygems#download_gem' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:459:in 'Bundler::Source::Rubygems#fetch_gem' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:443:in 'Bundler::Source::Rubygems#fetch_gem_if_possible' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:575:in 'Bundler::Source::Rubygems#rubygems_gem_installer' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/source/rubygems.rb:184:in 'Bundler::Source::Rubygems#download' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/installer/gem_installer.rb:29:in 'Bundler::GemInstaller#download' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/installer/parallel_installer.rb:148:in 'Bundler::ParallelInstaller#do_download' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/installer/parallel_installer.rb:132:in 'block in Bundler::ParallelInstaller#worker_pool' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/worker.rb:70:in 'Bundler::Worker#apply_func' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/worker.rb:65:in 'block in Bundler::Worker#process_queue' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/worker.rb:56:in 'Kernel#loop' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/worker.rb:56:in 'Bundler::Worker#process_queue' /home/jaruga/var/git/ruby/rubygems/bundler/lib/bundler/worker.rb:98:in 'block (2 levels) in Bundler::Worker#create_threads' ... ``` `Bundler::Fetcher` creates the connection object by the `#connection` calling `#bundler_cert_store` storing `Bundler.settings[:ssl_ca_cert]` in the following part. It is used in some parts. bundler/lib/bundler/fetcher.rb ``` def bundler_cert_store ... ssl_ca_cert = Bundler.settings[:ssl_ca_cert] || (Gem.configuration.ssl_ca_cert if Gem.configuration.respond_to?(:ssl_ca_cert)) ... end ``` However in the case of downloading gems in Bundler, Bundler calls `Bundler::Source::Rubygems#download_gem` calling `Bundler::Fetcher#gem_remote_fetcher` calling `Bundler::Fetcher::GemRemoteFetcher` extending `Gem::RemoteFetcher` managing `@cert_files` for RubyGems. Therefore, the `Bundler::Fetcher::GemRemoteFetcher` needs to update the `@cert_files` by adding the value of the `Bundler.settings[:ssl_ca_cert]`. As in the process of downloading gems, `Gem::Request.configure_connection_for_https` is called, and it gets `Gem.configuration.ssl_ca_cert`, we don't need to add the value in the `Bundler::Fetcher::GemRemoteFetcher`. According to the following logic, `@cert_files` is always not `nil`, as `Dir.glob(patterns` returns Array. We don't need to consider the `nil` case. lib/rubygems/remote_fetcher.rb ``` def initialize(proxy = nil, dns = nil, headers = {}) ... @cert_files = Gem::Request.get_cert_files ... end ``` lib/rubygems/request.rb ``` def self.get_cert_files pattern = File.expand_path("./ssl_certs/*/*.pem", __dir__) Dir.glob(pattern) end ``` Add unit tests for `Bundler::Fetcher::GemRemoteFetcher#initialize`.
1 parent 4630c50 commit ad9f1c7

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

bundler/lib/bundler/fetcher/gem_remote_fetcher.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ def initialize(*)
99
super
1010

1111
@pool_size = Bundler.settings.installation_parallelization
12+
ssl_ca_cert = Bundler.settings[:ssl_ca_cert]
13+
@cert_files << ssl_ca_cert if ssl_ca_cert
1214
end
1315

1416
def request(*args)

spec/bundler/fetcher/gem_remote_fetcher_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,36 @@
66
require "bundler/vendored_persistent.rb"
77

88
RSpec.describe Bundler::Fetcher::GemRemoteFetcher do
9+
describe "#initialize" do
10+
context "when ssl_ca_cert setting is not set" do
11+
before do
12+
allow(Bundler.settings).to receive(:[]).and_call_original
13+
allow(Bundler.settings).to receive(:[]).with(:ssl_ca_cert).and_return(nil)
14+
end
15+
16+
it "does not append the setting to @cert_files" do
17+
cert_files = subject.instance_variable_get(:@cert_files)
18+
gem_cert_files = Gem::Request.get_cert_files
19+
expect(cert_files).to eq(gem_cert_files)
20+
end
21+
end
22+
23+
context "when ssl_ca_cert setting is set" do
24+
let(:ca_cert_path) { "/path/to/ca_cert" }
25+
26+
before do
27+
allow(Bundler.settings).to receive(:[]).and_call_original
28+
allow(Bundler.settings).to receive(:[]).with(:ssl_ca_cert).and_return(ca_cert_path)
29+
end
30+
31+
it "appends the setting to @cert_files" do
32+
cert_files = subject.instance_variable_get(:@cert_files)
33+
gem_cert_files = Gem::Request.get_cert_files
34+
expect(cert_files).to eq(gem_cert_files + [ca_cert_path])
35+
end
36+
end
37+
end
38+
939
describe "Parallel download" do
1040
it "download using multiple connections from the pool" do
1141
unless Bundler.rubygems.provides?(">= 4.0.0.dev")

0 commit comments

Comments
 (0)