CallMeTechie
EN Anmelden
Home Produkte Blog Über mich Kontakt

Routes & HTTPS

v1.x · Updated vor 1 Monat

Überblick

GateControl spricht nicht selbst HTTP nach aussen — die öffentliche Terminierung übernimmt Caddy. Der Node-Prozess baut aus seiner Datenbank eine Caddy-JSON-Konfiguration und lädt sie über die Caddy-Admin-API neu. Es gibt zwei Route-Typen (http, l4) und zwei Ziel-Arten (peer, gateway). Diese beiden Dimensionen ergeben vier Kombinationen, die im Config-Builder unterschiedlich gerendert werden.

Die zentrale Erkenntnis, ohne die vieles nicht verständlich ist: Alle HTTP-Routen landen in einem einzigen Caddy-Server srv0 mit listen: [':443', ':80']. Das pro-Route-listen-Feld wird bei der Assembly verworfen. Caddys Automatic-HTTPS stellt Zertifikate für jede Domain in srv0 aus, unabhängig vom https_enabled-Flag der Route. Dieses Flag wirkt nur noch als Hinweis für Redirects.

Routing-Kern lebt in src/services/caddyConfig.js (~900 Zeilen) und src/services/l4.js. Der CRUD-Teil der Routes-Tabelle sitzt in src/services/routes.js und delegiert die Caddy-Seite komplett an caddyConfig.syncToCaddy.

Architektur

   Client ──HTTPS──▶ Caddy (srv0, :443/:80)
                      │
                      ├──▶ reverse_proxy → Peer-IP:Port        (target_kind=peer)
                      │
                      ├──▶ reverse_proxy → Gateway-IP:8080     (target_kind=gateway)
                      │     mit Headers X-Gateway-Target=…
                      │                    │
                      │                    ▼
                      │           Gateway-HTTP-Proxy → LAN-Host:Port
                      │
                      └──▶ apps.layer4 (eigene L4-Server pro Port)
                                │
                                ├──▶ proxy → Peer-IP:Port
                                └──▶ proxy → Gateway-IP:ListenPort
                                             │
                                             ▼
                                     Gateway-TcpProxy → LAN-Host:Port

Route-Typen und Ziel-Arten

route_type target_kind Upstream in Caddy Backend
http peer peer.allowed_ips:route.target_port Peer-WG-IP, direkter TCP
http gateway gateway.allowed_ips:8080 Gateway-Container über WG-Tunnel, HTTP-Proxy
l4 peer peer.allowed_ips:route.target_port Peer-WG-IP, L4-Proxy
l4 gateway gateway.allowed_ips:l4_listen_port Gateway-Container, eigene L4-Listener

Die Spalte target_kind in routes steuert den Upstream-Build; target_peer_id zeigt bei Gateway-Routen auf den Gateway-Peer, target_lan_host + target_lan_port liegen als Instruktion für den Gateway im Header.

Datenmodell (Auszug)

routes (
  id, domain, route_type,           -- 'http' | 'l4'
  enabled, https_enabled,

  -- Peer-Routen
  peer_id,                          -- FK peers.id, Upstream-Peer
  target_ip, target_port,

  -- Gateway-Routen
  target_kind,                      -- 'peer' | 'gateway'
  target_peer_id,                   -- FK peers.id, Gateway-Peer
  target_lan_host, target_lan_port, -- LAN-Adresse HINTER dem Gateway

  -- HTTP-Features
  basic_auth_enabled, basic_auth_user, basic_auth_password_hash,
  acl_enabled, ip_filter_enabled,
  rate_limit_enabled, rate_limit_requests, rate_limit_window,
  compress_enabled, retry_enabled, retry_count,
  circuit_breaker_enabled, circuit_breaker_status,
  bot_blocker_enabled, bot_blocker_mode, bot_blocker_config,
  backend_https,                    -- nur peer-target
  custom_headers, mirror_enabled, mirror_targets,
  sticky_enabled, backends,          -- Load-Balancing

  -- L4-Felder
  l4_protocol, l4_listen_port, l4_tls_mode,

  -- WoL (hauptsächlich Gateway-Kontext)
  wol_enabled, wol_mac
)

HTTP-Routing-Flow

