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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user