diff --git a/pkg/controller/certificate-shim/sync.go b/pkg/controller/certificate-shim/sync.go index 112b7d8f4..3948e6a43 100644 --- a/pkg/controller/certificate-shim/sync.go +++ b/pkg/controller/certificate-shim/sync.go @@ -239,7 +239,7 @@ func validateIngressTLSBlock(path *field.Path, tlsBlock networkingv1.IngressTLS) return errs } -func validateGatewayListenerBlock(path *field.Path, l gwapi.Listener) field.ErrorList { +func validateGatewayListenerBlock(path *field.Path, l gwapi.Listener, ingLike metav1.Object) field.ErrorList { var errs field.ErrorList if l.Hostname == nil || *l.Hostname == "" { @@ -266,6 +266,11 @@ func validateGatewayListenerBlock(path *field.Path, l gwapi.Listener) field.Erro errs = append(errs, field.NotSupported(path.Child("tls").Child("certificateRef").Index(i).Child("kind"), *secretRef.Kind, []string{"Secret", ""})) } + + if secretRef.Namespace != nil && string(*secretRef.Namespace) != ingLike.GetNamespace() { + errs = append(errs, field.Invalid(path.Child("tls").Child("certificateRef").Index(i).Child("namespace"), + *secretRef.Namespace, "cross-namespace secret references are not allowed in listeners")) + } } } @@ -310,7 +315,7 @@ func buildCertificates( } case *gwapi.Gateway: for i, l := range ingLike.Spec.Listeners { - err := validateGatewayListenerBlock(field.NewPath("spec", "listeners").Index(i), l).ToAggregate() + err := validateGatewayListenerBlock(field.NewPath("spec", "listeners").Index(i), l, ingLike).ToAggregate() if err != nil { rec.Eventf(ingLike, corev1.EventTypeWarning, reasonBadConfig, "Skipped a listener block: "+err.Error()) continue diff --git a/pkg/controller/certificate-shim/sync_test.go b/pkg/controller/certificate-shim/sync_test.go index 5b4783cbf..1308bdf5e 100644 --- a/pkg/controller/certificate-shim/sync_test.go +++ b/pkg/controller/certificate-shim/sync_test.go @@ -3039,11 +3039,18 @@ func ptrMode(mode gwapi.TLSModeType) *gwapi.TLSModeType { func Test_validateGatewayListenerBlock(t *testing.T) { tests := []struct { name string + ingLike metav1.Object listener gwapi.Listener wantErr string }{ { name: "empty TLS block", + ingLike: &gwapi.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Namespace: "default", + }, + }, listener: gwapi.Listener{ Hostname: ptrHostname("example.com"), Port: gwapi.PortNumber(443), @@ -3053,6 +3060,12 @@ func Test_validateGatewayListenerBlock(t *testing.T) { }, { name: "empty hostname", + ingLike: &gwapi.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "gateway", + Namespace: "default", + }, + }, listener: gwapi.Listener{ Hostname: ptrHostname(""), Port: gwapi.PortNumber(443), @@ -3072,6 +3085,12 @@ func Test_validateGatewayListenerBlock(t *testing.T) { }, { name: "empty group", + ingLike: &gwapi.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + }, + }, listener: gwapi.Listener{ Hostname: ptrHostname("example.com"), Port: gwapi.PortNumber(443), @@ -3128,10 +3147,62 @@ func Test_validateGatewayListenerBlock(t *testing.T) { }, wantErr: "spec.listeners[0].tls.certificateRef[0].kind: Unsupported value: \"SomeOtherKind\": supported values: \"Secret\", \"\"", }, + { + name: "cross-namespace secret ref", + ingLike: &gwapi.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "default", + }, + }, + listener: gwapi.Listener{ + Hostname: ptrHostname("example.com"), + Port: gwapi.PortNumber(443), + Protocol: gwapi.HTTPSProtocolType, + TLS: &gwapi.GatewayTLSConfig{ + Mode: ptrMode(gwapi.TLSModeTerminate), + CertificateRefs: []gwapi.SecretObjectReference{ + { + Group: func() *gwapi.Group { g := gwapi.Group(""); return &g }(), + Kind: func() *gwapi.Kind { k := gwapi.Kind("Secret"); return &k }(), + Name: "example-com", + Namespace: func() *gwapi.Namespace { n := gwapi.Namespace("another-namespace"); return &n }(), + }, + }, + }, + }, + wantErr: "spec.listeners[0].tls.certificateRef[0].namespace: Invalid value: \"another-namespace\": cross-namespace secret references are not allowed in listeners", + }, + { + name: "same namespace secret ref", + ingLike: &gwapi.Gateway{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example", + Namespace: "another-namespace", + }, + }, + listener: gwapi.Listener{ + Hostname: ptrHostname("example.com"), + Port: gwapi.PortNumber(443), + Protocol: gwapi.HTTPSProtocolType, + TLS: &gwapi.GatewayTLSConfig{ + Mode: ptrMode(gwapi.TLSModeTerminate), + CertificateRefs: []gwapi.SecretObjectReference{ + { + Group: func() *gwapi.Group { g := gwapi.Group(""); return &g }(), + Kind: func() *gwapi.Kind { k := gwapi.Kind("Secret"); return &k }(), + Name: "example-com", + Namespace: func() *gwapi.Namespace { n := gwapi.Namespace("another-namespace"); return &n }(), + }, + }, + }, + }, + wantErr: "", + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - gotErr := validateGatewayListenerBlock(field.NewPath("spec", "listeners").Index(0), test.listener).ToAggregate() + gotErr := validateGatewayListenerBlock(field.NewPath("spec", "listeners").Index(0), test.listener, test.ingLike).ToAggregate() if test.wantErr == "" { assert.NoError(t, gotErr) } else {