buildCaddyConfig() (in caddyConfig.js) iteriert alle aktivierten Routen und baut pro Domain ein Caddy-Route-Objekt. Schritte pro HTTP-Route:

  1. Upstream bestimmen

    • target_kind='gateway' → Upstream ist gatewayPeerIp:8080.
    • Mehrere backends → Array für Load-Balancing.
    • Sonst Peer-IP oder target_ip + target_port.
  2. Gateway-Offline-Fallback — wenn der Gateway offline ist (Flag gateway_offline), wird statt reverse_proxy ein static_response mit 502 + Maintenance-HTML gerendert (Template gateway-offline.njk). Siehe patchGatewayRouteHandlers für Runtime-Patches via Admin-API.

  3. Gateway-Headers — bei target_kind='gateway' werden in die reverse_proxy-Headers gesetzt:

    • X-Gateway-Target: target_lan_host:target_lan_port
    • X-Gateway-Target-Domain: route.domain

    Der Gateway-Container interpretiert diese Header und forwardet an die LAN-Adresse. Siehe home-gateway.md.

  4. Backend-HTTPSbackend_https=true aktiviert insecure_skip_verify als Transport. Nur bei target_kind='peer', nie bei Gateway-Routen: zum Gateway-Port 8080 geht immer HTTP, TLS würde einen 502 auslösen.

  5. Handler-Chain aufbauen (in Reihenfolge):

    bot_blocker (defender) → trace (debug) → custom_headers (request)
    → rate_limit → mirror → compress → reverse_proxy
    
  6. Circuit-Breaker — wenn status='open', wird die gesamte Chain durch einen static_response 503 ersetzt (mit Retry-After-Header).

  7. Peer-ACLmatch.remote_ip.ranges aus der route_peer_acl-Tabelle. Nur Peers in der Allow-List dürfen durch.

  8. Route-Auth / Forward-Auth — siehe unten.

  9. Routen-Config einhängen unter caddyRoutes[route.domain].

Route-Auth (Forward-Auth)

Route-Auth fügt VOR der Haupt-Chain eine zweite Route ein:

caddyRoutes[domain] = {
  routes: [
    routeAuthProxy,    // match /route-auth/* → Node-Process (Login-UI + /verify)
    routeConfig        // match anything else → forward_auth-Subrequest → upstream
  ]
}

Der forwardAuthSubrequest ruft 127.0.0.1:3000/route-auth/verify mit den Request-Metadaten als Header auf. Bei 2xx wird die Request fortgesetzt; bei 4xx/5xx gibt Caddy einen 302 auf /route-auth/login?route=…&redirect=… zurück. Nach dem Login setzt der Node-Server einen Session-Cookie, den die nächste Subrequest wieder akzeptiert.

