Is production: true
#linux #nginx #lets_encrypt #docker

Title: How to Setup LetsEncrypt With NGINX

Created: 22 May 2023 Modified: 22 May 2023

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.



Demo

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.

Prequisite

Explanination

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.

Architecture

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.

diagram for certbot

Steps

  1. Start up certbot in standalone mode The certbot would access to ACME Server for request signing certificate
  2. ACME server request for challenge to proof the onwnership of the domain
  3. ACME server reache the target host for verify the ownership of domain
  4. ACME sign the SSL certificate when the verification complete successfully

Scripts

when execute first-init.sh, the script:

  1. 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};
    
    }
    
  2. 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 
    
  3. the ACME server verify the domain name by access www.example.com/.well-known/acme-challenge/

  4. 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;
    }
    }
    
  5. Start the docker compose again with the new configuration.

Renew

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