NGINX Reverse Proxy

remyg

Remy Gardette

Posted on July 30, 2018

NGINX Reverse Proxy

My home server setup is composed of several Raspberry Pi, where I host different web applications (this blog, an RSS reader, some home IOT apps…). I’ve decided to setup a front gateway, that proxies the request to the right server:

Infrastructure

The requests are proxied by an NGINX reverse proxy, running in a Docker container on the gateway. It redirects the HTTP requests based on the host (eg. remyg.ovh runs on rpi1 when rss.remyg.ovh runs on rpi2).

NGINX Configuration

The main NGINX conf file (nginx.conf) looks like this:

user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    sendfile on;
    #tcp_nopush on;

    keepalive_timeout 65;

    #gzip on;

    include /etc/nginx/sites-enabled/*.*;
}
Enter fullscreen mode Exit fullscreen mode

The only difference with the base conf file (from the default NGINX Docker image) is the last line:

include /etc/nginx/conf.d/*.conf;
Enter fullscreen mode Exit fullscreen mode

is replaced by

include /etc/nginx/sites-enabled/*.*;
Enter fullscreen mode Exit fullscreen mode

It ignores the default configuration (/etc/nginx/conf.d/default.conf) and uses the proxy configuration files that I defined.

Hosts Configuration

Each host has its own configuration file:

  • for remyg.ovh , running on rpi1 (with a local IP 192.168.0.10, and port 8080):
server {
    listen 80;
    server_name remyg.ovh;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    location / {
        proxy_pass http://192.168.0.10:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}
Enter fullscreen mode Exit fullscreen mode
  • for rss.remyg.ovh , running on rpi2 (with a local IP 192.168.0.11, and port 8081):
server {
    listen 80;
    server_name rss.remyg.ovh;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    location / {
        proxy_pass http://192.168.0.11:8081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}
Enter fullscreen mode Exit fullscreen mode

These files indicate that a request incoming to rss.remyg.ovh:80 (server_name and listen) will be redirected to 192.168.0.11:8081 (proxy_pass).

That’s all the configuration you need to serve websites on HTTP.

Running in Container

To run the reverse proxy in a Docker container, the file tree looks like this:

nginx-reverse-proxy
  -> conf
    -> nginx.conf
  -> sites
    -> remyg.ovh
       rss.remyg.ovh
Enter fullscreen mode Exit fullscreen mode

With this structure, the command launching the container will be:

docker run --name mynginx-proxy \
-v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
-v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
-p 80:80 -d nginx:alpine
Enter fullscreen mode Exit fullscreen mode

HTTPS

To enable HTTPS on the different sites, I’m using Let’s Encrypt, and their utility app Certbot.

I’m starting by installing the certbot package:

sudo apt install certbot
Enter fullscreen mode Exit fullscreen mode

When generating a certificate, Certbot will need to validate that it can access a specific file that it generates, pointing to the URL http://your-host/.well-known/acme-challenge/{token}. To do that, start by creating and mounting a new volume on the reverse proxy container:

docker run --name mynginx-proxy \
        -v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
        -v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
        -v /home/pi/letsencrypt_www:/var/www/letsencrypt \
        -p 80:80 -p 443:443 -d nginx:alpine
Enter fullscreen mode Exit fullscreen mode

Then specify in the sites proxy configuration that this volume is used when pointing to /.well-known/acme-challenge/:

server {
    listen 80;
    server_name remyg.ovh;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }

    location / {
        proxy_pass http://192.168.0.10:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}
Enter fullscreen mode Exit fullscreen mode

And reload your NGINX config:

docker exec -it mynginx-proxy nginx -s reload
Enter fullscreen mode Exit fullscreen mode

Now you can generate the certificate(s) :

sudo certbot certonly --authenticator webroot -w /home/pi/letsencrypt_www -d remyg.ovh -d rss.remyg.ovh
Enter fullscreen mode Exit fullscreen mode

This will generate the ACME challenge files in /home/pi/letsencrypt_www, and validate the challenge. It will also generate the certificates, in /etc/letsencrypt/certs/live/remyg.ovh/ and /etc/letsencrypt/certs/live/rss.remyg.ovh/.

The last step is to use the new certificates, and only allow HTTPS requests.

Start by mounting a new volume, containing the certificates:

docker run --name mynginx-proxy \
        -v /home/pi/nginx-reverse-proxy/sites:/etc/nginx/sites-enabled:ro \
        -v /home/pi/nginx-reverse-proxy/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
        -v /etc/letsencrypt:/etc/nginx/certs \
        -v /home/pi/letsencrypt_www:/var/www/letsencrypt \
        -p 80:80 -p 443:443 -d nginx:alpine
Enter fullscreen mode Exit fullscreen mode

Then update your proxy configuration:

server {
    listen 80;
    server_name remyg.ovh;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name remyg.ovh;

    ssl_certificate certs/live/remyg.ovh/fullchain.pem;
    ssl_certificate_key certs/live/remyg.ovh/privkey.pem;

    location / {
        proxy_pass http://192.168.0.10:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}
Enter fullscreen mode Exit fullscreen mode

Reload the NGINX configuration, and you’re all set!

💖 💪 🙅 🚩
remyg
Remy Gardette

Posted on July 30, 2018

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

NGINX Reverse Proxy
docker NGINX Reverse Proxy

July 30, 2018