From a89bde14faee428320b5e99ad7cedec24c4a5b46 Mon Sep 17 00:00:00 2001 From: Aaron Lippold Date: Sat, 28 Sep 2019 14:17:19 -0400 Subject: [PATCH] - added helper scripts to dianose and resolve the SSL issues - added docs to help explain and safe the user time and research Fixes #89 Signed-off-by: Aaron Lippold --- docs/.ruby-version | 2 +- docs/_docs/ssl_errors.md | 41 ++++++++++++++ docs/utils/ssl-doctor.rb | 89 +++++++++++++++++++++++++++++++ docs/utils/test-aws-api-access.rb | 11 ++++ docs/utils/update-cert-chains.sh | 11 ++++ 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 docs/_docs/ssl_errors.md create mode 100755 docs/utils/ssl-doctor.rb create mode 100755 docs/utils/test-aws-api-access.rb create mode 100755 docs/utils/update-cert-chains.sh diff --git a/docs/.ruby-version b/docs/.ruby-version index aedc15bb..e70b4523 100644 --- a/docs/.ruby-version +++ b/docs/.ruby-version @@ -1 +1 @@ -2.5.3 +2.6.0 diff --git a/docs/_docs/ssl_errors.md b/docs/_docs/ssl_errors.md new file mode 100644 index 00000000..d961b76d --- /dev/null +++ b/docs/_docs/ssl_errors.md @@ -0,0 +1,41 @@ +--- +Title: SSL Errors +# nav_order: +--- + +UFO uses the AWS Ruby SDK and the underlying default SSL certificate chain configured in your active Ruby and +OpenSSL to communicate to your AWS environment. This means that you _must correctly configure_ your Ruby and OpenSSL to have all the needed ROOT certificates for UFO to be able to communicate to AWS - _especially_ if you are behind a proxy or a corporate SSL-Proxy. + +If you are behind a corporate SSL proxy and you have not updated system, OpenSSL and Ruby certificate chains to include the needed corporate root certificates, you will see errors, such as: + +``` +Seahorse::Client::NetworkingError: SSL_connect returned=1 errno=0 state=error: certificate verify failed (self signed certificate in certificate chain) + ~/.rbenv/versions/2.6.0/lib/ruby/2.6.0/net/protocol.rb:44:in `connect_nonblock' + ~/.rbenv/versions/2.6.0/lib/ruby/2.6.0/net/protocol.rb:44:in `ssl_socket_connect' + ~/.rbenv/versions/2.6.0/lib/ruby/2.6.0/net/http.rb:996:in `connect' + ~/.rbenv/versions/2.6.0/lib/ruby/2.6.0/net/http.rb:930:in `do_start' + ~/.rbenv/versions/2.6.0/lib/ruby/2.6.0/net/http.rb:925:in `start' +``` + +## Helper Scripts + +The `docs/utils` directory has a few scripts that should be able to help you resolve these issues and track down which certs are giving you issues. + +- `ssl-doctor.rb` if from the very useful examples at , and it can help you find the missing ROOT cert in your certificate chain and give suggestion on getting OpenSSL working correctly. +- `update-cert-chains.sh` will help you update your Ruby and OpenSSL chains by adding in the missing ROOT cert and also pulling in the OSX System Root to your rbenv environment. +- `test-aws-api-access.rb` should now return a list of the S3 buckets for the current AWS profile that is active. + +## Trouble-shooting + +### Update Brew and OpenSSL + +- `brew update` +- `brew upgrade openssl` + +### Use the Helper Scripts to find the trouble spot + +Once you have updated OpenSSL and your `brew` packages, use the helper scripts above to see if you can track down the missing certificate in your certificate chain. + +The `update-cert-chain.sh` file was created using the suggestions from . Please review the information at if the `Helper Scripts` above do not fully resolve your issue. + +The `test-aws-api-access.rb` uses examples from the for using and configuring the Ruby AWS SDK on your system. diff --git a/docs/utils/ssl-doctor.rb b/docs/utils/ssl-doctor.rb new file mode 100755 index 00000000..d7426670 --- /dev/null +++ b/docs/utils/ssl-doctor.rb @@ -0,0 +1,89 @@ +# Usage: ruby doctor.rb [HOST=status.github.com[:PORT=443]] +# see: https://github.com/mislav/ssl-tools +require 'rbconfig' +require 'net/https' + +if ARGV[0] =~ /^[^-]/ + host, port = ARGV[0].split(':', 2) +else + host = 'status.github.com' +end +port ||= 443 + +ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) +ruby_version = RUBY_VERSION +if patch = RbConfig::CONFIG['PATCHLEVEL'] + ruby_version += "-p#{patch}" +end +puts "%s (%s)" % [ruby, ruby_version] + +openssl_dir = OpenSSL::X509::DEFAULT_CERT_AREA +mac_openssl = '/System/Library/OpenSSL' == openssl_dir +puts "%s: %s" % [OpenSSL::OPENSSL_VERSION, openssl_dir] +[OpenSSL::X509::DEFAULT_CERT_DIR_ENV, OpenSSL::X509::DEFAULT_CERT_FILE_ENV].each do |key| + puts "%s=%s" % [key, ENV[key].to_s.inspect] +end + +ca_file = ENV[OpenSSL::X509::DEFAULT_CERT_FILE_ENV] || OpenSSL::X509::DEFAULT_CERT_FILE +ca_path = (ENV[OpenSSL::X509::DEFAULT_CERT_DIR_ENV] || OpenSSL::X509::DEFAULT_CERT_DIR).chomp('/') + +puts "\nHEAD https://#{host}:#{port}" +http = Net::HTTP.new(host, port) +http.use_ssl = true + +# Explicitly setting cert_store like this is not needed in most cases but it +# seems necessary in edge cases such as when using `verify_callback` in some +# combination of Ruby + OpenSSL versions. +http.cert_store = OpenSSL::X509::Store.new +http.cert_store.set_default_paths + +http.verify_mode = OpenSSL::SSL::VERIFY_PEER +failed_cert = failed_cert_reason = nil + +if mac_openssl + warn "warning: will not be able show failed certificate info on OS X's OpenSSL" + # This drives me absolutely nuts. It seems that on Rubies compiled against OS X's + # system OpenSSL, the mere fact of defining a `verify_callback` makes the + # cert verification fail for requests that would otherwise be successful. +else + http.verify_callback = lambda { |verify_ok, store_context| + if !verify_ok + failed_cert = store_context.current_cert + failed_cert_reason = "%d: %s" % [ store_context.error, store_context.error_string ] + end + verify_ok + } +end + +user_agent = "net/http #{ruby_version}" +req = Net::HTTP::Head.new('/', 'user-agent' => user_agent) + +begin + res = http.start { http.request(req) } + abort res.inspect if res.code.to_i >= 500 + puts "OK" +rescue Errno::ECONNREFUSED + puts "Error: connection refused" + exit 1 +rescue OpenSSL::SSL::SSLError => e + puts "#{e.class}: #{e.message}" + + if failed_cert + puts "\nThe server presented a certificate that could not be verified:" + puts " subject: #{failed_cert.subject}" + puts " issuer: #{failed_cert.issuer}" + puts " error code %s" % failed_cert_reason + end + + ca_file_missing = !File.exist?(ca_file) && !mac_openssl + ca_path_empty = Dir["#{ca_path}/*"].empty? + + if ca_file_missing || ca_path_empty + puts "\nPossible causes:" + puts " `%s' does not exist" % ca_file if ca_file_missing + puts " `%s/' is empty" % ca_path if ca_path_empty + end + + exit 1 +end + diff --git a/docs/utils/test-aws-api-access.rb b/docs/utils/test-aws-api-access.rb new file mode 100755 index 00000000..37e0d205 --- /dev/null +++ b/docs/utils/test-aws-api-access.rb @@ -0,0 +1,11 @@ +# usage 'ruby s3-cert-chain-test.rb' +# see: https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/quick-start-guide.html + +require 'aws-sdk-s3' # v2: require 'aws-sdk' +#Aws.use_bundled_cert! + +s3 = Aws::S3::Resource.new(region: 'us-east-1') + +s3.buckets.limit(50).each do |b| + puts "#{b.name}" +end diff --git a/docs/utils/update-cert-chains.sh b/docs/utils/update-cert-chains.sh new file mode 100755 index 00000000..f5760b9f --- /dev/null +++ b/docs/utils/update-cert-chains.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cert_file=$(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE' 2>/dev/null) +echo 'What is the uri to your organizations root certificate chain?' +read -p 'org_root_chain: ' org_root_chain +echo "$org_root_chain" +curl "$org_root_chain" -o org_chain.txt +cat org_chain.txt >> "$cert_file" +mkdir -p "${cert_file%/*}" +security find-certificate -a -p /Library/Keychains/System.keychain > "$cert_file" +security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >> "$cert_file"