3 changed files with 316 additions and 3 deletions
@ -0,0 +1,313 @@ |
|||
--- |
|||
title: "Nginx with TLS on OpenWRT" |
|||
date: 2019-12-19T00:00:00+02:00 |
|||
opensource: |
|||
- OpenWRT |
|||
- nginx |
|||
--- |
|||
|
|||
In the article "[Install OpenWRT on your Raspberry PI](../install-openwrt-raspberry-pi/)", I explained how to install OpenWRT on a Raspberry PI and the first steps as an OpenWRT user. |
|||
As I plan to use my Raspberry PI to host plenty of web applications, I wanted to setup a versatile reverse proxy to protect them all, along with TLS support to meet nowadays security requirements. |
|||
|
|||
OpenWRT has an [nginx package](https://openwrt.org/packages/pkgdata/nginx), ready to be installed using *opkg* but unfortunately it does not have TLS enabled. So we need to recompile nginx with TLS enabled! |
|||
|
|||
## Install the OpenWRT build system |
|||
|
|||
Install a Linux distribution [supported by the OpenWRT build system](https://openwrt.org/docs/guide-developer/build-system/install-buildsystem) in a Virtual Machine. For instance, [Ubuntu Server LTS 18.04.3](http://cdimage.ubuntu.com/releases/18.04.3/release/). |
|||
|
|||
Install the build system pre-requisites. |
|||
|
|||
```sh |
|||
sudo apt-get install -y build-essential libncurses5-dev gawk git libssl-dev gettext unzip zlib1g-dev file python-dev libmodule-build-perl libmodule-install-perl libthread-queue-any-perl |
|||
``` |
|||
|
|||
Clone the OpenWRT GIT repository. Change the branch name to your version number, if you are not on 18.06. |
|||
|
|||
```sh |
|||
git clone https://git.openwrt.org/openwrt/openwrt.git -b openwrt-18.06 |
|||
cd openwrt |
|||
``` |
|||
|
|||
Run *make menuconfig* to configure the build system. |
|||
|
|||
```sh |
|||
make menuconfig |
|||
``` |
|||
|
|||
Select the following options: |
|||
|
|||
- Target System: **Broadcom BCM27xx** |
|||
- Subtarget: **BCM2710 64 bit based boards** |
|||
- Target Profile: **Raspberry Pi 3B/3B+** |
|||
- Enable **Build the OpenWRT SDK** |
|||
|
|||
 |
|||
|
|||
Launch a complete build. According to [the documentation](https://oldwiki.archive.openwrt.org/doc/techref/buildroot), the parameter *V=s* in the following command is used to display verbose output. |
|||
|
|||
```sh |
|||
make V=s |
|||
``` |
|||
|
|||
## Recompile the nginx package |
|||
|
|||
Fetch the existing nginx package sources. |
|||
|
|||
```sh |
|||
scripts/feeds update |
|||
scripts/feeds install nginx |
|||
``` |
|||
|
|||
Run *make menuconfig* again. |
|||
|
|||
```sh |
|||
make menuconfig |
|||
``` |
|||
|
|||
Drill down to **Network** > **Web Servers/Proxies** and: |
|||
|
|||
- Press space to select **nginx** |
|||
- Press enter to enter configuration |
|||
- Choose **Configuration** |
|||
- Press space to select "**Enable SSL Module**" |
|||
- Exit five times and save |
|||
|
|||
Build the nginx package. |
|||
|
|||
```sh |
|||
make V=s |
|||
``` |
|||
|
|||
Once the build finished, the nginx package will be in *bin/packages*. |
|||
|
|||
```raw |
|||
$ find . -type f -name 'nginx*.ipk' |
|||
./bin/packages/aarch64_cortex-a53/packages/nginx_1.12.2-1_aarch64_cortex-a53.ipk |
|||
``` |
|||
|
|||
## Install nginx |
|||
|
|||
Install the freshly compiled nginx package on your OpenWRT system. |
|||
|
|||
```sh |
|||
scp ./bin/packages/aarch64_cortex-a53/packages/nginx_1.12.2-1_aarch64_cortex-a53.ipk root@raspberry-pi.example.test:/tmp |
|||
ssh root@raspberry-pi.example.test opkg install /tmp/nginx_1.12.2-1_aarch64_cortex-a53.ipk |
|||
``` |
|||
|
|||
## Install Lego |
|||
|
|||
[Lego](https://go-acme.github.io/lego/) is a client for the Let's Encrypt CA. It will help us get valid TLS certificates for our nginx instance. |
|||
|
|||
Install lego from the [binaries](https://github.com/go-acme/lego/releases). |
|||
|
|||
```sh |
|||
mkdir -p /opt/lego/bin |
|||
wget -O /tmp/lego.tgz https://github.com/go-acme/lego/releases/download/v3.2.0/lego_v3.2.0_linux_arm64.tar.gz |
|||
tar -C /opt/lego/bin -zxvf /tmp/lego.tgz lego |
|||
chown root:root /opt/lego/bin/lego |
|||
chmod 755 /opt/lego/bin/lego |
|||
``` |
|||
|
|||
## Get a public TLS certificate |
|||
|
|||
Request a public certificate for your Raspberry PI hostname. In the following example, I'm using the DNS challenge method and the Gandi DNS provider. Of course, you would have also to replace *raspberry-pi.example.test* with your Raspberry PI's hostname. |
|||
|
|||
```sh |
|||
mkdir /etc/nginx/tls |
|||
GANDIV5_API_KEY=[REDACTED] /opt/lego/bin/lego -m replace.with@your.email -d raspberry-pi.example.test -a --dns gandiv5 --path /etc/nginx/tls run --no-bundle |
|||
``` |
|||
|
|||
If everything went fine, you will find the freshly issued certificates in */etc/nginx/tls/certificates/$hostname.{key,crt}*. |
|||
|
|||
```raw |
|||
root@OpenWrt:~# find /etc/nginx/tls/ |
|||
/etc/nginx/tls/ |
|||
/etc/nginx/tls/certificates |
|||
/etc/nginx/tls/certificates/raspberry-pi.example.test.crt |
|||
/etc/nginx/tls/certificates/raspberry-pi.example.test.issuer.crt |
|||
/etc/nginx/tls/certificates/raspberry-pi.example.test.key |
|||
/etc/nginx/tls/certificates/raspberry-pi.example.test.json |
|||
``` |
|||
|
|||
## Configure nginx |
|||
|
|||
Create a user for the nginx workers. |
|||
|
|||
```sh |
|||
opkg update |
|||
opkg install shadow-useradd |
|||
useradd -d /var/run/nginx -s /bin/false -m -r nginx |
|||
``` |
|||
|
|||
Create the nginx configuration file in **/etc/nginx/nginx.conf**. |
|||
|
|||
```raw |
|||
user nginx nginx; |
|||
worker_processes 1; |
|||
|
|||
error_log syslog:server=unix:/dev/log,nohostname warn; |
|||
|
|||
events { |
|||
worker_connections 1024; |
|||
} |
|||
|
|||
http { |
|||
server_names_hash_bucket_size 64; |
|||
include mime.types; |
|||
|
|||
log_format main '$remote_addr "$request" => $status'; |
|||
access_log syslog:server=unix:/dev/log,nohostname main; |
|||
|
|||
sendfile on; |
|||
keepalive_timeout 65; |
|||
gzip off; |
|||
|
|||
server { |
|||
listen 443 ssl default_server; |
|||
|
|||
ssl_certificate /etc/nginx/tls/certificates/raspberry-pi.example.test.crt; |
|||
ssl_certificate_key /etc/nginx/tls/certificates/raspberry-pi.example.test.key; |
|||
ssl_session_cache shared:SSL:1m; |
|||
ssl_session_timeout 5m; |
|||
ssl_ciphers HIGH:!aNULL:!MD5; |
|||
ssl_prefer_server_ciphers on; |
|||
|
|||
# Error pages |
|||
error_page 404 /404.html; |
|||
error_page 500 502 503 504 /50x.html; |
|||
location ~ /[45][0-9x][0-9x].html { |
|||
root /srv/nginx/default; |
|||
} |
|||
|
|||
# Main content |
|||
index index.html index.htm; |
|||
location / { |
|||
root /srv/nginx/default; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Create the default page and the error pages. |
|||
|
|||
```sh |
|||
mkdir -p /srv/nginx/default |
|||
echo "None of your business." > /srv/nginx/default/index.html |
|||
echo "Nope. Not here." > /srv/nginx/default/404.html |
|||
echo "OOPS..." > /srv/nginx/default/50x.html |
|||
``` |
|||
|
|||
## Start nginx |
|||
|
|||
Start the nginx instance. |
|||
|
|||
```sh |
|||
service nginx enable |
|||
service nginx start |
|||
``` |
|||
|
|||
If your configuration is valid, the port 443 should be bound to nginx. |
|||
|
|||
```raw |
|||
root@OpenWrt:~# netstat -tlnp |grep :443 |
|||
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 11305/nginx.conf -g |
|||
``` |
|||
|
|||
If nginx does not start, you can get some details using the *logread* command. |
|||
|
|||
```raw |
|||
logread |tail -n 10 |
|||
``` |
|||
|
|||
If you cannot get any details using *logread*, try to start nginx manually. |
|||
|
|||
```sh |
|||
nginx -g "daemon off;" -c /etc/nginx/nginx.conf |
|||
``` |
|||
|
|||
From your workstation, try to query your nginx instance by its IP address. |
|||
|
|||
```raw |
|||
$ curl -k https://192.168.2.2/ |
|||
None of your business. |
|||
``` |
|||
|
|||
## Further configuration |
|||
|
|||
So far, our nginx instance is only serving a few static files with unpleasant messages. |
|||
This is the default virtual host of our nginx instance that is used when bots try to scan your web server by its IP address without knowing its actual hostname. |
|||
|
|||
To add another virtual host, just add a new *server* block after the default one. |
|||
|
|||
```raw |
|||
server { |
|||
listen 443 ssl; |
|||
server_name raspberry-pi.example.test |
|||
|
|||
ssl_certificate /etc/nginx/tls/certificates/raspberry-pi.example.test.crt; |
|||
ssl_certificate_key /etc/nginx/tls/certificates/raspberry-pi.example.test.key; |
|||
ssl_session_cache shared:SSL:1m; |
|||
ssl_session_timeout 5m; |
|||
ssl_ciphers HIGH:!aNULL:!MD5; |
|||
ssl_prefer_server_ciphers on; |
|||
|
|||
# Error pages |
|||
error_page 404 /404.html; |
|||
error_page 500 502 503 504 /50x.html; |
|||
location ~ /[45][0-9x][0-9x].html { |
|||
root /srv/nginx/default; |
|||
} |
|||
|
|||
# Main content |
|||
index index.html index.htm; |
|||
location / { |
|||
root /srv/nginx/main; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Serve some nice content. |
|||
|
|||
```sh |
|||
mkdir -p /srv/nginx/main |
|||
echo 'Welcome!' > /srv/nginx/main/index.html |
|||
``` |
|||
|
|||
From your workstation, try to query your nginx instance by its hostname. |
|||
|
|||
```raw |
|||
$ curl https://raspberry-pi.itix.fr/ |
|||
Welcome! |
|||
``` |
|||
|
|||
## Certificate renewal |
|||
|
|||
The TLS certificate we fetched from Let's Encrypt is valid for ninety days. |
|||
If you do not want to manually renew the certificate every ninety days, you will have to setup automatic renewal in a cron job. |
|||
|
|||
Install pkill. We will use it to tell nginx to reload its configuration and the renewed certificates. |
|||
|
|||
```sh |
|||
opkg update |
|||
opkg install procps-ng-pkill |
|||
``` |
|||
|
|||
Edit the crontab of the root user. |
|||
|
|||
```sh |
|||
crontab -e -u root |
|||
``` |
|||
|
|||
And an entry to renew the certificate using lego. |
|||
|
|||
```crontab |
|||
# At 3:59 the first day of the month, renew the Let's Encrypt certificates |
|||
3 59 1 * * GANDIV5_API_KEY=[REDACTED] /opt/lego/bin/lego -m replace.with@your.email -d raspberry-pi.example.test -a --dns gandiv5 --path /etc/nginx/tls run --no-bundle && pkill -SIGHUP 'nginx: master' |
|||
``` |
|||
|
|||
## Conclusion |
|||
|
|||
Nginx is now installed on your Raspberry PI, with TLS support enabled and a valid public certificate from Let's Encrypt that will be renewed automatically. |
|||
The configuration serves a default virtual host to every bot that queries the nginx instance by its IP address and can serve any number of virtual host, provided you add the matching *server* block. |
|||
|
|||
Discover in the next article how to deploy a real world application: miniflux, an RSS reader. |
|||
|
After Width: | Height: | Size: 488 KiB |
Loading…
Reference in new issue