Certbot Cloudflare DNS Authenticator
The `certbot-dns-cloudflare` plugin provides a DNS authenticator for Certbot, allowing you to obtain Let's Encrypt certificates using Cloudflare's DNS API. This is particularly useful for wildcard certificates. It is part of the larger Certbot project, currently at version 5.5.0, with minor releases typically aligned with Certbot's bimonthly schedule.
Warnings
- breaking Certbot 5.0.0 and subsequent versions (including certbot-dns-cloudflare 5.x.x) require Python 3.10 or newer. Users on older Python versions will need to upgrade their Python environment.
- gotcha The credentials file containing your Cloudflare API token/key must have restricted permissions (owner read-only, e.g., `0o400`) to prevent unauthorized access. Certbot will refuse to use files with broader permissions.
- gotcha Cloudflare recommends using API Tokens (granular permissions) over Global API Keys (full account access). While the plugin supports both, API Tokens are more secure and should be preferred. Ensure the token has 'Zone DNS' 'Edit' permissions for the specific zones you intend to manage.
- gotcha DNS changes need time to propagate across the internet. If Certbot fails with a 'DNS problem' error, it might be due to insufficient propagation time. The default `30` seconds might not always be enough.
- gotcha Managing Certbot installations can be complex due to various methods (pip, snap, OS package managers). Mixing methods or using an outdated Certbot installation can lead to plugin not found errors or dependency conflicts.
Install
-
pip install certbot certbot-dns-cloudflare -
sudo snap install certbot --classic sudo snap set certbot trust-plugin-with-root=ok sudo snap install certbot-dns-cloudflare
Imports
- certbot-dns-cloudflare
This plugin is used via the `certbot` command-line tool, not typically imported in Python code directly by end-users. Its functionality is exposed via CLI flags like `--dns-cloudflare`.
Quickstart
import os
import subprocess
import tempfile
import stat
# --- Configuration for your domain and Cloudflare ---
DOMAIN = "yourdomain.com" # Replace with your actual domain
EMAIL = "your@email.com" # Replace with your actual email
# --- Cloudflare API Token (recommended) ---
# For production, ensure this token has Zone DNS Write permissions for your domain.
# Generate it at: https://dash.cloudflare.com/profile/api-tokens
# Set this as an environment variable: export CLOUDFLARE_API_TOKEN="YOUR_TOKEN"
CLOUDFLARE_API_TOKEN = os.environ.get('CLOUDFLARE_API_TOKEN', 'YOUR_PLACEHOLDER_TOKEN')
if CLOUDFLARE_API_TOKEN == 'YOUR_PLACEHOLDER_TOKEN':
print("WARNING: CLOUDFLARE_API_TOKEN environment variable not set. Using a placeholder.")
print(" This quickstart will likely fail without a valid token.")
print(" Set it using: export CLOUDFLARE_API_TOKEN=\"<YOUR_TOKEN>\"")
# --- Create a temporary credentials file ---
# This file will store your Cloudflare API token securely.
# Certbot requires this file to have restricted permissions (read-only for owner).
temp_dir = tempfile.mkdtemp()
credentials_path = os.path.join(temp_dir, 'cloudflare.ini')
try:
with open(credentials_path, 'w') as f:
f.write(f"dns_cloudflare_api_token = {CLOUDFLARE_API_TOKEN}\n")
# Set permissions: owner read-only (0o400)
os.chmod(credentials_path, stat.S_IRUSR)
print(f"Created temporary credentials file: {credentials_path}")
# --- Construct and run the Certbot command ---
# This command obtains a certificate for your domain(s) using Cloudflare DNS.
# --dns-cloudflare-propagation-seconds: Adjust if DNS changes are slow to propagate.
# --test-cert: Use for testing to avoid hitting Let's Encrypt rate limits. Remove for production.
certbot_command = [
"certbot",
"certonly",
"--dns-cloudflare",
f"--dns-cloudflare-credentials={credentials_path}",
"--dns-cloudflare-propagation-seconds", "60",
"-d", DOMAIN,
"-d", f"*.{DOMAIN}", # Uncomment if you need a wildcard certificate
"--email", EMAIL,
"--agree-tos",
"--non-interactive",
"--keep-until-expiring",
"--test-cert" # IMPORTANT: Use this for initial testing! Remove for actual certificate issuance.
]
print("\nAttempting to run Certbot command:")
print(f"$ {' '.join(certbot_command)}")
# Execute the command
result = subprocess.run(certbot_command, capture_output=True, text=True, check=False) # check=False to capture output on error
print("\n--- Certbot Output ---")
print(result.stdout)
if result.stderr:
print("\n--- Certbot Errors ---")
print(result.stderr)
if result.returncode == 0:
print("\nSUCCESS: Certbot command completed. Check output for certificate path.")
else:
print(f"\nFAILURE: Certbot command exited with code {result.returncode}.")
print("Please review the output above, ensure your Cloudflare API token is valid and has correct permissions, and that your domain is managed by Cloudflare.")
except FileNotFoundError:
print("\nERROR: 'certbot' command not found. Please ensure Certbot is installed and in your PATH.")
print(" (e.g., pip install certbot certbot-dns-cloudflare or snap install certbot --classic)")
except Exception as e:
print(f"\nAn unexpected Python error occurred: {e}")
finally:
# --- Clean up temporary files ---
if os.path.exists(credentials_path):
os.remove(credentials_path)
print(f"\nRemoved temporary credentials file: {credentials_path}")
if os.path.exists(temp_dir):
os.rmdir(temp_dir)
print(f"Removed temporary directory: {temp_dir}")