Skip to content

Architecture Diagram

Phase 1 — ExzenTCG Homelab


Network Topology

flowchart TB
    subgraph Internet
        USER["User Browser"]
        CF["Cloudflare Edge<br/>TLS termination"]
        CFZT["Cloudflare Zero Trust<br/>Access Policy"]
        WEBHOOK["External Webhooks<br/>(Stripe, Telegram, etc.)"]
    end

    subgraph Proxmox["Proxmox VE (192.168.0.200)"]
        subgraph CT101["CT 101 — edge-gateway (192.168.0.51)"]
            CFLRD["cloudflared<br/>:7844 → CF edge"]
            NPM["Nginx Proxy Manager<br/>:80 (localhost only)<br/>:81 (admin, LAN)"]
        end

        subgraph CT102["CT 102 — n8n-app (192.168.0.52)"]
            N8N["n8n v1.80.0<br/>:5678 (LAN only)"]
        end

        PVFW["Proxmox Firewall<br/>Per-CT rules + IP sets"]
    end

    subgraph Admin["Admin Access"]
        DESKTOP["Admin Desktop<br/>192.168.0.16"]
        LAPTOP["Admin Laptop<br/>via Tailscale"]
        TS["Tailscale Mesh<br/>100.89.70.63"]
    end

    ROUTER["Home Router<br/>192.168.0.1<br/>DNS / DHCP / NAT"]

    %% Traffic flow
    USER -->|"HTTPS :443"| CF
    WEBHOOK -->|"HTTPS :443"| CF
    CF -->|"Access check"| CFZT
    CFZT -->|"Tunnel (TCP :7844)"| CFLRD
    CFLRD -->|"HTTP localhost:80"| NPM
    NPM -->|"HTTP :5678"| N8N

    %% Admin access
    DESKTOP -->|":81 NPM admin"| NPM
    DESKTOP -->|":22 SSH"| N8N
    DESKTOP -->|":8006 WebUI"| Proxmox
    LAPTOP -->|"WireGuard"| TS
    TS -->|":8006 WebUI"| Proxmox

    %% DNS
    CT101 -->|"UDP :53"| ROUTER
    CT102 -->|"UDP :53"| ROUTER

    %% Firewall
    PVFW -.->|"enforces"| CT101
    PVFW -.->|"enforces"| CT102

    style CF fill:#f38020,color:#fff
    style CFZT fill:#f38020,color:#fff
    style CT101 fill:#1a3a4a,color:#fff
    style CT102 fill:#1a3a4a,color:#fff
    style N8N fill:#ea4b71,color:#fff
    style TS fill:#4c8bf5,color:#fff

Traffic Flow (request lifecycle)

1. User visits https://n8n.exzentcg.com
2. DNS resolves → CNAME → cfargotunnel.com → Cloudflare edge IP
3. Cloudflare terminates TLS
4. Cloudflare Zero Trust checks Access policy
   → /webhook/* paths: bypass (no auth)
   → all other paths: require email login (Google OAuth or one-time PIN)
5. Authenticated request enters Cloudflare Tunnel
6. cloudflared (CT 101, network_mode: host) receives on TCP 7844
7. cloudflared forwards to http://localhost:80 (NPM)
8. NPM matches Host header "n8n.exzentcg.com"
9. NPM proxies to http://192.168.0.52:5678 (n8n-app)
10. n8n processes request and responds
11. Response travels back through the same chain

Firewall Boundaries

                    ┌─────────────────────────────┐
                    │      INTERNET (any)          │
                    └─────────┬───────────────────-┘
                              │
                    ┌─────────▼───────────────────-┐
                    │    Cloudflare Edge + Tunnel   │
                    │    (TLS + Access + WAF)       │
                    └─────────┬───────────────────-┘
                              │ TCP 7844
              ┌───────────────▼───────────────────-┐
              │  CT 101 — edge-gateway             │
              │                                    │
              │  IN:  localhost:80,443 only         │
              │       +admin_desktop:81             │
              │       everything else: DROP         │
              │                                    │
              │  OUT: router:53 (DNS)              │
              │       192.168.0.52:5678 (n8n)      │
              │       DROP +lan_subnet ◄── key!    │
              │       7844 (tunnel)                │
              │       443, 80 (APIs)               │
              └───────────────┬───────────────────-┘
                              │ TCP 5678
              ┌───────────────▼───────────────────-┐
              │  CT 102 — n8n-app                  │
              │                                    │
              │  IN:  +edge_gw:5678 only           │
              │       +admin_desktop:22 (SSH)      │
              │       everything else: DROP         │
              │                                    │
              │  OUT: router:53 (DNS)              │
              │       DROP +lan_subnet ◄── key!    │
              │       443 only (HTTPS APIs)        │
              └───────────────────────────────────-┘

Important

The DROP +lan_subnet rule before ACCEPT :443 is the critical lateral movement prevention. It ensures that even if a container is compromised, it cannot reach other LAN devices (desktop, router admin, other containers) — even on port 443.


IP Address Map

IP Role Access
192.168.0.1 Home router DNS/DHCP/NAT
192.168.0.16 Admin desktop In admin_desktop IP set
192.168.0.51 edge-gateway (CT 101) NPM + cloudflared
192.168.0.52 n8n-app (CT 102) n8n only
192.168.0.200 Proxmox host Hypervisor
100.89.70.63 Proxmox via Tailscale Remote admin