reverse proxy (nginx) - tulliolo/mobybolt GitHub Wiki

Nginx Logo

Several components of this guide will expose a communication port, for example, the Block Explorer, or the ThunderHub web interface for your Lightning node. Even if you use these services only within your own home network, communication should always be encrypted. Otherwise, any device in the same network can listen to the exchanged data, including passwords.

We use Ngnix to encrypt the communication with SSL/TLS (Transport Layer Security). This setup is called a "reverse proxy": Nginx provides secure communication to the outside and routes the traffic back to the internal service without encryption.

â„šī¸ Even if some services, such as Fulcrum, natively support encrypted communication, for simplicity and architectural cleanliness, we will still use nginx as a single point of access to all services.

❗ To follow this section, log in to your node as admin user via Secure Shell (SSH) and access the project's home:

$ cd apps/mobybolt

Table of contents

Prepare

Create the nginx directory:

$ mkdir nginx

Prepare the environment

Edit the .env file and append the following content to the end:

$ nano .env
# nginx
NGINX_VERSION=mainline
NGINX_GID=101
NGINX_UID=101

💡 In this file:

  1. we define the NGINX_VERSION as mainline (stable)
  2. we define the uid (user id) and gid (group id) of the nginx user

Prepare the Dockerfile

Create the Dockerfile and populate it with the following content:

$ nano nginx/Dockerfile
ARG NGINX_VERSION='latest'

# cert image
FROM alpine AS certimage

ARG DOMAIN_NAME=localhost
ARG DAYS_VALID=3650

# install dependencies
RUN set -eux && \
    apk add --no-cache openssl

RUN set -eux && \
    echo "Creating self-signed certificate valid for ${DAYS_VALID} days for domain ${DOMAIN_NAME}" && \
    openssl \
      req -x509 \
      -nodes \
      -subj "/CN=${DOMAIN_NAME}" \
      -addext "subjectAltName=DNS:${DOMAIN_NAME}" \
      -days ${DAYS_VALID} \
      -newkey rsa:4096 \
      -keyout /tmp/nginx-selfsigned.key \
      -out /tmp/nginx-selfsigned.crt


# base image
FROM nginx:$NGINX_VERSION AS base

