From 88715974eb45bdf1fb189292685f408dac237d96 Mon Sep 17 00:00:00 2001 From: "Y.C.Huang" <33322926+ShiriNmi1520@users.noreply.github.com> Date: Sat, 7 Jan 2023 06:58:50 +0800 Subject: [PATCH] docs: Provision a cert with the ACME DNS-01 challenge via Certbot + Cloudflare (#2968) * docs: Certbot cloudflare Add docs for implement certbot-dns-cloudflare to generate certificate for mail server * Apply suggestions from code review * fix: certbot-cloudflare docs Fix the docker-compose command according to the advice * feat: DNS-Cloudflare certificate renew Add docs for implementing renewing certificate with crontab * Apply suggestions from code review Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com> --- docs/content/config/security/ssl.md | 137 ++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/docs/content/config/security/ssl.md b/docs/content/config/security/ssl.md index a3823c86..3791affe 100644 --- a/docs/content/config/security/ssl.md +++ b/docs/content/config/security/ssl.md @@ -158,6 +158,143 @@ Certbot provisions certificates to `/etc/letsencrypt`. Add a volume to store the Certbot does support [alternative certificate providers via the `--server`][certbot::custom-ca] option. In most cases you'll want to use the default _Let's Encrypt_. +#### Example using `certbot-dns-cloudflare` with Docker { data-toc-label='certbot-dns-cloudflare with Docker' } + +If you are unable get a certificate via the `HTTP-01` (port 80) or `TLS-ALPN-01` (port 443) [challenge types](https://letsencrypt.org/docs/challenge-types/), the `DNS-01` challenge can be useful (_this challenge can additionally issue wildcard certificates_). This guide shows how to use the `DNS-01` challenge with Cloudflare as your DNS provider. + +Obtain a Cloudflare API token: + +1. Login into your Cloudflare dashboard. +2. Navigate to the [API Tokens page](https://dash.cloudflare.com/profile/api-tokens). +3. Click "Create Token", and choose the `Edit zone DNS` template (_Certbot [requires the `ZONE:DNS:Edit` permission](https://certbot-dns-cloudflare.readthedocs.io/en/stable/#credentials)_). + + !!! warning "Only include the necessary Zone resource configuration" + + Be sure to configure "Zone Resources" section on this page to `Include -> Specific zone -> `. + + This restricts the API token to only this zone (domain) which is an important security measure. + +4. Store the _API token_ you received in a file `cloudflare.ini` with content: + + ```dosini + dns_cloudflare_api_token = YOUR_CLOUDFLARE_TOKEN_HERE + ``` + + - As this is sensitive data, you should restrict access to it with `chmod 600` and `chown 0:0`. + - Store the file in a folder if you like, such as `docker-data/certbot/secrets/`. +5. Your `docker-compose.yml` should include the following: + + ```yaml + services: + mailserver: + environments: + # Set SSL certificate type. + - SSL_TYPE=letsencrypt + volumes: + # Mount the cert folder generated by Certbot into mail-server: + - ./docker-data/certbot/certs/:/etc/letsencrypt/:ro + + certbot-cloudflare: + image: certbot/dns-cloudflare:latest + command: certonly --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token -d mail.example.com + volumes: + - ./docker-data/certbot/certs/:/etc/letsencrypt/ + - ./docker-data/certbot/logs/:/var/log/letsencrypt/ + secrets: + - cloudflare-api-token + + # Docs: https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose + # WARNING: In compose configs without swarm, the long syntax options have no effect, + # Ensure that you properly `chmod 600` and `chown 0:0` the file on disk. Effectively treated as a bind mount. + secrets: + cloudflare-api-token: + file: ./docker-data/certbot/secrets/cloudflare.ini + ``` + + Alternative using the `docker run` command (`secrets` feature is not available): + + ```sh + docker run \ + --volume "${PWD}/docker-data/certbot/certs/:/etc/letsencrypt/" \ + --volume "${PWD}/docker-data/certbot/logs/:/var/log/letsencrypt/" \ + --volume "${PWD}/docker-data/certbot/secrets/:/tmp/secrets/certbot/" + certbot/dns-cloudflare \ + certonly --dns-cloudflare --dns-cloudflare-credentials /tmp/secrets/certbot/cloudflare.ini -d mail.example.com + ``` + +6. Run the service to provision a certificate: + + ```sh + docker-compose run certbot-cloudflare + ``` + +7. You should see the following log output: + + ```log + Saving debug log to /var/log/letsencrypt/letsencrypt. log | Requesting a certificate for mail.example.com + Waiting 10 seconds for DNS changes to propagate + Successfully received certificate. + Certificate is saved at: /etc/letsencrypt/live/mail.example.com/fullchain.pem + Key is saved at: /etc/letsencrypt/live/mail.example.com/privkey.pem + This certificate expires on YYYY-MM-DD. + These files will be updated when the certificate renews. + NEXT STEPS: + - The certificate will need to be renewed before it expires. Certbot can automatically renew the certificate in background, but you may need to take steps to enable that functionality. See https://certbot.org/renewal structions. + ``` + +After completing the steps above, your certificate should be ready to use. + +??? tip "Renewing a certificate (Optional)" + + We've only demonstrated how to provision a certificate, but it will expire in 90 days and need to be renewed before then. + + In the following example, add a new service (`certbot-cloudflare-renew`) into `docker-compose.yml` that will handle certificate renewals: + + ```yml + services: + certbot-cloudflare-renew: + image: certbot/dns-cloudflare:latest + command: renew --dns-cloudflare --dns-cloudflare-credentials /run/secrets/cloudflare-api-token + volumes: + - ./docker-data/certbot/certs/:/etc/letsencrtypt/ + - ./docker-data/certbot/logs/:/var/log/letsencrypt/ + secrets: + - cloudflare-api-token + + ``` + + You can manually run this service to renew the cert within 90 days: + + ```sh + docker-compose run certbot-cloudflare-renew + ``` + + You should see the following output + (The following log was generated with `--dry-run` options) + + ```log + Saving debug log to /var/log/letsencrypt/letsencrypt.log + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Processing /etc/letsencrypt/renewal/mail.example.com.conf + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Account registered. + Simulating renewal of an existing certificate for mail.example.com + Waiting 10 seconds for DNS changes to propagate + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Congratulations, all simulated renewals succeeded: + /etc/letsencrypt/live/mail.example.com/fullchain.pem (success) + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + ``` + + It is recommended to automate this renewal via a task scheduler like a _systemd timer_ or in `crontab` + (`crontab` example: Checks every day if the certificate should be renewed) + + ```sh + 0 0 * * * docker-compose -f PATH_TO_YOUR_DOCKER_COMPOSE_YML up certbot-cloudflare-renew + ``` + #### Example using `nginx-proxy` and `acme-companion` with Docker { data-toc-label='nginx-proxy with Docker' } If you are running a web server already, port 80 will be in use which Certbot requires. You could use the [Certbot `--webroot`][certbot::webroot] feature, but it is more common to leverage a _reverse proxy_ that manages the provisioning and renewal of certificates for your services automatically.