Matt Holdsworth

3 posts
Latest Nginx and OpenSSL on Raspberry Pi

Latest Nginx and OpenSSL on Raspberry Pi

Note: to skip the reasoning and cut to the chase, see Installation below.

Why Nginx on Pi?

Although Apache still powers the majority of sites overall, its popularity is declining. Conversely, Nginx (pronounced engine-x) enjoys a fast-growing share of the market; almost two-thirds of the world's top 10,000 websites run on Nginx[1].
In large part, the key to Nginx's success is its event-driven design, meaning it scales very predictably, enabling sites to support massive amounts of traffic. It's extremely fast and flexible, with a reasonable configuration syntax.
If you need a web server, reverse proxy or software load balancer, you would be daft not to at least consider using Nginx.

On average, every minute one of the top 10 million websites starts to use Nginx

Nginx is so efficient that its performance is pretty good even when running on modest hardware like a Raspberry Pi 2 or 3.
I'm not suggesting you can run a major website from a Raspberry Pi, but the faster models are surprisingly capable as small web servers - especially when you consider their frugal power consumption of around 1.5W idle and 2-4W when busy[2].

Why not just apt-get it?

There are good reasons to use the latest and greatest version of Nginx. Some advanced features like proxying websocket connections (which I need for the web front ends of my Ubiquiti Edgerouter Lite and Unifi wifi controller) have only matured recently. I serve both ECDSA and RSA certificates for my sites, to get the best of both worlds in terms of performance and compatibility with older clients. Such 'hybrid' certificates are only supported in Nginx >= 1.11.

If you want up-to-date cryptography for your web server, such as the ChaCha20-Poly1305 cipher suites for better performance on mobile devices, then you also need an up-to-date version of the OpenSSL cryptography toolkit for Nginx to use. There are good reasons to do so:

The new cipher suites are fast. As Adam Langley described, ChaCha20-Poly1305 is three times faster than AES-128-GCM on mobile devices. Spending less time on decryption means faster page rendering and better battery life. Although the cipher part of TLS may not be the biggest source of battery consumption, spending fewer CPU cycles on encryption saves battery life, especially on large files.[3]

New features like these take considerable time to filter down to the offical, packaged versions available for Raspbian Jessie. At the time of writing, using sudo apt-get install nginx would install version 1.6.2 - a couple of years behind the latest mainline version, 1.13.1.

Finally, just in case you're still not convinced, building from source allows you to control which optional modules are included or not for your specific Nginx build.
In other words, if you'll be needing modules like ngx_http_stub_status_module (which I use for monitoring the load on my Nginx servers via my dashboard), then you'll need to compile from source anyway.

What's the catch?

Compiling software from source isn't (usually) as easy as installing a package and requires a bit of extra work to set up things like a service definition for systemd, so that your web server runs automatically on startup following a reboot.

You need patience, too: it takes quite a long time to compile OpenSSL and Nginx, especially on older / less powerful Pi devices. Making a cup of tea helps with this one.

There is an argument for using older, package-managed versions of software rather than self-built versions simply because it's easy to update to a new version using your package manager (e.g. apt) when updates are made available in your chosen repositories. If it's easier to maintain your software in the future, you'll be more likely to apply any important security fixes, goes the theory.

We can mitigate these problems by using a script to make installation and upgrades almost as easy as using a packaged version.

Luckily for us, we don't have to write anything ourselves, as there's a great Nginx Build Script by Matthew Vance (based on an original script by Matt Wilcox), which automates most of the process for us.

How the script works

