├── .github └── workflows │ └── ci.yml ├── README.md ├── check.rb └── nginx_ssl.conf /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: >- 8 | ${{ matrix.os }} ${{ matrix.ruby }} 9 | 10 | runs-on: ${{ matrix.os }}-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ ubuntu, macos, windows ] 15 | ruby: [ 2.7, '3.0', 3.1, 3.2, 3.3, 3.4, head ] 16 | include: 17 | - { os: windows , ruby: mingw } 18 | - { os: windows , ruby: ucrt } 19 | - { os: windows , ruby: mswin } 20 | exclude: 21 | - { os: windows , ruby: head } 22 | 23 | steps: 24 | - name: repo checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: load ruby 28 | uses: ruby/setup-ruby@v1 29 | with: 30 | ruby-version: ${{ matrix.ruby }} 31 | 32 | - name: run check.rb 33 | run: | 34 | ruby check.rb 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > This repo is deprecated because `bundle doctor --ssl` now provides information 3 | > to troubleshoot SSL issues. It will still be maintained until all supported 4 | > rubies ship with a version of Bundler providing `bundle doctor --ssl`, because 5 | > many times SSL issues prevent users from upgrading Bundler. 6 | 7 | # ruby-ssl-check 8 | 9 | This script tries to help you diagnose SSL problems, especially related to certificates or TLS version while connecting to https://rubygems.org. 10 | 11 | It can be run by the following: 12 | ``` 13 | curl -Lks 'https://git.io/rg-ssl' | ruby 14 | ``` 15 | -------------------------------------------------------------------------------- /check.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Encoding: utf-8 3 | 4 | if RUBY_VERSION < "2.7" 5 | warn "!!! WARNING !!!", 6 | "Ruby #{RUBY_VERSION} has reached end-of-life, and is unsupported.", 7 | "This script may not work.", 8 | "" 9 | end 10 | 11 | if ARGV.include?("-h") || ARGV.include?("--help") 12 | puts "USAGE: check.rb [HOSTNAME] [TLS_VERSION] [VERIFY]" 13 | puts " default: check.rb rubygems.org auto VERIFY_PEER" 14 | puts " example: check.rb github.com TLSv1_2 VERIFY_NONE" 15 | exit 0 16 | end 17 | 18 | host = ARGV.shift || "rubygems.org" 19 | 20 | require 'uri' 21 | require 'net/http' 22 | 23 | begin 24 | require 'openssl' 25 | rescue LoadError 26 | puts "Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to #{host}.", 27 | "You'll need to recompile or reinstall Ruby with OpenSSL support and try again." 28 | exit 1 29 | end 30 | 31 | begin 32 | # Some versions of Ruby need this require to do HTTPS 33 | require 'net/https' 34 | # Try for RubyGems version 35 | require 'rubygems' 36 | # Try for Bundler version 37 | require 'bundler' 38 | require 'bundler/vendor/uri/lib/uri' 39 | rescue LoadError 40 | end 41 | 42 | uri = URI("https://#{host}") 43 | tls_version = ARGV.shift 44 | verify_mode = ARGV.any? ? OpenSSL::SSL.const_get(ARGV.shift) : OpenSSL::SSL::VERIFY_PEER 45 | 46 | if defined?(RUBY_DESCRIPTION) 47 | ruby_version = RUBY_DESCRIPTION 48 | else 49 | ruby_version = RUBY_VERSION.dup 50 | ruby_version << "p#{RUBY_PATCHLEVEL}" if defined?(RUBY_PATCHLEVEL) 51 | ruby_version << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION})" 52 | ruby_version << " [#{RUBY_PLATFORM}]" 53 | end 54 | 55 | puts "", "Here's your Ruby and OpenSSL environment:" 56 | puts 57 | puts "Ruby: %s" % ruby_version 58 | puts "RubyGems: %s" % Gem::VERSION if defined?(Gem::VERSION) 59 | puts "Bundler: %s" % Bundler::VERSION if defined?(Bundler::VERSION) 60 | puts "OpenSSL: %s" % OpenSSL::VERSION if defined?(OpenSSL::VERSION) 61 | puts "Compiled with: %s" % OpenSSL::OPENSSL_VERSION 62 | puts "Loaded with: %s" % OpenSSL::OPENSSL_LIBRARY_VERSION if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) 63 | puts 64 | 65 | def show_ssl_certs 66 | puts "", "Below affect only Ruby net/http connections:" 67 | puts 68 | t = ENV['SSL_CERT_FILE'] || OpenSSL::X509::DEFAULT_CERT_FILE 69 | ssl_file = File.exist?(t) ? "✅ exists #{t}" : "❌ is missing #{t}" 70 | puts "SSL_CERT_FILE: %s" % ssl_file 71 | 72 | t = ENV['SSL_CERT_DIR'] || OpenSSL::X509::DEFAULT_CERT_DIR 73 | ssl_dir = Dir.exist?(t) ? "✅ exists #{t}" : "❌ is missing #{t}" 74 | puts "SSL_CERT_DIR: %s" % ssl_dir 75 | puts 76 | end 77 | 78 | def error_reason(error) 79 | case error.message 80 | when /certificate verify failed/ 81 | "certificate verification" 82 | when /read server hello A/ 83 | "SSL/TLS protocol version mismatch" 84 | when /tlsv1 alert protocol version/ 85 | "requested TLS version is too old" 86 | else 87 | error.message 88 | end 89 | end 90 | 91 | puts "Trying connections to #{uri.to_s}:" 92 | puts 93 | begin 94 | b_uri = defined?(Bundler::URI) ? Bundler::URI(uri.to_s) : uri 95 | Bundler::Fetcher.new(Bundler::Source::Rubygems::Remote.new(b_uri)).send(:connection).request(b_uri) 96 | bundler_status = "✅ success" 97 | rescue => error 98 | bundler_status = "❌ failed (#{error_reason(error)})" 99 | end 100 | puts "Bundler: #{bundler_status}" 101 | 102 | begin 103 | require 'rubygems/remote_fetcher' 104 | Gem::RemoteFetcher.fetcher.fetch_path(uri) 105 | rubygems_status = "✅ success" 106 | rescue => error 107 | rubygems_status = "❌ failed (#{error_reason(error)})" 108 | end 109 | puts "RubyGems: #{rubygems_status}" 110 | 111 | begin 112 | # Try to connect using HTTPS 113 | Net::HTTP.new(uri.host, uri.port).tap do |http| 114 | http.use_ssl = true 115 | if tls_version 116 | if http.respond_to? :min_version= 117 | vers = tls_version.sub("v", "").to_sym 118 | http.min_version = vers 119 | http.max_version = vers 120 | else 121 | http.ssl_version = tls_version.to_sym 122 | end 123 | end 124 | http.verify_mode = verify_mode 125 | end.start 126 | 127 | puts "Ruby net/http: ✅ success" 128 | puts 129 | rescue => error 130 | puts "Ruby net/http: ❌ failed" 131 | puts 132 | puts "Unfortunately, this Ruby can't connect to #{host}. 😡" 133 | 134 | case error.message 135 | # Check for certificate errors 136 | when /certificate verify failed/ 137 | show_ssl_certs 138 | puts "\nYour Ruby can't connect to #{host} because you are missing the certificate", 139 | "files OpenSSL needs to verify you are connecting to the genuine #{host} servers.", "" 140 | # Check for TLS version errors 141 | when /read server hello A/, /tlsv1 alert protocol version/ 142 | if tls_version == "TLSv1_3" 143 | puts "\nYour Ruby can't connect to #{host} because #{tls_version} isn't supported yet.\n\n" 144 | else 145 | puts "\nYour Ruby can't connect to #{host} because your version of OpenSSL is too old.", 146 | "You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.", "" 147 | end 148 | # OpenSSL doesn't support TLS version specified by argument 149 | when /unknown SSL method/ 150 | puts "\nYour Ruby can't connect because #{tls_version} isn't supported by your version of OpenSSL.\n\n" 151 | else 152 | puts "\nEven worse, we're not sure why. 😕" 153 | puts 154 | puts "Here's the full error information:", 155 | "#{error.class}: #{error.message}", 156 | " #{error.backtrace.join("\n ")}" 157 | puts 158 | puts "You might have more luck using Mislav's SSL doctor.rb script. You can get it here:", 159 | "https://github.com/mislav/ssl-tools/blob/8b3dec4/doctor.rb", 160 | "Read more about the script and how to use it in this blog post:", 161 | "https://mislav.net/2013/07/ruby-openssl/", "" 162 | end 163 | exit 1 164 | end 165 | 166 | guide_url = "http://ruby.to/ssl-check-failed" 167 | if bundler_status =~ /success/ && rubygems_status =~ /success/ 168 | # Whoa, it seems like it's working! 169 | puts "Hooray! This Ruby can connect to #{host}.", 170 | "You are all set to use Bundler and RubyGems. 👌", "" 171 | elsif rubygems_status !~ /success/ 172 | puts "It looks like Ruby and Bundler can connect to #{host}, but RubyGems itself", 173 | "cannot. You can likely solve this by manually downloading and installing a", 174 | "RubyGems update. Visit #{guide_url} for instructions on how to manually upgrade RubyGems. 💎" 175 | elsif bundler_status !~ /success/ 176 | puts "Although your Ruby installation and RubyGems can both connect to #{host},", 177 | "Bundler is having trouble. The most likely way to fix this is to upgrade", 178 | "Bundler by running `gem install bundler`. Run this script again after doing", 179 | "that to make sure everything is all set. If you're still having trouble,", 180 | "check out the troubleshooting guide at #{guide_url} 📦" 181 | else 182 | puts "For some reason, your Ruby installation can connect to #{host}, but neither", 183 | "RubyGems nor Bundler can. The most likely fix is to manually upgrade RubyGems by", 184 | "following the instructions at #{guide_url}. After you've done that, run `gem install", 185 | "bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣️" 186 | end 187 | 188 | def tls12_supported? 189 | ctx = OpenSSL::SSL::SSLContext.new 190 | if ctx.methods.include?(:min_version=) 191 | ctx.min_version = ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION 192 | true 193 | else 194 | OpenSSL::SSL::SSLContext::METHODS.include?(:TLSv1_2) 195 | end 196 | rescue 197 | end 198 | 199 | # We were able to connect, but perhaps this Ruby will have trouble when we require TLSv1.2 200 | unless tls12_supported? 201 | puts "\nWARNING: Although your Ruby can connect to #{host} today, your OpenSSL is very old! 👴", 202 | "WARNING: You will need to upgrade OpenSSL to use #{host}." 203 | exit 1 204 | end 205 | 206 | exit 0 207 | -------------------------------------------------------------------------------- /nginx_ssl.conf: -------------------------------------------------------------------------------- 1 | # A minimal nginx configuration to test SSL connections against. 2 | # Generate the needed files by running these commands: 3 | # 4 | # mkdir -p /tmp/nginx/public 5 | # echo "hi" > /tmp/nginx/public/index.html 6 | # openssl req -x509 -nodes -newkey rsa:2048 -keyout /tmp/nginx/selfsigned.key -out /tmp/nginx/selfsigned.crt 7 | # 8 | 9 | http { 10 | server { 11 | listen 443 ssl; 12 | server_name localhost; 13 | ssl_certificate /tmp/nginx/selfsigned.crt; 14 | ssl_certificate_key /tmp/nginx/selfsigned.key; 15 | ssl_protocols TLSv1.2; 16 | # To allow all protocols, use this instead: 17 | # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; 18 | 19 | location / { 20 | root /tmp/nginx/public; 21 | } 22 | } 23 | } 24 | --------------------------------------------------------------------------------