Nur /route-auth/* wird intercepted — frühere Versionen haben auch /css, /js, /fonts durch das Login-Asset-Bundle beansprucht; das hat mit Upstream-Panels (Synology, Speedport, TR-064) kollidiert. Siehe Kommentar in caddyConfig.js ab Zeile ~420.

Load-Balancing

backends ist ein JSON-Array mit {peer_id, port, weight}. Beim Build werden Peer-IPs aufgelöst und disabled Peers gefiltert.

Modus Policy
sticky_enabled=true cookie + konfigurierbarer Cookie-Name + TTL
Alle Weights gleich round_robin
Unterschiedliche Weights weighted_round_robin

retry_enabled erhöht zusätzlich load_balancing.retries = retry_count.

Server-Assembly

Nach dem Pro-Route-Build werden alle Domains in einen einzigen Server srv0 zusammengeführt:

caddyConfig.apps.http.servers.srv0 = {
  listen: [':443', ':80'],
  routes: serverRoutes,             // [{match: {host: [domain]}, handle: [...]}]
  protocols: ['h1', 'h2'],
}

Einzelne Routen werden per {match: {host: [domain]}, handle: [...], terminal: true} eingehängt. Compound-Routen (Route-Auth) werden in ein subroute-Handler gewickelt, damit die interne Route-Liste erhalten bleibt.

Die Management-UI-Route (aus GC_BASE_URL) wird ebenfalls automatisch eingefügt mit 127.0.0.1:{config.app.port} als Upstream — der Node-Prozess bedient also seine eigene Admin-Oberfläche über Caddy.

Konsequenz für TLS

Da srv0 auf :443 lauscht und alle Host-Matcher darin enthalten sind, provisioniert Caddys Automatic-HTTPS für jede Domain ein Zertifikat, unabhängig vom https_enabled-Flag der einzelnen Route. Das Flag dient im Client-UI nur noch als Hinweis, ob der Admin einen TLS-Endpunkt erwartet — es steuert keinen Listener.

L4-Routing

L4-Routes liegen unter apps.layer4.servers.<name> (Caddy-L4-Plugin). buildL4Servers in src/services/l4.js gruppiert Routen nach (protocol, listen_port, tls_mode) und erzeugt pro Gruppe einen Server:

servers['l4-tls-8443'] = {
  listen: ['tcp/:8443'],
  routes: [
    { match: [{tls: {sni: ['app.example.com']}}], handle: [{handler: 'tls'}, proxyHandler] },
    ...
  ]
}

TLS-Modi

l4_tls_mode Verhalten
none Rohes TCP/UDP durchschleifen, kein match, kein TLS
passthrough Match via SNI, aber Caddy terminiert NICHT — Zertifikat liegt am Backend
terminate Match via SNI, Caddy terminiert TLS (Zertifikat wird provisioniert), Backend sieht Klartext

Port-Konflikte

validatePortConflicts() prüft:

  • Reservierte Ports (via isPortBlocked) — z.B. 22, 53, 443, 51820.
  • Doppelte TLS-None-Routen auf demselben Port.
  • Überlappende Port-Ranges (8000-8100 vs 8050-8150).

Konflikte werfen und brechen den Config-Push ab.

Gateway-L4

Bei target_kind='gateway' + route_type='l4' wird der Upstream auf gateway-peer-ip:l4_listen_port gesetzt. Der Gateway-Container lauscht selbst auf diesem Port (über seinen TcpProxyManager) und forwardet zum LAN-Host. Caddy auf dem Server reicht nur die TCP-Verbindung zum Gateway weiter.

Sync-Flow und Self-Healing

syncToCaddy() in caddyConfig.js:

  1. Previous-Config via GET /config/ holen (für Rollback).
  2. Neue Config bauen mit buildCaddyConfig().
  3. runtime.json schreiben nach /data/caddy/runtime.json (atomic rename). Diese Datei ist Source-of-Truth beim Caddy-Boot durch entrypoint.sh.
  4. Live-Reload via POST /load.
  5. Verify via GET /config/ — Config muss angekommen sein.
  6. TLS-Canary_verifyLocalTls(managementHost) öffnet eine TLS-Verbindung zu 127.0.0.1:443 mit der Management-UI-Domain als SNI. Wenn das scheitert, obwohl /load ok war, ist Caddys Listener-State korrupt. Dann:
  7. Caddy neustart via pkill -TERM -x caddy. Supervisord startet Caddy neu aus runtime.json.
  8. Bei Verify-Fehler ohne TLS-Issue: Rollback auf previousConfig.

Der TLS-Canary adressiert einen konkreten Bug: /load kann in bestimmten Situationen (Listener-Rebind, neuer Cert) erfolgreich antworten, während der Server trotzdem jeden TLS-Handshake mit internal error abwürgt. Supervisord-Restart resolved den Zustand zuverlässig.

Partial-Patches für Gateway-State

patchGatewayRouteHandlers({peerId, offline, ...}) wird vom gatewayHealth-Statemachine aufgerufen, wenn ein Gateway von online nach offline (oder zurück) wechselt. Jede Route bekommt ein @id-Marker (gc_route_<id>) bei der Assembly, sodass PATCH /id/gc_route_<id>/handle den Handler im Live-Config austauschen kann — ohne kompletten /load-Round-Trip.

  • offline=true → Handler auf static_response 502 + Maintenance-HTML.
  • offline=false/revert (Handler zurück auf Original).

Siehe auch

Quelldateien

  • src/services/caddyConfig.js — Config-Builder, Sync, Self-Heal, Partial-Patches.
  • src/services/l4.jsbuildL4Servers, buildL4Route, validatePortConflicts.
  • src/services/routes.js — CRUD, delegiert Sync an caddyConfig.syncToCaddy.
  • src/services/routeAuth.js — Route-Auth-Lookup-Helper für den Forward-Auth-Handler.
  • templates/gateway-offline.njk — Maintenance-Page für offline Gateways.
  • entrypoint.sh — Caddy-Boot aus /data/caddy/runtime.json.

Cookie Settings

Wir verwenden Cookies, um Ihre Erfahrung zu verbessern. Essentielle Cookies sind immer aktiv.

Datenschutzerklärung
ESC
↑↓ navigate open esc close