Here's a summary of what it does:

  1. Installs/updates tools essential to the build
  2. Downloads the required source code packages, comparing the SHA256 checksum values of each download against known good ones, to make sure the download wasn't corrupted somehow:
  • PCRE (Perl Compatible Regular Expressions), to enable and optimise the use of regular expressions in Nginx configurations
  • zlib, enabling ngx_http_gzip_module, for gzip encoding of responses
  • OpenSSL for cryptographic functions i.e. SSL/TLS
  • Nginx itself, of course
  1. Double-checks that the Nginx and OpenSSL sources are genuine downloads, by checking their code-signing signatures
  2. Unpacks the compressed download files
  3. Renames the current /etc/nginx folder (if present) to make a dated backup copy
  4. Creates various subfolders for Nginx caches in /var/cache
  5. Sets up the nginx user and group (if they don't already exist) ready for Nginx to run as
  6. Tests the local gcc compiler capabilities and optionally enables a special performance boost, if possible
  7. Builds Nginx, which in turn builds OpenSSL, with various optional modules enabled/disabled
  8. Restores the backed up config from the previous install (if available)
  9. Creates a systemd service file, to enable Nginx to run as a service from startup

By customising the script, we can fine-tune which optional modules are included/excluded from our own version of Nginx.
The defaults are quite sensible for general web server and reverse proxy uses, but you can always run the script again with different options - see Upgrades for how to do this.

Installation

I've reproduced these installation instructions from the nginx-build Github page, with some explanatory comments:

# create a working folder where the source will be unpacked and compiled
sudo mkdir /usr/local/src/nginx/

# switch to the working folder we just created
cd /usr/local/src/nginx/

# download the latest version of the script
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh

This next step is really important. Please take the time to check the contents of the script to make sure you're happy with it. You don't have to be an expert to compare the steps listed above explaining what the script does (in how the script works), against the copy you've just downloaded.

You're going to make this script executable and then effectively give it root privileges by running it with sudo, so it would be madness not to check it before you run it.

# review downloaded code before executing
cat build_nginx.sh

# make the script executable
sudo chmod +x build_nginx.sh

# run it
sudo ./build_nginx.sh

It will take quite a while for the script to do its work. You can watch along, or make yourself a well-earned cuppa at this point.

When finished, you can start Nginx once using sudo nginx, but you probably really want to make Nginx run automatically on startup, so use these commands:

# start nginx service
sudo systemctl start nginx

# make it start automatically when the system starts
sudo systemctl enable nginx

Upgrades

When a new version of Nginx and/or OpenSSL are released, the script is updated shortly afterwards to use the new version(s). You might also decide to re-run the script after customising the list of optional modules in the compilation settings in the script.

Whatever the reason, the steps involved in upgrading are very similar to the initial installation:

# move to the working folder
cd /usr/local/src/nginx/

# remove the old version of the script
# skip this step if you're just adding/removing Nginx modules
sudo rm /usr/local/src/nginx/build_nginx.sh

# download the latest version of the script
# (don't bother if you're just re-running to add/remove modules)
sudo curl -L https://raw.githubusercontent.com/MatthewVance/nginx-build/master/build-nginx.sh -o build_nginx.sh

# IMPORTANT! review downloaded code before executing
cat build_nginx.sh

# make the downloaded script executable
sudo chmod +x build_nginx.sh

# run the script
sudo ./build_nginx.sh

# restart the service
sudo systemctl restart nginx

# confirm the new version number is reported (if applicable)
nginx -v

# check the full version details, including OpenSSL version and compilation options
nginx -V

Conclusion

Rolling your own Nginx - including the latest stable OpenSSL - is well worth doing. Any concerns over complex compilation syntax or difficulty keeping your custom copy up-to-date, can be addressed using a script specially written to make building and maintaining your own Nginx easy peasy, lemon squeezy!


  1. https://w3techs.com/blog/entry/nginx_reaches_33_3_percent_web_server_market_share_while_apache_falls_below_50_percent ↩︎

  2. https://www.pidramble.com/wiki/benchmarks/power-consumption ↩︎

  3. https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/ ↩︎

Headless Raspberry Pi setup

Headless Raspberry Pi setup

I follow these steps when I get a shiny new Raspberry Pi or want to re-purpose one completely and 'start from scratch':

  1. Download and unzip the latest Raspbian Lite.
  2. Burn the unzipped image to your microSD card, using Etcher if on a Mac, or see Raspbian installation instructions for other OSes.
  3. Enable ssh for headless setup of the Pi, by keeping the Raspbian SD card mounted, then creating a file (an empty one will do fine) called 'ssh' in the root of the 'boot' partition:
  • In Terminal, type cd /Volumes/boot/
  • then ls
  • check that the folder has a start.elf file listed (just to make sure you're in the right place).
  • type touch ssh (to create an empty file called ssh, which magically enables the Pi's ssh server at bootup time).
  1. Unmount/eject the microSD card and put it into your Pi. Connect a network cable from the Pi to your switch/router, then connect power. You should see various LEDs on the Pi flashing as it boots for the first time.
  2. Use the Admin UI for your router to find out the local IP that's been assigned to the Pi (e.g. 192.168.1.10 or whatever).
  3. [Optional] Create a MAC/IP reservation in your router's DHCP configuration for the Pi's MAC address, so the Pi always gets the local IP address and that you assign to it. (Alternatively, you could assign a static IP to the Pi later, but I prefer to have the Pi pick up an IP and control things from the router.)
  4. Connect to the Pi via ssh:
  • In Terminal, type ssh pi@192.168.1.10, where you replace 192.168.1.10 with the actual IP address you looked up in the previous step.
  • The default password is raspberry.
  1. Install any updates available for Raspbian and existing packages:
  • sudo apt-get update
  • sudo apt-get dist-upgrade
  1. Update the Pi's firmware:
  • sudo rpi-update
  1. Reboot the Pi:
  • sudo reboot
  1. Wait for the Pi to reboot, then connect to it again (see step 7).
  2. Configure the Pi for typical headless server use:
  • sudo raspi-config
  • Change the default password for the pi user (use a strong password, of course!)
  • Set the hostname (something simple, short and easy to remember)
  • Boot Options > Desktop / CLI > Console
  • Boot Options > Wait for Network at Boot > Yes
  • Interfacing Options > SSH > Yes
  • Advanced Options > Expand Filesystem
  • Advanced Options > Memory Split > 16 (we don't need lots of graphics memory as we aren't connecting a display)
  • (Press right arrow twice then) Finish
  1. Reboot the Pi to expand the filesystem and apply any other changes:
  • sudo reboot

Next, I often setup a simple firewall (ufw), setup passwordless (key based) SSH from my trusted devices and enable two factor authentication (2FA) for SSH using Google Authenticator, but this post covers the basic setup of a fresh Raspberry Pi, for use as a headless server on a private network.

I've made a blog

I've made a blog

I discovered the beautiful Ghost open source blogging software about a year ago. I love the clean UI, great default theme and unashamed focus on writing, paired with the simplicity of Markdown for writing naturally and formatting posts using an easy-to-learn syntax.

It was another fun little technical project for me to build on one of my many Raspberry Pi devices (a Pi 2 in this case). I like playing with new toys, and I'd never played with Node.js or configured an Nginx cache before.

In common with all my previous (failed) attempts at setting up a blog, so far I've just been tinkering: adding a comments system, choosing and customising a theme, adding favicons, optimising my Nginx web server config, etc... all without actually writing anything.

Hopefully I'll get around to putting some content on here one day. If not, it was a fun experiment anyway. :)