- Added clarity and structure to README files, including recent updates and getting started sections. - Improved manual verification documentation for CrowdSec authentication, emphasizing expected outputs and success criteria. - Updated debugging guide with detailed output examples and automatic trace capture information. - Refined best practices for E2E tests, focusing on efficient polling, locator strategies, and state management. - Documented triage report for DNS Provider feature tests, highlighting issues fixed and test results before and after improvements. - Revised E2E test writing guide to include when to use specific helper functions and patterns for better test reliability. - Enhanced troubleshooting documentation with clear resolutions for common issues, including timeout and token configuration problems. - Updated tests README to provide quick links and best practices for writing robust tests.
21 KiB
DNS Challenge (DNS-01) for SSL Certificates
Charon supports DNS-01 challenge validation for issuing SSL/TLS certificates, enabling wildcard certificates and secure automation through 15+ integrated DNS providers.
Table of Contents
- Overview
- Why Use DNS Challenge?
- Supported DNS Providers
- Getting Started
- Manual DNS Challenge
- Troubleshooting
- Related Documentation
Overview
What is DNS-01 Challenge?
The DNS-01 challenge is an ACME (Automatic Certificate Management Environment) validation method where you prove domain ownership by creating a specific DNS TXT record. When you request a certificate, the Certificate Authority (CA) provides a challenge token that must be published as a DNS record at _acme-challenge.yourdomain.com.
How It Works
┌─────────────┐ 1. Request Certificate ┌──────────────┐
│ Charon │ ─────────────────────────────────▶ │ Let's │
│ │ │ Encrypt │
│ │ ◀───────────────────────────────── │ (CA) │
└─────────────┘ 2. Receive Challenge Token └──────────────┘
│
│ 3. Create TXT Record via DNS Provider API
▼
┌─────────────┐
│ DNS │ _acme-challenge.example.com TXT "token123..."
│ Provider │
└─────────────┘
│
│ 4. CA Verifies DNS Record
▼
┌──────────────┐ 5. Certificate Issued ┌─────────────┐
│ Let's │ ─────────────────────────────────▶│ Charon │
│ Encrypt │ │ │
└──────────────┘ └─────────────┘
Key Features
| Feature | Description |
|---|---|
| Wildcard Certificates | Issue certificates for *.example.com |
| 15+ DNS Providers | Native integration with major DNS services |
| Secure Credentials | AES-256-GCM encryption with automatic key rotation |
| Plugin Architecture | Extend with custom providers via webhooks or scripts |
| Manual Option | Support for any DNS provider via manual record creation |
| Auto-Renewal | Certificates renew automatically before expiration |
Why Use DNS Challenge?
DNS-01 vs HTTP-01 Comparison
| Feature | DNS-01 Challenge | HTTP-01 Challenge |
|---|---|---|
| Wildcard Certificates | ✅ Yes | ❌ No |
| Requires Port 80 | ❌ No | ✅ Yes |
| Works Behind Firewall | ✅ Yes | ⚠️ Requires port forwarding |
| Internal Networks | ✅ Yes | ❌ No |
| Multiple Servers | ✅ One validation, many servers | ❌ Each server validates |
| Setup Complexity | Medium (API credentials) | Low (no credentials) |
When to Use DNS-01
Choose DNS-01 challenge when you need:
- ✅ Wildcard certificates (
*.example.com) — DNS-01 is the only method that supports wildcards - ✅ Servers without public port 80 — Firewalls, NAT, or security policies blocking HTTP
- ✅ Internal/private networks — Servers not accessible from the internet
- ✅ Multi-server deployments — One certificate for load-balanced or clustered services
- ✅ CI/CD automation — Fully automated certificate issuance without HTTP exposure
When HTTP-01 May Be Better
Consider HTTP-01 challenge when:
- You don't need wildcard certificates
- Port 80 is available and publicly accessible
- You want simpler setup without managing DNS credentials
- Your DNS provider isn't supported by Charon
Supported DNS Providers
Charon integrates with 15+ DNS providers for automatic DNS record management.
Tier 1: Full API Support
These providers have complete, tested integration with automatic record creation and cleanup:
| Provider | API Type | Documentation |
|---|---|---|
| Cloudflare | REST API | Cloudflare Setup Guide |
| AWS Route53 | AWS SDK | Route53 Setup Guide |
| DigitalOcean | REST API | DigitalOcean Setup Guide |
| Google Cloud DNS | GCP SDK | Google Cloud Setup Guide |
| Azure DNS | Azure SDK | Azure Setup Guide |
Tier 2: Standard API Support
Fully functional providers with standard API integration:
| Provider | API Type | Notes |
|---|---|---|
| Hetzner | REST API | Hetzner Cloud DNS |
| Linode | REST API | Linode DNS Manager |
| Vultr | REST API | Vultr DNS |
| OVH | REST API | OVH API credentials required |
| Namecheap | XML API | API access must be enabled in account |
| GoDaddy | REST API | Production API key required |
| DNSimple | REST API | v2 API |
| NS1 | REST API | NS1 Managed DNS |
Tier 3: Alternative Methods
For providers without direct API support or custom DNS infrastructure:
| Method | Use Case | Documentation |
|---|---|---|
| RFC 2136 | Self-hosted BIND9, PowerDNS, Knot DNS | RFC 2136 Setup |
| Webhook | Custom DNS APIs, automation platforms | Webhook Provider |
| Script | Legacy tools, custom integrations | Script Provider |
| Manual | Any DNS provider (user creates records) | Manual DNS Challenge |
Getting Started
Prerequisites
Before configuring DNS challenge:
- ✅ A domain name you control
- ✅ Access to your DNS provider's control panel
- ✅ API credentials from your DNS provider (see provider-specific guides below)
- ✅ Charon installed and running
Step 1: Add a DNS Provider
- Navigate to Settings → DNS Providers in Charon
- Click "Add DNS Provider"
- Select your DNS provider from the dropdown
- Enter a descriptive name (e.g., "Cloudflare - Production")
Step 2: Configure API Credentials
Each provider requires specific credentials. See provider-specific sections below.
Security Note: All credentials are encrypted with AES-256-GCM before storage. See Key Rotation for credential security best practices.
Step 3: Test the Connection
- After saving, click "Test Connection" on the provider card
- Charon will verify API access by attempting to list DNS zones
- A green checkmark indicates successful authentication
Step 4: Request a Certificate
- Navigate to Certificates → Request Certificate
- Enter your domain name:
- For standard certificate:
example.com - For wildcard certificate:
*.example.com
- For standard certificate:
- Select "DNS-01" as the challenge type
- Choose your configured DNS provider
- Click "Request Certificate"
Step 5: Monitor Progress
The certificate request progresses through these stages:
Pending → Creating DNS Record → Waiting for Propagation → Validating → Issued
- Creating DNS Record: Charon creates the
_acme-challengeTXT record - Waiting for Propagation: DNS changes propagate globally (typically 30-120 seconds)
- Validating: CA verifies the DNS record
- Issued: Certificate is ready for use
Provider-Specific Setup
Cloudflare Setup
Cloudflare is the recommended DNS provider due to fast propagation and excellent API support.
Creating API Credentials
Option A: API Token (Recommended)
- Log in to Cloudflare Dashboard
- Go to My Profile → API Tokens
- Click "Create Token"
- Select "Edit zone DNS" template
- Configure permissions:
- Zone: DNS → Edit
- Zone Resources: Include → Specific zone → Your domain
- Click "Continue to summary" → "Create Token"
- Copy the token (shown only once)
Option B: Global API Key (Not Recommended)
- Go to My Profile → API Tokens
- Scroll to "API Keys" section
- Click "View" next to "Global API Key"
- Copy the key
Charon Configuration
| Field | Value |
|---|---|
| Provider Type | Cloudflare |
| API Token | Your API token (from Option A) |
| (Required only for Global API Key) |
Route53 Setup
AWS Route53 requires IAM credentials with specific DNS permissions.
Creating IAM Policy
Create a custom IAM policy with these permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:GetHostedZone",
"route53:ListHostedZones",
"route53:ListHostedZonesByName",
"route53:ChangeResourceRecordSets",
"route53:GetChange"
],
"Resource": "*"
}
]
}
Security Tip: For production, restrict
Resourceto specific hosted zone ARNs.
Creating IAM User
- Go to IAM → Users → Add Users
- Enter username (e.g.,
charon-dns) - Select "Access key - Programmatic access"
- Attach the custom policy created above
- Complete user creation and save the Access Key ID and Secret Access Key
Charon Configuration
| Field | Value |
|---|---|
| Provider Type | Route53 |
| Access Key ID | Your IAM access key |
| Secret Access Key | Your IAM secret key |
| Region | (Optional) AWS region, e.g., us-east-1 |
DigitalOcean Setup
Creating API Token
- Log in to DigitalOcean Control Panel
- Go to API → Tokens/Keys
- Click "Generate New Token"
- Enter a name (e.g., "Charon DNS")
- Select "Write" scope
- Click "Generate Token"
- Copy the token (shown only once)
Charon Configuration
| Field | Value |
|---|---|
| Provider Type | DigitalOcean |
| API Token | Your personal access token |
Google Cloud Setup
Creating Service Account
- Go to Google Cloud Console
- Select your project
- Navigate to IAM & Admin → Service Accounts
- Click "Create Service Account"
- Enter name (e.g.,
charon-dns) - Grant role: DNS Administrator (
roles/dns.admin) - Click "Create Key" → JSON
- Download and secure the JSON key file
Charon Configuration
| Field | Value |
|---|---|
| Provider Type | Google Cloud DNS |
| Project ID | Your GCP project ID |
| Service Account JSON | Contents of the JSON key file |
Azure Setup
Creating Service Principal
# Create service principal with DNS Zone Contributor role
az ad sp create-for-rbac \
--name "charon-dns" \
--role "DNS Zone Contributor" \
--scopes "/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Network/dnszones/<zone-name>"
Save the output containing appId, password, and tenant.
Charon Configuration
| Field | Value |
|---|---|
| Provider Type | Azure DNS |
| Subscription ID | Your Azure subscription ID |
| Resource Group | Resource group containing DNS zone |
| Tenant ID | Azure AD tenant ID |
| Client ID | Service principal appId |
| Client Secret | Service principal password |
Manual DNS Challenge
For DNS providers not directly supported by Charon, you can use the Manual DNS Challenge workflow.
When to Use Manual Challenge
- Your DNS provider lacks API support
- Company policies restrict API credential storage
- You prefer manual control over DNS records
- Testing or one-time certificate requests
Manual Challenge Workflow
Step 1: Initiate the Challenge
- Navigate to Certificates → Request Certificate
- Enter your domain name
- Select "DNS-01 (Manual)" as the challenge type
- Click "Request Certificate"
Step 2: Create DNS Record
Charon displays the required DNS record:
┌──────────────────────────────────────────────────────────────────────┐
│ Manual DNS Challenge Instructions │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ Create the following DNS TXT record at your DNS provider: │
│ │
│ Record Name: _acme-challenge.example.com │
│ Record Type: TXT │
│ Record Value: dGVzdC12YWx1ZS1mb3ItYWNtZS1jaGFsbGVuZ2U= │
│ TTL: 120 (or minimum allowed) │
│ │
│ ⏳ Waiting for confirmation... │
│ │
│ [ Copy Record Value ] [ I've Created the Record ] │
└──────────────────────────────────────────────────────────────────────┘
Step 3: Add Record to DNS Provider
Log in to your DNS provider and create the TXT record:
Example: Generic DNS Provider
- Navigate to DNS management for your domain
- Click "Add Record" or "New DNS Record"
- Configure:
- Type: TXT
- Name/Host:
_acme-challenge(some providers auto-append domain) - Value/Content: The challenge token from Charon
- TTL: 120 seconds (or minimum allowed)
- Save the record
Step 4: Verify DNS Propagation
Before confirming, verify the record has propagated:
Using dig command:
dig TXT _acme-challenge.example.com +short
Expected output:
"dGVzdC12YWx1ZS1mb3ItYWNtZS1jaGFsbGVuZ2U="
Using online tools:
Step 5: Confirm Record Creation
- Return to Charon
- Click "I've Created the Record"
- Charon verifies the record and completes validation
- Certificate is issued upon successful verification
Step 6: Cleanup (Automatic)
Charon displays instructions to remove the TXT record after certificate issuance. While optional, removing challenge records is recommended for cleaner DNS configuration.
Manual Challenge Tips
- ✅ Wait for propagation: DNS changes can take 1-60 minutes to propagate globally
- ✅ Check exact record name: Some providers require
_acme-challenge, others need_acme-challenge.example.com - ✅ Verify before confirming: Use
digor online tools to confirm the record exists - ✅ Mind the TTL: Lower TTL values speed up propagation but may not be supported by all providers
- ❌ Don't include quotes: The TXT value should be the raw token, not wrapped in quotes (unless your provider requires it)
Troubleshooting
Common Issues
DNS Propagation Delays
Symptom: Certificate request stuck at "Waiting for Propagation" or validation fails.
Causes:
- DNS TTL is high (cached old records)
- DNS provider has slow propagation
- Regional DNS inconsistency
Solutions:
-
Verify the record exists locally:
dig TXT _acme-challenge.example.com @8.8.8.8 -
Check multiple DNS servers:
dig TXT _acme-challenge.example.com @1.1.1.1 dig TXT _acme-challenge.example.com @208.67.222.222 -
Wait longer: Some providers take up to 60 minutes for full propagation
-
Lower TTL: If possible, set TTL to 120 seconds or lower before requesting certificates
-
Retry the request: Cancel and retry after confirming DNS propagation
Invalid API Credentials
Symptom: "Authentication failed" or "Invalid credentials" error when testing connection.
Solutions:
| Provider | Common Issues |
|---|---|
| Cloudflare | Token expired, wrong zone permissions, using Global API Key without email |
| Route53 | IAM policy missing required actions, wrong region |
| DigitalOcean | Token has read-only scope (needs write) |
| Google Cloud | Wrong project ID, service account lacks DNS Admin role |
Verification Steps:
- Re-check credentials: Copy-paste directly from provider, avoid manual typing
- Verify permissions: Ensure API token/key has DNS edit permissions
- Test API directly: Use provider's API documentation to test credentials independently
- Check for typos: Especially in email addresses and project IDs
Permission Denied / Access Denied
Symptom: Connection test passes, but record creation fails.
Causes:
- API token has read-only permissions
- Zone/domain not accessible with current credentials
- Rate limiting or account restrictions
Solutions:
- Cloudflare: Ensure token has "Zone:DNS:Edit" permission for the specific zone
- Route53: Verify IAM policy includes
ChangeResourceRecordSetsaction - DigitalOcean: Confirm token has "Write" scope
- Google Cloud: Service account needs "DNS Administrator" role
DNS Record Already Exists
Symptom: "Record already exists" error during certificate request.
Causes:
- Previous challenge attempt left orphaned record
- Manual DNS record with same name exists
- Another ACME client managing the same domain
Solutions:
- Delete existing record: Log in to DNS provider and remove the
_acme-challengeTXT record - Wait for deletion: Allow time for deletion to propagate
- Retry certificate request
CAA Record Issues
Symptom: Certificate Authority refuses to issue certificate despite successful DNS validation.
Cause: CAA (Certificate Authority Authorization) DNS records restrict which CAs can issue certificates.
Solutions:
-
Check CAA records:
dig CAA example.com -
Add Let's Encrypt to CAA (if CAA records exist):
example.com. CAA 0 issue "letsencrypt.org" example.com. CAA 0 issuewild "letsencrypt.org" -
Remove restrictive CAA records (if you don't need CAA enforcement)
Rate Limiting
Symptom: "Too many requests" or "Rate limit exceeded" errors.
Causes:
- Too many certificate requests in short period
- DNS provider API rate limits
- Let's Encrypt rate limits
Solutions:
- Wait and retry: Most rate limits reset within 1 hour
- Use staging environment: For testing, use Let's Encrypt staging to avoid production rate limits
- Consolidate domains: Use SANs or wildcards to reduce certificate count
- Check provider limits: Some DNS providers have low API rate limits
DNS Provider-Specific Issues
Cloudflare
| Issue | Solution |
|---|---|
| "Invalid API Token" | Regenerate token with correct zone permissions |
| "Zone not found" | Ensure domain is active in Cloudflare account |
| "Rate limited" | Wait 5 minutes; Cloudflare allows 1200 requests/5 min |
Route53
| Issue | Solution |
|---|---|
| "Access Denied" | Check IAM policy includes all required actions |
| "NoSuchHostedZone" | Verify hosted zone ID is correct |
| "Throttling" | Implement exponential backoff; Route53 has strict rate limits |
DigitalOcean
| Issue | Solution |
|---|---|
| "Unable to authenticate" | Regenerate token with Write scope |
| "Domain not found" | Ensure domain is added to DigitalOcean DNS |
Getting Help
If issues persist:
- Check Charon logs: Look for detailed error messages in container logs
- Enable debug mode: Set
LOG_LEVEL=debugfor verbose logging - Search existing issues: GitHub Issues
- Open a new issue: Include Charon version, provider type, and sanitized error messages
Related Documentation
Feature Guides
- DNS Provider Types — RFC 2136, Webhook, and Script providers
- DNS Auto-Detection — Automatic provider identification
- Multi-Credential Support — Managing multiple credentials per provider
- Key Rotation — Credential encryption and rotation
General Documentation
- Getting Started — Initial Charon setup
- Security Best Practices — Securing your Charon installation
- API Reference — Programmatic certificate management
- Troubleshooting Guide — General troubleshooting
External Resources
Last Updated: January 2026 Charon Version: 0.1.0-beta