Add Cloudflare DNS-01 Challenge

* Add Cloudflare support

* Ensure cron install

* linting

* Add Redhat cron install

* Update vars
This commit is contained in:
Simon Caron 2025-08-19 20:56:49 -04:00 committed by GitHub
parent 3b3cc546d1
commit d408884f18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 126 additions and 57 deletions

4
.github/FUNDING.yml vendored
View File

@ -1,4 +0,0 @@
# These are supported funding model platforms
---
github: geerlingguy
patreon: geerlingguy

View File

@ -10,7 +10,7 @@ name: CI
defaults:
run:
working-directory: 'geerlingguy.certbot'
working-directory: 'simoncaron.certbot'
jobs:
@ -21,7 +21,7 @@ jobs:
- name: Check out the codebase.
uses: actions/checkout@v4
with:
path: 'geerlingguy.certbot'
path: 'simoncaron.certbot'
- name: Set up Python 3.
uses: actions/setup-python@v5
@ -59,7 +59,7 @@ jobs:
- name: Check out the codebase.
uses: actions/checkout@v4
with:
path: 'geerlingguy.certbot'
path: 'simoncaron.certbot'
- name: Set up Python 3.
uses: actions/setup-python@v5

View File

@ -13,7 +13,7 @@ name: Release
defaults:
run:
working-directory: 'geerlingguy.certbot'
working-directory: 'simoncaron.certbot'
jobs:
@ -24,7 +24,7 @@ jobs:
- name: Check out the codebase.
uses: actions/checkout@v4
with:
path: 'geerlingguy.certbot'
path: 'simoncaron.certbot'
- name: Set up Python 3.
uses: actions/setup-python@v5

View File

