Using NGINX Reverse Proxy (Authentication and HTTPS)

I’ve set this up only running openHAB and nginx in Docker. I’ll post the differences here.

One big thing that should be made clear. Either I did something wrong or else the redirect from HTTP to HTTPS is not optional. When I tried it without the redirect I would get as far as entering my username and password and then I would get connection refused. I’m not sure if this is a side effect of running in Docker or a fundamental step.

installation
I downloaded the latest base official nginx image using

docker pull nginx

I configured it to run as a service on port 80 and 443 with the following .service file

nginx.service : NOTE, edits get made to this file later, I include each version at each step though so you can test as you go.

[Unit]
Description=nginx
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker run --name=%n \
  -p 80:80 -p 443:443 \
  -v /etc/localtime:/etc/localtime:ro \
  -v /etc/timezone:/etc/timezone:ro \
  nginx
ExecStop=/usr/bin/docker stop -t 2 %n ; /usr/bin/docker rm -f %n

[Install]
WantedBy=multi-user.target

NOTE: We map in localtime and timezone so nginx gets its time from the host.

Copy that file to /etc/systemd/system and run the following commands:

sudo systemctl daemon-reload
sudo systemctl enable nginx.service
sudo systemctl start nginx.service
systemctl status nginx.service

You should see that it is up and running now. If you go to http://host (where host is your host machine’s name or address) you should see the NGINX welcome message.

#Basic Configuration
Now we need to get the nginx.conf file so we can update the config.

sudo mkdir /opt/nginx
sudo chmod a+rwx /opt/nginx
cd /opt/nginx
docker ps
# look for nginx in the list, highlight the CONTAINER ID
docker exec <container id> cat /etc/nginx/nginx.conf > nginx.conf
mkdir conf.d
cd conf.d
docker exec <container id> cat /etc/nginx/conf.d/default.conf > default.conf

At this point I had to do me some learn’n on nginx because the Docker image doesn’t have a sites-enabled folder. :expressionless: It turns out that file is located in conf.d.

Edit /opt/nginx/conf.d/default.conf per the instructions above. NOTE: Your openHAB will NOT be running in this container so you must use the http://youropenhabhostname:8080/ address

TODO: Figure out how to take advantage of Docker networking so the only way to get to openHAB, even locally, is through nginx.

NOTE: there is a section about error pages that you will need to comment out to match the above.

Now that we have these config files we need to map them into the container. We do this by editing the start script to add them as volumes. While we are at it, we will also mount a volume for the logs so we can monitor for access attempts (fail2ban perhaps?) outside the container.

mkdir /opt/nginx/logs

New nginx.service file:

[Unit]
Description=nginx
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker run --name=%n \
  -p 80:80 -p 443:443 \
  -v /etc/localtime:/etc/localtime:ro \
  -v /etc/timezone:/etc/timezone:ro \
  -v /opt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \
  -v /opt/nginx/logs:/var/log/nginx \
  nginx
ExecStop=/usr/bin/docker stop -t 2 %n ; /usr/bin/docker rm -f %n

[Install]
WantedBy=multi-user.target

sudo systemctl daemon-reload
sudo systemctl restart nginx.service
systemctl status nginx.service

If all went well you should see the service is up and running. You should now also see an access.log and error.log in /opt/nginx/logs.

To run the test of the config as described above:

docker ps
# Find the CONTAINER ID of the nginx.service container
docker exec <container id> nginx -t

authentication with NGINX
We will be generating and managing the user/password external to the Docker Image so make sure to install apache2-utils on your host machine as described above.

From the /opt/nginx folder run

sudo htpasswd -c .htpasswd username

create the password when asked. Now we have another file to mount into the image to add it as a read only volume same as we did above.

[Unit]
Description=nginx
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker run --name=%n \
  -p 80:80 -p 443:443 \
  -v /etc/localtime:/etc/localtime:ro \
  -v /etc/timezone:/etc/timezone:ro \
  -v /opt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \
  -v /opt/nginx/logs:/var/log/nginx \
  -v /opt/nginx/.htpasswd:/etc/nginx/.htpasswd:ro \
  nginx
ExecStop=/usr/bin/docker stop -t 2 %n ; /usr/bin/docker rm -f %n

[Install]
WantedBy=multi-user.target

Don’t forget to daemon-reload and restart the service.

Now edit /opt/nginx/conf.d/default.conf as described above.

Adding and removing users is the same as above only you do so to /opt/nginx/.htpasswd

Setting up a domain

I’ll add:

  1. Some routers come with access to a free dynamic dns service. I can confirm that the Netgear R7000 is one such router.

##Using OpenSSL to Generate Self-Signed Certificates

As with the htpasswd, we will be creating these certs on the host machine and passing them into the container using a volume. Follow the instructions above, only put the ssl certs into /opt/nginx/certs.

TODO: Determine the correct permissions so nginx can read these but other users cannot. Find out what user it is running as in the container…

Add the certs folder to the container as we have done above.

[Unit]
Description=nginx
Requires=docker.service
After=docker.service

[Service]
Restart=always
ExecStart=/usr/bin/docker run --name=%n \
  -p 80:80 -p 443:443 \
  -v /opt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
  -v /opt/nginx/conf.d:/etc/nginx/conf.d:ro \
  -v /opt/nginx/logs:/var/log/nginx \
  -v /opt/nginx/.htpasswd:/etc/nginx/.htpasswd:ro \
  -v /opt/nginx/certs:/etc/ssl/certs:ro \
  nginx
ExecStop=/usr/bin/docker stop -t 2 %n ; /usr/bin/docker rm -f %n

[Install]
WantedBy=multi-user.target

Edit the default.conf file per the instructions. Do not forget to add the port 80 autoforwarding or else it won’t work. You will get connection refused after authenticating with nginx.

Using Let’s Encrypt to Generate Trusted Certificates.

To be continued…

TODO: Configure the reverse proxy to resolve https://external.domain.com/openhab to go to openHAB to more easily differentiate and coexist with multiple web servers.