summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/bundler/cli/doctor/ssl.rb76
-rw-r--r--libexec/ssl_check.rb67
-rw-r--r--spec/bundler/commands/ssl_spec.rb94
3 files changed, 170 insertions, 67 deletions
diff --git a/lib/bundler/cli/doctor/ssl.rb b/lib/bundler/cli/doctor/ssl.rb
index f0d8a9b986..7ddca0c8a4 100644
--- a/lib/bundler/cli/doctor/ssl.rb
+++ b/lib/bundler/cli/doctor/ssl.rb
@@ -17,6 +17,8 @@ module Bundler
output_ssl_environment
bundler_success = bundler_connection_successful?
rubygem_success = rubygem_connection_successful?
+
+ return unless net_http_connection_successful?
end
private
@@ -92,6 +94,29 @@ module Bundler
false
end
+ def net_http_connection_successful?
+ ::Gem::Net::HTTP.new(uri.host, uri.port).tap do |http|
+ http.use_ssl = true
+ http.min_version = tls_version
+ http.max_version = tls_version
+ http.verify_mode = verify_mode
+ end.start
+
+ Bundler.ui.info("Ruby net/http: success")
+
+ true
+ rescue StandardError => error
+ Bundler.ui.warn(<<~MSG)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to #{host}.
+
+ #{Explanation.explain_net_http_error(error, host, tls_version)}
+ MSG
+
+ false
+ end
+
module Explanation
extend self
@@ -107,6 +132,57 @@ module Bundler
error.message
end
end
+
+ def explain_net_http_error(error, host, tls_version)
+ case error.message
+ # Check for certificate errors
+ when /certificate verify failed/
+ <<~MSG
+ #{show_ssl_certs}
+ Your Ruby can't connect to #{host} because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine #{host} servers.
+ MSG
+ # Check for TLS version errors
+ when /read server hello A/, /tlsv1 alert protocol version/
+ if tls_version.to_s == "TLS1_3"
+ "Your Ruby can't connect to #{host} because #{tls_version} isn't supported yet.\n"
+ else
+ <<~MSG
+ Your Ruby can't connect to #{host} because your version of OpenSSL is too old.
+ You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
+ MSG
+ end
+ # OpenSSL doesn't support TLS version specified by argument
+ when /unknown SSL method/
+ "Your Ruby can't connect because #{tls_version} isn't supported by your version of OpenSSL."
+ else
+ <<~MSG
+ Even worse, we're not sure why.
+
+ Here's the full error information:
+ #{error.class}: #{error.message}
+ #{error.backtrace.join("\n ")}
+
+ You might have more luck using Mislav's SSL doctor.rb script. You can get it here:
+ https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb
+
+ Read more about the script and how to use it in this blog post:
+ https://mislav.net/2013/07/ruby-openssl/
+ MSG
+ end
+ end
+
+ private
+
+ def show_ssl_certs
+ ssl_cert_file = ENV["SSL_CERT_FILE"] || OpenSSL::X509::DEFAULT_CERT_FILE
+ ssl_cert_dir = ENV["SSL_CERT_DIR"] || OpenSSL::X509::DEFAULT_CERT_DIR
+
+ <<~MSG
+ Below affect only Ruby net/http connections:
+ SSL_CERT_FILE: #{File.exist?(ssl_cert_file) ? "exists #{ssl_cert_file}" : "is missing #{ssl_cert_file}"}
+ SSL_CERT_DIR: #{Dir.exist?(ssl_cert_dir) ? "exists #{ssl_cert_dir}" : "is missing #{ssl_cert_dir}"}
+ MSG
+ end
end
end
end
diff --git a/libexec/ssl_check.rb b/libexec/ssl_check.rb
index 39f60c47a1..c29d3bfb0f 100644
--- a/libexec/ssl_check.rb
+++ b/libexec/ssl_check.rb
@@ -30,73 +30,6 @@ puts "Ruby: %s" % ruby_version
puts "RubyGems: %s" % Gem::VERSION if defined?(Gem::VERSION)
puts "Bundler: %s" % Bundler::VERSION if defined?(Bundler::VERSION)
-def show_ssl_certs
- puts "", "Below affect only Ruby net/http connections:"
- puts
- t = ENV['SSL_CERT_FILE'] || OpenSSL::X509::DEFAULT_CERT_FILE
- ssl_file = File.exist?(t) ? "✅ exists #{t}" : "❌ is missing #{t}"
- puts "SSL_CERT_FILE: %s" % ssl_file
-
- t = ENV['SSL_CERT_DIR'] || OpenSSL::X509::DEFAULT_CERT_DIR
- ssl_dir = Dir.exist?(t) ? "✅ exists #{t}" : "❌ is missing #{t}"
- puts "SSL_CERT_DIR: %s" % ssl_dir
- puts
-end
-
-begin
- # Try to connect using HTTPS
- Net::HTTP.new(uri.host, uri.port).tap do |http|
- http.use_ssl = true
- if tls_version
- if http.respond_to? :min_version=
- vers = tls_version.sub("v", "").to_sym
- http.min_version = vers
- http.max_version = vers
- else
- http.ssl_version = tls_version.to_sym
- end
- end
- http.verify_mode = verify_mode
- end.start
-
- puts "Ruby net/http: ✅ success"
- puts
-rescue => error
- puts "Ruby net/http: ❌ failed"
- puts
- puts "Unfortunately, this Ruby can't connect to #{host}. 😡"
-
- case error.message
- # Check for certificate errors
- when /certificate verify failed/
- show_ssl_certs
- puts "\nYour Ruby can't connect to #{host} because you are missing the certificate",
- "files OpenSSL needs to verify you are connecting to the genuine #{host} servers.", ""
- # Check for TLS version errors
- when /read server hello A/, /tlsv1 alert protocol version/
- if tls_version == "TLSv1_3"
- puts "\nYour Ruby can't connect to #{host} because #{tls_version} isn't supported yet.\n\n"
- else
- puts "\nYour Ruby can't connect to #{host} because your version of OpenSSL is too old.",
- "You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.", ""
- end
- # OpenSSL doesn't support TLS version specified by argument
- when /unknown SSL method/
- puts "\nYour Ruby can't connect because #{tls_version} isn't supported by your version of OpenSSL.\n\n"
- else
- puts "\nEven worse, we're not sure why. 😕"
- puts
- puts "Here's the full error information:",
- "#{error.class}: #{error.message}",
- " #{error.backtrace.join("\n ")}"
- puts
- puts "You might have more luck using Mislav's SSL doctor.rb script. You can get it here:",
- "https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb",
- "Read more about the script and how to use it in this blog post:",
- "https://mislav.net/2013/07/ruby-openssl/", ""
- end
- exit 1
-end
guide_url = "http://ruby.to/ssl-check-failed"
if bundler_status =~ /success/ && rubygems_status =~ /success/
diff --git a/spec/bundler/commands/ssl_spec.rb b/spec/bundler/commands/ssl_spec.rb
index 1172bc9da7..4fc9db0016 100644
--- a/spec/bundler/commands/ssl_spec.rb
+++ b/spec/bundler/commands/ssl_spec.rb
@@ -67,6 +67,15 @@ RSpec.describe "bundle doctor ssl" do
expected_err = <<~MSG
Bundler: failed (certificate verification)
RubyGems: failed (certificate verification)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Below affect only Ruby net/http connections:
+ SSL_CERT_FILE: exists #{OpenSSL::X509::DEFAULT_CERT_FILE}
+ SSL_CERT_DIR: exists #{OpenSSL::X509::DEFAULT_CERT_DIR}
+
+ Your Ruby can't connect to rubygems.org because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine rubygems.org servers.
MSG
@@ -100,11 +109,54 @@ RSpec.describe "bundle doctor ssl" do
expected_err = <<~MSG
Bundler: failed (SSL/TLS protocol version mismatch)
RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because your version of OpenSSL is too old.
+ You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
+
+ MSG
+
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to unsupported tls 1.3 version" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because TLS1_3 isn't supported yet.
MSG
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3")
expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
end
+
end
context "when no diagnostic fails" do
@@ -119,11 +171,53 @@ RSpec.describe "bundle doctor ssl" do
Trying connections to https://rubygems.org:
Bundler: success
RubyGems: success
+ Ruby net/http: success
MSG
subject = Bundler::CLI::Doctor::SSL.new({})
expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
end
+
+ it "uses the tls_version verify mode and host when given as option" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ class << self
+ attr_accessor :verify_mode, :min_version, :max_version
+ end
+
+ def connect
+ self.class.verify_mode = verify_mode
+ self.class.min_version = min_version
+ self.class.max_version = max_version
+
+ super
+ end
+ end
+
+ net_http.endpoint = @dummy_endpoint
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://example.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3", "verify-mode": :none, host: "example.org")
+ expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
+ expect(net_http.verify_mode).to eq(0)
+ expect(net_http.min_version.to_s).to eq("TLS1_3")
+ expect(net_http.max_version.to_s).to eq("TLS1_3")
+ end
end
end