@ -1,34 +0,0 @@
---
name: Close inactive issues
'on':
schedule:
- cron: "55 12 * * 1" # semi-random time
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v8
with:
days-before-stale: 120
days-before-close: 60
exempt-issue-labels: bug,pinned,security,planned
exempt-pr-labels: bug,pinned,security,planned
stale-issue-label: "stale"
stale-pr-label: "stale"
stale-issue-message: |
This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution!
Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale.
close-issue-message: |
This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details.
stale-pr-message: |
This pr has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution!
Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale.
close-pr-message: |
This pr has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details.
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,6 @@
# Ansible Role: Certbot (for Let's Encrypt)
[![CI](https://github.com/geerlingguy/ansible-role-certbot/actions/workflows/ci.yml/badge.svg)](https://github.com/geerlingguy/ansible-role-certbot/actions/workflows/ci.yml)
[![CI](https://github.com/simoncaron/ansible-role-certbot/actions/workflows/ci.yml/badge.svg)](https://github.com/simoncaron/ansible-role-certbot/actions/workflows/ci.yml)
Installs and configures Certbot (for Let's Encrypt).
@ -26,7 +26,7 @@ By default, this role configures a cron job to run under the provided user accou
### Automatic Certificate Generation
Currently the `standalone` and `webroot` method are supported for generating new certificates using this role.
Currently the `standalone`, `webroot`, and `dns-cloudflare` methods are supported for generating new certificates using this role.
**For a complete example**: see the fully functional test playbook in [molecule/default/playbook-standalone-nginx-aws.yml](molecule/default/playbook-standalone-nginx-aws.yml).
@ -36,7 +36,7 @@ Set `certbot_create_if_missing` to `yes` or `True` to let this role generate cer
certbot_create_method: standalone
Set the method used for generating certs with the `certbot_create_method` variable — current allowed values are: `standalone` or `webroot`.
Set the method used for generating certs with the `certbot_create_method` variable — current allowed values are: `standalone`, `webroot`, or `dns-cloudflare`.
certbot_testmode: false
@ -86,6 +86,25 @@ This install method is currently experimental and may or may not work across all
When using the `webroot` creation method, a `webroot` item has to be provided for every `certbot_certs` item, specifying which directory to use for the authentication. Also, make sure your webserver correctly delivers contents from this directory.
#### DNS-01 Challenge with Cloudflare
When using the `dns-cloudflare` creation method, you need to configure Cloudflare DNS credentials:
certbot_cloudflare_email: "your-email@example.com"
certbot_cloudflare_api_key: "your-global-api-key"
# OR use API token instead (recommended):
certbot_cloudflare_api_token: "your-api-token"
certbot_cloudflare_propagation_seconds: 10
You can use either the email + Global API Key combination OR an API token. The API token method is recommended as it's more secure and allows for more granular permissions.
For API token setup:
1. Go to Cloudflare Dashboard → My Profile → API Tokens
2. Create a token with `Zone:DNS:Edit` permissions for the zones you want certificates for
3. Set the `certbot_cloudflare_api_token` variable with this token
This method supports wildcard certificates and doesn't require your server to be publicly accessible on ports 80/443.
### Source Installation from Git
You can install Certbot from it's Git source repository if desired with `certbot_install_method: source`. This might be useful in several cases, but especially when older distributions don't have Certbot packages available (e.g. CentOS < 7, Ubuntu < 16.10 and Debian < 8).
@ -102,7 +121,7 @@ The directory inside which Certbot will be cloned.
### Wildcard Certificates
Let's Encrypt supports [generating wildcard certificates](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579), but the process for generating and using them is slightly more involved. See comments in [this pull request](https://github.com/geerlingguy/ansible-role-certbot/pull/60#issuecomment-423919284) for an example of how to use this role to maintain wildcard certs.
Let's Encrypt supports [generating wildcard certificates](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579), but the process for generating and using them is slightly more involved. See comments in [this pull request](https://github.com/simoncaron/ansible-role-certbot/pull/60#issuecomment-423919284) for an example of how to use this role to maintain wildcard certs.
Michael Porter also has a walkthrough of [Creating A Lets Encrypt Wildcard Cert With Ansible](https://www.michaelpporter.com/2018/09/creating-a-wildcard-cert-with-ansible/), specifically with Cloudflare.
@ -120,7 +139,7 @@ None.
certbot_auto_renew_hour: "5"
roles:
- geerlingguy.certbot
- simoncaron.certbot
See other examples in the `tests/` directory.

View File

@ -12,12 +12,18 @@ certbot_hsts: false
# Parameters used when creating new Certbot certs.
certbot_create_if_missing: false
certbot_create_method: standalone
certbot_create_method: dns-cloudflare
certbot_create_extra_args: ""
certbot_admin_email: email@example.com
certbot_expand: false
# Default webroot, overwritten by individual per-cert webroot directories
# Cloudflare DNS credentials (use either API token OR email+api_key)
certbot_cloudflare_email: ""
certbot_cloudflare_api_key: ""
certbot_cloudflare_api_token: ""
certbot_cloudflare_propagation_seconds: 60
# Default webroot, overwritten by individual per-cert webroot directories (not used with DNS-01)
certbot_webroot: /var/www/letsencrypt
certbot_certs: []
@ -39,6 +45,8 @@ certbot_create_command: >-
{{ '--expand' if certbot_expand else '' }}
{{ '--webroot-path ' if certbot_create_method == 'webroot' else '' }}
{{ cert_item.webroot | default(certbot_webroot) if certbot_create_method == 'webroot' else '' }}
{{ '--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini' if certbot_create_method == 'dns-cloudflare' else '' }}
{{ '--dns-cloudflare-propagation-seconds ' + (certbot_cloudflare_propagation_seconds | string) if certbot_create_method == 'dns-cloudflare' else '' }}
{{ certbot_create_extra_args }}
--cert-name {{ cert_item_name }}
-d {{ cert_item.domains | join(',') }}

View File

@ -3,7 +3,7 @@ dependencies: []
galaxy_info:
role_name: certbot
author: geerlingguy
author: simoncaron
description: "Installs and configures Certbot (for Let's Encrypt)."
company: "Midwestern Mac, LLC"
license: "license (BSD, MIT)"

View File

@ -25,4 +25,4 @@
when: ansible_os_family == 'Debian'
roles:
- geerlingguy.certbot
- simoncaron.certbot

View File

@ -23,4 +23,4 @@
roles:
- geerlingguy.git
- geerlingguy.certbot
- simoncaron.certbot

View File

@ -23,4 +23,4 @@
roles:
- geerlingguy.git
- geerlingguy.certbot
- simoncaron.certbot

View File

@ -126,7 +126,7 @@
when: ansible_os_family == 'Debian'
roles:
- geerlingguy.certbot
- simoncaron.certbot
- geerlingguy.nginx
tasks:

View File

@ -0,0 +1,39 @@
---
- name: Determine certificate name
set_fact:
cert_item_name: "{{ cert_item.name | default(cert_item.domains | first | replace('*.', '')) }}"
- name: Check if certificate already exists.
stat:
path: /etc/letsencrypt/live/{{ cert_item_name }}/cert.pem
register: letsencrypt_cert
- name: Create Cloudflare credentials file
template:
src: cloudflare.ini.j2
dest: /etc/letsencrypt/cloudflare.ini
owner: root
group: root
mode: 0600
when: certbot_cloudflare_email or certbot_cloudflare_api_token
- name: Check if domains have changed
block:
- name: Register certificate domains
shell: "{{ certbot_script }} certificates --cert-name {{ cert_item_name }} | grep Domains | cut -d':' -f2"
changed_when: false
register: letsencrypt_cert_domains_dirty
- name: Cleanup domain list
set_fact:
letsencrypt_cert_domains: "{{ letsencrypt_cert_domains_dirty.stdout | trim | split(' ') | map('trim') | select('!=', '') | list | sort }}"
- name: Determine if domains have changed
set_fact:
letsencrypt_cert_domains_changed: "{{ letsencrypt_cert_domains != (cert_item.domains | map('trim') | select('!=', '') | list | sort) }}"
when: letsencrypt_cert.stat.exists
- name: Generate new certificate if one doesn't exist.
command: "{{ certbot_create_command }}"
when: not letsencrypt_cert.stat.exists or letsencrypt_cert_domains_changed | default(false)

View File

@ -15,3 +15,9 @@
file:
path: "{{ certbot_script }}"
mode: 0755
- name: Install certbot-dns-cloudflare plugin via pip.
pip:
name: certbot-dns-cloudflare
state: present
when: certbot_create_method == 'dns-cloudflare'

View File

@ -4,6 +4,12 @@
name: "{{ certbot_package }}"
state: present
- name: Install Certbot Cloudflare DNS plugin.
package:
name: "{{ certbot_dns_cloudflare_package }}"
state: present
when: certbot_create_method == 'dns-cloudflare'
- name: Set Certbot script variable.
set_fact:
certbot_script: "{{ certbot_package }}"

View File

@ -29,6 +29,11 @@
name: certbot
classic: true
- name: Install certbot-dns-cloudflare plugin via snap.
snap:
name: certbot-dns-cloudflare
when: certbot_create_method == 'dns-cloudflare'
- name: Symlink certbot into place.
file:
src: /snap/bin/certbot

View File

@ -29,5 +29,13 @@
loop_control:
loop_var: cert_item
- include_tasks: create-cert-dns-cloudflare.yml
with_items: "{{ certbot_certs }}"
when:
- certbot_create_if_missing
- certbot_create_method == 'dns-cloudflare'
loop_control:
loop_var: cert_item
- import_tasks: renew-cron.yml
when: certbot_auto_renew

View File

@ -1,4 +1,9 @@
---
- name: Install cron package (if not present).
package:
name: "{{ certbot_cron_package }}"
state: present
- name: Add cron job for certbot renewal (if configured).
cron:
name: Certbot automatic renewal.

View File

@ -0,0 +1,7 @@
# Cloudflare API credentials
{% if certbot_cloudflare_api_token %}
dns_cloudflare_api_token = {{ certbot_cloudflare_api_token }}
{% else %}
dns_cloudflare_email = {{ certbot_cloudflare_email }}
dns_cloudflare_api_key = {{ certbot_cloudflare_api_key }}
{% endif %}

4
vars/RedHat.yml Normal file
View File

@ -0,0 +1,4 @@
---
certbot_package: certbot
certbot_dns_cloudflare_package: python3-certbot-dns-cloudflare
certbot_cron_package: cronie

View File

@ -1,2 +0,0 @@
---
certbot_package: letsencrypt

View File

@ -1,2 +1,4 @@
---
certbot_package: certbot
certbot_dns_cloudflare_package: python3-certbot-dns-cloudflare
certbot_cron_package: cron