person sitting at a computer compiling ruby
Ruby Linux Development
March 27, 2023 - Robert C.

Getting Ruby to Compile in Modern Linux Distros

If we lived and worked in a laboratory environment, everything everywhere would run, compile and “just work” with the right versions of dependencies and requirements working “just fine” - and we would all have world peace, and no children would go hungry. Back in the real world, though, it happens from time to time that the version of a dependency you need just isn’t working anymore. Usually, this means updating something or making a small adjustment. Every once in a while, though, dependencies get out of date, and updating things just isn’t an option. That’s where we find ourselves today.

Use Modern Linux

New versions of Linux have been released that dropped support for OpenSSL version 1.1 and instead only support newer versions of OpenSSL. This doesn’t mean that OpenSSL 1.1 can’t work, just that newer versions of some Linux distros are not going to release it in their package managers. That’s ok. Linux users have to move on at some point in time. OpenSSL 1.1 is 4 years old, and while the OpenSSL team has said it will EOL in 2023, current Linux LTS releases have to support what they release until around 2027. Back in Ruby, OpenSSL 3 is not new. It was released in 2021. So there has been time to prepare for the transition to OpenSSL 3. This starts to be a problem if you can’t run ruby versions that are up to date. If you are running or can run Ruby 3.1 then you have no issues. But what about those projects running older versions of Rails that need older versions of ruby? Like Ruby 2.7, Ruby 2.5, or even Ruby 1.9? Well, they won’t compile without a bit of “extra” work. Trying to compile them on a “new” Linux distro will give an error like this: (many lines omitted to make it workable)

compiling openssl_missing.c compiling ossl_bn.c ossl.c: In function ‘ossl_protect_x509ary2sk’: ossl.c:47:13: warning: cast between incompatible function types from ‘struct stack_st_X509 * (*)(VALUE)’ {aka ‘struct stack_st_X509 * (*)(long unsigned int)’} to ‘VALUE (*)(VALUE)’ {aka ‘long unsigned int (*)(long unsigned int)’} [-Wcast-function-type] 47 | (VALUE (*)(VALUE))ossl##name##_ary2sk0,
| ^ ossl.c:63:1: note: in expansion of macro ‘OSSL_IMPL_ARY2SK’ 63 | OSSL_IMPL_ARY2SK(x509, X509, cX509Cert, DupX509CertPtr) | ^<<<<<<<<<<<<<<< ossl_asn1.c: In function ‘decode_int’: ossl_asn1.c:328:22: warning: cast between incompatible function types from ‘VALUE ()(const ASN1_INTEGER *)’ {aka ‘long unsigned int ()(const struct asn1_string_st )’} to ‘VALUE ()(VALUE)’ {aka ‘long unsigned int ()(long unsigned int)’} [-Wcast-function-type] 328 | ret = rb_protect((VALUE ()(VALUE))asn1integer_to_num, | ^ ossl_asn1.c: In function ‘decode_enum’: ossl_asn1.c:368:22: warning: cast between incompatible function types from ‘VALUE ()(const ASN1_INTEGER *)’ {aka ‘long unsigned int ()(const struct asn1_string_st )’} to ‘VALUE ()(VALUE)’ {aka ‘long unsigned int ()(long unsigned int)’} [-Wcast-function-type] 368 | ret = rb_protect((VALUE ()(VALUE))asn1integer_to_num, | ^ ossl_asn1.c: In function ‘decode_time’: ossl_asn1.c:430:22: warning: cast between incompatible function types from ‘VALUE ()(const ASN1_TIME *)’ {aka ‘long unsigned int ()(const struct asn1_string_st )’} to ‘VALUE ()(VALUE)’ {aka ‘long unsigned int ()(long unsigned int)’} [-Wcast-function-type] 430 | ret = rb_protect((VALUE ()(VALUE))asn1time_to_time, | ^ ossl.c: In function ‘ossl_clear_error’: ossl.c:311:9: error: ‘ERR_get_error_line_data’ is deprecated: Since OpenSSL 3.0 [-Werror=deprecated-declarations] 311 | while ((e = ERR_get_error_line_data(&file, &line, &data, &flags))) { | ^<<<< In file included from ossl.h:21, from ossl.c:10: /usr/include/openssl/err.h:413:15: note: declared here 413 | unsigned long ERR_get_error_line_data(const char **file, int *line, | ^<<<<<<<<<<<<<<<<<<<<<<

What does it mean?

Well, in short, that older version of Ruby doesn’t support the newer version of OpenSSL. Normally, I would say, “update Ruby to 3.1”, and for many projects, that would work fine. But this project is using Rails 4. Ok, fine, I admit the project needs to be updated to a more recent version of Rails. But before I can do that, I need to get it running. And just running it on Ruby 3.1 isn’t an option.

Recheck the facts

  • My Distro doesn’t support OpenSSL 1.1
  • My Project is using ruby 2.5
  • Ruby 2.5 doesn’t support OpenSSL 3
  • My Project is using Rails 4
  • Rails 4 won’t run on Ruby 3.1
  • I can’t upgrade ruby versions until I can get the project running
  • I can’t upgrade rails versions until I can get the project running

Compiling Ruby in Linux Distros

Just because the distro doesn’t support OpenSSL 1.1 doesn’t mean it doesn’t work. OpenSSL 1.1 is not EOL yet, and it will work for a while longer. You just have to compile it yourself. More importantly, you need to compile it in a way that doesn’t break everything else on your system. So what we are going to do, is build and install OpenSSL 1.1, but not system-wide. Then only link to it when we need to.

The Fix

Install build dependencies. You probably already have these. Also, note that I am using Pop OS; you may need to adjust this for your distro. Especially if not using an apt-based distro.

sudo apt install build-essential checkinstall zlib1g-dev

Download and extract a specific version

cd /Downloads
wget https://www.openssl.org/source/openssl-1.1.1o.tar.gz
tar xf openssl-1.1.1o.tar.gz
cd /Downloads/openssl-1.1.1o

Then we start the normal Linux build process. Note that we are sticking this in /opt and not in the normal path. This is how we make sure it doesn’t affect your current system version of Open SSL.

./config --prefix=/opt/openssl-1.1.1o --openssldir=/opt/openssl-1.1.1o shared zlib
make
make test
sudo make install

Now, we link to the system version of the certs installed. These grants trust to the same people that you grant trust to on your machine. This could be critical as the system certs get updated.

sudo rm -rf /opt/openssl-1.1.1o/certs
sudo ln -s /etc/ssl/certs /opt/openssl-1.1.1o

Finally we build ruby using that specific version of OpenSSL.

RUBY_CONFIGURE_OPTS=--with-openssl-dir=/opt/openssl-1.1.1o rbenv install 2.5.0

The End Result

Now, you have an older version of Ruby that you need for your older project running on your system using OpenSSL 1.1, but without messing up your entire system or newer builds of Ruby. In all honesty, this is really a band-aid. If you find yourself needing to do this, it’s past time to make plans to upgrade Ruby, Rails, Rake, and bundler to more current versions.

YOU MAY ALSO LIKE

Ruby on Rails Options
November 8, 2022 - Robert C.

Extending the Options Class in Ruby on Rails

Ruby on Rails options and configurations
November 1, 2022 - Robert C.

Options and Configurations in Ruby on Rails