From 37a093189e58f94ded745bffb161c91b413e0b0b Mon Sep 17 00:00:00 2001 From: kelin Date: Mon, 12 Jan 2026 23:24:38 -0500 Subject: [PATCH] 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. --- docs/getting-started.md | 34 ++++++++++++++++-------- docs/quick-reference.md | 28 ++++++++++++++++++++ docs/service-docs/traefik.md | 50 +++++++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 15 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index a9d45c4..7500589 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -26,18 +26,27 @@ For most users, the automated setup script handles everything: sudo ./scripts/setup-homelab.sh ``` 5. **Log out and back in** (or run `newgrp docker`) -6. **Generate Authelia/Authentik Secrets +6. **Generate Authelia Secrets**: ```bash - # TODO: provide instructions for generating required secrets - AUTHELIA_JWT_SECRET=your-jwt-secret-here-64-chars - AUTHELIA_SESSION_SECRET=your-session-secret-here-64-chars - AUTHELIA_STORAGE_ENCRYPTION_KEY=your-encryption-key-here-64-chars - AUTHENTIK_SECRET_KEY=your-authentik-secret-key-here-100-chars - + # Generate three required secrets for Authelia (128 characters each) + echo "AUTHELIA_JWT_SECRET=$(openssl rand -hex 64)" + echo "AUTHELIA_SESSION_SECRET=$(openssl rand -hex 64)" + echo "AUTHELIA_STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 64)" + + # 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**: ```bash 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**: ```bash @@ -176,9 +185,12 @@ Use Dockge to deploy stacks like: - **Network conflicts**: Check existing networks with `docker network ls` ### Service Issues -- **Can't access services**: Check Traefik dashboard -- **SSL certificate errors**: Wait for Let's Encrypt -- **Authelia login fails**: Check user database configuration +- **Can't access services**: Check Traefik dashboard at `https://traefik.yourdomain.duckdns.org` +- **SSL certificate errors**: Wait 2-5 minutes for wildcard certificate to be obtained from Let's Encrypt + - 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 ```bash diff --git a/docs/quick-reference.md b/docs/quick-reference.md index d103237..5da57ca 100644 --- a/docs/quick-reference.md +++ b/docs/quick-reference.md @@ -323,6 +323,34 @@ https://prometheus.${DOMAIN} - Metrics collection 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 ### Service won't start diff --git a/docs/service-docs/traefik.md b/docs/service-docs/traefik.md index 8eb6dd8..7cff011 100644 --- a/docs/service-docs/traefik.md +++ b/docs/service-docs/traefik.md @@ -119,8 +119,12 @@ certificatesResolvers: acme: email: your-email@example.com storage: /acme.json - httpChallenge: - entryPoint: web + dnsChallenge: + provider: duckdns + disablePropagationCheck: true + resolvers: + - "1.1.1.1:53" + - "8.8.8.8:53" providers: docker: @@ -148,7 +152,7 @@ services: - "traefik.enable=true" - "traefik.http.routers.myservice.rule=Host(`myservice.${DOMAIN}`)" - "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.services.myservice.loadbalancer.server.port=8080" networks: @@ -218,6 +222,8 @@ traefik: - "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)" - "traefik.http.routers.traefik.entrypoints=websecure" - "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.service=api@internal" ``` @@ -344,6 +350,42 @@ docker inspect service-name | grep Networks 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 ```bash @@ -352,7 +394,7 @@ ls -la /opt/stacks/core/traefik/acme.json # Should be: -rw------- (600) # 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 curl -I http://yourdomain.duckdns.org