I recently had a legacy machine running an ancient Ruby 1.8.7 app which I needed to upgrade to support TLS 1.2. A few bespoke requirements made it infeasible to upgrade the core OS to a more modern version, but fortunately, it was relatively straightforward to get it back to a servicable state. While it would be vastly preferable to rebuild the application for a more modern stack, it wasn’t feasible in this case, so this is the next-best option.
The biggest issue with upgrading these kinds of machines is the ancient OpenSSL versions linked against tools like curl and wget, which can make it difficult to retrieve files from remote locations for upgrading! Our path forward is SCP.
The tl;dr here is that we’re going to:
- scp tarballs of the openssl and curl sources to the target machine
- Build and install them, with Curl pointed to our newly-installed OpenSSL
- Use the upgraded Curl to bootstrap (or ugprade RVM)
- Use RVM to install an RVM-specific OpenSSL
- Use RVM to install Ruby with our new RVM OpenSSL install
- Custom-compile the Passenger agent.
Let’s get started.
Curl’s a fundamental tool, one that we take for granted far too often. Old versions which only support TLS 1.0 can’t communicate with large parts of the web now, so our first order of business is to get a working communications line out to the rest of the web.
scp curl-7.60.0.tar.gz user@target:~ scp openssl-1.0.2o.tar.gz user@target:~
Then on my target machine, as root:
mv /home/user/curl-7.60.0.tar.gz /usr/local/src mv /home/user/openssl-1.0.2o.tar.gz /usr/local/src cd /usr/local/src tar -xzf curl-7.60.0.tar.gz tar -xzf openssl-1.0.2o.tar.gz
Time to compile OpenSSL:
cd openssl-1.0.2o ./configure --prefix=/opt/openssl-1.0.2o make && make install
cd ../curl-7.60.0 PKG_CONFIG_PATH=/opt/openssl-1.0.2o/lib/pkgconfig ./configure make && make install
Verify that we have the right version:
$ curl -V curl 7.60.0 (i686-pc-linux-gnu) libcurl/7.60.0 OpenSSL/1.0.2o zlib/1.2.3 Release-Date: 2018-05-16 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP UnixSockets HTTPS-proxy
Great! We have a working lifeline to the outside world. Let’s make sure we can speak TLS 1.2:
$ curl --tlsv1.2 -I https://google.com/ HTTP/1.1 301 Moved Permanently
Getting and installing RVM
Now that we have Curl, we can install RVM via the usual mechanism:
\curl -sSL https://get.rvm.io | bash
Relog your session, and then you have RVM ready:
rvm pkg install openssl
This will custom-compile OpenSSL for RVM to use with the Ruby versions it installs. Then we just install Ruby:
rvm install ree --with-openssl-dir=/usr/local/rvm/usr
And test it:
rvm use ree ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' OpenSSL 1.0.1i 6 Aug 2014
Finally, we needed a updated passenger install. This is slightly fiddly because:
- Newer gem versions won’t work on Ruby 1.8.7
- Ruby 1.8.7 ships with a version of Rubygems that doesn’t realize that
- Passenger on Ruby 1.8.7 won’t boot up apps with newer versions of Rubygems.
We’ll work around this by doing the install with a newer Rubygems, then downgrading it for compatibility.
First, upgrade Rubygems:
gem install rubygems-update update_rubygems
Then install Passenger:
gem install passenger -v 5.3.2
We’ll need to custom-compile Passenger’s agent against our custom OpenSSL version:
EXTRA_PRE_LDFLAGS="-L/usr/local/rvm/lib" EXTRA_CXXFLAGS="-I/usr/local/rvm/include" EXTRA_CFLAGS="-I/usr/local/rvm/include" passenger-install-nginx-module
Finally, downgrade Rubygems:
rvm rubygems latest-1.8
Restart nginx and you should be up and running.