Security¶
Layered Security Architecture¶
Layer 1: Cloudflare (Edge)
DDoS protection, WAF, bot management, CDN
SSL/TLS termination (client-facing cert)
Rate limiting, IP obfuscation
Layer 2: UDM Firewall
Only ports 80/443 forwarded to 192.168.50.10
All other ports blocked from internet
VLAN segmentation (50 gateway, 51 internal)
Layer 3: Traefik (Reverse Proxy)
Service routing, SSL/TLS (Let's Encrypt)
HTTP-01 challenge for public domains
DNS-01 challenge (Cloudflare) for internal-only domains
Trusted Cloudflare IPs for header forwarding
Middleware: BasicAuth, rate limiting, security headers
Access logging
Layer 4: Docker Swarm
Overlay network isolation (public_net vs data_net)
Docker secrets (encrypted at rest)
Resource limits per service
Health checks and auto-restart
Cloudflare Configuration¶
| Setting | Value | Status |
|---|---|---|
| Proxy mode | Orange cloud (proxied) | All domains |
| SSL mode | FULL | End-to-end HTTPS |
| DDoS | Automatic | Active |
| WAF | Enabled | Active |
| Bot management | Enabled | Active |
| Real IP hidden | Yes | Cloudflare IPs visible instead |
Traffic flow:
The internal hop (Traefik to app) uses HTTP on isolated overlay networks, which is acceptable since it never leaves the host network.
Network Security¶
For full details on port exposure, firewall rules, and VLAN segmentation, see Networking.
Network Isolation¶
- public_net: Only services needing external access via Traefik
- data_net: Backend services connecting to databases/cache
- Databases are never on
public_net - Inter-service communication restricted to shared networks
Secrets Management¶
Docker Secrets (Swarm)¶
All sensitive data stored as Docker secrets (encrypted at rest in Swarm raft log):
# List secrets
docker secret ls
# Create secret
echo "value" | docker secret create my_secret -
# Services access secrets at /run/secrets/{name}
HashiCorp Vault (Centralized)¶
Vault provides centralized secrets management, accessible at https://vault.apps.jlwaller.com (Traefik) or http://192.168.51.30:8200 (direct, port published on data stack). KV v2 engine at secret/ path with PostgreSQL storage backend.
Key secrets stored in Vault:
secret/unifi/udm-- UniFi API keysecret/pushover-- Pushover notification credentialssecret/recipicity/prod/analytics-- Recipicity GA key (G-9NC4HEHCZ0)secret/generationsoftrust/prod/analytics-- GoT GA key (G-2MSRPL704E)
Auto-unseal: A systemd service on dockerrr (vault-unseal) automatically checks Vault seal status every 30 seconds and unseals with Shamir keys if needed.
Best practices:
- No hardcoded credentials in code or stack files
- Use
*_FILEenvironment variables pointing to/run/secrets/ - Store API keys, tokens, and third-party credentials in Vault
- Rotate secrets on a 90-day schedule (see Credential Rotation)
Security Score Breakdown¶
| Category | Score | Notes |
|---|---|---|
| Network Security | 9/10 | Cloudflare + proper firewall |
| Access Control | 7/10 | Could add more staging auth |
| Secrets Management | 9/10 | Docker secrets throughout |
| TLS/SSL | 8/10 | FULL mode, could upgrade to Strict |
| DDoS Protection | 10/10 | Cloudflare enterprise-grade |
| Rate Limiting | 8/10 | Cloudflare + Traefik middleware |
| Monitoring | 9/10 | 29 alert rules, Prometheus -> Alertmanager -> Pushover direct, Vault auto-unseal |
| Port Exposure | 9/10 | Only 80/443 exposed |
Recommendations¶
- Upgrade to Cloudflare Full (Strict) -- Use Cloudflare Origin Certificate for stricter validation
- Traefik dashboard auth -- Add BasicAuth even though it's internal-only (defense in depth)
- Cloudflare WAF tuning -- Custom rules for admin endpoints, rate limits per path
Verification Commands¶
# Check what ports are listening
ss -tlnp
# Verify Cloudflare proxy (should show Cloudflare IPs, not yours)
dig +short recipicity.com
# Test SSL chain
openssl s_client -connect recipicity.com:443 -servername recipicity.com </dev/null 2>/dev/null | openssl x509 -noout -issuer
# Check for redirect loops
curl -I https://recipicity.com