- Updated DNSProviderForm to include multi-credential mode toggle. - Integrated CredentialManager component for managing multiple credentials. - Added hooks for enabling multi-credentials and managing credential operations. - Implemented tests for CredentialManager and useCredentials hooks.
45 KiB
Multi-Credential per DNS Provider
Feature Overview
What is Multi-Credential Support?
Multi-Credential per Provider is an advanced feature that allows you to configure multiple sets of API credentials for the same DNS provider. Instead of using a single API key for all domains managed by a provider (e.g., Cloudflare), you can configure different credentials for different DNS zones.
Why Use Multi-Credentials?
Security Benefits:
- Zone-level Isolation: Compromise of one credential doesn't expose all your domains
- Least Privilege Principle: Each credential can have minimal permissions for only the zones it manages
- Independent Rotation: Rotate credentials for specific zones without affecting others
- Audit Trail: Track which credentials were used for certificate operations
Business Use Cases:
- Managed Service Providers (MSPs): Use separate customer-specific credentials for each client's domains
- Large Enterprises: Isolate credentials by department, environment, or geographic region
- Multi-Tenant Platforms: Provide credential isolation between tenants
- Compliance Requirements: Meet security standards requiring credential segregation
Single vs Multi-Credential Architecture
Single Credential Mode:
┌─────────────────────────┐
│ Cloudflare Provider │
│ API Key: xyz123 │
└───────────┬─────────────┘
│
┌───────┴────────┬────────────┬──────────────┐
│ │ │ │
example.com customer-a.com *.dev.com acme.org
Multi-Credential Mode:
┌──────────────────────────────────────────────────┐
│ Cloudflare Provider │
├──────────────────────────────────────────────────┤
│ Credential 1: "Production" │
│ Zone Filter: example.com │
│ ├─→ example.com │
│ └─→ www.example.com │
├──────────────────────────────────────────────────┤
│ Credential 2: "Customer A" │
│ Zone Filter: *.customer-a.com │
│ └─→ *.customer-a.com │
├──────────────────────────────────────────────────┤
│ Credential 3: "Development" │
│ Zone Filter: *.dev.com │
│ └─→ *.dev.com │
├──────────────────────────────────────────────────┤
│ Credential 4: "Catch-all" │
│ Zone Filter: (empty - matches anything else) │
│ └─→ acme.org, other.net, etc. │
└──────────────────────────────────────────────────┘
When to Use Multi-Credentials
Decision Criteria
Use Multi-Credentials When:
- You manage domains for multiple customers or tenants
- You need credential isolation for security or compliance
- Different teams or departments manage different zones
- You want to limit blast radius of credential compromise
- You have different security postures for different environments (prod/staging/dev)
- You need independent credential rotation schedules
Use Single Credential When:
- You manage a small number of domains under one organization
- All domains have the same security requirements
- Simpler management is preferred over isolation
- You're just getting started with Charon
Comparison Matrix
| Aspect | Single Credential | Multi-Credential |
|---|---|---|
| Security Isolation | ❌ All zones use same key | ✅ Per-zone isolation |
| Blast Radius | ❌ High (all zones affected) | ✅ Limited to filtered zones |
| Setup Complexity | ✅ Simple | ⚠️ Moderate |
| Credential Rotation | ❌ Affects all zones | ✅ Independent per zone |
| Audit Granularity | ⚠️ Provider-level only | ✅ Credential-level detail |
| Multi-Tenancy | ❌ Not suitable | ✅ Ideal |
| Best For | Small deployments | MSPs, enterprises, multi-tenant |
Enabling Multi-Credential Mode
Prerequisites
- Charon v1.3.0 or later
- Admin access to the Charon dashboard
- DNS provider account with API access
- (Optional) Multiple API keys already generated at your DNS provider
Step-by-Step Enable Process
-
Navigate to DNS Provider Settings
- Go to Settings → DNS Providers
- Locate the provider you want to enable multi-credential for (e.g., Cloudflare)
-
Click "Enable Multi-Credential"
- Click the Enable Multi-Credential button next to the provider
- A confirmation dialog will appear
-
Review Migration Impact
⚠️ IMPORTANT: This action will: - Convert your existing provider credential into a "catch-all" credential - Preserve all existing proxy host configurations - Enable credential management UI for this provider - This change is reversible (you can disable and revert) -
Confirm Enable
- Click Enable to proceed
- The provider will now show "Multi-Credential Mode: Enabled"
-
Verify Migration
- Your existing credential is now listed as a credential with no zone filter (catch-all)
- All existing proxy hosts continue to work without interruption
- You can now add additional credentials
What Happens During Migration
- Existing Configuration Preserved: Your current API key/token remains active
- Automatic Credential Creation: The existing credential is converted to a credential entry with:
- Name: "{Provider} Primary Credential"
- Zone Filter: Empty (matches all zones)
- Description: "Migrated from single-credential mode"
- Zero Downtime: All certificate operations continue without interruption
- Backward Compatible: If you disable multi-credential mode, you revert to the original setup
Backward Compatibility
- Disabling Multi-Credential: You can disable multi-credential mode by clicking Disable Multi-Credential
- Reversion: Disabling converts the first credential back to the provider's primary credential
- Data Loss: Other credentials will be retained in the database but won't be used
- Re-enabling: You can re-enable multi-credential mode at any time without data loss
Managing Credentials
Accessing Credential Management
- Navigate to Settings → DNS Providers
- Find your provider with "Multi-Credential Mode: Enabled"
- Click Manage Credentials
- The credential management interface displays all credentials for this provider
Adding a New Credential
Step 1: Click "Add Credential"
Click the Add Credential button in the credential management interface.
Step 2: Fill in Credential Details
Required Fields:
-
Credential Name: A descriptive name (e.g., "Customer A Production", "US Zones", "Dev Environment")
- Must be unique within the provider
- Recommended: Use descriptive names that indicate purpose or zone scope
-
API Credentials: Provider-specific authentication fields
- Cloudflare: API Token or Global API Key + Email
- Route53: Access Key ID + Secret Access Key
- DigitalOcean: API Token
- (Refer to provider-specific guides for required fields)
Optional Fields:
- Description: Additional notes about the credential's purpose
- Zone Filter: Comma-separated list of zones this credential manages (see Zone Filter Configuration below)
Step 3: Configure Zone Filter
The zone filter determines which domains this credential will be used for:
Option 1: Exact Domain Match
Zone Filter: example.com
Matches: example.com, www.example.com, api.example.com
Does NOT Match: subdomain.example.com.au, example.org
Option 2: Wildcard Match
Zone Filter: *.customer-a.com
Matches: shop.customer-a.com, api.customer-a.com, *.customer-a.com
Does NOT Match: customer-a.com (root), customer-b.com
Option 3: Multiple Zones (Comma-Separated)
Zone Filter: example.com,api.example.org,*.dev.example.net
Matches:
- example.com and all subdomains
- api.example.org and all subdomains under api.example.org
- *.dev.example.net (all subdomains of dev.example.net)
Option 4: Catch-All (Empty Filter)
Zone Filter: (leave empty)
Matches: Any domain not matched by other credentials
Use Case: Fallback credential for miscellaneous domains
Step 4: Test the Credential (Recommended)
- Click Test Credential before saving
- Charon will attempt to authenticate with the DNS provider using the supplied credentials
- If successful, you'll see: ✅ "Credential validated successfully"
- If failed, review the error message and correct the credentials
Step 5: Save the Credential
- Click Save Credential
- The credential is now active and will be used for matching domains
Zone Filter Configuration
Syntax Rules
- Comma-separated: Separate multiple patterns with commas (no spaces)
- Case-insensitive:
Example.commatchesexample.com - Wildcard prefix: Use
*.at the start for subdomain matching - No regex: Only exact and wildcard matches are supported
- No trailing dots: Don't use
example.com.(trailing dot is stripped)
Examples
| Zone Filter | Matches | Does NOT Match |
|---|---|---|
example.com |
example.com, www.example.com, api.example.com |
example.org, sub.example.com.au |
*.customer-a.com |
shop.customer-a.com, api.customer-a.com |
customer-a.com (root) |
*.staging.example.com |
app.staging.example.com, api.staging.example.com |
staging.example.com, prod.example.com |
example.com,example.org |
Both example.com and example.org domains |
example.net |
| (empty) | Any domain not matched by other credentials | (none - this is catch-all) |
Validation Rules
When saving a credential, Charon validates:
- ✅ Zone filter syntax is correct
- ✅ No duplicate exact matches across credentials
- ⚠️ Warning if multiple wildcard patterns could overlap
- ✅ At most one credential per provider can have an empty zone filter (catch-all)
Editing Credentials
- In the credential management interface, click Edit next to the credential
- Modify any field (name, description, credentials, zone filter)
- Click Test Credential to validate changes
- Click Save Changes
⚠️ Important Notes:
- Changing zone filters may affect which credential is used for existing proxy hosts
- Charon will re-evaluate credential matching for all proxy hosts after the change
- Consider testing in a non-production environment first if making significant changes
Testing Credentials
You can test credentials at any time to verify they still work:
- Click Test next to the credential in the management interface
- Charon will attempt a test DNS record lookup or API call
- Results:
- ✅ Success: Credential is valid and working
- ❌ Failed: Credential is invalid or has insufficient permissions
- ⚠️ Warning: Credential works but may have limited permissions
Deleting Credentials
- Click Delete next to the credential
- Charon will check if any proxy hosts are currently using this credential
- If in use: You'll be warned and must migrate those proxy hosts to another credential first
- If not in use: Confirm deletion and the credential will be removed
⚠️ Warning: Deleting a credential that is actively in use for certificate operations will cause certificate renewals to fail. Always ensure proxy hosts are migrated to another credential before deletion.
Zone Matching Rules
How Zone Matching Works
When Charon needs to issue or renew a certificate for a domain, it selects a credential using this process:
1. Extract DNS zone from the domain
Example: For "www.api.example.com", zone is "example.com"
2. Query all credentials for the provider (e.g., Cloudflare)
3. Match credentials against the zone using priority order:
a. Exact match (highest priority)
b. Wildcard match
c. Catch-all (empty zone filter) (lowest priority)
4. Return the first matching credential
5. Use the credential to perform DNS-01 challenge
Priority Order
Credentials are evaluated in this order:
1. Exact Match (Highest Priority)
Zone Filter: example.com
Domain: www.example.com → Zone: example.com → ✅ MATCH
2. Wildcard Match
Zone Filter: *.customer-a.com
Domain: shop.customer-a.com → Zone: customer-a.com → ✅ MATCH (after exact check fails)
3. Catch-All (Lowest Priority)
Zone Filter: (empty)
Domain: anything.com → Zone: anything.com → ✅ MATCH (if no exact or wildcard matches)
Matching Examples
Example 1: MSP with Multiple Customers
Configured Credentials:
1. Name: "Customer A Production"
Zone Filter: *.customer-a.com
Priority: Wildcard
2. Name: "Customer B Production"
Zone Filter: *.customer-b.com
Priority: Wildcard
3. Name: "Catch-all"
Zone Filter: (empty)
Priority: Catch-all
Domain Matching:
shop.customer-a.com→ Credential 1 ("Customer A Production")api.customer-b.com→ Credential 2 ("Customer B Production")example.com→ Credential 3 ("Catch-all")random.org→ Credential 3 ("Catch-all")
Example 2: Environment Separation
Configured Credentials:
1. Name: "Production"
Zone Filter: example.com
Priority: Exact
2. Name: "Staging"
Zone Filter: *.staging.example.com
Priority: Wildcard
3. Name: "Development"
Zone Filter: *.dev.example.com
Priority: Wildcard
Domain Matching:
www.example.com→ Credential 1 ("Production")api.example.com→ Credential 1 ("Production")app.staging.example.com→ Credential 2 ("Staging")api.dev.example.com→ Credential 3 ("Development")staging.example.com→ Credential 1 ("Production") - root of staging doesn't match*.staging.example.com
Example 3: Geographic Separation
Configured Credentials:
1. Name: "US Zones"
Zone Filter: *.us.example.com
Priority: Wildcard
2. Name: "EU Zones"
Zone Filter: *.eu.example.com
Priority: Wildcard
3. Name: "APAC Zones"
Zone Filter: *.apac.example.com
Priority: Wildcard
Domain Matching:
shop.us.example.com→ Credential 1 ("US Zones")api.eu.example.com→ Credential 2 ("EU Zones")portal.apac.example.com→ Credential 3 ("APAC Zones")www.example.com→ ❌ NO MATCH (no catch-all defined)
Overlapping Patterns
⚠️ What Happens When Patterns Overlap?
If multiple credentials could match the same zone, Charon uses first match based on priority order:
Example:
Credential A: Zone Filter: example.com (Exact)
Credential B: Zone Filter: *.example.com (Wildcard)
Domain: www.example.com
Zone: example.com
Match Process:
1. Check Exact: Credential A matches "example.com" → ✅ Use Credential A
2. (Wildcard check not reached)
Best Practice: Avoid overlapping patterns when possible. Design zone filters to be mutually exclusive.
Troubleshooting Zone Matching
Issue: Domain doesn't match any credential
Symptoms:
- Certificate issuance fails with "No matching credential for zone"
- Error message:
No credential found for provider 'cloudflare' and zone 'example.com'
Solutions:
- Add a catch-all credential: Create a credential with an empty zone filter
- Add specific credential: Create a credential with zone filter matching your domain
- Check zone extraction: Ensure Charon is correctly extracting the zone from your domain
Issue: Wrong credential is being used
Symptoms:
- Expected credential "Production" but "Catch-all" is being used
- Certificate issued but not with the intended credential
Solutions:
- Check zone filter syntax: Verify your zone filters are correctly configured
- Check priority order: Exact matches override wildcards; ensure your exact match is configured
- Review audit logs: Check which credential was actually selected and why
Issue: Zone filter validation error
Symptoms:
- Error: "Invalid zone filter format"
- Error: "Zone filter 'example.com' conflicts with existing credential"
Solutions:
- Check syntax: Ensure no spaces, only commas separating patterns
- Check for duplicates: Ensure no two credentials have the exact same zone filter pattern
- Review wildcard syntax: Wildcards must be
*.domain.com, not*domain.com
Creating Proxy Hosts with Multi-Credentials
Automatic Credential Selection
When you create a proxy host with multi-credential mode enabled:
- You don't select a credential - Charon selects automatically
- Zone matching - Charon extracts the DNS zone from your domain
- Credential lookup - Charon finds the best matching credential using zone matching rules
- Certificate issuance - The selected credential is used for DNS-01 challenge
Step-by-Step Process
-
Create Proxy Host as Normal
- Go to Proxy Hosts → Add Proxy Host
- Enter domain name (e.g.,
shop.customer-a.com) - Configure forward host/port and other settings
- Enable SSL/TLS and select Let's Encrypt
-
Charon Selects Credential Automatically
- Charon extracts zone:
customer-a.com - Searches for matching credentials for the DNS provider
- Finds: "Customer A Production" (Zone Filter:
*.customer-a.com) - Uses this credential for certificate issuance
- Charon extracts zone:
-
Certificate Issuance
- Charon requests certificate from Let's Encrypt
- Uses selected credential to create DNS TXT record for
_acme-challenge.shop.customer-a.com - Completes DNS-01 challenge
- Certificate is issued
-
View Selected Credential
- After creation, view the proxy host details
- The Certificate section shows: "Issued using credential: Customer A Production"
Viewing Which Credential Was Used
Method 1: Proxy Host Details
- Open the proxy host from the dashboard
- In the SSL/TLS section, look for:
Certificate: Active (Expires: 2026-04-01) Credential Used: Customer A Production (Cloudflare) Last Renewed: 2026-01-02 14:30 UTC
Method 2: Audit Logs
- Go to Settings → Audit Logs
- Filter by:
Action: certificate_issuedorAction: certificate_renewed - View log entry:
{ "timestamp": "2026-01-02T14:30:00Z", "action": "certificate_issued", "domain": "shop.customer-a.com", "provider": "cloudflare", "credential_id": 42, "credential_name": "Customer A Production", "user": "admin@example.com", "result": "success" }
Method 3: Credential Statistics
- Go to Settings → DNS Providers → Manage Credentials
- Each credential shows:
- Usage Count: Number of domains using this credential
- Last Used: Timestamp of last certificate operation
- Success Rate: Success/failure ratio
Troubleshooting Certificate Issuance
Issue: Certificate issuance fails with "No matching credential"
Error Message:
Failed to issue certificate for shop.customer-a.com:
No credential found for provider 'cloudflare' and zone 'customer-a.com'
Solution:
- Check DNS provider has multi-credential enabled
- Verify a credential exists with zone filter matching
customer-a.com - Add a credential with zone filter:
*.customer-a.comor use catch-all
Issue: Certificate issuance fails with "API authentication failed"
Error Message:
Failed to issue certificate for shop.customer-a.com:
Cloudflare API returned 403: Invalid credentials
Solution:
- Test the credential being used: Manage Credentials → Test
- Verify API token/key is still valid in your DNS provider dashboard
- Check API token has correct permissions (
Zone:DNS:Edit) - Update the credential with valid API credentials
Issue: Wrong credential is being used
Symptoms:
- Certificate issued successfully but with unexpected credential
- Audit logs show different credential than expected
Solution:
- Review zone filter configuration for all credentials
- Check priority order (exact > wildcard > catch-all)
- Ensure your expected credential has the most specific zone filter
- Test zone matching logic in Manage Credentials interface
Credential Organization Best Practices
Naming Conventions
Recommended Naming Patterns:
By Customer/Tenant:
- "Customer A - Production"
- "Customer B - Staging"
- "Tenant XYZ - All Zones"
By Environment:
- "Production Zones"
- "Staging Zones"
- "Development Zones"
By Department:
- "Marketing - example.com"
- "Engineering - api.example.com"
- "Sales - shop.example.com"
By Geography:
- "US East Zones"
- "EU West Zones"
- "APAC Zones"
Zone Filter Strategies
Strategy 1: Exact Domain Per Credential
Use Case: Small number of high-value domains
Example:
Credential: "example.com Primary"
Zone Filter: example.com
Credential: "api.example.org"
Zone Filter: api.example.org
Pros:
- Maximum specificity
- Easy to understand
- Clear audit trail
Cons:
- Requires one credential per domain
- Not scalable for many domains
Strategy 2: Wildcard by Namespace
Use Case: Logical grouping of subdomains
Example:
Credential: "Customer Zones"
Zone Filter: *.customers.example.com
Credential: "Internal Services"
Zone Filter: *.internal.example.com
Pros:
- Scalable for many subdomains
- Clear organizational boundaries
- Reduces credential count
Cons:
- Broader scope than exact match
- Requires careful namespace planning
Strategy 3: Hybrid Approach
Use Case: Most common for production deployments
Example:
1. Exact matches for critical domains:
- "Production Root" → example.com
2. Wildcards for namespaces:
- "Customer A" → *.customer-a.com
- "Customer B" → *.customer-b.com
- "Staging" → *.staging.example.com
3. Catch-all for miscellaneous:
- "Legacy & Misc" → (empty)
Pros:
- Balance of specificity and scalability
- Flexible and maintainable
- Handles edge cases
Cons:
- More credentials to manage
- Requires understanding of priority order
When to Use Catch-All Credentials
✅ Good Use Cases:
- Proof-of-Concept/Testing: Start with catch-all, refine later
- Backward Compatibility: Ensure existing domains continue working during migration
- Miscellaneous Domains: Handle legacy or one-off domains
- Gradual Migration: Add specific credentials over time while catch-all handles the rest
❌ Avoid Catch-All When:
- High-Security Environments: Catch-all defeats purpose of zone isolation
- Multi-Tenancy: Each tenant should have explicit credentials
- Compliance: Regulations may require explicit credential assignment
- Credential Rotation: Catch-all makes rotation harder
Credential Rotation Strategy
Best Practice Rotation Schedule:
- High-Risk Credentials (catch-all, root domains): Every 30 days
- Production Credentials: Every 90 days
- Staging/Development: Every 180 days
- Test Credentials: Every 365 days or as needed
Rotation Process:
- Generate New Credentials at DNS provider
- Add New Credential in Charon with same zone filter
- Test New Credential - verify it works
- Update Zone Filter of old credential to
__deprecated__(forces Charon to use new credential) - Wait for Certificate Renewals - monitor for 7-30 days
- Delete Old Credential once confirmed new credential is working
Automation:
- Use provider API to programmatically generate new credentials
- Use Charon API to add/update credentials
- Schedule rotation using cron or CI/CD pipeline
- Monitor audit logs for credential usage
Monitoring and Maintenance
Viewing Credential Usage Statistics
Dashboard View:
- Navigate to Settings → DNS Providers
- For each provider with multi-credential enabled, click View Statistics
- Dashboard shows:
- Total credentials configured
- Active credentials (used in last 30 days)
- Inactive credentials (not used in last 90 days)
- Top credentials by usage
Per-Credential View:
- Go to Settings → DNS Providers → Manage Credentials
- Each credential displays:
┌─────────────────────────────────────────────────┐
│ Customer A Production │
│ Zone Filter: *.customer-a.com │
├─────────────────────────────────────────────────┤
│ Domains Using: 12 │
│ Success Count: 156 │
│ Failure Count: 2 │
│ Success Rate: 98.7% │
│ Last Used: 2026-01-03 10:45 UTC │
│ Last Success: 2026-01-03 10:45 UTC │
│ Last Failure: 2025-12-28 03:12 UTC │
├─────────────────────────────────────────────────┤
│ [Test] [Edit] [View Domains] [Delete] │
└─────────────────────────────────────────────────┘
Success/Failure Counts
Success Count: Number of successful certificate operations (issuance, renewal) using this credential
Failure Count: Number of failed certificate operations
Success Rate: Percentage of successful operations (Success / (Success + Failure) × 100%)
⚠️ Low Success Rate Alert:
- If success rate drops below 90%, investigate immediately
- Common causes: expired API token, insufficient permissions, DNS provider API issues
- Click Test Credential to diagnose
Last Used Timestamps
Last Used: Timestamp of the most recent certificate operation (success or failure)
Last Success: Timestamp of the most recent successful operation
Last Failure: Timestamp of the most recent failed operation
Use Cases:
- Identify Unused Credentials: If "Last Used" is > 90 days ago, consider removing
- Credential Rotation: Track when credentials were last active
- Incident Response: Correlate failures with outages or credential changes
Audit Trail for Credential Operations
Viewing Audit Logs:
- Go to Settings → Audit Logs
- Filter by:
- Action Type:
credential_created,credential_updated,credential_deleted,certificate_issued,certificate_renewed - Provider: Filter by DNS provider
- User: Filter by who performed the action
- Action Type:
Log Entry Example:
{
"timestamp": "2026-01-04T15:30:00Z",
"action": "credential_created",
"resource_type": "dns_credential",
"resource_id": 42,
"resource_name": "Customer A Production",
"provider": "cloudflare",
"zone_filter": "*.customer-a.com",
"user": "admin@example.com",
"ip_address": "192.168.1.100",
"details": {
"description": "Credential for Customer A production domains",
"created_via": "web_ui"
}
}
Exported Logs:
- Export to CSV or JSON for external analysis
- Integrate with SIEM (Security Information and Event Management) systems
- Use for compliance reporting and security audits
Troubleshooting
Common Issues and Solutions
Issue 1: No matching credential for domain
Symptoms:
- Certificate issuance fails
- Error:
No credential found for provider 'cloudflare' and zone 'example.com' - Proxy host shows certificate status: "Failed"
Root Causes:
- No credential configured for the DNS zone
- Zone filter doesn't match the domain's zone
- Multi-credential mode not enabled
Solutions:
Step 1: Verify Multi-Credential Mode is Enabled
Settings → DNS Providers → Check "Multi-Credential Mode: Enabled"
Step 2: Check Existing Credentials
Settings → DNS Providers → Manage Credentials
Review zone filters for all credentials
Step 3: Add Missing Credential or Catch-All
Option A: Add Specific Credential
Credential Name: example.com Production
Zone Filter: example.com
Option B: Add Catch-All
Credential Name: Catch-All
Zone Filter: (leave empty)
Step 4: Retry Certificate Issuance
Proxy Hosts → Select proxy host → SSL/TLS → Renew Certificate
Issue 2: Certificate issuance fails with API error
Symptoms:
- Certificate issuance fails
- Error:
Cloudflare API returned 403: Invalid credentialsor similar - Credential test fails
Root Causes:
- API token/key expired or revoked
- Insufficient API permissions
- DNS provider account suspended
- Rate limiting or API quota exceeded
Solutions:
Step 1: Test the Credential
Settings → DNS Providers → Manage Credentials → Click "Test" next to credential
Step 2: Check API Token Validity
- Log in to your DNS provider dashboard (e.g., Cloudflare)
- Navigate to API Tokens
- Verify token is active and not expired
- Check token permissions:
Zone:DNS:Editpermission required
Step 3: Regenerate API Token
- Generate new API token at DNS provider
- Update credential in Charon:
Settings → DNS Providers → Manage Credentials → Edit → Update API credentials → Test → Save
Step 4: Check API Rate Limits
- Review DNS provider's rate limit documentation
- Check if you've exceeded API quotas
- Wait for rate limit to reset (typically hourly)
- Consider spreading certificate operations over time
Step 5: Retry Certificate Issuance
Proxy Hosts → Select proxy host → SSL/TLS → Renew Certificate
Issue 3: Zone filter validation error
Symptoms:
- Cannot save credential
- Error:
Invalid zone filter format: 'example..com' - Error:
Zone filter 'example.com' conflicts with existing credential
Root Causes:
- Syntax error in zone filter (typo, invalid characters)
- Duplicate zone filter across multiple credentials
- Conflicting exact and wildcard patterns
Solutions:
Step 1: Check Syntax
Valid Formats:
✅ example.com
✅ *.customer-a.com
✅ example.com,api.example.org
✅ *.staging.example.com,*.dev.example.com
✅ (empty - catch-all)
Invalid Formats:
❌ example..com (double dot)
❌ example.com. (trailing dot)
❌ *customer-a.com (missing dot after asterisk)
❌ example.com, api.example.org (space after comma)
❌ example.com; api.example.org (semicolon instead of comma)
Step 2: Check for Duplicates
- Review all credentials for the provider
- Ensure no two credentials have the exact same zone filter pattern
- If duplicate found, edit one credential to have a different zone filter
Step 3: Resolve Conflicts
- If you have both
example.comand*.example.com, this is allowed but may cause confusion - Ensure you understand priority order: exact match takes precedence
Step 4: Save Again
- After fixing syntax/duplicates, click Save Credential
Issue 4: Wrong credential is being used
Symptoms:
- Certificate issued successfully but audit logs show unexpected credential
- Credential statistics don't match expectations
- Security/compliance concern about which credential was used
Root Causes:
- Zone filter misconfiguration (too broad or too narrow)
- Misunderstanding of zone matching priority
- Overlapping patterns causing unexpected matches
Solutions:
Step 1: Review Zone Matching Logic
Priority Order:
1. Exact match: example.com
2. Wildcard match: *.customer-a.com
3. Catch-all: (empty)
Step 2: Check Zone Extraction
- For domain
shop.customer-a.com, zone iscustomer-a.com - For domain
www.example.com, zone isexample.com - For domain
api.sub.example.com, zone isexample.com(notsub.example.com)
Step 3: Review All Credential Zone Filters
Settings → DNS Providers → Manage Credentials
List all zone filters and check for overlaps:
Credential A: example.com (exact)
Credential B: *.example.com (wildcard)
Credential C: (empty - catch-all)
For www.example.com:
- Zone: example.com
- Match: Credential A (exact match wins)
Step 4: Adjust Zone Filters
- Make zone filters more specific to avoid unwanted matches
- Remove or narrow catch-all if it's too broad
- Use exact matches for critical domains
Step 5: Test Zone Matching
- Some Charon versions may include a zone matching test utility
- Go to Settings → DNS Providers → Test Zone Matching
- Enter a domain and see which credential would be selected
Issue 5: Credential test succeeds but certificate issuance fails
Symptoms:
- Credential test passes: ✅ "Credential validated successfully"
- Certificate issuance fails with DNS-related error
- Error:
DNS propagation timeoutorTXT record not found
Root Causes:
- API permissions sufficient for test but not for DNS-01 challenge
- DNS propagation delay
- Credential has access to different zones than expected
- Firewall/network issue blocking DNS updates
Solutions:
Step 1: Check API Permissions
Cloudflare:
- Required:
Zone:DNS:Editpermission - Test permission alone may only check
Zone:DNS:Read
Route53:
- Required:
route53:ChangeResourceRecordSets,route53:GetChange - Test permission alone may only check
route53:ListHostedZones
Step 2: Verify Zone Access
- Ensure credential has access to the specific zone
- Check DNS provider dashboard for zone visibility
- For Route53, ensure IAM policy includes the correct hosted zone ID
Step 3: Check DNS Propagation
- DNS-01 challenge requires TXT record to propagate
- Default timeout: 60 seconds
- Increase timeout in Charon settings if DNS provider is slow:
Settings → Advanced → DNS Propagation Timeout: 120 seconds
Step 4: Manual DNS Test
- After certificate issuance fails, check if TXT record was created:
dig TXT _acme-challenge.shop.customer-a.com nslookup -type=TXT _acme-challenge.shop.customer-a.com - If record exists, issue is with propagation delay
- If record doesn't exist, issue is with API permissions or credential
Step 5: Review Let's Encrypt Logs
- View detailed certificate issuance logs:
Settings → Logs → Filter by: "certificate_issuance" - Look for specific error messages from Let's Encrypt or DNS provider
Getting Help
If you continue to experience issues:
- Check Documentation: Review DNS provider-specific guides for configuration details
- Review Audit Logs: Check audit trail for clues about what went wrong
- Test Credentials: Use credential test feature to isolate API issues
- Enable Debug Logging: Temporarily enable debug logging for certificate operations
- Community Support: Visit Charon community forums or GitHub discussions
- Professional Support: Contact Charon support team for enterprise customers
API Reference
Authentication
All API requests require authentication using a Charon API token:
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://your-charon-instance/api/v1/...
Getting an API Token:
- Go to Settings → API Tokens
- Click Generate Token
- Copy token (shown only once)
Credential Management API Endpoints
List Credentials
Endpoint: GET /api/v1/dns-providers/{providerId}/credentials
Description: List all credentials for a DNS provider
Request:
curl -X GET \
https://your-charon-instance/api/v1/dns-providers/1/credentials \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"credentials": [
{
"id": 42,
"provider_id": 1,
"name": "Customer A Production",
"description": "Credential for Customer A production domains",
"zone_filter": "*.customer-a.com",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-03T10:45:00Z",
"last_used_at": "2026-01-03T10:45:00Z",
"usage_count": 12,
"success_count": 156,
"failure_count": 2
},
{
"id": 43,
"provider_id": 1,
"name": "Catch-All",
"description": "Fallback credential",
"zone_filter": "",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z",
"last_used_at": "2026-01-02T15:30:00Z",
"usage_count": 5,
"success_count": 10,
"failure_count": 0
}
],
"total": 2
}
Get Credential
Endpoint: GET /api/v1/dns-providers/{providerId}/credentials/{credentialId}
Description: Get details of a specific credential
Request:
curl -X GET \
https://your-charon-instance/api/v1/dns-providers/1/credentials/42 \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"id": 42,
"provider_id": 1,
"name": "Customer A Production",
"description": "Credential for Customer A production domains",
"zone_filter": "*.customer-a.com",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-03T10:45:00Z",
"last_used_at": "2026-01-03T10:45:00Z",
"usage_count": 12,
"success_count": 156,
"failure_count": 2,
"domains": [
"shop.customer-a.com",
"api.customer-a.com",
"portal.customer-a.com"
]
}
Create Credential
Endpoint: POST /api/v1/dns-providers/{providerId}/credentials
Description: Create a new credential for a DNS provider
Request:
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/credentials \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer B Production",
"description": "Credential for Customer B production domains",
"zone_filter": "*.customer-b.com",
"credentials": {
"api_token": "your-cloudflare-api-token"
}
}'
Provider-Specific Credential Fields:
Cloudflare:
"credentials": {
"api_token": "your-cloudflare-api-token"
}
// OR
"credentials": {
"api_key": "your-cloudflare-api-key",
"email": "your-email@example.com"
}
Route53:
"credentials": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
DigitalOcean:
"credentials": {
"api_token": "your-digitalocean-api-token"
}
Response:
{
"id": 44,
"provider_id": 1,
"name": "Customer B Production",
"description": "Credential for Customer B production domains",
"zone_filter": "*.customer-b.com",
"created_at": "2026-01-04T15:30:00Z",
"updated_at": "2026-01-04T15:30:00Z",
"last_used_at": null,
"usage_count": 0,
"success_count": 0,
"failure_count": 0
}
Update Credential
Endpoint: PATCH /api/v1/dns-providers/{providerId}/credentials/{credentialId}
Description: Update an existing credential
Request:
curl -X PATCH \
https://your-charon-instance/api/v1/dns-providers/1/credentials/44 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated credential description",
"zone_filter": "*.customer-b.com,*.customer-b.net"
}'
Response:
{
"id": 44,
"provider_id": 1,
"name": "Customer B Production",
"description": "Updated credential description",
"zone_filter": "*.customer-b.com,*.customer-b.net",
"created_at": "2026-01-04T15:30:00Z",
"updated_at": "2026-01-04T16:00:00Z",
"last_used_at": null,
"usage_count": 0,
"success_count": 0,
"failure_count": 0
}
Delete Credential
Endpoint: DELETE /api/v1/dns-providers/{providerId}/credentials/{credentialId}
Description: Delete a credential (fails if credential is in use)
Request:
curl -X DELETE \
https://your-charon-instance/api/v1/dns-providers/1/credentials/44 \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response (Success):
{
"message": "Credential deleted successfully",
"id": 44
}
Response (Error - In Use):
{
"error": "Cannot delete credential: 3 proxy hosts are using this credential",
"affected_domains": [
"shop.customer-b.com",
"api.customer-b.com",
"portal.customer-b.com"
]
}
Test Credential
Endpoint: POST /api/v1/dns-providers/{providerId}/credentials/{credentialId}/test
Description: Test if a credential is valid and has correct permissions
Request:
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/credentials/42/test \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response (Success):
{
"status": "success",
"message": "Credential validated successfully",
"details": {
"provider": "cloudflare",
"test_performed": "zone_list",
"accessible_zones": [
"customer-a.com"
]
}
}
Response (Failure):
{
"status": "failed",
"message": "API authentication failed",
"details": {
"provider": "cloudflare",
"error_code": "403",
"error_message": "Invalid credentials"
}
}
Enable Multi-Credential Mode
Endpoint: POST /api/v1/dns-providers/{providerId}/enable-multi-credential
Description: Enable multi-credential mode for a DNS provider
Request:
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/enable-multi-credential \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"message": "Multi-credential mode enabled",
"provider_id": 1,
"migrated_credential": {
"id": 45,
"name": "Cloudflare Primary Credential",
"zone_filter": "",
"description": "Migrated from single-credential mode"
}
}
Disable Multi-Credential Mode
Endpoint: POST /api/v1/dns-providers/{providerId}/disable-multi-credential
Description: Disable multi-credential mode (reverts to first credential as primary)
Request:
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/disable-multi-credential \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"message": "Multi-credential mode disabled",
"provider_id": 1,
"primary_credential": {
"id": 45,
"name": "Cloudflare Primary Credential"
},
"note": "Other credentials are retained but not used. Re-enable multi-credential mode to use them again."
}
Error Responses
All API endpoints may return the following error responses:
400 Bad Request:
{
"error": "Invalid zone filter format",
"details": "Zone filter cannot contain spaces"
}
401 Unauthorized:
{
"error": "Unauthorized",
"message": "Invalid or expired API token"
}
403 Forbidden:
{
"error": "Forbidden",
"message": "Insufficient permissions to manage credentials"
}
404 Not Found:
{
"error": "Not found",
"message": "Credential with ID 999 not found"
}
409 Conflict:
{
"error": "Conflict",
"message": "Zone filter 'example.com' conflicts with existing credential",
"conflicting_credential_id": 42
}
500 Internal Server Error:
{
"error": "Internal server error",
"message": "An unexpected error occurred. Please contact support.",
"request_id": "req_abc123xyz"
}
Cross-References
Related Documentation
- DNS Provider Setup Guides - Configure individual DNS providers
- Audit Logging - View and export audit logs for credential operations
- Security Best Practices - Security guidelines for credential management
- Key Rotation - Automated credential rotation strategies
- Certificate Management - Understanding Let's Encrypt certificate lifecycle
- API Documentation - Complete API reference for automation
- Multi-Tenancy Guide - Deploying Charon for multi-tenant scenarios
- Backup and Recovery - Backing up credential configuration
Provider-Specific Guides
- Cloudflare Multi-Credential Setup
- Route53 IAM Policies for Multi-Credential
- DigitalOcean Token Scoping
Tutorials
- Tutorial: Setting Up Multi-Credential for an MSP
- Tutorial: Environment Separation with Multi-Credentials
- Tutorial: Migrating from Single to Multi-Credential Mode
Support and Feedback
Questions? Visit the Charon Community Forums
Found a Bug? Report it on GitHub Issues
Feature Request? Submit your ideas in GitHub Discussions
Need Help? Contact support at support@charon.example.com
Last Updated: January 4, 2026 Version: 1.3.0