From 20acdb025442cc91426617c255ffa4116e43b669 Mon Sep 17 00:00:00 2001 From: Audiobuzz Date: Sun, 5 Jul 2026 00:52:19 +0000 Subject: [PATCH] fleet-manager: add behind-proxy admin mode (passkey over plaintext behind a TLS proxy) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decouples "passkey auth ON" from "the listener terminates TLS". A new FLEET_MGR_ADMIN_BEHIND_PROXY=true keeps WebAuthn/session auth fully on but serves plaintext, for running behind a trusted TLS-terminating ingress (Traefik now, a k8s Ingress later) that owns the LE cert. WebAuthn only needs RP_ORIGIN to match the browser's HTTPS origin, not our transport; session cookies stay Secure. The listener must be bound private (loopback/mgmt) so only the proxy reaches it — logged as a warning. Insecure (dev, no auth) and self-TLS (default) modes unchanged. --- serving/fleet-manager/main.go | 46 +++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/serving/fleet-manager/main.go b/serving/fleet-manager/main.go index dbbc647..fd71c24 100644 --- a/serving/fleet-manager/main.go +++ b/serving/fleet-manager/main.go @@ -26,10 +26,11 @@ import ( ) type config struct { - adminAddr string - adminInsecure bool - adminCert string - adminKey string + adminAddr string + adminInsecure bool + adminBehindProxy bool // passkey auth ON, but TLS terminated by a trusted upstream proxy + adminCert string + adminKey string agentAddr string agentCert string @@ -60,10 +61,11 @@ type config struct { func loadConfig() config { return config{ - adminAddr: env("FLEET_MGR_ADMIN_ADDR", "127.0.0.1:9500"), - adminInsecure: env("FLEET_MGR_ADMIN_INSECURE", "") == "true", - adminCert: env("FLEET_MGR_ADMIN_CERT", ""), - adminKey: env("FLEET_MGR_ADMIN_KEY", ""), + adminAddr: env("FLEET_MGR_ADMIN_ADDR", "127.0.0.1:9500"), + adminInsecure: env("FLEET_MGR_ADMIN_INSECURE", "") == "true", + adminBehindProxy: env("FLEET_MGR_ADMIN_BEHIND_PROXY", "") == "true", + adminCert: env("FLEET_MGR_ADMIN_CERT", ""), + adminKey: env("FLEET_MGR_ADMIN_KEY", ""), agentAddr: env("FLEET_MGR_AGENT_ADDR", "127.0.0.1:9600"), agentCert: env("FLEET_MGR_AGENT_CERT", ""), @@ -233,18 +235,30 @@ func main() { IdleTimeout: 60 * time.Second, } - if cfg.adminInsecure { + switch { + case cfg.adminInsecure: log.Printf("WARNING: admin plane INSECURE (plaintext, NO passkey auth) on %s — dev/lab only", cfg.adminAddr) log.Fatal(srv.ListenAndServe()) - } - tlsCfg, err := adminTLS(cfg) - if err != nil { - log.Fatalf("admin TLS: %v", err) + case cfg.adminBehindProxy: + // Passkey auth ON, but a trusted upstream proxy (Traefik / k8s Ingress) + // terminates TLS and forwards plaintext. WebAuthn only needs RP_ORIGIN to + // match the browser's HTTPS origin, not our own transport; session cookies + // stay Secure (the browser sees HTTPS). The listener MUST be reachable ONLY + // via the proxy — bind it private (loopback / a private net), never a + // routable interface. + log.Printf("fleet-manager admin (passkey, behind TLS proxy) plaintext listening on %s (rp=%s) — ensure only the proxy can reach this address", cfg.adminAddr, cfg.rpID) + log.Fatal(srv.ListenAndServe()) + + default: + tlsCfg, err := adminTLS(cfg) + if err != nil { + log.Fatalf("admin TLS: %v", err) + } + srv.TLSConfig = tlsCfg + log.Printf("fleet-manager admin (HTTPS + passkey) listening on %s (rp=%s)", cfg.adminAddr, cfg.rpID) + log.Fatal(srv.ListenAndServeTLS("", "")) // certs are in TLSConfig } - srv.TLSConfig = tlsCfg - log.Printf("fleet-manager admin (HTTPS + passkey) listening on %s (rp=%s)", cfg.adminAddr, cfg.rpID) - log.Fatal(srv.ListenAndServeTLS("", "")) // certs are in TLSConfig } // adminTLS builds the admin plane's SERVER-AUTH TLS. Browser identity is a passkey