Update documentation for wildcard SSL certificates
- Add wildcard certificate configuration to Traefik docs - Document DuckDNS DNS challenge limitations - Add SSL troubleshooting commands to quick reference - Update getting-started with certificate verification steps - Emphasize single wildcard cert vs individual certs best practice Documentation now reflects production wildcard certificate setup.
This commit is contained in:
@@ -26,18 +26,27 @@ For most users, the automated setup script handles everything:
|
|||||||
sudo ./scripts/setup-homelab.sh
|
sudo ./scripts/setup-homelab.sh
|
||||||
```
|
```
|
||||||
5. **Log out and back in** (or run `newgrp docker`)
|
5. **Log out and back in** (or run `newgrp docker`)
|
||||||
6. **Generate Authelia/Authentik Secrets
|
6. **Generate Authelia Secrets**:
|
||||||
```bash
|
```bash
|
||||||
# TODO: provide instructions for generating required secrets
|
# Generate three required secrets for Authelia (128 characters each)
|
||||||
AUTHELIA_JWT_SECRET=your-jwt-secret-here-64-chars
|
echo "AUTHELIA_JWT_SECRET=$(openssl rand -hex 64)"
|
||||||
AUTHELIA_SESSION_SECRET=your-session-secret-here-64-chars
|
echo "AUTHELIA_SESSION_SECRET=$(openssl rand -hex 64)"
|
||||||
AUTHELIA_STORAGE_ENCRYPTION_KEY=your-encryption-key-here-64-chars
|
echo "AUTHELIA_STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 64)"
|
||||||
AUTHENTIK_SECRET_KEY=your-authentik-secret-key-here-100-chars
|
|
||||||
|
|
||||||
|
# Copy these values and add them to your .env file
|
||||||
|
```
|
||||||
|
7. **Generate Authelia Admin Password Hash**:
|
||||||
|
```bash
|
||||||
|
# Replace 'yourpassword' with your desired admin password
|
||||||
|
docker run --rm authelia/authelia:4.37 authelia crypto hash generate argon2 --password 'yourpassword'
|
||||||
|
|
||||||
|
# Copy the output hash and update /opt/stacks/core/authelia/users_database.yml
|
||||||
|
# Replace the password field for the admin user
|
||||||
|
```
|
||||||
8. **Configure environment**:
|
8. **Configure environment**:
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
nano .env # Edit with your settings
|
nano .env # Edit with your settings and paste the Authelia secrets
|
||||||
```
|
```
|
||||||
9. **Deploy core services**:
|
9. **Deploy core services**:
|
||||||
```bash
|
```bash
|
||||||
@@ -176,9 +185,12 @@ Use Dockge to deploy stacks like:
|
|||||||
- **Network conflicts**: Check existing networks with `docker network ls`
|
- **Network conflicts**: Check existing networks with `docker network ls`
|
||||||
|
|
||||||
### Service Issues
|
### Service Issues
|
||||||
- **Can't access services**: Check Traefik dashboard
|
- **Can't access services**: Check Traefik dashboard at `https://traefik.yourdomain.duckdns.org`
|
||||||
- **SSL certificate errors**: Wait for Let's Encrypt
|
- **SSL certificate errors**: Wait 2-5 minutes for wildcard certificate to be obtained from Let's Encrypt
|
||||||
- **Authelia login fails**: Check user database configuration
|
- Check status: `python3 -c "import json; d=json.load(open('/opt/stacks/core/traefik/acme.json')); print(f'Certificates: {len(d[\"letsencrypt\"][\"Certificates\"])}')"`
|
||||||
|
- View logs: `docker exec traefik tail -50 /var/log/traefik/traefik.log | grep certificate`
|
||||||
|
- **Authelia login fails**: Check user database configuration at `/opt/stacks/core/authelia/users_database.yml`
|
||||||
|
- **"Not secure" warnings**: Clear browser cache or wait for DNS propagation (up to 5 minutes)
|
||||||
|
|
||||||
### Common Fixes
|
### Common Fixes
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -323,6 +323,34 @@ https://prometheus.${DOMAIN} - Metrics collection
|
|||||||
https://status.${DOMAIN} - Uptime monitoring
|
https://status.${DOMAIN} - Uptime monitoring
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### SSL Certificates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check wildcard certificate status
|
||||||
|
python3 -c "import json; d=json.load(open('/opt/stacks/core/traefik/acme.json')); print(f'Certificates: {len(d[\"letsencrypt\"][\"Certificates\"])}')"
|
||||||
|
|
||||||
|
# Verify certificate being served
|
||||||
|
echo | openssl s_client -connect auth.yourdomain.duckdns.org:443 -servername auth.yourdomain.duckdns.org 2>/dev/null | openssl x509 -noout -subject -issuer
|
||||||
|
|
||||||
|
# Check DNS TXT records (for DNS challenge)
|
||||||
|
dig +short TXT _acme-challenge.yourdomain.duckdns.org
|
||||||
|
|
||||||
|
# View Traefik certificate logs
|
||||||
|
docker exec traefik tail -50 /var/log/traefik/traefik.log | grep -E "acme|certificate"
|
||||||
|
|
||||||
|
# Reset certificates (if needed)
|
||||||
|
docker compose -f /opt/stacks/core/docker-compose.yml down
|
||||||
|
rm /opt/stacks/core/traefik/acme.json
|
||||||
|
touch /opt/stacks/core/traefik/acme.json
|
||||||
|
chmod 600 /opt/stacks/core/traefik/acme.json
|
||||||
|
sleep 60 # Wait for DNS to clear
|
||||||
|
docker compose -f /opt/stacks/core/docker-compose.yml up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important:** With DuckDNS, only Traefik should request certificates (wildcard cert covers all subdomains). Other services use `tls=true` without `certresolver`.
|
||||||
|
|
||||||
## Troubleshooting Quick Fixes
|
## Troubleshooting Quick Fixes
|
||||||
|
|
||||||
### Service won't start
|
### Service won't start
|
||||||
|
|||||||
@@ -119,8 +119,12 @@ certificatesResolvers:
|
|||||||
acme:
|
acme:
|
||||||
email: your-email@example.com
|
email: your-email@example.com
|
||||||
storage: /acme.json
|
storage: /acme.json
|
||||||
httpChallenge:
|
dnsChallenge:
|
||||||
entryPoint: web
|
provider: duckdns
|
||||||
|
disablePropagationCheck: true
|
||||||
|
resolvers:
|
||||||
|
- "1.1.1.1:53"
|
||||||
|
- "8.8.8.8:53"
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
docker:
|
docker:
|
||||||
@@ -148,7 +152,7 @@ services:
|
|||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.myservice.rule=Host(`myservice.${DOMAIN}`)"
|
- "traefik.http.routers.myservice.rule=Host(`myservice.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.myservice.entrypoints=websecure"
|
- "traefik.http.routers.myservice.entrypoints=websecure"
|
||||||
- "traefik.http.routers.myservice.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.myservice.tls=true" # Uses wildcard cert automatically
|
||||||
- "traefik.http.routers.myservice.middlewares=authelia@docker"
|
- "traefik.http.routers.myservice.middlewares=authelia@docker"
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
||||||
networks:
|
networks:
|
||||||
@@ -218,6 +222,8 @@ traefik:
|
|||||||
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
|
||||||
- "traefik.http.routers.traefik.entrypoints=websecure"
|
- "traefik.http.routers.traefik.entrypoints=websecure"
|
||||||
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.routers.traefik.tls.domains[0].main=${DOMAIN}"
|
||||||
|
- "traefik.http.routers.traefik.tls.domains[0].sans=*.${DOMAIN}"
|
||||||
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
- "traefik.http.routers.traefik.middlewares=authelia@docker"
|
||||||
- "traefik.http.routers.traefik.service=api@internal"
|
- "traefik.http.routers.traefik.service=api@internal"
|
||||||
```
|
```
|
||||||
@@ -344,6 +350,42 @@ docker inspect service-name | grep Networks
|
|||||||
docker inspect service-name | grep traefik
|
docker inspect service-name | grep traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Wildcard Certificates with DuckDNS
|
||||||
|
|
||||||
|
**IMPORTANT:** When using DuckDNS for DNS challenge, only ONE service should request certificates:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# ✅ CORRECT: Only Traefik requests wildcard certificate
|
||||||
|
traefik:
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.routers.traefik.tls.domains[0].main=${DOMAIN}"
|
||||||
|
- "traefik.http.routers.traefik.tls.domains[0].sans=*.${DOMAIN}"
|
||||||
|
|
||||||
|
# ✅ CORRECT: Other services just enable TLS
|
||||||
|
other-service:
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.service.tls=true" # Uses wildcard cert
|
||||||
|
|
||||||
|
# ❌ WRONG: Multiple services requesting individual certs
|
||||||
|
other-service:
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.service.tls.certresolver=letsencrypt" # Causes conflicts!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why?** DuckDNS can only maintain ONE TXT record at `_acme-challenge.yourdomain.duckdns.org`. Multiple simultaneous certificate requests will fail with "Incorrect TXT record" errors.
|
||||||
|
|
||||||
|
**Solution:** Use a wildcard certificate (`*.yourdomain.duckdns.org`) that covers all subdomains.
|
||||||
|
|
||||||
|
**Verify Certificate:**
|
||||||
|
```bash
|
||||||
|
# Check wildcard certificate is obtained
|
||||||
|
python3 -c "import json; d=json.load(open('/opt/stacks/core/traefik/acme.json')); print(f'Certificates: {len(d[\"letsencrypt\"][\"Certificates\"])}')"
|
||||||
|
|
||||||
|
# Test certificate being served
|
||||||
|
echo | openssl s_client -connect yourdomain.duckdns.org:443 -servername yourdomain.duckdns.org 2>/dev/null | openssl x509 -noout -subject -issuer
|
||||||
|
```
|
||||||
|
|
||||||
#### SSL Certificate Issues
|
#### SSL Certificate Issues
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -352,7 +394,7 @@ ls -la /opt/stacks/core/traefik/acme.json
|
|||||||
# Should be: -rw------- (600)
|
# Should be: -rw------- (600)
|
||||||
|
|
||||||
# Check certificate generation logs
|
# Check certificate generation logs
|
||||||
docker logs traefik | grep acme
|
docker exec traefik tail -50 /var/log/traefik/traefik.log | grep -E "acme|certificate"
|
||||||
|
|
||||||
# Verify ports 80/443 are accessible
|
# Verify ports 80/443 are accessible
|
||||||
curl -I http://yourdomain.duckdns.org
|
curl -I http://yourdomain.duckdns.org
|
||||||
|
|||||||
Reference in New Issue
Block a user