Files
Charon/docs/features/multi-credential.md
2026-01-13 22:11:35 +00:00

1617 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
1. **Navigate to DNS Provider Settings**
- Go to **Settings****DNS Providers**
- Locate the provider you want to enable multi-credential for (e.g., Cloudflare)
2. **Click "Enable Multi-Credential"**
- Click the **Enable Multi-Credential** button next to the provider
- A confirmation dialog will appear
3. **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)
```
4. **Confirm Enable**
- Click **Enable** to proceed
- The provider will now show "Multi-Credential Mode: Enabled"
5. **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
1. **Existing Configuration Preserved**: Your current API key/token remains active
2. **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"
3. **Zero Downtime**: All certificate operations continue without interruption
4. **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
1. Navigate to **Settings** → **DNS Providers**
2. Find your provider with "Multi-Credential Mode: Enabled"
3. Click **Manage Credentials**
4. 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)
1. Click **Test Credential** before saving
2. Charon will attempt to authenticate with the DNS provider using the supplied credentials
3. If successful, you'll see: ✅ "Credential validated successfully"
4. 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.com` matches `example.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
1. In the credential management interface, click **Edit** next to the credential
2. Modify any field (name, description, credentials, zone filter)
3. Click **Test Credential** to validate changes
4. 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:
1. Click **Test** next to the credential in the management interface
2. Charon will attempt a test DNS record lookup or API call
3. 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
1. Click **Delete** next to the credential
2. Charon will check if any proxy hosts are currently using this credential
3. **If in use**: You'll be warned and must migrate those proxy hosts to another credential first
4. **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:**
1. **Add a catch-all credential**: Create a credential with an empty zone filter
2. **Add specific credential**: Create a credential with zone filter matching your domain
3. **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:**
1. **Check zone filter syntax**: Verify your zone filters are correctly configured
2. **Check priority order**: Exact matches override wildcards; ensure your exact match is configured
3. **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:**
1. **Check syntax**: Ensure no spaces, only commas separating patterns
2. **Check for duplicates**: Ensure no two credentials have the exact same zone filter pattern
3. **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:
1. **You don't select a credential** - Charon selects automatically
2. **Zone matching** - Charon extracts the DNS zone from your domain
3. **Credential lookup** - Charon finds the best matching credential using zone matching rules
4. **Certificate issuance** - The selected credential is used for DNS-01 challenge
### Step-by-Step Process
1. **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
2. **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
3. **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
4. **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**
1. Open the proxy host from the dashboard
2. 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**
1. Go to **Settings** → **Audit Logs**
2. Filter by: `Action: certificate_issued` or `Action: certificate_renewed`
3. View log entry:
```json
{
"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**
1. Go to **Settings** → **DNS Providers** → **Manage Credentials**
2. 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:**
1. Check DNS provider has multi-credential enabled
2. Verify a credential exists with zone filter matching `customer-a.com`
3. Add a credential with zone filter: `*.customer-a.com` or 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:**
1. Test the credential being used: **Manage Credentials** → **Test**
2. Verify API token/key is still valid in your DNS provider dashboard
3. Check API token has correct permissions (`Zone:DNS:Edit`)
4. 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:**
1. Review zone filter configuration for all credentials
2. Check priority order (exact > wildcard > catch-all)
3. Ensure your expected credential has the most specific zone filter
4. 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:**
1. **Proof-of-Concept/Testing**: Start with catch-all, refine later
2. **Backward Compatibility**: Ensure existing domains continue working during migration
3. **Miscellaneous Domains**: Handle legacy or one-off domains
4. **Gradual Migration**: Add specific credentials over time while catch-all handles the rest
**❌ Avoid Catch-All When:**
1. **High-Security Environments**: Catch-all defeats purpose of zone isolation
2. **Multi-Tenancy**: Each tenant should have explicit credentials
3. **Compliance**: Regulations may require explicit credential assignment
4. **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:**
1. **Generate New Credentials** at DNS provider
2. **Add New Credential** in Charon with same zone filter
3. **Test New Credential** - verify it works
4. **Update Zone Filter** of old credential to `__deprecated__` (forces Charon to use new credential)
5. **Wait for Certificate Renewals** - monitor for 7-30 days
6. **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:**
1. Navigate to **Settings** → **DNS Providers**
2. For each provider with multi-credential enabled, click **View Statistics**
3. 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:**
1. Go to **Settings** → **DNS Providers** → **Manage Credentials**
2. 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:**
1. Go to **Settings** → **Audit Logs**
2. 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
**Log Entry Example:**
```json
{
"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:**
1. No credential configured for the DNS zone
2. Zone filter doesn't match the domain's zone
3. 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 credentials` or similar
- Credential test fails
**Root Causes:**
1. API token/key expired or revoked
2. Insufficient API permissions
3. DNS provider account suspended
4. 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:Edit` permission 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:**
1. Syntax error in zone filter (typo, invalid characters)
2. Duplicate zone filter across multiple credentials
3. 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.com` and `*.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:**
1. Zone filter misconfiguration (too broad or too narrow)
2. Misunderstanding of zone matching priority
3. 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 is `customer-a.com`
- For domain `www.example.com`, zone is `example.com`
- For domain `api.sub.example.com`, zone is `example.com` (not `sub.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 timeout` or `TXT record not found`
**Root Causes:**
1. API permissions sufficient for test but not for DNS-01 challenge
2. DNS propagation delay
3. Credential has access to different zones than expected
4. Firewall/network issue blocking DNS updates
**Solutions:**
**Step 1: Check API Permissions**
**Cloudflare:**
- Required: `Zone:DNS:Edit` permission
- 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:
```bash
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:
1. **Check Documentation**: Review [DNS provider-specific guides](#) for configuration details
2. **Review Audit Logs**: Check audit trail for clues about what went wrong
3. **Test Credentials**: Use credential test feature to isolate API issues
4. **Enable Debug Logging**: Temporarily enable debug logging for certificate operations
5. **Community Support**: Visit Charon community forums or GitHub discussions
6. **Professional Support**: Contact Charon support team for enterprise customers
## API Reference
### Authentication
All API requests require authentication using a Charon API token:
```bash
curl -H "Authorization: Bearer YOUR_API_TOKEN" \
https://your-charon-instance/api/v1/...
```
**Getting an API Token:**
1. Go to **Settings** → **API Tokens**
2. Click **Generate Token**
3. 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:**
```bash
curl -X GET \
https://your-charon-instance/api/v1/dns-providers/1/credentials \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response:**
```json
{
"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:**
```bash
curl -X GET \
https://your-charon-instance/api/v1/dns-providers/1/credentials/42 \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response:**
```json
{
"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:**
```bash
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:**
```json
"credentials": {
"api_token": "your-cloudflare-api-token"
}
// OR
"credentials": {
"api_key": "your-cloudflare-api-key",
"email": "your-email@example.com"
}
```
**Route53:**
```json
"credentials": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
```
**DigitalOcean:**
```json
"credentials": {
"api_token": "your-digitalocean-api-token"
}
```
**Response:**
```json
{
"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:**
```bash
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:**
```json
{
"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:**
```bash
curl -X DELETE \
https://your-charon-instance/api/v1/dns-providers/1/credentials/44 \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response (Success):**
```json
{
"message": "Credential deleted successfully",
"id": 44
}
```
**Response (Error - In Use):**
```json
{
"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:**
```bash
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/credentials/42/test \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response (Success):**
```json
{
"status": "success",
"message": "Credential validated successfully",
"details": {
"provider": "cloudflare",
"test_performed": "zone_list",
"accessible_zones": [
"customer-a.com"
]
}
}
```
**Response (Failure):**
```json
{
"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:**
```bash
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/enable-multi-credential \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response:**
```json
{
"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:**
```bash
curl -X POST \
https://your-charon-instance/api/v1/dns-providers/1/disable-multi-credential \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
**Response:**
```json
{
"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:**
```json
{
"error": "Invalid zone filter format",
"details": "Zone filter cannot contain spaces"
}
```
**401 Unauthorized:**
```json
{
"error": "Unauthorized",
"message": "Invalid or expired API token"
}
```
**403 Forbidden:**
```json
{
"error": "Forbidden",
"message": "Insufficient permissions to manage credentials"
}
```
**404 Not Found:**
```json
{
"error": "Not found",
"message": "Credential with ID 999 not found"
}
```
**409 Conflict:**
```json
{
"error": "Conflict",
"message": "Zone filter 'example.com' conflicts with existing credential",
"conflicting_credential_id": 42
}
```
**500 Internal Server Error:**
```json
{
"error": "Internal server error",
"message": "An unexpected error occurred. Please contact support.",
"request_id": "req_abc123xyz"
}
```
## Cross-References
### Related Documentation
- **[DNS Provider Setup Guides](../dns-providers/)** - Configure individual DNS providers
- **[Audit Logging](../security/audit-logging.md)** - View and export audit logs for credential operations
- **[Security Best Practices](../security/best-practices.md)** - Security guidelines for credential management
- **[Key Rotation](../security/key-rotation.md)** - Automated credential rotation strategies
- **[Certificate Management](../certificates/)** - Understanding Let's Encrypt certificate lifecycle
- **[API Documentation](../api/)** - Complete API reference for automation
- **[Multi-Tenancy Guide](../deployment/multi-tenancy.md)** - Deploying Charon for multi-tenant scenarios
- **[Backup and Recovery](../maintenance/backup-recovery.md)** - Backing up credential configuration
### Provider-Specific Guides
- **[Cloudflare Multi-Credential Setup](../dns-providers/cloudflare-multi-credential.md)**
- **[Route53 IAM Policies for Multi-Credential](../dns-providers/route53-multi-credential.md)**
- **[DigitalOcean Token Scoping](../dns-providers/digitalocean-multi-credential.md)**
### Tutorials
- **[Tutorial: Setting Up Multi-Credential for an MSP](../tutorials/msp-multi-credential.md)**
- **[Tutorial: Environment Separation with Multi-Credentials](../tutorials/environment-separation.md)**
- **[Tutorial: Migrating from Single to Multi-Credential Mode](../tutorials/migration-multi-credential.md)**
---
## Support and Feedback
**Questions?** Visit the [Charon Community Forums](https://community.charon.example.com)
**Found a Bug?** Report it on [GitHub Issues](https://github.com/charon/charon/issues)
**Feature Request?** Submit your ideas in [GitHub Discussions](https://github.com/charon/charon/discussions)
**Need Help?** Contact support at [support@charon.example.com](mailto:support@charon.example.com)
---
_Last Updated: January 4, 2026_
_Version: 1.3.0_