Network Flow

How traffic flows from the public internet through Bifrost VPS into the Talos cluster — and how LAN clients bypass the VPS entirely.

Overview

Traffic takes one of three paths depending on where the client is and which service they're accessing:

PathClientRoute
Public → ProtectedInternet browserCloudflare → Bifrost Traefik → Authentik ForwardAuth → WireGuard → Cilium Gateway → Pod
Public VPS-nativeInternet browserCloudflare → Bifrost Traefik → container on VPS (Authentik, NetBird)
LAN directHome network deviceDNS → Cilium L2 LB → Gateway API → Pod
VPN remoteNetBird client (laptop/phone)WireGuard mesh → routing peer → cluster subnet

DNS Split

The split happens at DNS. Specific hostnames point to Bifrost (Hetzner VPS); everything else resolves to the Cilium L2 LoadBalancer IP on the home network.

HostnameDNS A recordWhere traffic lands
auth.madhan.app178.156.199.250 (Hetzner)Bifrost — Authentik container
netbird.madhan.app178.156.199.250 (Hetzner)Bifrost — NetBird dashboard
proxy.madhan.app178.156.199.250 (Hetzner)Bifrost — NetBird reverse proxy
grafana.madhan.app178.156.199.250 (Hetzner)Bifrost → WireGuard → Cluster
harbor.madhan.app178.156.199.250 (Hetzner)Bifrost → WireGuard → Cluster
*.madhan.app (all others)192.168.1.220 (LAN)Cilium Gateway — LAN only

Opting in a service to internet exposure: edit the publicServices slice in core/cloud/cloudflare.go and run just core cloudflare up. Traefik routes are auto-generated by generateTraefikPublicServices() in hetzner.go.


Full Traffic Diagram

 flowchart TB
    subgraph INET["Internet"]
        BROWSER["Browser / Client"]
        CF["Cloudflare DNS<br/>auth · netbird · grafana · harbor"]
    end

    subgraph BIF["Bifrost VPS · 178.156.199.250"]
        TR["Traefik :443<br/>TLS termination<br/>ForwardAuth middleware"]
        AUTH["Authentik<br/>SSO · GitHub OAuth<br/>ForwardAuth provider"]
        NBS["NetBird Server<br/>management + signal + relay + STUN"]
        NBD["NetBird Dashboard"]
    end

    subgraph WG["WireGuard Mesh"]
        NBP["NetBird routing peer<br/>K8s pod · 192.168.1.0/24"]
        NBLAP["NetBird client<br/>laptop / phone"]
    end

    subgraph CLUSTER["Talos Cluster · 192.168.1.0/24"]
        GW["Cilium Gateway API<br/>192.168.1.220"]
        POD["Service Pods<br/>grafana · harbor · etc."]
    end

    subgraph LAN["Home LAN"]
        LANCLI["LAN browser<br/>DNS → 192.168.1.220"]
    end

    BROWSER -->|"DNS lookup<br/>grafana.madhan.app"| CF
    CF -->|"178.156.199.250"| TR
    TR -->|"ForwardAuth check"| AUTH
    AUTH -->|"401 → redirect to login<br/>200 → allow request"| TR
    TR -->|"authenticated request<br/>via WireGuard"| GW
    GW --> POD

    BROWSER -->|"auth.madhan.app"| CF
    CF -->|direct| AUTH
    BROWSER -->|"netbird.madhan.app"| CF
    CF -->|direct| NBD
    NBS <-->|"WireGuard<br/>tunnel"| NBP
    NBS <-->|"WireGuard<br/>tunnel"| NBLAP
    NBLAP -->|"192.168.1.0/24<br/>via routing peer"| NBP
    NBP --> GW

    LANCLI -->|"DNS → 192.168.1.220<br/>no VPS hop"| GW
    GW --> POD

Public Request Step-by-Step

When a user opens https://grafana.madhan.app from the internet:

  1. DNS resolves grafana.madhan.app178.156.199.250 (Hetzner VPS)
  2. Traefik receives the HTTPS request, terminates TLS (wildcard cert from Cloudflare ACME)
  3. ForwardAuth middleware sends a sub-request to Authentik: Is this session authenticated?
  4. Authentik returns:
    • 401 — user is redirected to https://auth.madhan.app to log in (GitHub OAuth)
    • 200 — request is forwarded
  5. Traefik proxies the authenticated request through the WireGuard mesh to the Cilium Gateway (192.168.1.220)
  6. Cilium Gateway API routes to the Grafana pod via HTTPRoute

LAN Direct Access

LAN clients resolve *.madhan.app (except the explicitly listed public ones) to 192.168.1.220 — the Cilium L2 LoadBalancer IP. Traffic never leaves the home network.

Bypassing SSO on LAN: For grafana.madhan.app or harbor.madhan.app, you can override DNS locally to hit the cluster directly and skip the Authentik redirect:

# /etc/hosts — bypasses Hetzner + ForwardAuth
192.168.1.220  grafana.madhan.app harbor.madhan.app

VPN Remote Access

From anywhere in the world, a connected NetBird client (laptop or phone) can reach cluster services directly:

  1. NetBird client connects to the WireGuard mesh via netbird.madhan.app:443 (Traefik → NetBird server)
  2. Routing peer (K8s pod) advertises 192.168.1.0/24 into the mesh
  3. Cluster IPs (192.168.1.220–230) are routable from the client — no browser, no SSO redirect

This is how the devcontainer connects to the cluster remotely.


Traefik Routing in Detail

Traefik on Bifrost uses the file provider only (no Docker provider). Routes are defined in core/cloud/bifrost/traefik/dynamic/:

FileContents
services.ymlStatic routes: Authentik, NetBird GRPC, NetBird REST, NetBird dashboard, TCP wildcard for *.proxy.madhan.app
public-services.ymlAuto-generated by hetzner.go — public homelab routes with ForwardAuth

public-services.yml is gitignored and regenerated on every just core hetzner up. To expose a new service, add its name to publicServices in core/cloud/cloudflare.go.