CA Certificate

The request and configuration of the CA certificate can be done in two phases. The first step is to request the certificate form 'Let's Encrypt' and the second phase is to configure the web server ( or reverse proxy) to use the issued certificate.

Request Certificate

To request a certificate we will need the following:

  • Web sever that handles web requests from 'Let's Encrypt'
  • Handle the '/.well-known/acme-challenge'

We will be using 'Let's Encrypt' provided tool known as 'Certbot' to initiate a CA Certificate request. This tool exists as a Docker container which we will use in our example. Before initiating the certificate request, we will setup the needed files.

Image showing the 'letsencrypt' directory and 'letsencrypt-site' sub directory
version: '3.1'

services:

  letsencrypt-nginx-container:
    container_name: 'letsencrypt-nginx-container'
    image: nginx:latest
    ports:
      - "80:80"

    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./letsencrypt-site:/usr/share/nginx/html
    networks:
      - docker-network

networks:
  docker-network:
    driver: bridge
docker-compose.yml file
server {
    listen 80;
    listen [::]:80;
    server_name planningtasks.com www.planningtasks.com;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /usr/share/nginx/html;
    }

    root /usr/share/nginx/html;
    index index.html;
}
nginx.config file
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Let's Encrypt Landing Page</title>
</head>
<body>
    <h1>Cert Landing Page</h1>
</body>
</html>
index.html

Once you have all the files setup, run the following command to start an Nginx Docker instance. This will create a web server configured to handle the 'Let's Encrypt' certificate challenge. Check that the site is up using curl command.

docker-compose up -d

After the Nginx web server is up, use Docker Certbot to initiate a CA domain certificate challenge. The Certificate files generated as part of the challange will be located in the 'docker-volumes/etc/...'  directory.

docker run -it --rm \
-v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \
-v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \
-v /docker/letsencrypt-docker-nginx/src/letsencrypt/letsencrypt-site:/data/letsencrypt \
-v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot \
certonly --webroot \
--register-unsafely-without-email --agree-tos \
--webroot-path=/data/letsencrypt \
--staging \
-d example.com -d www.example.com
Remove --staging option when ready to request the production certificates. 
Example of running the CA Certificate request using Certbot.

Check Certificates

Verify the certificates by using the following command, if valid, you should see the relevant certificate details:

docker run --rm -it --name certbot \
-v /docker-volumes/etc/letsencrypt:/etc/letsencrypt \
-v /docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt \
certbot/certbot \
--staging \
certificates
Example of validating certificates for planningtasks.com

Configure NGINX

At this point, the docker created in the "Request Certificate" phase is no longer needed and should be cleaned up. The certificates that are generated is what you want to retain.

Generate DH Param (optional)

This is optional and server to enhance the security of the web server. Note that if this step is skipped, subsequent scripts below might need to be adjusted. To generate the (.pem) file, use 'openssl' and the below command.

openssl dhparam -out /docker/planningtask/src/production/dh-param/dhparam-2048.pem 2048

The command generates a 'dhparam-2048.pem' file in the 'dh-param' directory.

.pem file generated using openssl

Configure Nginx to use the certificate by adding configurations to the web server settings. In this example, we will be configuring Nginx as a reverse proxy web server.

upstream planningtasks {
	server localbox:5000;
	server localbox:5001;
}
server{
	listen 80;	
	server_name planningtasks.com;
	location / {
		proxy_pass http://planningtasks;
		proxy_set_header Host $host;
		proxy_set_header X-Forwarded-For $remote_addr;
	}
    # Useful for a certificate renew challange by Certbot
	location ~ /.well-known/acme-challenge {
		allow all;
		root /data/letsencrypt;
	}
	#return 301 https://$host$request_uri;
}
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name planningtasks.com;
    server_tokens off;

    ssl_certificate /etc/letsencrypt/live/planningtasks.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/planningtasks.com/privkey.pem;
    ssl_buffer_size 8k;
    ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;
    ssl_ecdh_curve secp384r1;
    ssl_session_tickets off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8;
	
	location / {
		proxy_pass http://planningtasks;
		#proxy_set_header X-Forwarded-For $remote_addr;
		proxy_redirect off;
		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-Host $server_name;
		proxy_set_header X-Forwarded-Proto https;
	}
}
Example Nginx configuration for a production site

Before starting up a Docker Nginx instance using the below command, prepare the following files in the relative location. Note the location for the 'production.conf' and the 'dhparam-2048.pem' files.

version: '3.1'

services:

  production-nginx-container:
    container_name: 'production-nginx-container'
    image: nginx:latest
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./production.conf:/etc/nginx/conf.d/default.conf
      - ./dh-param/dhparam-2048.pem:/etc/ssl/certs/dhparam-2048.pem
      - /docker-volumes/etc/letsencrypt/live/mycloudology.com/fullchain.pem:/etc/letsencrypt/live/mycloudology.com/fullchain.pem
      - /docker-volumes/etc/letsencrypt/live/mycloudology.com/privkey.pem:/etc/letsencrypt/live/mycloudology.com/privkey.pem
      - /docker-volumes/data/letsencrypt:/data/letsencrypt
    networks:
      - docker-network      
    extra_hosts:
      - "localbox:xxx.xx.xxx.xxx"

networks:
  docker-network:
    driver: bridge
docker-compose.yml file, xxx.xx.xxx.xxx points to the docker host IP

For more details on how to find out the Docker host IP:

Customizing Nginx Docker container
Example of using Dockerfile to custom a base docker image.

Renew Certificate

Invoke Certbot to renew certificate that are references in the '/docker-volumes/data/letsencrypt' location.

# renew certbot cert
docker run --rm -it --name certbot \
-v "/docker-volumes/etc/letsencrypt:/etc/letsencrypt" \
-v "/docker-volumes/var/lib/letsencrypt:/var/lib/letsencrypt" \
-v "/docker-volumes/data/letsencrypt:/data/letsencrypt" \
-v "/docker-volumes/var/log/letsencrypt:/var/log/letsencrypt" \
certbot/certbot renew --webroot -w /data/letsencrypt --quiet

Restarting Nginx Docker:

docker kill --signal=HUP production-nginx-container

# Check for renewal errors in the logs
cat /docker-volumes/var/log/letsencrypt/letsencrypt.log

# Check that date on the files has been renewed by Certbot
ls -l /docker-volumes/etc/letsencrypt/renewal

For more articles that cover Let's Encrypt:

Install NGINX and configure TLS
Install Nginx and configure instance on a Ubuntu instance.

source - Lets Encrypt container
source - Reverse proxy Nginx
source - Details on Docker, Nginx and 'Let's Encrypt'.