NGINX, ALPN and HTTP/2 on CentOS7

Written by Luke Arntz on December 14, 2016
[ NGINX ] [ HTTP/2 ] [ OpenSSL ] [ Use the Source ] [ CentOS7 ] [ Chrome ]

Contents

HTTP/2 ON CENTOS7 WITH NGINX

You’ve probably heard about the fancy new lightning fast HTTP/2 protocol, right? Fortunately, the latest versions of NGINX support HTTP/2. Unfortunately, most distributions are still stuck on OpenSSL < 1.0.2.

Versions of OpenSSL below 1.0.2 don’t support the new Application-Layer Protocol Negotition standard (ALPN) which means browsers must use Next Protocol Negotiation (NPN) to upgrade the connection to HTTP/2.

whaaaaat

BUT WHAT DOES THIS MEAN? WHY DOES IT MATTER?

This matters because Google Chrome will fall back to HTTP/1.1 if ALPN is not supported. Since most Linux distributions don’t officially support a version of OpenSSL that implements ALPN that means something must be done to if we want to implement HTTP/2 properly.

The official NGINX Blog has a great entry about this exact topic which happens to be where I began this journey. They list which distros support ALPN (at the time of the writing) and which don’t. CentOS7 is in the don’t column.

Our options from here are switch to Ubuntu 16.x, use third party OpenSSL packages, or compile NGINX against a version of OpenSSL that implements ALPN. While compiling your own packages is a bit old school and generally frowned upon for security reasons (any update requires a recompile and that can become a chore) this particular case is pretty easy and low maintenance.

We can compile NGINX against a newer version of OpenSSL without updating the version the rest of the system uses. In fact, we don’t even have to compile openssl. However, please, for the love of all that is holy, make sure you keep your nginx up to date. Any security updates for nginx or openssl means you need to repeat this process with the updated versions.

SECURITY DISCLAIMER

If you decide to go this route please take security seriously, and keep your software up to date. After you’ve gone through this once updating shouldn’t take more than ten minutes. If you get lazy with security you will eventually regret it. If you aren’t comfortable doing this use a distro that can officially support ALPN.

Here we go.

COMPILING NGINX AGAINST OPENSSL 1.1.0C

I chose to use OpenSSL 1.1.0c because it’s the newest version. Make sure to get the most recent version of 1.1.0 or 1.0.2 depending on which version you decide to use. OpenSSL is updated frequently with security updates.

Ok, so step 1, get a box ^H^H^H^H^H I mean get the OpenSSL and NGINX sources. You want the mainline version of nginx (currently nginx-1.11.7) and OpenSSL 1.0.2 or newer.

We also need to have the “Development Tools” group installed on our system.

COMMANDS

# change dir to where we compile NGINX and OpenSSL
cd /usr/local/src/

# install required packages
sudo yum install wget pcre-devel
sudo yum groupinstall "Development Tools"

# download nginx and openssl sources
sudo wget http://nginx.org/download/nginx-1.11.7.tar.gz
sudo wget https://www.openssl.org/source/openssl-1.1.0c.tar.gz

# extract nginx and openssl sources
sudo tar xf nginx-1.11.7.tar.gz
sudo tar xf openssl-1.1.0c.tar.gz

# compile nginx
cd nginx-1.11.7
sudo ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' --with-openssl=/usr/local/src/openssl-1.1.0c/
sudo make 
sudo make install

We can verify that nginx is now compiled against openssl-1.1.0c by running the command nginx -V.

$ nginx -V
nginx version: nginx/1.11.7
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-11) (GCC)
built with OpenSSL 1.1.0c  10 Nov 2016
TLS SNI support enabled

Now we need an init script. There is a sample script provided on nginx.com. Save this as /etc/init.d/nginx and then make it executable. If you want nginx to start with the system you’ll need to enable it using systemctl also. Finally, you need to allow ports 80 (http) and 443 (https) through the system firewall.

COMMANDS

# make /etc/init.d/nginx executable
sudo chmod +x /etc/init.d/nginx

# start nginx when the system boots
sudo systemctl enable nginx

# permanently allow http and https through firewall
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

# start nginx now
sudo systemctl start nginx

REFERENCES

NGINX Blog - Supporting HTTP/2 for Google Chrome Users

NGINX Sample init Script

ALPN Standard

NPN Standard

Top