@@ -727,6 +727,96 @@ spiffe: {}
727727 }
728728}
729729
730+ // --- TLS bridge config ---
731+
732+ // The tls_bridge block decodes into TLSBridgeConfig and Load surfaces it.
733+ func TestConfig_TLSBridgeBlockDecodes (t * testing.T ) {
734+ y := []byte ("mode: proxy-sidecar\n " +
735+ "tls_bridge:\n " +
736+ " mode: enabled\n " +
737+ " ca_dir: /etc/authbridge/tls-bridge-ca\n " +
738+ " passthrough_hosts: [\" pinned.example.com\" ]\n " +
739+ " ports: [443, 9443]\n " )
740+ p := filepath .Join (t .TempDir (), "cfg.yaml" )
741+ if err := os .WriteFile (p , y , 0o600 ); err != nil {
742+ t .Fatal (err )
743+ }
744+ cfg , err := Load (p )
745+ if err != nil {
746+ t .Fatalf ("load: %v" , err )
747+ }
748+ if cfg .TLSBridge == nil || cfg .TLSBridge .Mode != "enabled" || cfg .TLSBridge .CADir != "/etc/authbridge/tls-bridge-ca" {
749+ t .Fatalf ("tls_bridge block did not decode: %+v" , cfg .TLSBridge )
750+ }
751+ if len (cfg .TLSBridge .PassthroughHosts ) != 1 || len (cfg .TLSBridge .Ports ) != 2 {
752+ t .Fatalf ("passthrough_hosts/ports did not decode: %+v" , cfg .TLSBridge )
753+ }
754+ }
755+
756+ func TestTLSBridgeConfig_Validate (t * testing.T ) {
757+ cases := []struct {
758+ name string
759+ cfg TLSBridgeConfig
760+ wantErr bool
761+ }{
762+ {"valid empty" , TLSBridgeConfig {}, false },
763+ {"valid disabled" , TLSBridgeConfig {Mode : "disabled" }, false },
764+ {"valid enabled with ca_dir" , TLSBridgeConfig {Mode : "enabled" , CADir : "/etc/authbridge/tls-bridge-ca" }, false },
765+ {"bad mode" , TLSBridgeConfig {Mode : "external" }, true },
766+ {"enabled without ca_dir" , TLSBridgeConfig {Mode : "enabled" }, true },
767+ {"valid ports" , TLSBridgeConfig {Ports : []int {443 , 8443 , 9443 }}, false },
768+ {"port zero" , TLSBridgeConfig {Ports : []int {0 }}, true },
769+ {"port too high" , TLSBridgeConfig {Ports : []int {70000 }}, true },
770+ {"port negative" , TLSBridgeConfig {Ports : []int {- 1 }}, true },
771+ }
772+ for _ , tc := range cases {
773+ t .Run (tc .name , func (t * testing.T ) {
774+ err := tc .cfg .Validate ()
775+ if (err != nil ) != tc .wantErr {
776+ t .Errorf ("Validate() error = %v, wantErr %v" , err , tc .wantErr )
777+ }
778+ })
779+ }
780+ }
781+
782+ func TestForceLocalhost (t * testing.T ) {
783+ cases := map [string ]string {
784+ ":9094" : "127.0.0.1:9094" ,
785+ "0.0.0.0:9094" : "127.0.0.1:9094" ,
786+ "[::]:9094" : "127.0.0.1:9094" ,
787+ "127.0.0.1:9094" : "127.0.0.1:9094" ,
788+ "" : "" ,
789+ "no-port-malformed" : "no-port-malformed" , // left as-is; bind surfaces the error
790+ }
791+ for in , want := range cases {
792+ if got := forceLocalhost (in ); got != want {
793+ t .Errorf ("forceLocalhost(%q) = %q, want %q" , in , got , want )
794+ }
795+ }
796+ }
797+
798+ // With the bridge enabled, Load() restricts the session API to loopback so
799+ // other pods can't scrape decrypted bodies.
800+ func TestLoad_TLSBridgeHardensSessionAPI (t * testing.T ) {
801+ y := []byte ("mode: proxy-sidecar\n " +
802+ "listener:\n " +
803+ " session_api_addr: \" :9094\" \n " +
804+ "tls_bridge:\n " +
805+ " mode: enabled\n " +
806+ " ca_dir: /etc/authbridge/tls-bridge-ca\n " )
807+ p := filepath .Join (t .TempDir (), "cfg.yaml" )
808+ if err := os .WriteFile (p , y , 0o600 ); err != nil {
809+ t .Fatal (err )
810+ }
811+ cfg , err := Load (p )
812+ if err != nil {
813+ t .Fatalf ("load: %v" , err )
814+ }
815+ if cfg .Listener .SessionAPIAddr != "127.0.0.1:9094" {
816+ t .Errorf ("session_api_addr = %q, want 127.0.0.1:9094 (bridge on => loopback)" , cfg .Listener .SessionAPIAddr )
817+ }
818+ }
819+
730820// Absent mtls block leaves cfg.MTLS nil — today's behavior, no TLS.
731821func TestLoad_MTLS_AbsentBlock (t * testing.T ) {
732822 dir := t .TempDir ()
0 commit comments