Skip to content

Instantly share code, notes, and snippets.

@sergiks
Last active April 17, 2024 01:10
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save sergiks/4c1ccddc097e61e6fe5e45c53072a944 to your computer and use it in GitHub Desktop.
Save sergiks/4c1ccddc097e61e6fe5e45c53072a944 to your computer and use it in GitHub Desktop.
Let's Encrypt wildcard certificates in docker

NGINX and Certbot example with CloudFlare API in Docker

Sample config files to demonstrate seup that creates and updates free SSL certificates from Let's Encrypt given that the domains are maintained at CloudFlare service.

How it works

Certbot verifies domains ownership by accessing CloudFlare API that adds temporary TXT DNS records. To enable it You must provide your CloudFlare API token. More details in documentation for dns-cloudflare Certbot plugin.

Certbot saves created certificates in Docker volume certbot_etc. Pay attention to output of the certbot run - it mentions path to the created certificates.

Steps to reproduce

  1. Setup docker, docker-compose, domains, nginx – make your website work via plain HTTP.

  2. docker-compose run certbot to create certificates. It will wait for 60 seconds in the middle. Note the output of the command – it will contain actual paths to certificates.

  3. Update nginx.conf to use the right paths to certificates.

  4. ssl-dhparams.pem is like a cryptographic "salt" - required by some of algorithms. Copy that file from somewhere or generate one with command: openssl dhparam -out ssl-dhparams.pem 2048 - that will take some minutes to generate.

    Copy the file into certbot_etc volume by command similar to: docker cp ./ssl-dhparams.pem my_app_nginx_1:/etc/letsencrypt/ssl-dhparams.pem supposing the running NGINX container name is "my_app_nginx_1" - check with docker ps

  5. Test if NGINX config is OK: docker-compose exec nginx nginx -t

  6. Make NGINX reload the updated config: docker-compose exec nginx nginx -s reload

# Cloudflare API credentials used by Certbot
# How to generate API token:
# https://developers.cloudflare.com/api/tokens/create
dns_cloudflare_api_token = XXXXXXXXXXXXXXXXXXXX
### Old insecure way. Not recommended.
# dns_cloudflare_email = YOUR@EMAIL.COM
# dns_cloudflare_api_key = XXXXXXXXXXXXXXXXXXXX
version: '3'
services:
certbot:
image: certbot/dns-cloudflare
volumes:
- certbot_etc:/etc/letsencrypt
- ./cloudflare.ini:/root/cloudflare.ini
command: >-
certonly --dns-cloudflare
--dns-cloudflare-credentials /root/cloudflare.ini
--dns-cloudflare-propagation-seconds 15
--email YOUR@EMAIL.COM
--agree-tos --no-eff-email
--force-renewal
-d domain1.com
-d *.domain1.com
-d domain2.com
-d *.domain2.com
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
restart: "always"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "10"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- certbot_etc:/etc/letsencrypt
volumes:
certbot_etc:
worker_processes 1;
events {
worker_connections 512;
}
http {
server {
listen 80;
root /var/www/;
index index.html;
### SSL LetsEncrypt
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl_certificate /etc/letsencrypt/live/YOUR_DOMAIN/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/YOUR_DOMAIN/privkey.pem;
### Create the dhparam file:
### openssl dhparam -out ssl-dhparams.pem 2048
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:1m; # about 4000 sessions
ssl_session_tickets off;
# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;
}
}
@chrishiggins29
Copy link

There's a tiny typo in the documentation under point 4, where it reads:

Copy the file into certbot_etc volume by command similar to: docker cp ./ssl-dhparams.pem my_app_nginx_1:/etc/certbot/ssl-dhparams.pem supposing the running NGINX container ...

The target path should be /etc/letsencrypt (not /etc/certbot !), so the proper docker command would be docker cp ./ssl-dhparams.pem my_app_nginx_1:/etc/letsencrypt/ssl-dhparams.pem

@sergiks
Copy link
Author

sergiks commented Dec 28, 2020

@chrishiggins29 thank you! Updated.

@red-avtovo
Copy link

@sergiks thank you very much for the manual.
I have one question though: why did you mount nginx.conf to certbot?

@sergiks
Copy link
Author

sergiks commented Oct 8, 2021

why did you mount nginx.conf to certbot?

@red-avtovo You are welcome. This gist is quite outdated by now.
CloudFlare has introduced API Tokens to replace the less secure global API Key. It is better to create a token using Zone:DNS:Edit template.
NGINX config file is not required at all in the certbot container. Thanks for spotting! Will remove.

@andreigavril27
Copy link

@sergiks will the certificate get always renewed when the container starts in the docker compose file? I've looked into the official doc of the container and there are no details about it.

@sergiks
Copy link
Author

sergiks commented Mar 16, 2022

@andreigavril27 I think it will, as there is the --force-renewal flag present. The documentation for this option says:

If a certificate already exists for the requested renew it now, regardless of whether it near expiry. (Often --keep-until-expiring is more appropriate). Also implies --expand. (default: False)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment