fleet-manager: add behind-proxy admin mode (passkey over plaintext behind a TLS proxy)

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.
This commit is contained in:
Audiobuzz
2026-07-05 00:52:19 +00:00
parent 3140d51d95
commit 20acdb0254
+16 -2
View File
@@ -28,6 +28,7 @@ import (
type config struct {
adminAddr string
adminInsecure bool
adminBehindProxy bool // passkey auth ON, but TLS terminated by a trusted upstream proxy
adminCert string
adminKey string
@@ -62,6 +63,7 @@ func loadConfig() config {
return config{
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", ""),
@@ -233,11 +235,22 @@ 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())
}
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)
@@ -246,6 +259,7 @@ func main() {
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
// (WebAuthn), not a client cert, so there is no ClientCAs/ClientAuth here — just