From fc39e05bebc17960580581d4c4b10d6601503ed6 Mon Sep 17 00:00:00 2001 From: m3ta-chiron Date: Mon, 11 May 2026 15:53:04 +0200 Subject: [PATCH] feat: Hermes Dashboard via m3-atlas Traefik with TLS + Netbird-only access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit m3-hermes: - Add --insecure flag (required for 0.0.0.0 bind, safe behind Netbird firewall) - Update comments to document the Traefik proxy flow m3-atlas Traefik: - New service: hermes-dashboard → http://100.81.231.152:9119 (Netbird) - New router: dash.m3ta.dev with GoDaddy TLS cert - New middleware: netbird-only (IP whitelist 100.64.0.0/16) Flow: Browser → dash.m3ta.dev (TLS) → Traefik → Netbird → m3-hermes:9119 --- hosts/m3-atlas/services/traefik.nix | 22 +++++++++++++++++++ hosts/m3-hermes/services/hermes-dashboard.nix | 13 ++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/hosts/m3-atlas/services/traefik.nix b/hosts/m3-atlas/services/traefik.nix index acb312d..83e4a1b 100644 --- a/hosts/m3-atlas/services/traefik.nix +++ b/hosts/m3-atlas/services/traefik.nix @@ -43,6 +43,12 @@ dynamicConfigOptions = { http = { services = { + # ── Hermes Dashboard (m3-hermes over Netbird) ──────────────── + hermes-dashboard = { + loadBalancer.servers = [ + {url = "http://100.81.231.152:9119";} + ]; + }; dummy = { loadBalancer.servers = [ {url = "http://192.168.0.1";} # Diese URL wird nie verwendet @@ -50,6 +56,12 @@ }; }; middlewares = { + # Hermes Dashboard — Netbird mesh only + netbird-only = { + ipWhiteList = { + sourceRange = ["100.64.0.0/16"]; + }; + }; domain-redirect = { redirectRegex = { regex = "^https://www\\.m3tam3re\\.com(.*)"; @@ -79,6 +91,16 @@ }; routers = { + # ── Hermes Dashboard — Netbird mesh only ───────────────────── + hermes-dashboard = { + rule = "Host(`dash.m3ta.dev`)"; + service = "hermes-dashboard"; + middlewares = ["netbird-only"]; + entrypoints = ["websecure"]; + tls = { + certResolver = "godaddy"; + }; + }; api = { rule = "Host(`r.m3tam3re.com`)"; service = "api@internal"; diff --git a/hosts/m3-hermes/services/hermes-dashboard.nix b/hosts/m3-hermes/services/hermes-dashboard.nix index 20b93de..931e03a 100644 --- a/hosts/m3-hermes/services/hermes-dashboard.nix +++ b/hosts/m3-hermes/services/hermes-dashboard.nix @@ -4,7 +4,8 @@ inputs, ... }: let - # Netbird mesh VPN range — dashboard only accessible from mesh peers + # Netbird mesh VPN range — dashboard only accessible from mesh peers. + # m3-atlas Traefik proxies to this port over Netbird. netbirdRange = "100.64.0.0/16"; # Reference the hermes-agent package from the running service config @@ -12,7 +13,11 @@ in { # ── Hermes Dashboard systemd service ─────────────────────────────────── # Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc. - # Binds to 0.0.0.0:9119 but firewall restricts to Netbird mesh only. + # + # Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :9119 + # + # --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise). + # Safe because firewall restricts port 9119 to Netbird mesh only. systemd.services.hermes-dashboard = { description = "Hermes Agent Web Dashboard"; after = ["network.target" "hermes-agent.service"]; @@ -24,7 +29,7 @@ in { User = "hermes"; Group = "hermes"; - ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open"; + ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open --insecure"; # Environment matching the hermes-agent service Environment = [ @@ -48,8 +53,6 @@ in { # ── Firewall: Dashboard only from Netbird mesh ───────────────────────── networking.firewall = { - # Use extraCommands for source-IP-restricted port (NixOS firewall - # allowedTCPPorts is all-or-nothing per port). extraCommands = '' # Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept -- 2.54.0