# 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](#overview) - [Why Use DNS Challenge?](#why-use-dns-challenge) - [Supported DNS Providers](#supported-dns-providers) - [Getting Started](#getting-started) - [Manual DNS Challenge](#manual-dns-challenge) - [Troubleshooting](#troubleshooting) - [Related Documentation](#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](#cloudflare-setup) | | **AWS Route53** | AWS SDK | [Route53 Setup Guide](#route53-setup) | | **DigitalOcean** | REST API | [DigitalOcean Setup Guide](#digitalocean-setup) | | **Google Cloud DNS** | GCP SDK | [Google Cloud Setup Guide](#google-cloud-setup) | | **Azure DNS** | Azure SDK | [Azure Setup Guide](#azure-setup) | ### 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](./dns-providers.md#rfc-2136-dynamic-dns) | | **Webhook** | Custom DNS APIs, automation platforms | [Webhook Provider](./dns-providers.md#webhook-provider) | | **Script** | Legacy tools, custom integrations | [Script Provider](./dns-providers.md#script-provider) | | **Manual** | Any DNS provider (user creates records) | [Manual DNS Challenge](#manual-dns-challenge) | --- ## Getting Started ### Prerequisites Before configuring DNS challenge: 1. ✅ A domain name you control 2. ✅ Access to your DNS provider's control panel 3. ✅ API credentials from your DNS provider (see provider-specific guides below) 4. ✅ Charon installed and running ### Step 1: Add a DNS Provider 1. Navigate to **Settings** → **DNS Providers** in Charon 2. Click **"Add DNS Provider"** 3. Select your DNS provider from the dropdown 4. 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](./key-rotation.md) for credential security best practices. ### Step 3: Test the Connection 1. After saving, click **"Test Connection"** on the provider card 2. Charon will verify API access by attempting to list DNS zones 3. A green checkmark indicates successful authentication ### Step 4: Request a Certificate 1. Navigate to **Certificates** → **Request Certificate** 2. Enter your domain name: - For standard certificate: `example.com` - For wildcard certificate: `*.example.com` 3. Select **"DNS-01"** as the challenge type 4. Choose your configured DNS provider 5. 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-challenge` TXT 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)** 1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com) 2. Go to **My Profile** → **API Tokens** 3. Click **"Create Token"** 4. Select **"Edit zone DNS"** template 5. Configure permissions: - **Zone**: DNS → Edit - **Zone Resources**: Include → Specific zone → Your domain 6. Click **"Continue to summary"** → **"Create Token"** 7. Copy the token (shown only once) **Option B: Global API Key (Not Recommended)** 1. Go to **My Profile** → **API Tokens** 2. Scroll to **"API Keys"** section 3. Click **"View"** next to **"Global API Key"** 4. Copy the key #### Charon Configuration | Field | Value | |-------|-------| | **Provider Type** | Cloudflare | | **API Token** | Your API token (from Option A) | | **Email** | (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: ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "route53:GetHostedZone", "route53:ListHostedZones", "route53:ListHostedZonesByName", "route53:ChangeResourceRecordSets", "route53:GetChange" ], "Resource": "*" } ] } ``` > **Security Tip**: For production, restrict `Resource` to specific hosted zone ARNs. #### Creating IAM User 1. Go to **IAM** → **Users** → **Add Users** 2. Enter username (e.g., `charon-dns`) 3. Select **"Access key - Programmatic access"** 4. Attach the custom policy created above 5. 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 1. Log in to [DigitalOcean Control Panel](https://cloud.digitalocean.com) 2. Go to **API** → **Tokens/Keys** 3. Click **"Generate New Token"** 4. Enter a name (e.g., "Charon DNS") 5. Select **"Write"** scope 6. Click **"Generate Token"** 7. 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 1. Go to [Google Cloud Console](https://console.cloud.google.com) 2. Select your project 3. Navigate to **IAM & Admin** → **Service Accounts** 4. Click **"Create Service Account"** 5. Enter name (e.g., `charon-dns`) 6. Grant role: **DNS Administrator** (`roles/dns.admin`) 7. Click **"Create Key"** → **JSON** 8. 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 ```bash # Create service principal with DNS Zone Contributor role az ad sp create-for-rbac \ --name "charon-dns" \ --role "DNS Zone Contributor" \ --scopes "/subscriptions//resourceGroups//providers/Microsoft.Network/dnszones/" ``` 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 1. Navigate to **Certificates** → **Request Certificate** 2. Enter your domain name 3. Select **"DNS-01 (Manual)"** as the challenge type 4. 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** 1. Navigate to DNS management for your domain 2. Click **"Add Record"** or **"New DNS Record"** 3. 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) 4. Save the record #### Step 4: Verify DNS Propagation Before confirming, verify the record has propagated: **Using dig command:** ```bash dig TXT _acme-challenge.example.com +short ``` **Expected output:** ``` "dGVzdC12YWx1ZS1mb3ItYWNtZS1jaGFsbGVuZ2U=" ``` **Using online tools:** - [DNSChecker](https://dnschecker.org) - [MXToolbox](https://mxtoolbox.com/TXTLookup.aspx) - [WhatsMyDNS](https://whatsmydns.net) #### Step 5: Confirm Record Creation 1. Return to Charon 2. Click **"I've Created the Record"** 3. Charon verifies the record and completes validation 4. 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 `dig` or 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**: 1. **Verify the record exists locally:** ```bash dig TXT _acme-challenge.example.com @8.8.8.8 ``` 2. **Check multiple DNS servers:** ```bash dig TXT _acme-challenge.example.com @1.1.1.1 dig TXT _acme-challenge.example.com @208.67.222.222 ``` 3. **Wait longer**: Some providers take up to 60 minutes for full propagation 4. **Lower TTL**: If possible, set TTL to 120 seconds or lower before requesting certificates 5. **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**: 1. **Re-check credentials**: Copy-paste directly from provider, avoid manual typing 2. **Verify permissions**: Ensure API token/key has DNS edit permissions 3. **Test API directly**: Use provider's API documentation to test credentials independently 4. **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**: 1. **Cloudflare**: Ensure token has "Zone:DNS:Edit" permission for the specific zone 2. **Route53**: Verify IAM policy includes `ChangeResourceRecordSets` action 3. **DigitalOcean**: Confirm token has "Write" scope 4. **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**: 1. **Delete existing record**: Log in to DNS provider and remove the `_acme-challenge` TXT record 2. **Wait for deletion**: Allow time for deletion to propagate 3. **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**: 1. **Check CAA records:** ```bash dig CAA example.com ``` 2. **Add Let's Encrypt to CAA** (if CAA records exist): ``` example.com. CAA 0 issue "letsencrypt.org" example.com. CAA 0 issuewild "letsencrypt.org" ``` 3. **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**: 1. **Wait and retry**: Most rate limits reset within 1 hour 2. **Use staging environment**: For testing, use Let's Encrypt staging to avoid production rate limits 3. **Consolidate domains**: Use SANs or wildcards to reduce certificate count 4. **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: 1. **Check Charon logs**: Look for detailed error messages in container logs 2. **Enable debug mode**: Set `LOG_LEVEL=debug` for verbose logging 3. **Search existing issues**: [GitHub Issues](https://github.com/Wikid82/charon/issues) 4. **Open a new issue**: Include Charon version, provider type, and sanitized error messages --- ## Related Documentation ### Feature Guides - [DNS Provider Types](./dns-providers.md) — RFC 2136, Webhook, and Script providers - [DNS Auto-Detection](./dns-auto-detection.md) — Automatic provider identification - [Multi-Credential Support](./multi-credential.md) — Managing multiple credentials per provider - [Key Rotation](./key-rotation.md) — Credential encryption and rotation ### General Documentation - [Getting Started](../getting-started.md) — Initial Charon setup - [Security Best Practices](../security.md) — Securing your Charon installation - [API Reference](../api.md) — Programmatic certificate management - [Troubleshooting Guide](../troubleshooting/) — General troubleshooting ### External Resources - [Let's Encrypt Documentation](https://letsencrypt.org/docs/) - [ACME Protocol RFC 8555](https://datatracker.ietf.org/doc/html/rfc8555) - [DNS-01 Challenge Specification](https://letsencrypt.org/docs/challenge-types/#dns-01-challenge) --- *Last Updated: January 2026* *Charon Version: 0.1.0-beta*