Gabe Evans

A catchy tagline.

Fixing RubyGems SSL Issues (Certificate Verify Failed)

Can you guess what took a significant portion of my time debugging this afternoon? I was going about my usual business, cloning a new project and bundling when I encountered this deliciously vague error:

Gem::RemoteFetcher::FetchError: SSL_connect returned=1 errno=0 state=SSLv3
read server certificate B: certificate verify failed
(https://s3.amazonaws.com/production.s3.rubygems.org/gems/builder-3.1.4.gem)
An error occurred while installing builder (3.1.4), and Bundler cannot
continue. Make sure that `gem install builder -v '3.1.4'` succeeds
before bundling.

I haven’t run into this one before, I thought to myself. I hadn’t updated any packages, changed any configurations, or done anything that could be the obvious cause of this issue. My first thought was to blame the repository I had cloned. Unfortunately, when I switched to another project I was greeted with the same message.

I obviously tried what bundler recommended and ran gem install builder -v '3.1.4', fully expecting the same message (as that’s usually how it goes). My expectations were met.

Being fairly experienced with Linux and OSX, I’m used to dealing with outdated SSL certificate bundles and explicitly specifying the path to OpenSSL. I figured it was a simple build issue or that the version of RubyGems I was using just needed a quick update. I was wrong:

$ gem update
Updating installed gems
Nothing to update

$ gem update --system
Latest version currently installed. Aborting.

The Worst Solution

I was surprised by the number of Stack Overflow answers suggesting that I change my Gemfile’s source line to http://rubygems.org (non-SSL). I considered it for a moment but couldn’t force myself into doing it.

The Solution

Insanity: doing the same thing over and over again and expecting different results.

(Albert Einstein)

$ bundle
Fetching source index from https://rubygems.org/
Could not verify the SSL certificate for https://rubygems.org/.
There is a chance you are experiencing a man-in-the-middle attack, but most
likely your system doesn't have the CA certificates needed for verification.
For information about OpenSSL certificates, see bit.ly/ruby-ssl. To connect
without using SSL, edit your Gemfile sources and change 'https' to 'http'.

The entertaining part of development is that you sometimes do get different results when doing the same thing over and over again. Making a quick visit to http://bit.ly/ruby-ssl gave me something to go off of.

$ ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE'
/usr/local/etc/openssl/cert.pem

When running the above in a terminal, I was able to see the exact location Ruby was getting its SSL certificate authority bundle. Mine was outdated which was causing the errors (when connecting to RubyGems.org, it would fail epicly, not being able to verify the authenticity of its certificate).

One dirty hack and all was back to normal:

$ curl http://curl.haxx.se/ca/cacert.pem > /usr/local/etc/openssl/cert.pem

TL;DR – The Solution

I’m not completely satisfied and I’m positive I’ll have to reference notes again in the future. Regardless, the following works:

Plan A: Homebrew/OSX

Let Homebrew keep everything updated for you:

rm $(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE') && \
brew install curl-ca-bundle && ln -s /usr/local/opt/curl-ca-bundle/share/ca-bundle.crt \
$(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE') \
echo 'export SSL_CERT_FILE=/usr/local/opt/curl-ca-bundle/share/ca-bundle.crt' >> ~/.bash_profile

The above commands will remove the old CA bundle, install curl-ca-bundle, a package containing the latest CA bundle directly from Mozilla, link it back to the old location, and set $SSL_CERT_FILE within your terminal for future use.

Plan B: Everything Else

$ curl http://curl.haxx.se/ca/cacert.pem > $(ruby -ropenssl -e 'puts OpenSSL::X509::DEFAULT_CERT_FILE')

The above command feels more like a band-aid than anything else but it will replace the outdated CA bundle with the latest one available from the cURL maintainers. My only issue (and at the same time, it’s a bit of inception): Why can’t we access the bundle over SSL to get the certificates needed to verify the SSL connection?

Ruby

Comments