Skip to content

Commit b1f2401

Browse files
committed
fix: isHTTPRouteFilterReferencingSecret always returns true and cross-namespace index
Two bugs in the HTTPRouteFilter secret predicate path: 1. predicates.go: isHTTPRouteFilterReferencingSecret had 'return true' where 'return false' was intended when no HTTPRouteFilter references the given Secret. This caused validateSecretForReconcile to always return true when hrfCRDExists=true, triggering unnecessary reconciliations on every Secret change. 2. indexers.go: secretRouteFilterIndexFunc indexed the Secret reference under filter.Namespace instead of the Secret's actual namespace. When ValueRef.Namespace points to a different namespace (allowed by the CRD with a ReferenceGrant), the index key was wrong and cross-namespace credential rotation would not trigger reconciliation. Fixed by using gatewayapi.NamespaceDerefOr to resolve the correct namespace, matching the pattern used by secretSecurityPolicyIndexFunc and secretEnvoyExtensionPolicyIndexFunc. Added unit tests covering: - Secret not referenced by any HTTPRouteFilter (expect false) - Secret referenced by HTTPRouteFilter CredentialInjection, same namespace (expect true) - Secret in another namespace referenced via ValueRef.Namespace (expect true)
1 parent 3619397 commit b1f2401

3 files changed

Lines changed: 59 additions & 2 deletions

File tree

internal/provider/kubernetes/indexers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,7 +1154,7 @@ func secretRouteFilterIndexFunc(rawObj client.Object) []string {
11541154
if filter.Spec.CredentialInjection != nil {
11551155
secretReferences = append(secretReferences,
11561156
types.NamespacedName{
1157-
Namespace: filter.Namespace,
1157+
Namespace: gatewayapi.NamespaceDerefOr(filter.Spec.CredentialInjection.Credential.ValueRef.Namespace, filter.Namespace),
11581158
Name: string(filter.Spec.CredentialInjection.Credential.ValueRef.Name),
11591159
}.String(),
11601160
)

internal/provider/kubernetes/predicates.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ func (r *gatewayAPIReconciler) isHTTPRouteFilterReferencingSecret(nsName *types.
320320
return true
321321
}
322322

323-
return true
323+
return false
324324
}
325325

326326
func (r *gatewayAPIReconciler) isBackendTLSPolicyReferencingSecret(nsName *types.NamespacedName) bool {

internal/provider/kubernetes/predicates_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,61 @@ func TestValidateSecretForReconcile(t *testing.T) {
865865
secret: test.GetSecret(types.NamespacedName{Namespace: "default", Name: "secret"}),
866866
expect: true,
867867
},
868+
{
869+
name: "secret not referenced by any HTTPRouteFilter",
870+
configs: []client.Object{
871+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
872+
},
873+
secret: test.GetSecret(types.NamespacedName{Namespace: "default", Name: "unrelated-secret"}),
874+
expect: false,
875+
},
876+
{
877+
name: "secret referenced by HTTPRouteFilter CredentialInjection",
878+
configs: []client.Object{
879+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
880+
&egv1a1.HTTPRouteFilter{
881+
ObjectMeta: metav1.ObjectMeta{
882+
Name: "credential-filter",
883+
Namespace: "default",
884+
},
885+
Spec: egv1a1.HTTPRouteFilterSpec{
886+
CredentialInjection: &egv1a1.HTTPCredentialInjectionFilter{
887+
Credential: egv1a1.InjectedCredential{
888+
ValueRef: gwapiv1.SecretObjectReference{
889+
Name: "credential-secret",
890+
},
891+
},
892+
},
893+
},
894+
},
895+
},
896+
secret: test.GetSecret(types.NamespacedName{Namespace: "default", Name: "credential-secret"}),
897+
expect: true,
898+
},
899+
{
900+
name: "secret in another namespace referenced by HTTPRouteFilter CredentialInjection",
901+
configs: []client.Object{
902+
test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil),
903+
&egv1a1.HTTPRouteFilter{
904+
ObjectMeta: metav1.ObjectMeta{
905+
Name: "credential-filter",
906+
Namespace: "default",
907+
},
908+
Spec: egv1a1.HTTPRouteFilterSpec{
909+
CredentialInjection: &egv1a1.HTTPCredentialInjectionFilter{
910+
Credential: egv1a1.InjectedCredential{
911+
ValueRef: gwapiv1.SecretObjectReference{
912+
Name: "credential-secret",
913+
Namespace: gatewayapi.NamespacePtr("other-ns"),
914+
},
915+
},
916+
},
917+
},
918+
},
919+
},
920+
secret: test.GetSecret(types.NamespacedName{Namespace: "other-ns", Name: "credential-secret"}),
921+
expect: true,
922+
},
868923
}
869924

870925
// Create the reconciler.
@@ -877,6 +932,7 @@ func TestValidateSecretForReconcile(t *testing.T) {
877932
spCRDExists: true,
878933
epCRDExists: true,
879934
eepCRDExists: true,
935+
hrfCRDExists: true,
880936
envoyGateway: &egv1a1.EnvoyGateway{
881937
EnvoyGatewaySpec: egv1a1.EnvoyGatewaySpec{
882938
ExtensionAPIs: &egv1a1.ExtensionAPISettings{
@@ -895,6 +951,7 @@ func TestValidateSecretForReconcile(t *testing.T) {
895951
WithIndex(&egv1a1.EnvoyProxy{}, secretEnvoyProxyIndex, secretEnvoyProxyIndexFunc).
896952
WithIndex(&egv1a1.EnvoyExtensionPolicy{}, secretEnvoyExtensionPolicyIndex, secretEnvoyExtensionPolicyIndexFunc).
897953
WithIndex(&egv1a1.Backend{}, secretBackendIndex, secretBackendIndexFunc).
954+
WithIndex(&egv1a1.HTTPRouteFilter{}, secretHTTPRouteFilterIndex, secretRouteFilterIndexFunc).
898955
Build()
899956
t.Run(tc.name, func(t *testing.T) {
900957
res := r.validateSecretForReconcile(tc.secret)

0 commit comments

Comments
 (0)