Status
Accepted — Deployed and operational since Q4 2025. Revised January 2026 to include Cloudflare Access policy updates.
Context
Running self-hosted services requires a secure, maintainable method for external access. The platform must handle multiple trust levels (admin-only, team-shared, public-facing) without exposing the underlying infrastructure to the internet.
Requirements
- No public IP exposure — The network perimeter should not have open inbound ports
- Per-service access control — Each application gets its own authentication and authorization policy
- Single sign-on — Users authenticate once and access all authorized services
- Centralized identity — One identity provider for all services, with group-based policies
- Defense in depth — Multiple layers of security, no single point of failure for access control
- Operational simplicity — Adding or removing services should not require infrastructure changes
Constraints
- All services run on a single Proxmox VE hypervisor
- Budget is homelab-scale (minimize paid dependencies)
- Must support both web applications and TCP/SSH access
- The operator (me) is the primary admin; occasional shared access for specific services
Decision
Implement a layered access architecture using Cloudflare Tunnels for transport, Traefik for routing, and Authentik for identity management, all running on Proxmox VE.
Architecture Overview
Internet
│
▼
┌─────────────────┐
│ Cloudflare Edge │
│ (WAF, DDoS, │
│ TLS termination)│
└────────┬────────┘
│ Tunnel (outbound only)
▼
┌─────────────────┐
│ cloudflared │
│ (tunnel agent) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Traefik │
│ (reverse proxy, │
│ middleware, │
│ TLS internal) │
└────────┬────────┘
│ Forward Auth
▼
┌─────────────────┐
│ Authentik │
│ (identity, │
│ SSO, policies) │
└────────┬────────┘
│ Authenticated
▼
┌─────────────────┐
│ Target Service │
│ (Grafana, etc.) │
└─────────────────┘
Component Responsibilities
| Component | Responsibility | Trust Level |
|---|---|---|
| Cloudflare Edge | DDoS protection, WAF, TLS termination, bot mitigation | External boundary |
| cloudflared | Outbound tunnel transport, route mapping | Transport |
| Traefik | Request routing, middleware chain, internal TLS | Internal routing |
| Authentik | Authentication, authorization, SSO, group policies | Identity |
| Target Service | Application logic | Application |
Trust Boundaries
Boundary 1: Internet to Cloudflare Edge
This is the outermost trust boundary. All traffic from the internet passes through Cloudflare before reaching any infrastructure component.
Threats mitigated:
- DDoS attacks (Cloudflare absorbs volumetric attacks)
- Known exploit patterns (WAF rules)
- Bot traffic (challenge pages, rate limiting)
- Direct IP scanning (no public IP exposed)
Residual risk: Cloudflare itself is a trust dependency. A compromise of Cloudflare's infrastructure could theoretically expose traffic content. Mitigated by the fact that Authentik adds its own authentication layer independent of Cloudflare.
Boundary 2: Cloudflare Tunnel to Traefik
The tunnel carries traffic from Cloudflare's edge to the internal Traefik instance. This is an outbound-only connection established by cloudflared.
Threats mitigated:
- Port scanning (no inbound ports open)
- Man-in-the-middle (tunnel uses authenticated, encrypted transport)
- Unauthorized tunnel creation (tunnel credentials are unique per installation)
Residual risk: If the tunnel credentials are compromised, an attacker could potentially establish a rogue tunnel. Mitigated by storing credentials as Docker secrets and restricting file permissions.
Boundary 3: Traefik to Authentik
Every request to a protected service passes through Traefik's forward-auth middleware, which calls Authentik to verify the user's session.
Threats mitigated:
- Unauthenticated access (every request is validated)
- Session hijacking (Authentik manages session tokens with configurable expiry)
- Privilege escalation (group-based policies restrict access per application)
Residual risk: A misconfigured Traefik router that omits the auth middleware would bypass Authentik entirely. Mitigated by using a default middleware chain and testing new routes in staging before production.
Boundary 4: Authentik to Target Service
After Authentik validates the user's identity and authorization, the request proceeds to the target service with identity headers injected by Authentik.
Threats mitigated:
- Unauthorized application access (policy-gated)
- Identity spoofing (headers are set by the trusted Authentik outpost, not the client)
Residual risk: If a service is accessible on the internal network without going through Traefik, the auth layer is bypassed. Mitigated by firewall rules that restrict inter-container communication to defined paths.
Deployment Topology
All components run as Docker containers on a Proxmox VM:
Proxmox VE Host
└── VM: docker-host (Ubuntu 24.04)
├── cloudflared (tunnel agent)
├── traefik (reverse proxy)
├── authentik-server (identity provider)
├── authentik-worker (background tasks)
├── postgresql (Authentik database)
├── redis (Authentik cache/sessions)
└── [application containers]
Network Isolation
Docker networks provide isolation between components:
proxy— Shared network for Traefik, cloudflared, and services that need external routingauthentik-internal— Isolated network for Authentik server, worker, PostgreSQL, and Redismonitoring— Separate network for observability tools (Grafana, Prometheus, Loki)
Services only join the networks they require. The PostgreSQL and Redis containers for Authentik are on the authentik-internal network only -- they are not reachable from the proxy network.
Security Posture
What is hardened
- No inbound ports on the host firewall (all access via tunnel)
- Docker containers run as non-root users where supported
- Authentik enforces MFA for admin accounts
- Traefik's dashboard is not exposed externally
- PostgreSQL listens only on the Docker internal network
- All secrets managed via Docker secrets, not environment variables
- TLS between Traefik and backend services where the service supports it
What is monitored
- Authentik login events (success and failure) forwarded to Wazuh
- Traefik access logs ingested by Loki
- Cloudflare analytics for edge-level traffic patterns
- Container health checks with alerting on failure
What is not yet hardened
- No network policy enforcement at the container level (planned: move to K3s with NetworkPolicies)
- Authentik backup is manual (planned: automated PostgreSQL dumps to encrypted off-site storage)
- No automated certificate rotation for internal TLS (planned: step-ca integration)
Alternatives Considered
VPN-only (WireGuard/Tailscale)
Rejected as the primary access method because it provides network-level access rather than application-level access. Still used for administrative SSH access to the hypervisor.
Nginx Proxy Manager
Rejected because it provides a GUI-first experience with limited automation capabilities. Traefik's Docker label integration and file-based configuration align better with infrastructure-as-code practices.
Authelia
Considered as an alternative to Authentik. Authelia is lighter weight but lacks Authentik's SCIM support, application management UI, and flexible policy engine. Authentik was chosen for its feature completeness at the cost of higher resource usage.
Direct Cloudflare Access (without Authentik)
Cloudflare Access provides its own identity-aware proxy. This was rejected because it creates a single-vendor dependency for both transport and identity. Running Authentik independently means the identity layer is portable and not tied to Cloudflare.
Consequences
Positive
- Zero inbound ports reduces the attack surface to near zero
- Adding new services is a configuration change, not an infrastructure change
- SSO reduces credential fatigue and enables centralized audit logging
- Multiple independent security layers (Cloudflare WAF, Authentik policies, service-level auth) provide defense in depth
Negative
- Complexity is higher than a simple VPN setup
- Cloudflare is a runtime dependency for external access
- Authentik consumes non-trivial resources (~500MB RAM for server + worker + DB + cache)
- Debugging authentication issues requires understanding three layers of middleware
Neutral
- Operational cost is zero (all components are open source, Cloudflare free tier is sufficient)
- The architecture is transferable to other environments (cloud VMs, Kubernetes) with minimal changes to the component configuration
Revision History
| Date | Change |
|---|---|
| 2025-11-20 | Initial architecture decision |
| 2025-12-15 | Added WireGuard as complementary admin access path |
| 2026-01-05 | Updated Cloudflare Access policy section, added monitoring details |