Wordpress, Nginx Reverse Proxy and Certbot
*this is what AI thinks of my problem
Welcome, everyone! In this post, I’ll walk you through the process of setting up a Wordpress installation behind Docker, under a NGINX reverse proxy (to route different domains to different services), and Certbot to secure with SSL the site. Throughout this journey, I encountered and and somewhat successfully addressed various challenges.
Introduction
To provide a bit of context, I manage a Virtual Private Server (VPS) on Digital Ocean. On this VPS, I have Docker installed, and I utilize an NGINX reverse proxy to serve not only this Wordpress installation but also my personal website and other services I need. My personal domain is pointing to this VPS, creating a cohesive and centralized environment for all my online endeavors.
Configuring Docker and WordPress
I kicked off the process by installing WordPress through Docker. I have all my docker configs organized in folders depending on the service. My docker compose file for all Wordpress needs looks like this:
version: '3.4'
services:
database:
image: mysql:5.7
command:
- "--character-set-server=utf8"
- "--collation-server=utf8_unicode_ci"
ports:
- "3306:3306" # (*)
restart: on-failure:5
environment:
MYSQL_USER: wordpress
MYSQL_DATABASE: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_ROOT_PASSWORD: wordpress
wordpress:
depends_on:
- database
image: wordpress:latest
ports:
- "8008:80" # (*)
restart: on-failure:5
volumes:
- ./public:/var/www/html
environment:
WORDPRESS_DB_HOST: database:3306
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_NAME: wordpress
phpmyadmin:
depends_on:
- database
image: phpmyadmin/phpmyadmin
ports:
- 8084:80 # (*)
restart: on-failure:5
environment:
PMA_HOST: database
wordmove:
tty: true
depends_on:
- wordpress
image: mfuezesi/wordmove
restart: on-failure:5
container_name: luablu_wordmove # (+)
volumes:
- ./config:/home
- ./public:/var/www/html
- ~/.ssh:/root/.ssh # (!)
networks:
default:
external:
name: nginx-reverse_default
The most important part of this config is define the network and make all defined services belong to that network.
Configuring NGINX
I had to be able to route the new domain to wordpress and I wasn’t able to find the host and port that contained the wordpress intallation. I ended up with this config.
server {
server_name domain.com;
listen 80 ;
# Do not HTTPS redirect Let'sEncrypt ACME challenge
location /.well-known/acme-challenge/ {
root /home/static-websites/letsencrypt;
auth_basic off;
allow all;
try_files $uri =404;
break;
}
location / {
return 301 https://$server_name$request_uri;
}
}
server {
server_name domain.com;
listen 443 ssl http2 ;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/nginx/letsencrypt/live/domain.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://wordpress:80/;
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;
}
}
The acme challenge will be relevant for later CERTBOT config. the firts block redirects to HTTPS. For now the important part is the proxy_pass. Which is pointing to the CONTAINER name and the exposed port. I actually didnt know you could use container’s name to point to it. Non of Ips or other known host names worked for me.
With these adjustments, I achieved accessibility to the WordPress site from any external location.
SSL Certificate Configuration with Certbot
Moving on, I installed Certbot (from official docs)[https://certbot.eff.org/instructions?ws=other&os=ubuntufocal] on my Digital Ocean VPS to obtain an SSL certificate. This step presented 2 main hurdles:
-
Incorrect DNS redirect configuration: My domain’s IPV6 was not pointing to the correct IP from DO, this meant the Certbot challenges were failing
-
Utilizing the Certbot web-root plugin: Overcoming Let’s Encrypt challenges involved using the web-root plugin, with specific configuration for the challenge location pointing to the directory created by Certbot.
Successfully navigating these challenges, I generated the SSL certificates necessary to enhance the security of the site.
- Nginx reverse proxy config: My Nginx is another container in the same system so I had to create volume to expose the certificates created by certbot to it. The folder wew certbot may vary. I exposed the whole thing to be able to navigate through everything. My new volume looked like this:
- /etc/letsencrypt/:/etc/nginx/letsencrypt
Automating the Renewal Process
Recognizing that security extends beyond the initial certificate installation, I created a script that automates the renewal process. This script accesses the container and triggers a reload to apply the new certificates. This automation is seamlessly integrated into the Certbot renewal process, executed as a convenient hook.
To streamline the entire process, I added the certbot renewal
command with the hook to a cronjob on my Digital Ocean VPS. This ensures that the certificates are renewed at regular intervals, maintaining the site’s security and constant accessibility.
Since the certificate are only loaded once at boot by Nginx, after the cert is regenerated we need to restart nginx. I did thig by attaching a POST RENEWAL script to certbot with the following content.
#!/bin/bash
docker exec -it nginx-proxy service nginx reload
nginx-proxy is the name of the container that has my nginx reverser proxy, and the rest the command to execute inside the machine. Now to include the trigger we execute like this:
certbot renew --deploy-hook /root/scripts/letsencrypt/post-deploy-certbot-renewal.sh
Make sure you add the deploy hook for the auto renewal. It can be by placing it in the deploy hook folder inside etc/letsencrypt or defining it when you create the cert. When reniewing, certbot will remember.
Thats ALL!