SSL Certificate Configuration Prompt: Securing Your Homelab
Overview
This reference provides quick patterns for implementing SSL/TLS certificates in homelab environments, covering both public and private certificate authorities.
Certificate Types Comparison
| Type | Trust | Renewal | Cost | Use Case |
|---|---|---|---|---|
| Let’s Encrypt | Public | 90 days (auto) | Free | Public domains |
| Self-Signed | None | Manual | Free | Testing only |
| mkcert | Local CA | Manual | Free | Internal services |
| Cloudflare Origin | Cloudflare | 15 years | Free | CF-proxied sites |
Method 1: Let’s Encrypt (DNS Challenge)
When to Use
- Public domain with Cloudflare DNS
- Services behind firewall (no port 80 exposed)
- Automatic renewal required
Configuration
# Install certbot
sudo apt install certbot python3-certbot-dns-cloudflare
# Create Cloudflare credentials
nano ~/.secrets/cloudflare.inidns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN# Obtain certificate
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/cloudflare.ini \
-d proxmox.yourdomain.com \
-d '*.yourdomain.com'
# Certificates saved to:
# /etc/letsencrypt/live/yourdomain.com/fullchain.pem
# /etc/letsencrypt/live/yourdomain.com/privkey.pemMethod 2: mkcert (Local CA)
When to Use
- Internal services (LAN only)
- Development environment
- Multiple devices need to trust
Installation
# Install mkcert
sudo apt install libnss3-tools
wget https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
sudo mv mkcert-v1.4.4-linux-amd64 /usr/local/bin/mkcert
sudo chmod +x /usr/local/bin/mkcert
# Install local CA
mkcert -install
# Generate certificate
mkcert proxmox.lab "*.lab.local" localhost 127.0.0.1 ::1
# Output:
# proxmox.lab+4.pem (certificate)
# proxmox.lab+4-key.pem (private key)Client Installation
# Export CA certificate
cp "$(mkcert -CAROOT)/rootCA.pem" ~/ca-cert.pem
# Install on other devices:
# - Windows: Import to Trusted Root Certification Authorities
# - Linux: Copy to /usr/local/share/ca-certificates/
# - Android: Settings → Security → Install from storageMethod 3: Nginx Proxy Manager Integration
Let’s Encrypt via NPM
In NPM UI:
Proxy Host → SSL Tab
- Request new SSL certificate
- Use DNS Challenge
- DNS Provider: Cloudflare
- API Token: [your token]
- Force SSL: ON
- HTTP/2: ON
- HSTS: ON
Custom Certificate via NPM
Import mkcert certificate:
SSL Certificates → Add SSL Certificate → Custom
- Certificate Key: Upload privkey.pem
- Certificate: Upload fullchain.pem
Method 4: Proxmox ACME
Setup in Proxmox
Datacenter → ACME
1. Add Account: Let's Encrypt (Production)
2. Add Challenge Plugin:
- Plugin ID: cloudflare
- DNS API: Cloudflare Managed DNS
- CF_Token: [your API token]
Node → System → Certificates
1. Add: Using ACME
2. Domain: proxmox.yourdomain.com
3. Challenge Type: DNS
4. Plugin: cloudflare
5. Order Certificate Now
Certificate Renewal Automation
Let’s Encrypt Auto-Renewal
# Test renewal
sudo certbot renew --dry-run
# Auto-renewal is configured via systemd timer
sudo systemctl status certbot.timerCustom Renewal Script
#!/bin/bash
# renew-certs.sh
# Renew Let's Encrypt
certbot renew --quiet
# Reload services
systemctl reload nginx
systemctl reload pveproxy
# Send notification
echo "Certificates renewed successfully" | \
mail -s "Certificate Renewal" admin@example.comCron Job
# Add to crontab
crontab -e
# Run weekly
0 3 * * 0 /root/renew-certs.shTroubleshooting
Certificate Not Trusted
Problem: Browser shows “Not Secure” warning
Solutions:
# Verify certificate chain
openssl s_client -connect domain.com:443 -showcerts
# Check CA installation (mkcert)
mkcert -CAROOT
# Reinstall CA
mkcert -uninstall
mkcert -installDNS Challenge Fails
Problem: Let’s Encrypt can’t validate domain
Solutions:
# Verify DNS propagation
dig _acme-challenge.yourdomain.com TXT
# Check API token permissions
# Must have: Zone.DNS.Edit
# Test with verbose output
certbot certonly --dns-cloudflare --dry-run -vCertificate Expired
Problem: Service using expired certificate
Solutions:
# Check expiry date
openssl x509 -in /path/to/cert.pem -noout -dates
# Force renewal
certbot renew --force-renewal
# Reload service
systemctl reload nginxSecurity Best Practices
1. Strong Configuration
# Nginx SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000" always;
2. Certificate Monitoring
# Monitor expiry
openssl x509 -in cert.pem -noout -enddate
# Alert before expiry (30 days)
EXPIRY=$(openssl x509 -enddate -noout -in cert.pem | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 30 ]; then
echo "Certificate expires in $DAYS_LEFT days!" | \
mail -s "CERT EXPIRY WARNING" admin@example.com
fi3. Regular Audits
# Check all certificates
for cert in /etc/letsencrypt/live/*/cert.pem; do
echo "Certificate: $cert"
openssl x509 -in "$cert" -noout -subject -dates
echo "---"
doneQuick Decision Guide
Need public access? → Use Let’s Encrypt with DNS challenge
Only internal access? → Use mkcert for local CA
Testing/development? → Self-signed acceptable (but mkcert preferred)
Using Cloudflare? → Cloudflare Origin Certificate or Let’s Encrypt
Multiple services? → Wildcard certificate
(*.yourdomain.com)
Conclusion
Proper SSL/TLS configuration is essential for homelab security. Choose the certificate method based on your access requirements and infrastructure capabilities. Automate renewal to prevent expiry-related outages.
Key Points: - Always use HTTPS for web interfaces - Automate certificate renewal - Monitor certificate expiry - Use strong TLS configurations - Document your certificate infrastructure
For implementation details, see related articles on Nginx Proxy Manager and Cloudflare Tunnel.