Files
EZ-Homelab/docs/ssl-certificates.md
2026-01-21 15:54:58 -05:00

164 lines
5.8 KiB
Markdown

# SSL Certificates with Let's Encrypt and DuckDNS
Your homelab uses **Let's Encrypt** to automatically provide free SSL certificates for all your services. This ensures secure HTTPS connections without manual certificate management.
## How SSL Certificates Work in Your Homelab
### The Certificate Flow
1. **Domain Registration**: DuckDNS provides your dynamic domain (e.g., `yourname.duckdns.org`)
2. **Certificate Request**: Traefik requests a wildcard certificate (`*.yourname.duckdns.org`)
3. **Domain Validation**: Let's Encrypt validates you own the domain via DNS challenge
4. **Certificate Issuance**: Free SSL certificate is issued and stored
5. **Automatic Renewal**: Certificates renew automatically before expiration
### DuckDNS + Let's Encrypt Integration
**DuckDNS** handles dynamic DNS updates, while **Let's Encrypt** provides certificates:
- **DuckDNS**: Updates your public IP → domain mapping every 5 minutes
- **Let's Encrypt**: Issues trusted SSL certificates via ACME protocol
- **DNS Challenge**: Proves domain ownership by setting TXT records
### Wildcard Certificates Explained
Your setup uses a **wildcard certificate** (`*.yourdomain.duckdns.org`) that covers:
- `dockge.yourdomain.duckdns.org`
- `plex.yourdomain.duckdns.org`
- `jellyfin.yourdomain.duckdns.org`
- Any other subdomain automatically
**Why wildcard?** One certificate covers all services - no need for individual certificates per service.
### Certificate Storage & Management
- **Location**: `/opt/stacks/core/traefik/acme.json`
- **Permissions**: Must be `600` (read/write for owner only)
- **Backup**: Always backup this file - contains your certificates
- **Renewal**: Automatic, 30 days before expiration
## Testing vs Production Certificates
### Staging Server (For Testing)
```yaml
# In traefik.yml, add this line for testing:
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
```
**Staging Benefits:**
- Unlimited certificates (no rate limits)
- Fast issuance for testing
- **Not trusted by browsers** (shows "Not Secure")
**When to use staging:**
- Setting up new environments
- Testing configurations
- Learning/development
### Production Server (For Live Use)
```yaml
# Remove or comment out caServer line for production
# certificatesResolvers:
# letsencrypt:
# acme:
# # No caServer = production
```
**Production Limits:**
>This is why you want to use staging certificates for testing purposes!!!
>Always use staging certificates if you are running the setup & deploy scripts repeatedly
- **50 certificates per domain per week**
- **5 duplicate certificates per week**
- **Trusted by all browsers**
## Certificate Troubleshooting
### Check Certificate Status
```bash
# Count certificates in storage
python3 -c "import json; d=json.load(open('/opt/stacks/core/traefik/acme.json')); print(f'Certificates: {len(d[\"letsencrypt\"][\"Certificates\"])}')}"
```
### Common Issues & Solutions
**"Certificate not trusted" or "Not Secure" warnings:**
- **Staging certificates**: Expected - use production for live sites
- **DNS propagation**: Wait 5-10 minutes after setup
- **Browser cache**: Clear browser cache and try incognito mode
**Certificate request fails:**
- Check Traefik logs: `docker logs traefik | grep -i certificate`
- Verify DuckDNS token is correct in `.env`
- Ensure ports 80/443 are open and forwarded
- Wait 1+ hours between certificate requests
**Rate limit exceeded:**
- Switch to staging server for testing
- Wait 1 week for production limits to reset
- Check status at: https://letsencrypt.org/docs/rate-limits/
### DNS Challenge Process
When requesting certificates, Traefik:
1. Asks DuckDNS to set TXT record: `_acme-challenge.yourdomain.duckdns.org`
2. Let's Encrypt checks the TXT record to verify ownership
3. If valid, certificate is issued
4. TXT record is cleaned up automatically
**Note:** DuckDNS allows only ONE TXT record at a time. Multiple Traefik instances will conflict.
### Certificate Validation Commands
```bash
# Test certificate validity
echo | openssl s_client -connect yourdomain.duckdns.org:443 -servername dockge.yourdomain.duckdns.org 2>/dev/null | openssl x509 -noout -subject -issuer -dates
# Check if certificate covers wildcards
echo | openssl s_client -connect yourdomain.duckdns.org:443 -servername any-subdomain.yourdomain.duckdns.org 2>/dev/null | openssl x509 -noout -text | grep "Subject Alternative Name"
```
## Best Practices
### For Production
- Use production Let's Encrypt server
- Backup `acme.json` regularly
- Monitor certificate expiration (Traefik dashboard)
- Keep DuckDNS token secure
### For Development/Testing
- Use staging server to avoid rate limits
- Test with different subdomains
- Reset environments safely (preserve `acme.json` if possible)
### Security Notes
- Certificates are stored encrypted in `acme.json`
- Private keys never leave your server
- HTTPS provides encryption in transit
- Consider additional security headers in Traefik
## Port Forwarding Requirements
**Critical**: SSL certificates require ports 80 and 443 to be forwarded from your router to your homelab server.
### Router Configuration
1. Log into your router's admin interface (usually 192.168.1.1)
2. Find the "Port Forwarding" or "NAT" section
3. Create forwarding rules:
- **External Port**: 80 → **Internal IP**: your-server-ip **Internal Port**: 80
- **External Port**: 443 → **Internal IP**: your-server-ip **Internal Port**: 443
4. Protocol: TCP for both
5. Save changes
### Why This Is Required
- **Port 80**: Used by Let's Encrypt for domain ownership verification (HTTP-01 challenge)
- **Port 443**: Used for all HTTPS traffic to your services
- **Wildcard Certificates**: Enables automatic SSL for all `*.yourdomain.duckdns.org` subdomains
### Testing Port Forwarding
```bash
# Test from external network (not your local network)
curl -I http://yourdomain.duckdns.org
# Should return HTTP 200 or redirect to HTTPS
```