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