# SSL Certificate Management ## Overview We use **Let's Encrypt** certificates with **DNS-01 validation** via PowerDNS. This approach works for: - VPN-only domains (no HTTP access from internet) - Wildcard certificates - Multi-domain SAN certificates ## Architecture ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ acme.sh │────▶│ PowerDNS API │────▶│ Let's Encrypt │ │ (on host) │ │ (ns1/ns2) │ │ (ACME) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ ▼ ┌─────────────────┐ │ nginx ssl/ │ │ (certs) │ └─────────────────┘ ``` ## Quick Reference ### Issue Certificate ```bash # From lilith-platform/infrastructure directory ./scripts/security/issue-letsencrypt-cert.sh [domain2] ... ``` ### Examples ```bash # Staging: atlilith.com domains on black ./scripts/security/issue-letsencrypt-cert.sh black next.atlilith.com \ next.atlilith.com next.status.atlilith.com next.www.atlilith.com # Staging: trustedmeet.com domains on black ./scripts/security/issue-letsencrypt-cert.sh black next.trustedmeet.com \ next.trustedmeet.com next.www.trustedmeet.com # Production: atlilith.com domains on 0 ./scripts/security/issue-letsencrypt-cert.sh 0 atlilith.com \ atlilith.com www.atlilith.com status.atlilith.com api.atlilith.com # Production: trustedmeet.com domains on 0 ./scripts/security/issue-letsencrypt-cert.sh 0 trustedmeet.com \ trustedmeet.com www.trustedmeet.com ``` ## Current Certificates ### black (Staging) | Certificate | Domains | Nginx Config | |-------------|---------|--------------| | `next.atlilith.com` | next.atlilith.com, next.www.atlilith.com, next.status.atlilith.com | `/bigdisk/forgejo/ssl/next.atlilith.com.{crt,key}` | ### 0 (Production) | Certificate | Domains | Nginx Config | |-------------|---------|--------------| | `atlilith.com` | (to be issued) | `/etc/nginx/ssl/atlilith.com.{crt,key}` | ## Certificate Locations | Host | SSL Directory | Reload Command | |------|---------------|----------------| | black | `/bigdisk/forgejo/ssl/` | `docker exec forgejo-nginx nginx -s reload` | | 0 | `/etc/nginx/ssl/` | `systemctl reload nginx` | ## Renewal acme.sh automatically configures a cron job for renewal. Certificates renew ~30 days before expiry. Check renewal status: ```bash ssh black "~/.acme.sh/acme.sh --list" ssh 0 "~/.acme.sh/acme.sh --list" ``` Force renewal: ```bash ssh black "~/.acme.sh/acme.sh --renew -d next.atlilith.com --force" ``` ## Prerequisites ### First-time Setup (per host) ```bash # SSH to target host ssh black # or ssh 0 # Install acme.sh curl https://get.acme.sh | sh -s email=admin@atlilith.com source ~/.bashrc ``` ### PowerDNS API Access The script reads PowerDNS API credentials from: ``` vault/dns-servers-powerdns.txt ``` PowerDNS API must be accessible from the target host via VPN (10.0.0.11:8081). ## Nginx Configuration After issuing a certificate, update nginx config: ```nginx server { listen 443 ssl; server_name example.atlilith.com; ssl_certificate /path/to/ssl/cert-name.crt; ssl_certificate_key /path/to/ssl/cert-name.key; # Modern SSL settings ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:...; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # ... rest of config } ``` ## Troubleshooting ### DNS propagation issues ```bash # Check if TXT record was created dig +short TXT _acme-challenge.example.atlilith.com @ns1.nasty.sh ``` ### PowerDNS API connectivity ```bash # Test API from target host ssh black "curl -H 'X-API-Key: ' http://10.0.0.11:8081/api/v1/servers" ``` ### Certificate verification ```bash # Check certificate details ssh black "openssl x509 -in /bigdisk/forgejo/ssl/next.atlilith.com.crt -noout -text" # Check expiry ssh black "openssl x509 -in /bigdisk/forgejo/ssl/next.atlilith.com.crt -noout -enddate" ``` ## Related Files - `infrastructure/docker/forgejo/nginx.conf` - Nginx config for black (Docker) - `infrastructure/nginx/` - Nginx configs for production - `vault/dns-servers-powerdns.txt` - PowerDNS API credentials