Build Python 3.11 From Source With TLS/SSL on CentOS 7

Building Python 3.11 from source on CentOS 7 is straightforward but it is tricky when we want to build with TLS/SSL support. The reason is that OpenSSL version (1.0.2k) in CentOS 7 is older than the minimum required by Python 3.11 i.e. 1.1.1k. This is not an unsurmountable problem because the newer version is available in the EPEL repository.

If you are wondering why I am writing about CentOS 7 so close to its end of life, the reason is that it is not end of life yet and still in production in many places. CentOS 7 is also old enough that it needs extra steps to build a modern version of Python.

Most blog posts walk you from start to finish. Here we will take a different approach and go backwards. However, this is only to make it easier for you to understand the most significant matters first. When you are actually building Python on your machine, you must make sure all prerequisites are fulfilled first.

After you have downloaded the Python 3.11 source tarball and extracted it to some location, we can build it (assuming that all dependencies have been installed, which we will do later).

$ cd /tmp/Python-3.11.6
$ export TCLTK_LIBS='-ltk8.5 -ltcl8.5'
$ ./configure --prefix=/usr/local/python/3.11.6 --enable-shared --with-openssl=/usr/local/openssl11 --with-openssl-rpath=auto

We are doing three things above,

  1. Using a custom path, /usr/local/python/3.11.6, to install Python. Usually, it would be /usr/local/bin, /usr/local/lib, etc. The reason I am using a custom prefix is to demonstrate how to do it.

  2. Using a custom OpenSSL version, installed in a path of our choosing, /usr/local/openssl11.

  3. Not using --enable-optimizations because it has reported issues with the gcc version (4.8.5) available in CentOS 7.

  4. Exporting TCLTK_LIBS='-ltk8.5 -ltcl8.5' because Python 3.11 does not detect dependencies for _tkinter module (_tkinter build issue on Centos7 due changing TK/TCL detection by configure script.).

Let's build Python,

$ cd /tmp/Python-3.11.6
$ export TCLTK_LIBS='-ltk8.5 -ltcl8.5'
$ make

You may see the following message in the long output, which indicates issues with building optional modules. It's because we did not have the right dependencies installed. As of writing, this message should not pop up if you follow the steps in this post entirely.

The necessary bits to build these optional modules were not found:

Install Python to our custom location,

$ cd /tmp/Python-3.11.6
$ sudo make install

Since loadable modules required to run Python are in a custom location we need to tell ld about it. If we do not use a custom --prefix during ./configure then we do not need this step.

$ echo /usr/local/python/3.11.6/lib | sudo tee /etc/
$ sudo ldconfig

Verify that Python is running,

$ /usr/local/python/3.11.6/bin/python3 --version

We skipped a lot of prerequisites so let's get to them.

Create required directories,

$ sudo mkdir -p /usr/local/openssl11
$ sudo mkdir -p /usr/local/python/3.11.6

The custom directory, /usr/local/openssl11, will have some symlinks we must create,

$ sudo ln -s /usr/lib64/openssl11 /usr/local/openssl11/lib
$ sudo ln -s /usr/include/openssl11 /usr/local/openssl11/include

But we haven't installed openssl11. The paths /usr/lib64/openssl11 and /usr/include/openssl11 are not created yet; the symlinks are broken. Let's fix it,

$ sudo yum install -y epel-release
$ sudo yum makecache
$ sudo yum install -y openssl11 openssl11-devel openssl11-libs

We also haven't downloaded Python source tarball.

$ curl --insecure -o /tmp/Python-3.11.6.tar.xz
$ tar xf /tmp/Python-3.11.6.tar.xz -C /tmp

We must install all prerequisites,

$ sudo yum install -y bzip2-devel gcc gdbm-devel libffi-devel libuuid-devel make ncurses-devel readline-devel sqlite-devel tcl-devel tk-devel xz-devel xz zlib-devel

This is all it takes. For newer versions of CentOS Stream, we do not need to install openssl11; openssl will work. The nis module may also not build on CentOS Stream 9 because nis support has been removed (deprecated in RHEL 8) by Red Hat.