-
Notifications
You must be signed in to change notification settings - Fork 74
/
check.rb
executable file
·213 lines (192 loc) · 7.26 KB
/
check.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/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
puts "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."
exit 1
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}")
tls_version = ARGV.shift
verify_mode = ARGV.any? ? OpenSSL::SSL.const_get(ARGV.shift) : OpenSSL::SSL::VERIFY_PEER
if defined?(RUBY_DESCRIPTION)
ruby_version = RUBY_DESCRIPTION
else
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}]"
end
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 "OpenSSL: %s" % OpenSSL::VERSION if defined?(OpenSSL::VERSION)
puts "Compiled with: %s" % OpenSSL::OPENSSL_VERSION
puts "Loaded with: %s" % OpenSSL::OPENSSL_LIBRARY_VERSION if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION)
puts
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 = if Dir.exist? t
"✅ exists #{t}"
elsif RUBY_PLATFORM.end_with? 'linux'
t = '/etc/ssl/certs/ca-certificates.crt'
Dir.exist?(t) ? "✅ exists #{t}" : "❌ is missing #{t}"
else
"❌ is missing #{t}"
end
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
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
puts "Trying connections to #{uri.to_s}:"
puts
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: #{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: #{rubygems_status}"
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/
# 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 "\nWARNING: Although your Ruby can connect to #{host} today, your OpenSSL is very old! 👴",
"WARNING: You will need to upgrade OpenSSL to use #{host}."
exit 1
end
exit 0