55 Minutes

Welcome to the 55 Minutes blog.
55 Minutes is a web development consultancy in San Francisco building apps for design-led businesses and startups. On this blog we discuss the technology tools of our trade: Ruby on Rails, Django, Sass, OS X, and more.

TLS Error with Ruby Client and Tomcat An Easy Client-Side Fix

If your Ruby is compiled against a recent version of OpenSSL (1.0.0 and up), there is a good chance you will run into frustrating TLS errors when trying to connect to an HTTPS site hosted by Apache Tomcat. An easy workaround is to set ssl_version = :SSLv3 on the client side.

Who’s affected?

This bug bites clients that are using OpenSSL 1.0 when connecting to Tomcat 7. If you are on a Mac running Homebrew, you should be safe: Macs are still bundled with OpenSSL 0.9.8. Here’s what my Lion machine reports:

$ openssl version
OpenSSL 0.9.8r 8 Feb 2011

However, if you are using MacPorts or a recent version of a Linux distribution, you might be stuck with OpenSSL 1.0. For example, here’s what OpenSSL reports on a fresh Ubuntu 11.10 VPS:

$ openssl version
OpenSSL 1.0.0e 6 Sep 2011

Trouble happens when your Ruby is compiled against OpenSSL 1.0 and you try connecting to a Tomcat 7 server using HTTPS. Here’s how to check on your Ruby installation:

$ ruby -r openssl -e 'puts OpenSSL::OPENSSL_VERSION'
OpenSSL 1.0.0e 6 Sep 2011

When you connect to an affected Tomcat 7 server, you’ll be greeted with a vague tlsv1 alert internal error error that looks something like this:

SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: tlsv1 alert internal error

An easy workaround

If you control the Tomcat server, you can modify Tomcat’s configuration to restrict the ciphers Tomcat uses and eliminate the problem.

Otherwise, working around the problem on the client side in Ruby is straightforward. When using Net::HTTP, just set ssl_version = :SSLv3:1

http = Net::HTTP.new(host, port)
http.use_ssl = true
http.ssl_version = :SSLv3
http.start { ... }

The same trick also works with the net-http-persistent gem:

http = Net::HTTP::Persistent.new
http.ssl_version = :SSLv3
http.request(...)

Notes

  1. Always using SSLv3 is not a general-purpose solution. For best security, you want to stick with Ruby’s default, which is to try TLSv1 before falling back to older versions. Unfortunately, this particular SSL-Tomcat bug breaks the normal automatic fallback. Use this explicit SSLv3 workaround only after you’ve explored other options for fixing the issue on the Tomcat side.

comments powered by Disqus