# install dependencies
RUN set -eux && \
    apt update && \
    apt install -y curl && \
    rm -rf /var/lib/apt/lists/*


# result image
FROM base

ARG NGINX_GID=101
ARG NGINX_UID=101

# copy certificates
COPY --from=certimage /tmp/nginx-selfsigned.crt /tmp/nginx-selfsigned.key /etc/nginx/ssl/

RUN set -eux && \
    # setup user
    groupmod --gid $NGINX_GID nginx && \
    usermod --uid $NGINX_UID nginx && \
    # setup dirs
    mkdir /run/nginx/ && \
    # set ownership and permissions
    chown -R nginx:nginx /etc/nginx/ /run/nginx/ /var/cache/nginx/

# switch user
USER nginx

💡 In this file:

  1. we define a support image (certimage) to generate a self-signed SSL certificate;
  2. we extend the official nginx image:
    1. installing curl (for healthcheck purposes);
    2. importing the SSL certificate;
    3. configuring the nginx user and the directories to which he will have access.

Configure nginx

Create some directories:

$ mkdir -p nginx/config/sites-enabled 
$ mkdir -p nginx/config/streams-enabled

Create the nginx.conf file and populate it with the following content:

$ nano nginx/config/nginx.conf
worker_processes  auto;
error_log  /var/log/nginx/error.log notice;
pid        /run/nginx/nginx.pid;
worker_rlimit_nofile  1536;

events {
  worker_connections  768;
}

http {
  ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt;
  ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key;
  ssl_session_cache shared:HTTP-TLS:1m;
  ssl_session_timeout 4h;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  include /etc/nginx/sites-enabled/*.conf;
}

stream {
  ssl_certificate /etc/nginx/ssl/nginx-selfsigned.crt;
  ssl_certificate_key /etc/nginx/ssl/nginx-selfsigned.key;
  ssl_session_cache shared:STREAM-TLS:1m;
  ssl_session_timeout 4h;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  include /etc/nginx/streams-enabled/*.conf;
}

Create a test site to check the container health:

$ nano nginx/config/sites-enabled/test-site.conf
server {
  listen 80;
  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
  }
  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
      root   /usr/share/nginx/html;
  }
}

server {
  listen 443 ssl;
  error_page 497 =301 https://$host:$server_port$request_uri;
  location / {
    proxy_pass http://127.0.0.1:80;
  }
}

Prepare the Docker Compose file

Create the Docker Compose file and populate it with the following content:

$ nano nginx/docker-compose.yml
services:
  nginx:
    build:
      context: .
      args:
        NGINX_VERSION: ${NGINX_VERSION}
        NGINX_GID: ${NGINX_GID}
        NGINX_UID: ${NGINX_UID}
    container_name: ${COMPOSE_PROJECT_NAME}_nginx
    healthcheck:
      test: ["CMD-SHELL", "curl -fk https://localhost || exit 1"]
      interval: 1m
      timeout: 10s
      retries: 3
    image: ${COMPOSE_PROJECT_NAME}/nginx:${NGINX_VERSION}
    restart: unless-stopped
    volumes:
      - ./config/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./config/sites-enabled/:/etc/nginx/sites-enabled/:ro
      - ./config/streams-enabled/:/etc/nginx/streams-enabled/:ro

💡 In this file:

  1. we build the Dockerfile and create an image named mobybolt/nginx:mainline;
  2. we define a healthcheck that will query the test site every minute;
  3. we define the restart policy of the container in case of failures;
  4. we provide the container with the previously defined configurations (bind mount)

Link the Docker Compose File

Edit the main Docker Compose file and link the nginx docker compose file in the include section:

$ nano docker-compose.yml

The file should look like this:

include:
  - nginx/docker-compose.yml

âš ī¸ Be very careful to respect the indentation. Yaml is very sensitive to this! âš ī¸

Test the Docker Compose File

Run the following command and check the output:

$ docker compose config --quiet && printf "OK\n" || printf "ERROR\n"
> OK

💡 If the output is not OK, check the error reported... Maybe some wrong indentation in the yaml files?

Build

Let's build the nginx image by typing:

$ docker compose build nginx

Check for a new image called mobybolt/nginx:mainline:

$ docker images | grep nginx
> mobybolt/nginx   mainline   64ea9ecb52dd   37 seconds ago   188MB

Run

Run the following command and check the output:

$ docker compose up -d nginx
> [+] Running 2/2
> ✔ Network mobybolt_default  Created 
> ✔ Container mobybolt_nginx  Started

Check the container logs:

$ docker logs mobybolt_nginx
> ...
> 2024/05/07 11:50:50 [notice] 1#1: nginx/1.25.5
> 2024/05/07 11:50:50 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) 
> 2024/05/07 11:50:50 [notice] 1#1: OS: Linux 6.1.0-20-amd64
> 2024/05/07 11:50:50 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
> 2024/05/07 11:50:50 [notice] 1#1: start worker processes
> ...

Check the container status:

$ docker ps | grep nginx
> bec6cf2a7403   mobybolt/nginx:mainline   "/docker-entrypoint.â€Ļ"   5 minutes ago   Up 5 minutes (healthy)   80/tcp    mobybolt_nginx

âš ī¸ The output of the previous command must be (healthy), or (health: starting). Any other status is incorrect.

💡 If the container is in (health: starting) state, wait a few minutes and repeat the above command until the state changes to (healthy). If this does not happen, the run has failed.

â„šī¸ If not already present, docker will also create the mobybolt network. You can check for it with the command:

$ docker network ls | grep mobybolt
> b73804f9dc97   mobybolt_default   bridge    local

Upgrade

To upgrade nginx, you can simply redo the steps described in:

  1. Build
  2. Run

If a new version of nginx has been released, docker will rebuild the image and hot-swap the old container with the updated one.

Later, you can delete the old image and clean the build cache with the following commands (type y when prompted):

$ docker image prune
> WARNING! This will remove all dangling images.
> Are you sure you want to continue? [y/N] y
> Deleted Images:
> deleted: sha256:2aafbb312ce58b974c131de147bb9d22cf7e200b825496b25fad2413685e808c
$ docker buildx prune
> WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
> ID                                              RECLAIMABLE     SIZE            LAST ACCESSED
> fjap6n77y7o38fzxb0p76xtz3                       true            15.4kB          7 hours ago
> sg8tzhtf1m1vhufjzvetukgmy*                      true            1.212kB         13 minutes ago
> ...
> Total:  5.862MB

Uninstall

Follow the next steps to uninstall nginx:

  1. Remove the container:

    $ docker compose down nginx
    > [+] Running 2/1
    > ✔ Container mobybolt_nginx  Removed
    > ...
    
  2. Remove the image:

    $ docker image rm $(docker images | grep nginx | awk '{print $3}')
    > Untagged: mobybolt/nginx:mainline
    > Deleted: sha256:411a5102d61a972fcf049c85dfbe387fe15560640b0fe7da9ada1562085afdc8
    
  3. Clean the build cache:

    $ docker buildx prune
    > WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
    > ID                                              RECLAIMABLE     SIZE            LAST ACCESSED
    > 7r8ccrpq0g0e03deu2dh53ob6*                      true            9.69MB          19 minutes ago
    > ndrhcdo756vejnx17qm775t08*                      true            1.212kB         24 minutes ago
    > ...
    
  4. Remove files and directories (optional):

    $ rm -rf nginx
    
âš ī¸ **GitHub.com Fallback** âš ī¸