# SSL/TLS Certificate Management *Managing SSL certificates for the homelab infrastructure* --- ## Overview The homelab uses Nginx Proxy Manager (NPM) as the primary certificate authority, with Let's Encrypt providing free SSL certificates. --- ## Certificate Authorities ### Primary: Let's Encrypt - **Provider:** Let's Encrypt - **Validation:** HTTP-01 (automatic via NPM) - **Renewal:** Automatic at 90 days - **Domains:** *.vish.local, *.vish.gg ### Secondary: Self-Signed - **Use:** Internal services (non-public) - **Tool:** OpenSSL - **Regeneration:** As needed --- ## Certificate Locations ### Nginx Proxy Manager ``` /opt/docker/npm/data/ ├── letsencrypt/ │ └── accounts/ │ └── acme-v02.api.letsencrypt.org/ └── ssl/ └── / ├── fullchain.pem ├── privkey.pem └── bundle.crt ``` ### Services with Own Certs - **Authentik:** `/opt/authentik/ssl/` - **Matrix:** `/etc/matrix-synapse/ssl/` - **PostgreSQL:** `/etc/ssl/private/` --- ## Adding New Certificates ### Via NPM UI (Recommended) 1. Access NPM: `http://calypso.vish.local:81` 2. Navigate to **SSL Certificates** → **Add SSL Certificate** 3. Enter domain names: - `service.vish.local` (internal) - `service.vish.gg` (public) 4. Enable **Force SSL** 5. Click **Save** ### Via CLI (Automation) ```bash # Using certbot directly certbot certonly --webroot \ -w /var/www/html \ -d service.vish.local \ --agree-tos \ --email admin@vish.local ``` --- ## Certificate Renewal ### Automatic (Default) - NPM auto-renews 7 days before expiration - No action required - Check logs: NPM → Logs ### Manual Renewal ```bash # Force renewal via NPM docker exec nginx-proxy-manager npm --root /etc/npm \ force-renew # Or via API curl -X POST http://npm/api/nginx/certificates//renew ``` ### Ansible Playbook ```bash ansible-playbook ansible/automation/playbooks/certificate_renewal.yml ``` --- ## Certificate Status ### Check Expiration ```bash # Via NPM # Navigate to SSL Certificates tab # Via openssl echo | openssl s_client -connect service.vish.local:443 2>/dev/null | openssl x509 -noout -dates # Via script cd /opt/npm/letsencrypt/live/ for cert in */; do echo "$cert: $(openssl x509 -enddate -noout -in "$cert/cert.pem" | cut -d= -f2)" done ``` ### Certificate Dashboard | Domain | Expiry | Status | Renews | |--------|--------|--------|--------| | vish.gg | +85 days | ✅ Active | Auto | | *.vish.local | +85 days | ✅ Active | Auto | --- ## Common Issues ### Rate Limiting **Problem:** Too many certificate requests **Solution:** - Wait 1 hour (Let's Encrypt limit) - Use staging environment for testing - Request multiple domains in one cert ### DNS Validation Failure **Problem:** ACME challenge fails **Solution:** - Verify DNS A record points to public IP - Check firewall allows port 80 - Ensure no CNAME conflicts ### Mixed Content Warnings **Problem:** HTTP resources on HTTPS page **Solution:** - Update service config to use HTTPS URLs - For internal services, use HTTP (NPM handles SSL) - Check browser console for details ### Certificate Mismatch **Problem:** Wrong certificate served **Solution:** 1. Check NPM proxy host settings 2. Verify certificate is assigned 3. Clear browser cache 4. Check for multiple certificates --- ## Internal Services (Self-Signed) ### Creating Self-Signed Cert ```bash # Create directory mkdir -p /opt/service/ssl # Generate certificate openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 \ -keyout /opt/service/ssl/key.pem \ -out /opt/service/ssl/cert.pem \ -addext "subjectAltName=DNS:service.local,DNS:service" # Set permissions chmod 600 /opt/service/ssl/key.pem ``` ### Adding to Trust Store ```bash # Linux (Ubuntu/Debian) sudo cp /opt/service/ssl/cert.pem /usr/local/share/ca-certificates/service.crt sudo update-ca-certificates # macOS sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /opt/service/ssl/cert.pem ``` --- ## Matrix/Synapse Certificates ### Custom Certificate Setup ```yaml # docker-compose.yml services: synapse: environment: - SYNAPSE_TLS_CERT_FILE=/ssl/tls.crt - SYNAPSE_TLS_KEY_FILE=/ssl/tls.key volumes: - ./ssl:/ssl:ro ``` ### Federation Certificates ```bash # Add to TLS certificates /usr/local/bin/REDACTED_APP_PASSWORD \ --server-name vish.local \ --tls-cert /opt/npm/ssl/vish.gg/fullchain.pem \ --tls-key /opt/npm/ssl/vish.gg/privkey.pem ``` --- ## Security Best Practices ### Key Permissions ```bash # Private keys should be readable only by root chmod 600 /path/to/privkey.pem chown root:root /path/to/privkey.pem ``` ### Cipher Suites Configure in NPM under **Settings → SSL → Advanced**: ``` ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256 ``` ### HSTS Enable in NPM: - **Settings → SSL → Force HSTS** - Preload recommended --- ## Backup ### Backup Certificates ```bash # Backup NPM certificates tar -czf backups/ssl-$(date +%Y%m%d).tar.gz \ /opt/docker/npm/data/letsencrypt/ \ /opt/docker/npm/data/ssl/ ``` ### Restore ```bash # Restore tar -xzf backups/ssl-20240101.tar.gz -C / # Restart NPM docker-compose -f /opt/docker/npm/docker-compose.yml restart ``` --- ## Monitoring ### Expiration Alerts Configure in Prometheus/Alertmanager: ```yaml groups: - name: certificates rules: - alert: REDACTED_APP_PASSWORD expr: (certify_not_after - time()) < (86400 * 30) for: 1h labels: severity: warning annotations: summary: "Certificate expiring soon" ``` --- ## Useful Commands ```bash # Check all certificates docker exec nginx-proxy-manager npm --root /etc/npm list # Force renewal docker exec nginx-proxy-manager npm --root /etc/npm force-renew # Manual ACME challenge docker exec -it nginx-proxy-manager sh cd /etc/letsencrypt/renewal-hooks/deploy/ # Verify certificate openssl s_client -connect vish.gg:443 -servername vish.gg ``` --- ## Links - [NPM Documentation](https://nginxproxymanager.com/) - [Let's Encrypt Docs](https://letsencrypt.org/docs/) - [SSL Labs Test](https://www.ssllabs.com/ssltest/)