Feat v0.6.7 native mTLS (#42)
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-frontend (pull_request) Successful in 6s
CI/CD / test-go-pg (pull_request) Successful in 2m44s
CI/CD / test-sqlite (pull_request) Successful in 2m50s
CI/CD / build-and-deploy (pull_request) Successful in 1m4s
All checks were successful
CI/CD / detect-changes (pull_request) Successful in 4s
CI/CD / test-frontend (pull_request) Successful in 6s
CI/CD / test-go-pg (pull_request) Successful in 2m44s
CI/CD / test-sqlite (pull_request) Successful in 2m50s
CI/CD / build-and-deploy (pull_request) Successful in 1m4s
End-to-end mutual TLS without a reverse proxy. Go binary terminates TLS itself via ListenAndServeTLS. TLS_MODE config (none/server/mtls) is independent of AUTH_MODE. - MTLSNativeProvider reads PeerCertificates directly (no header trust) - Shared helpers extracted to mtls_helpers.go (ParseDN, FingerprintCert) - MTLSProvider renamed to MTLSProxyProvider for clarity - BuildPeerTLSConfig for future node-to-node mTLS - switchboard-ca.sh: CA init, issue-node, issue-user (ECDSA P-256) - 12 new tests (unit + TLS integration) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,11 @@ func main() {
|
||||
startTime := time.Now()
|
||||
cfg := config.Load()
|
||||
|
||||
// Validate TLS_MODE early
|
||||
if err := config.ValidateTLSMode(cfg.TLSMode); err != nil {
|
||||
log.Fatalf("❌ %v", err)
|
||||
}
|
||||
|
||||
// Structured logging — must be first so all subsequent
|
||||
// log output goes through slog.
|
||||
logging.Init(cfg.LogFormat, cfg.LogLevel)
|
||||
@@ -349,13 +354,24 @@ func main() {
|
||||
case auth.ModeBuiltin:
|
||||
authProvider = auth.NewBuiltinProvider()
|
||||
case auth.ModeMTLS:
|
||||
authProvider = auth.NewMTLSProvider(auth.MTLSConfig{
|
||||
HeaderDN: cfg.MTLSHeaderDN,
|
||||
HeaderVerify: cfg.MTLSHeaderVerify,
|
||||
HeaderFingerprint: cfg.MTLSHeaderFingerprint,
|
||||
AutoActivate: cfg.MTLSAutoActivate,
|
||||
DefaultTeam: cfg.MTLSDefaultTeam,
|
||||
})
|
||||
if cfg.TLSMode == config.TLSModeMTLS {
|
||||
// Native mTLS: Go binary terminates TLS, reads peer certs directly
|
||||
authProvider = auth.NewMTLSNativeProvider(auth.MTLSNativeConfig{
|
||||
AutoActivate: cfg.MTLSAutoActivate,
|
||||
DefaultTeam: cfg.MTLSDefaultTeam,
|
||||
})
|
||||
log.Printf(" Auth: native mTLS (TLS_MODE=mtls)")
|
||||
} else {
|
||||
// Proxy mTLS: reverse proxy terminates TLS, injects headers
|
||||
authProvider = auth.NewMTLSProxyProvider(auth.MTLSProxyConfig{
|
||||
HeaderDN: cfg.MTLSHeaderDN,
|
||||
HeaderVerify: cfg.MTLSHeaderVerify,
|
||||
HeaderFingerprint: cfg.MTLSHeaderFingerprint,
|
||||
AutoActivate: cfg.MTLSAutoActivate,
|
||||
DefaultTeam: cfg.MTLSDefaultTeam,
|
||||
})
|
||||
log.Printf(" Auth: proxy mTLS (headers)")
|
||||
}
|
||||
case auth.ModeOIDC:
|
||||
var err error
|
||||
authProvider, err = auth.NewOIDCProvider(auth.OIDCConfig{
|
||||
@@ -893,8 +909,26 @@ func main() {
|
||||
}
|
||||
log.Printf(" EventBus: ready, WebSocket on %s/ws", cfg.BasePath)
|
||||
log.Printf(" Pages: template engine active (%d surfaces registered)", len(pageEngine.Surfaces()))
|
||||
if err := r.Run(":" + cfg.Port); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
|
||||
addr := ":" + cfg.Port
|
||||
if cfg.TLSMode == config.TLSModeNone || cfg.TLSMode == "" {
|
||||
if err := r.Run(addr); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
} else {
|
||||
tlsCfg, err := config.BuildTLSConfig(cfg)
|
||||
if err != nil {
|
||||
log.Fatalf("❌ TLS config error: %v", err)
|
||||
}
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: r,
|
||||
TLSConfig: tlsCfg,
|
||||
}
|
||||
log.Printf(" TLS: mode=%s", cfg.TLSMode)
|
||||
if err := srv.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey); err != nil {
|
||||
log.Fatalf("Failed to start TLS server: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user