Description: Nginx is a very common web server used for reverse proxy and load balancing. We would use the nginx to do reverse proxy very often, in case we are not using a paid SSL certifcate. Lets encrypt is free and available choice for introducing the website to the world of SSL / TLS. This post is targeted introduce how to use lets encrypt's http mode with nginx to generate a SSL certificate for the public available host.
There is a ready to use git repository on github. clone and follow instruction to setup.
Create dummy web service to test the result. you may remove these docker container later on.
docker run --rm -d -p 8080:8080 --name hello-world-app xethhung/hello-world-web:1.0.0
If you access the localhost with curl http://localhost:8080 you will receive message like hello world from ...
Clone project and initialize the proxy to port 8080 after the certificate signed.
For example, let’s assume the domain name is www.example.com and use the certbot proxy prot is 53388
git clone https://github.com/xh-dev/lets-encrypt-nginx-quickstart.git
cd lets-encrypt-nginx-quickstart
#./first-init.sh {domain} {port}
./first-init.sh www.example.com 53388
After the above shell script execution complete, we can access hello-world-app through https (curl https://www.example.com) in the internet.
We have followed the demo to setup a SSL certificate for the our domain name (www.example.com).
Now I’m going to go explaine the detail of what does the first-init.sh do.
Below diagram shows a very simple version of how the let’s encrypt and nginx work togather to complete the SSL certificate signing process. \ For detailed information about the ACME, please go throught rfc8555.
when execute first-init.sh, the script:
create a custom nginx configuration for domain (www.example.com) using ./conf/half_conf_template. Any request to www.example.com/.well-known/acme-challenge/ will be route to the port 53388. start the with docker compose up -d
server {
listen 80;
server_name {hostname};
location /.well-known/acme-challenge/ {
proxy_pass http://mainhost:{port};
}
#return 301 https://{hostname};
}
start up certbot with standalone mode with port 53388 and perform ACME http verification. (certbox_template)
docker run \
-p 53388:80 \
-v "{dir}/letsencrypt:/etc/letsencrypt" \
certbot/certbot \
certonly \
--standalone --email [email protected]\
--agree-tos \
--preferred-challenges http \
--keep-until-expiring \
-d www.example.com
the ACME server verify the domain name by access www.example.com/.well-known/acme-challenge/
After the SSL certificate signed, replace the nginx config with ./conf/full_conf_template
server {
listen 80;
server_name www.example.com;
location /.well-known/acme-challenge/ {
proxy_pass http://mainhost:53388;
}
#return 301 https://{hostname};
}
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate /letsencrypt/live/www.example.com/fullchain.pem;
ssl_certificate_key /letsencrypt/live/www.example.com/privkey.pem;
location / {
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_pass http://mainhost:8080;
}
}
Start the docker compose again with the new configuration.
Since the let’s encrypt generated certificate only available for 90 day, we could renew the certificate with Certbot scheduled as daily.
The Certbot is intelligent enough to only renew the certificate that is about to be expired and renew them without affecting other certificates.
The renew.sh serve the purpose to renew certificate and reload the nginx server.
See the script below:
#!/bin/bash
p=$(readlink -f $(dirname $0))
docker run \
-p 53388:80 \
-v "${p}/letsencrypt:/etc/letsencrypt" \
certbot/certbot renew
${p}/reload-nginx.sh
Add crontab job as below, the Certbot renewal will be triggered every day on 5:18 pm.
5 18 * * * renew.sh