Skip to content

Security Architecture

Network security

Problem: Shared WiFi, no router access, roommates shouldn't see services.

Solution: Three layers of protection:

  1. Cloudflare Tunnel — Zero ports exposed on LAN. Traffic flow: Internet → Cloudflare → Tunnel → Traefik → Service. No router port forwarding needed.

  2. 127.0.0.1 binding — Traefik's ports (80, 443) bind to 127.0.0.1, not 0.0.0.0. No other containers expose host ports at all. This means roommates on the same WiFi cannot discover or connect to any service — only cloudflared (inside Docker) can reach Traefik. Verify with docker compose ps — you should see 127.0.0.1:80->80 not 0.0.0.0:80->80.

  3. Tailscale VPN — Mesh VPN using WireGuard under the hood. Runs in userspace (no kernel modules). Advertises the Docker network as a subnet route, so your phone/laptop can reach all services directly when on the Tailscale network. Acts as an exit node for remote access.

  4. CrowdSec — Community-driven intrusion detection. Reads Traefik access logs. Automatically bans malicious IPs. The Traefik bouncer blocks banned IPs at the proxy level before requests reach any service.

SSO & Access control

Authentik provides three authentication mechanisms:

  1. Forward-auth (via Traefik) — every request to subdomains listed in setup_rbac.py is intercepted. Unauthenticated users see the Authentik login page at auth.h5h.me. Includes sh.h5h.me (Dashboard, Admin-only) — there is no public tier in the tunneled surface.
  2. Social login — users can sign in with Google via OAuth (provisioned by setup_rbac.py).
  3. OIDC provider — Authentik acts as an identity provider for apps that support OpenID Connect (Grafana is pre-configured; group admins maps to Grafana Admin, otherwise Viewer, with anonymous Viewer enabled).

(See authentik_rbac.md for the exact tier → service mapping — which is regenerated from setup_rbac.py by make gen-docs.)

Container hardening

  • no-new-privileges on all sensitive containers
  • Docker socket mounted as :ro everywhere
  • No host ports exposed except Traefik (127.0.0.1:80/443)
  • Health checks on critical databases and infrastructure limits restart loops
  • Log rotation on all containers via JSON file driver (10 MB × 3 files max)
  • CrowdSec bouncer is first middleware in the Traefik chain
  • Internal Docker network (h5h_internal, internal: true) isolates all databases

Alerting (Prometheus)

Prometheus alert rules fire on:

  • High unauthorized request rate (401/403)
  • High server error rate (5xx)
  • Infrastructure downtime
  • Container OOM kills (fires immediately when any container is killed, annotated locally in Grafana as red vertical lines)

Log aggregation

  • Promtail ships Docker container logs + Traefik access logs (JSON) to Loki
  • Grafana queries Loki for the Security & Access dashboard

MIT License