#!/usr/bin/env ruby # Encoding: utf-8 if RUBY_VERSION < "2.7" warn "!!! WARNING !!!", "Ruby #{RUBY_VERSION} has reached end-of-life, and is unsupported.", "This script may not work.", "" end if ARGV.include?("-h") || ARGV.include?("--help") puts "USAGE: check.rb [HOSTNAME] [TLS_VERSION] [VERIFY]" puts " default: check.rb rubygems.org auto VERIFY_PEER" puts " example: check.rb github.com TLSv1_2 VERIFY_NONE" exit 0 end host = ARGV.shift || "rubygems.org" require 'uri' require 'net/http' begin require 'openssl' rescue LoadError abort "Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to #{host}. " \ "You'll need to recompile or reinstall Ruby with OpenSSL support and try again." end begin # Some versions of Ruby need this require to do HTTPS require 'net/https' # Try for RubyGems version require 'rubygems' # Try for Bundler version require 'bundler' require 'bundler/vendor/uri/lib/uri' rescue LoadError end uri = URI("https://#{host}") ssl_version = ARGV.shift verify_mode = ARGV.any? ? OpenSSL::SSL.const_get(ARGV.shift) : OpenSSL::SSL::VERIFY_PEER ruby_version = RUBY_VERSION.dup ruby_version << "p#{RUBY_PATCHLEVEL}" if defined?(RUBY_PATCHLEVEL) ruby_version << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION})" ruby_version << " [#{RUBY_PLATFORM}]" puts "Here's your Ruby and OpenSSL environment:" puts puts "Ruby: %s" % ruby_version puts "RubyGems: %s" % Gem::VERSION if defined?(Gem::VERSION) puts "Bundler: %s" % Bundler::VERSION if defined?(Bundler::VERSION) puts "Compiled with: %s" % OpenSSL::OPENSSL_VERSION puts "Loaded version: %s" % OpenSSL::OPENSSL_LIBRARY_VERSION if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) puts "SSL_CERT_FILE: %s" % OpenSSL::X509::DEFAULT_CERT_FILE puts "SSL_CERT_DIR: %s" % OpenSSL::X509::DEFAULT_CERT_DIR puts puts "With that out of the way, let's see if you can connect to #{host}..." puts def error_reason(error) case error.message when /certificate verify failed/ "certificate verification" when /read server hello A/ "SSL/TLS protocol version mismatch" when /tlsv1 alert protocol version/ "requested TLS version is too old" else error.message end end begin b_uri = defined?(Bundler::URI) ? Bundler::URI(uri.to_s) : uri Bundler::Fetcher.new(Bundler::Source::Rubygems::Remote.new(b_uri)).send(:connection).request(b_uri) bundler_status = "success ✅" rescue => error bundler_status = "failed ❌ (#{error_reason(error)})" end puts "Bundler connection to #{host}: #{bundler_status}" begin require 'rubygems/remote_fetcher' Gem::RemoteFetcher.fetcher.fetch_path(uri) rubygems_status = "success ✅" rescue => error rubygems_status = "failed ❌ (#{error_reason(error)})" end puts "RubyGems connection to #{host}: #{rubygems_status}" begin # Try to connect using HTTPS Net::HTTP.new(uri.host, uri.port).tap do |http| http.use_ssl = true if ssl_version if http.respond_to? :min_version= vers = ssl_version.sub("v", "").to_sym http.min_version = vers http.max_version = vers else http.ssl_version = ssl_version.to_sym end end http.verify_mode = verify_mode end.start puts "Ruby net/http connection to #{host}: success ✅" puts rescue => error puts "Ruby net/http connection to #{host}: failed ❌" puts puts "Unfortunately, this Ruby can't connect to #{host}. 😡" case error.message # Check for certificate errors when /certificate verify failed/ abort "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." # Check for TLS version errors when /read server hello A/, /tlsv1 alert protocol version/ if ssl_version == "TLSv1_3" abort "Your Ruby can't connect to #{host} because #{ssl_version} isn't supported yet." else abort "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." end else puts "Even worse, we're not sure why. 😕" puts puts "Here's the full error information:" puts "#{error.class}: #{error.message}" puts " " << error.backtrace.join("\n ") puts puts "You might have more luck using Mislav's SSL doctor.rb script. You can get it here:" puts "https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb" puts "Read more about the script and how to use it in this blog post:" puts "https://mislav.net/2013/07/ruby-openssl/" abort end end guide_url = "http://ruby.to/ssl-check-failed" if bundler_status =~ /success/ && rubygems_status =~ /success/ # Whoa, it seems like it's working! puts "Hooray! This Ruby can connect to #{host}. You are all set to use Bundler and RubyGems. 👌" elsif rubygems_status !~ /success/ puts "It looks like Ruby and Bundler can connect to #{host}, but RubyGems itself cannot. You can likely solve this by manually downloading and installing a RubyGems update. Visit #{guide_url} for instructions on how to manually upgrade RubyGems. 💎" elsif bundler_status !~ /success/ puts "Although your Ruby installation and RubyGems can both connect to #{host}, Bundler is having trouble. The most likely way to fix this is to upgrade Bundler by running `gem install bundler`. Run this script again after doing that to make sure everything is all set. If you're still having trouble, check out the troubleshooting guide at #{guide_url} 📦" else puts "For some reason, your Ruby installation can connect to #{host}, but neither RubyGems nor Bundler can. The most likely fix is to manually upgrade RubyGems by following the instructions at #{guide_url}. After you've done that, run `gem install bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣️" end def tls12_supported? ctx = OpenSSL::SSL::SSLContext.new if ctx.methods.include?(:min_version=) ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION true else OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) end rescue end # We were able to connect, but perhaps this Ruby will have trouble when we require TLSv1.2 unless tls12_supported? puts puts "WARNING: Although your Ruby can connect to #{host} today, your OpenSSL is very old! 👴" puts "WARNING: You will need to upgrade OpenSSL before January 2018 in order to keep using #{host}." abort end exit 0