From 97753a8c1e20fda4327125cd07a1b5fd9e3f00c4 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Fri, 24 Jan 2025 14:23:18 -0600 Subject: [PATCH 01/11] api: cross-namespace policy target selectors Signed-off-by: Guy Daich --- api/v1alpha1/policy_helpers.go | 14 +++++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 20 +++++++++++++++++++ ....envoyproxy.io_backendtrafficpolicies.yaml | 12 +++++++++++ ...y.envoyproxy.io_clienttrafficpolicies.yaml | 12 +++++++++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 12 +++++++++++ ...ateway.envoyproxy.io_securitypolicies.yaml | 12 +++++++++++ site/content/en/latest/api/extension_types.md | 14 +++++++++++++ site/content/zh/latest/api/extension_types.md | 14 +++++++++++++ 8 files changed, 110 insertions(+) diff --git a/api/v1alpha1/policy_helpers.go b/api/v1alpha1/policy_helpers.go index f1bc16e178..64dab44449 100644 --- a/api/v1alpha1/policy_helpers.go +++ b/api/v1alpha1/policy_helpers.go @@ -37,6 +37,20 @@ type TargetSelector struct { // MatchLabels are the set of label selectors for identifying the targeted resource MatchLabels map[string]string `json:"matchLabels"` + + // NamespaceSelector determines if the label selectors specified in MatchLabels are applied in + // the policy namespace or across all namespaces. + // Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be + // configured to allow the selection. + // +optional + // +notImplementedHide + NamespaceSelector *NamespaceSelector `json:"namespaceSelector,omitempty"` +} + +// NamespaceSelector is a selector for selecting either all namespaces or the current namespace. +type NamespaceSelector struct { + // Boolean describing whether all namespaces are selected. + Any bool `json:"any,omitempty"` } func (p PolicyTargetReferences) GetTargetRefs() []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ee36c4ef02..02e7e07241 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -4010,6 +4010,21 @@ func (in *Lua) DeepCopy() *Lua { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. +func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { + if in == nil { + return nil + } + out := new(NamespaceSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDC) DeepCopyInto(out *OIDC) { *out = *in @@ -5662,6 +5677,11 @@ func (in *TargetSelector) DeepCopyInto(out *TargetSelector) { (*out)[key] = val } } + if in.NamespaceSelector != nil { + in, out := &in.NamespaceSelector, &out.NamespaceSelector + *out = new(NamespaceSelector) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSelector. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index e0c56b6406..3f667a48f6 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1504,6 +1504,18 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object + namespaceSelector: + description: |- + NamespaceSelector determines if the label selectors specified in MatchLabels are applied in + the policy namespace or across all namespaces. + Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be + configured to allow the selection. + properties: + any: + description: Boolean describing whether all namespaces are + selected. + type: boolean + type: object required: - kind - matchLabels diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index d3afb65b30..703be1862c 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -614,6 +614,18 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object + namespaceSelector: + description: |- + NamespaceSelector determines if the label selectors specified in MatchLabels are applied in + the policy namespace or across all namespaces. + Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be + configured to allow the selection. + properties: + any: + description: Boolean describing whether all namespaces are + selected. + type: boolean + type: object required: - kind - matchLabels diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index a0740148a1..7ff4b13bd9 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1197,6 +1197,18 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object + namespaceSelector: + description: |- + NamespaceSelector determines if the label selectors specified in MatchLabels are applied in + the policy namespace or across all namespaces. + Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be + configured to allow the selection. + properties: + any: + description: Boolean describing whether all namespaces are + selected. + type: boolean + type: object required: - kind - matchLabels diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 103d1fb7ca..6b5ea1fbcc 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -4413,6 +4413,18 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object + namespaceSelector: + description: |- + NamespaceSelector determines if the label selectors specified in MatchLabels are applied in + the policy namespace or across all namespaces. + Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be + configured to allow the selection. + properties: + any: + description: Boolean describing whether all namespaces are + selected. + type: boolean + type: object required: - kind - matchLabels diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 71fedaf1d1..4adf1c8b75 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2898,6 +2898,20 @@ _Appears in:_ | `OpenTelemetry` | | +#### NamespaceSelector + + + +NamespaceSelector is a selector for selecting either all namespaces or the current namespace. + +_Appears in:_ +- [TargetSelector](#targetselector) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `any` | _boolean_ | true | | Boolean describing whether all namespaces are selected. | + + #### OIDC diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 71fedaf1d1..4adf1c8b75 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -2898,6 +2898,20 @@ _Appears in:_ | `OpenTelemetry` | | +#### NamespaceSelector + + + +NamespaceSelector is a selector for selecting either all namespaces or the current namespace. + +_Appears in:_ +- [TargetSelector](#targetselector) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `any` | _boolean_ | true | | Boolean describing whether all namespaces are selected. | + + #### OIDC From d28eb6899485a8ae855749fd6278d50b347cf9fa Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Wed, 29 Jan 2025 13:24:16 -0600 Subject: [PATCH 02/11] use gw-api style for namespace selector Signed-off-by: Guy Daich --- api/v1alpha1/policy_helpers.go | 23 ++++++---- api/v1alpha1/zz_generated.deepcopy.go | 36 ++++++++-------- ....envoyproxy.io_backendtrafficpolicies.yaml | 18 ++++---- ...y.envoyproxy.io_clienttrafficpolicies.yaml | 18 ++++---- ....envoyproxy.io_envoyextensionpolicies.yaml | 18 ++++---- ...ateway.envoyproxy.io_securitypolicies.yaml | 18 ++++---- site/content/en/latest/api/extension_types.md | 42 ++++++++++++------- site/content/zh/latest/api/extension_types.md | 42 ++++++++++++------- 8 files changed, 134 insertions(+), 81 deletions(-) diff --git a/api/v1alpha1/policy_helpers.go b/api/v1alpha1/policy_helpers.go index 64dab44449..57f6ba9200 100644 --- a/api/v1alpha1/policy_helpers.go +++ b/api/v1alpha1/policy_helpers.go @@ -38,19 +38,28 @@ type TargetSelector struct { // MatchLabels are the set of label selectors for identifying the targeted resource MatchLabels map[string]string `json:"matchLabels"` - // NamespaceSelector determines if the label selectors specified in MatchLabels are applied in - // the policy namespace or across all namespaces. + // Namespaces determines if the resource from all namespaces or the current namespace + // are considered when matching by label selectors specified in MatchLabels. // Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be // configured to allow the selection. // +optional // +notImplementedHide - NamespaceSelector *NamespaceSelector `json:"namespaceSelector,omitempty"` + Namespaces *TargetSelectorNamespaces `json:"namespaces,omitempty"` } -// NamespaceSelector is a selector for selecting either all namespaces or the current namespace. -type NamespaceSelector struct { - // Boolean describing whether all namespaces are selected. - Any bool `json:"any,omitempty"` +type FromNamespaces string + +const ( + // FromNamespacesAll indicates that the target selector should apply to targets from all namespaces + FromNamespacesAll FromNamespaces = "All" +) + +// TargetSelectorNamespaces determines which namespaces are used when selecting policy targets. +type TargetSelectorNamespaces struct { + // Indicates where targets would be selected for the Policy's TargetSelector. + // +kubebuilder:validation:Enum=All + // +kubebuilder:validation:Required + FromNamespaces FromNamespaces `json:"omitempty"` } func (p PolicyTargetReferences) GetTargetRefs() []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 02e7e07241..75047bc031 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -4010,21 +4010,6 @@ func (in *Lua) DeepCopy() *Lua { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *NamespaceSelector) DeepCopyInto(out *NamespaceSelector) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceSelector. -func (in *NamespaceSelector) DeepCopy() *NamespaceSelector { - if in == nil { - return nil - } - out := new(NamespaceSelector) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDC) DeepCopyInto(out *OIDC) { *out = *in @@ -5677,9 +5662,9 @@ func (in *TargetSelector) DeepCopyInto(out *TargetSelector) { (*out)[key] = val } } - if in.NamespaceSelector != nil { - in, out := &in.NamespaceSelector, &out.NamespaceSelector - *out = new(NamespaceSelector) + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(TargetSelectorNamespaces) **out = **in } } @@ -5694,6 +5679,21 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TargetSelectorNamespaces) DeepCopyInto(out *TargetSelectorNamespaces) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSelectorNamespaces. +func (in *TargetSelectorNamespaces) DeepCopy() *TargetSelectorNamespaces { + if in == nil { + return nil + } + out := new(TargetSelectorNamespaces) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Timeout) DeepCopyInto(out *Timeout) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 3f667a48f6..e94df2112d 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1504,17 +1504,21 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object - namespaceSelector: + namespaces: description: |- - NamespaceSelector determines if the label selectors specified in MatchLabels are applied in - the policy namespace or across all namespaces. + Namespaces determines if the resource from all namespaces or the current namespace + are considered when matching by label selectors specified in MatchLabels. Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - any: - description: Boolean describing whether all namespaces are - selected. - type: boolean + omitempty: + description: Indicates where targets would be selected for + the Policy's TargetSelector. + enum: + - All + type: string + required: + - omitempty type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 703be1862c..5a71e6cede 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -614,17 +614,21 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object - namespaceSelector: + namespaces: description: |- - NamespaceSelector determines if the label selectors specified in MatchLabels are applied in - the policy namespace or across all namespaces. + Namespaces determines if the resource from all namespaces or the current namespace + are considered when matching by label selectors specified in MatchLabels. Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - any: - description: Boolean describing whether all namespaces are - selected. - type: boolean + omitempty: + description: Indicates where targets would be selected for + the Policy's TargetSelector. + enum: + - All + type: string + required: + - omitempty type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 7ff4b13bd9..cbd45bed27 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1197,17 +1197,21 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object - namespaceSelector: + namespaces: description: |- - NamespaceSelector determines if the label selectors specified in MatchLabels are applied in - the policy namespace or across all namespaces. + Namespaces determines if the resource from all namespaces or the current namespace + are considered when matching by label selectors specified in MatchLabels. Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - any: - description: Boolean describing whether all namespaces are - selected. - type: boolean + omitempty: + description: Indicates where targets would be selected for + the Policy's TargetSelector. + enum: + - All + type: string + required: + - omitempty type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 6b5ea1fbcc..9b0419cf2f 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -4413,17 +4413,21 @@ spec: description: MatchLabels are the set of label selectors for identifying the targeted resource type: object - namespaceSelector: + namespaces: description: |- - NamespaceSelector determines if the label selectors specified in MatchLabels are applied in - the policy namespace or across all namespaces. + Namespaces determines if the resource from all namespaces or the current namespace + are considered when matching by label selectors specified in MatchLabels. Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - any: - description: Boolean describing whether all namespaces are - selected. - type: boolean + omitempty: + description: Indicates where targets would be selected for + the Policy's TargetSelector. + enum: + - All + type: string + required: + - omitempty type: object required: - kind diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 4adf1c8b75..af759c1450 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1801,6 +1801,20 @@ _Appears in:_ | `after` | _[EnvoyFilter](#envoyfilter)_ | true | | After defines the filter that should come after the filter.
Only one of Before or After must be set. | +#### FromNamespaces + +_Underlying type:_ _string_ + + + +_Appears in:_ +- [TargetSelectorNamespaces](#targetselectornamespaces) + +| Value | Description | +| ----- | ----------- | +| `All` | FromNamespacesAll indicates that the target selector should apply to targets from all namespaces
| + + #### GRPCActiveHealthChecker @@ -2898,20 +2912,6 @@ _Appears in:_ | `OpenTelemetry` | | -#### NamespaceSelector - - - -NamespaceSelector is a selector for selecting either all namespaces or the current namespace. - -_Appears in:_ -- [TargetSelector](#targetselector) - -| Field | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `any` | _boolean_ | true | | Boolean describing whether all namespaces are selected. | - - #### OIDC @@ -4318,6 +4318,20 @@ _Appears in:_ | `matchLabels` | _object (keys:string, values:string)_ | true | | MatchLabels are the set of label selectors for identifying the targeted resource | +#### TargetSelectorNamespaces + + + +TargetSelectorNamespaces determines which namespaces are used when selecting policy targets. + +_Appears in:_ +- [TargetSelector](#targetselector) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `omitempty` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | + + #### Timeout diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 4adf1c8b75..af759c1450 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -1801,6 +1801,20 @@ _Appears in:_ | `after` | _[EnvoyFilter](#envoyfilter)_ | true | | After defines the filter that should come after the filter.
Only one of Before or After must be set. | +#### FromNamespaces + +_Underlying type:_ _string_ + + + +_Appears in:_ +- [TargetSelectorNamespaces](#targetselectornamespaces) + +| Value | Description | +| ----- | ----------- | +| `All` | FromNamespacesAll indicates that the target selector should apply to targets from all namespaces
| + + #### GRPCActiveHealthChecker @@ -2898,20 +2912,6 @@ _Appears in:_ | `OpenTelemetry` | | -#### NamespaceSelector - - - -NamespaceSelector is a selector for selecting either all namespaces or the current namespace. - -_Appears in:_ -- [TargetSelector](#targetselector) - -| Field | Type | Required | Default | Description | -| --- | --- | --- | --- | --- | -| `any` | _boolean_ | true | | Boolean describing whether all namespaces are selected. | - - #### OIDC @@ -4318,6 +4318,20 @@ _Appears in:_ | `matchLabels` | _object (keys:string, values:string)_ | true | | MatchLabels are the set of label selectors for identifying the targeted resource | +#### TargetSelectorNamespaces + + + +TargetSelectorNamespaces determines which namespaces are used when selecting policy targets. + +_Appears in:_ +- [TargetSelector](#targetselector) + +| Field | Type | Required | Default | Description | +| --- | --- | --- | --- | --- | +| `omitempty` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | + + #### Timeout From e672802f65f4f52caf90f814e5e4297b68238dd1 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Wed, 29 Jan 2025 14:39:16 -0600 Subject: [PATCH 03/11] fix json tag Signed-off-by: Guy Daich --- api/v1alpha1/policy_helpers.go | 2 +- .../gateway.envoyproxy.io_backendtrafficpolicies.yaml | 4 ++-- .../gateway.envoyproxy.io_clienttrafficpolicies.yaml | 4 ++-- .../gateway.envoyproxy.io_envoyextensionpolicies.yaml | 4 ++-- .../generated/gateway.envoyproxy.io_securitypolicies.yaml | 4 ++-- site/content/en/latest/api/extension_types.md | 2 +- site/content/zh/latest/api/extension_types.md | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/v1alpha1/policy_helpers.go b/api/v1alpha1/policy_helpers.go index 57f6ba9200..1564b2d9b9 100644 --- a/api/v1alpha1/policy_helpers.go +++ b/api/v1alpha1/policy_helpers.go @@ -59,7 +59,7 @@ type TargetSelectorNamespaces struct { // Indicates where targets would be selected for the Policy's TargetSelector. // +kubebuilder:validation:Enum=All // +kubebuilder:validation:Required - FromNamespaces FromNamespaces `json:"omitempty"` + FromNamespaces FromNamespaces `json:"from,omitempty"` } func (p PolicyTargetReferences) GetTargetRefs() []gwapiv1a2.LocalPolicyTargetReferenceWithSectionName { diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index e94df2112d..1f190af95e 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1511,14 +1511,14 @@ spec: Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - omitempty: + from: description: Indicates where targets would be selected for the Policy's TargetSelector. enum: - All type: string required: - - omitempty + - from type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 5a71e6cede..c7b8811463 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -621,14 +621,14 @@ spec: Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - omitempty: + from: description: Indicates where targets would be selected for the Policy's TargetSelector. enum: - All type: string required: - - omitempty + - from type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index cbd45bed27..fd8b05c088 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1204,14 +1204,14 @@ spec: Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - omitempty: + from: description: Indicates where targets would be selected for the Policy's TargetSelector. enum: - All type: string required: - - omitempty + - from type: object required: - kind diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 9b0419cf2f..6a109cf3fb 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -4420,14 +4420,14 @@ spec: Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be configured to allow the selection. properties: - omitempty: + from: description: Indicates where targets would be selected for the Policy's TargetSelector. enum: - All type: string required: - - omitempty + - from type: object required: - kind diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index af759c1450..20dacf5e73 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -4329,7 +4329,7 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | -| `omitempty` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | +| `from` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | #### Timeout diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index af759c1450..20dacf5e73 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -4329,7 +4329,7 @@ _Appears in:_ | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | -| `omitempty` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | +| `from` | _[FromNamespaces](#fromnamespaces)_ | true | | Indicates where targets would be selected for the Policy's TargetSelector. | #### Timeout From 9044f4ec1e05c3faa91329a273e7840bb6e26634 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 7 Apr 2026 12:02:48 +0800 Subject: [PATCH 04/11] update API Signed-off-by: Huabing (Robin) Zhao --- api/v1alpha1/policy_helpers.go | 53 +-- api/v1alpha1/zz_generated.deepcopy.go | 15 +- ....envoyproxy.io_backendtrafficpolicies.yaml | 76 ++++- ...y.envoyproxy.io_clienttrafficpolicies.yaml | 76 ++++- ....envoyproxy.io_envoyextensionpolicies.yaml | 76 ++++- ...ateway.envoyproxy.io_securitypolicies.yaml | 76 ++++- ....envoyproxy.io_backendtrafficpolicies.yaml | 76 ++++- ...y.envoyproxy.io_clienttrafficpolicies.yaml | 76 ++++- ....envoyproxy.io_envoyextensionpolicies.yaml | 76 ++++- ...ateway.envoyproxy.io_securitypolicies.yaml | 76 ++++- site/content/en/latest/api/extension_types.md | 40 +-- test/helm/gateway-crds-helm/all.out.yaml | 304 ++++++++++++++++-- test/helm/gateway-crds-helm/e2e.out.yaml | 304 ++++++++++++++++-- .../envoy-gateway-crds.out.yaml | 304 ++++++++++++++++-- 14 files changed, 1426 insertions(+), 202 deletions(-) diff --git a/api/v1alpha1/policy_helpers.go b/api/v1alpha1/policy_helpers.go index 2d80fc282e..3913c54988 100644 --- a/api/v1alpha1/policy_helpers.go +++ b/api/v1alpha1/policy_helpers.go @@ -42,6 +42,8 @@ type PolicyTargetReferences struct { } // +kubebuilder:validation:XValidation:rule="has(self.group) ? self.group == 'gateway.networking.k8s.io' : true ", message="group must be gateway.networking.k8s.io" +// +kubebuilder:validation:XValidation:rule="!has(self.namespaces) || self.namespaces.from != 'Selector' || has(self.namespaces.selector)", message="namespaces.selector must be specified when namespaces.from is Selector" +// +kubebuilder:validation:XValidation:rule="has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions)", message="at least one of namespaces, matchLabels, or matchExpressions must be specified" type TargetSelector struct { // Group is the group that this selector targets. Defaults to gateway.networking.k8s.io // @@ -51,7 +53,18 @@ type TargetSelector struct { // Kind is the resource kind that this selector targets. Kind gwapiv1.Kind `json:"kind"` - // MatchLabels are the set of label selectors for identifying the targeted resource + // Namespaces determines which namespaces are considered for target selection. + // + // If unspecified, only targets in the same namespace as this policy are considered. + // + // When specified, the effective set of namespaces is always constrained to the + // namespaces watched by Envoy Gateway. + // + // +optional + Namespaces *TargetSelectorNamespaces `json:"namespaces,omitempty"` + + // MatchLabels are the set of label selectors for identifying the targeted resource. + // // +optional MatchLabels map[string]string `json:"matchLabels,omitempty"` @@ -60,30 +73,34 @@ type TargetSelector struct { // +optional // +listType=atomic MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty"` - - // Namespaces determines if the resource from all namespaces or the current namespace - // are considered when matching by label selectors specified in MatchLabels. - // Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - // configured to allow the selection. - // +optional - Namespaces *TargetSelectorNamespaces `json:"namespaces,omitempty"` } -type FromNamespaces string +type TargetNamespaceFrom string const ( - // FromNamespacesAll indicates that the target selector should apply to targets from all namespaces - FromNamespacesAll FromNamespaces = "All" - // FromNamespacesSame indicates that the target selector should apply to targets from the same namespace as the policy - FromNamespacesSame FromNamespaces = "Same" + // TargetNamespaceFromSame limits target selection to the policy's namespace. + TargetNamespaceFromSame TargetNamespaceFrom = "Same" + // TargetNamespaceFromAll allows target selection from all watched namespaces. + TargetNamespaceFromAll TargetNamespaceFrom = "All" + // TargetNamespaceFromSelector allows target selection from watched namespaces matching the selector. + TargetNamespaceFromSelector TargetNamespaceFrom = "Selector" ) -// TargetSelectorNamespaces determines which namespaces are used when selecting policy targets. +// TargetSelectorNamespaces determines which namespaces are considered for target selection. type TargetSelectorNamespaces struct { - // Indicates where targets would be selected for the Policy's TargetSelector. - // +kubebuilder:validation:Enum=All;Same - // +kubebuilder:default:="Same" - FromNamespaces FromNamespaces `json:"from,omitempty"` + // From indicates how namespaces are selected for this target selector. + // + // All means all namespaces watched by Envoy Gateway. + // Selector means namespaces watched by Envoy Gateway that match Selector. + // + // +kubebuilder:validation:Enum=Same;All;Selector + // +kubebuilder:default:=Same + From TargetNamespaceFrom `json:"from,omitempty"` + + // Selector selects namespaces when From is set to Selector. + // + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` } func (p PolicyTargetReferences) GetTargetRefs() []gwapiv1.LocalPolicyTargetReferenceWithSectionName { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 0a61912499..8eecb5f4d4 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -7959,6 +7959,11 @@ func (in *TargetSelector) DeepCopyInto(out *TargetSelector) { *out = new(v1.Group) **out = **in } + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(TargetSelectorNamespaces) + (*in).DeepCopyInto(*out) + } if in.MatchLabels != nil { in, out := &in.MatchLabels, &out.MatchLabels *out = make(map[string]string, len(*in)) @@ -7973,11 +7978,6 @@ func (in *TargetSelector) DeepCopyInto(out *TargetSelector) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(TargetSelectorNamespaces) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSelector. @@ -7993,6 +7993,11 @@ func (in *TargetSelector) DeepCopy() *TargetSelector { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetSelectorNamespaces) DeepCopyInto(out *TargetSelectorNamespaces) { *out = *in + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(metav1.LabelSelector) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TargetSelectorNamespaces. diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 8f18bd67ea..db6ad2b470 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -2614,23 +2614,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -2639,6 +2692,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 41043fd481..baa1413b05 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -1170,23 +1170,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -1195,6 +1248,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 2e645e9ed9..ee50963b8c 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1764,23 +1764,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -1789,6 +1842,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array wasm: description: |- diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml index 5f83797df3..4e759b0de7 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -6873,23 +6873,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -6898,6 +6951,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array type: object x-kubernetes-validations: diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index f3b5f33e73..b609428095 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -2613,23 +2613,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -2638,6 +2691,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 39090c4ddd..aaccaa3c8c 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -1169,23 +1169,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -1194,6 +1247,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 10264484a1..802527b238 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1763,23 +1763,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -1788,6 +1841,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array wasm: description: |- diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index e7f1c18922..227e20c75e 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -6872,23 +6872,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -6897,6 +6950,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array type: object x-kubernetes-validations: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 0a8f4854ce..7225ef320e 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2396,21 +2396,6 @@ _Appears in:_ | `minEndpointsInZoneThreshold` | _integer_ | false | | MinEndpointsInZoneThreshold is the minimum number of upstream endpoints in the local zone required to honor the forceLocalZone
override. This is useful for protecting zones with fewer endpoints. | -#### FromNamespaces - -_Underlying type:_ _string_ - - - -_Appears in:_ -- [TargetSelectorNamespaces](#targetselectornamespaces) - -| Value | Description | -| ----- | ----------- | -| `All` | FromNamespacesAll indicates that the target selector should apply to targets from all namespaces
| -| `Same` | FromNamespacesSame indicates that the target selector should apply to targets from the same namespace as the policy
| - - #### GRPCActiveHealthChecker @@ -5888,6 +5873,22 @@ _Appears in:_ | `1.3` | TLSv1.3 specifies TLS version 1.3
| +#### TargetNamespaceFrom + +_Underlying type:_ _string_ + + + +_Appears in:_ +- [TargetSelectorNamespaces](#targetselectornamespaces) + +| Value | Description | +| ----- | ----------- | +| `Same` | TargetNamespaceFromSame limits target selection to the policy's namespace.
| +| `All` | TargetNamespaceFromAll allows target selection from all watched namespaces.
| +| `Selector` | TargetNamespaceFromSelector allows target selection from watched namespaces matching the selector.
| + + #### TargetSelector @@ -5905,23 +5906,24 @@ _Appears in:_ | --- | --- | --- | --- | --- | | `group` | _[Group](#group)_ | true | gateway.networking.k8s.io | Group is the group that this selector targets. Defaults to gateway.networking.k8s.io | | `kind` | _[Kind](#kind)_ | true | | Kind is the resource kind that this selector targets. | -| `matchLabels` | _object (keys:string, values:string)_ | false | | MatchLabels are the set of label selectors for identifying the targeted resource | +| `namespaces` | _[TargetSelectorNamespaces](#targetselectornamespaces)_ | false | | Namespaces determines which namespaces are considered for target selection.
If unspecified, only targets in the same namespace as this policy are considered.
When specified, the effective set of namespaces is always constrained to the
namespaces watched by Envoy Gateway. | +| `matchLabels` | _object (keys:string, values:string)_ | false | | MatchLabels are the set of label selectors for identifying the targeted resource. | | `matchExpressions` | _[LabelSelectorRequirement](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#labelselectorrequirement-v1-meta) array_ | false | | MatchExpressions is a list of label selector requirements. The requirements are ANDed. | -| `namespaces` | _[TargetSelectorNamespaces](#targetselectornamespaces)_ | false | | Namespaces determines if the resource from all namespaces or the current namespace
are considered when matching by label selectors specified in MatchLabels.
Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be
configured to allow the selection. | #### TargetSelectorNamespaces -TargetSelectorNamespaces determines which namespaces are used when selecting policy targets. +TargetSelectorNamespaces determines which namespaces are considered for target selection. _Appears in:_ - [TargetSelector](#targetselector) | Field | Type | Required | Default | Description | | --- | --- | --- | --- | --- | -| `from` | _[FromNamespaces](#fromnamespaces)_ | true | Same | Indicates where targets would be selected for the Policy's TargetSelector. | +| `from` | _[TargetNamespaceFrom](#targetnamespacefrom)_ | true | Same | From indicates how namespaces are selected for this target selector.
All means all namespaces watched by Envoy Gateway.
Selector means namespaces watched by Envoy Gateway that match Selector. | +| `selector` | _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#labelselector-v1-meta)_ | false | | Selector selects namespaces when From is set to Selector. | #### Timeout diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index 8df74aec24..bfb2881faa 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -25141,23 +25141,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -25166,6 +25219,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -26932,23 +26992,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -26957,6 +27070,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -29604,23 +29724,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -29629,6 +29802,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array wasm: description: |- @@ -56012,23 +56192,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -56037,6 +56270,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array type: object x-kubernetes-validations: diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index ef919d945d..1c9e0a5c3d 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -3114,23 +3114,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -3139,6 +3192,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -4905,23 +4965,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -4930,6 +5043,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -7577,23 +7697,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -7602,6 +7775,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array wasm: description: |- @@ -33985,23 +34165,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -34010,6 +34243,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array type: object x-kubernetes-validations: diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 83ac0e76fc..62580e533c 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -3114,23 +3114,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -3139,6 +3192,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -4905,23 +4965,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -4930,6 +5043,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array tcpKeepalive: description: |- @@ -7577,23 +7697,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -7602,6 +7775,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array wasm: description: |- @@ -33985,23 +34165,76 @@ spec: additionalProperties: type: string description: MatchLabels are the set of label selectors for - identifying the targeted resource + identifying the targeted resource. type: object namespaces: description: |- - Namespaces determines if the resource from all namespaces or the current namespace - are considered when matching by label selectors specified in MatchLabels. - Note: when referencing targets in a different namespace, appropriate ReferenceGrants must be - configured to allow the selection. + Namespaces determines which namespaces are considered for target selection. + + If unspecified, only targets in the same namespace as this policy are considered. + + When specified, the effective set of namespaces is always constrained to the + namespaces watched by Envoy Gateway. properties: from: default: Same - description: Indicates where targets would be selected for - the Policy's TargetSelector. + description: |- + From indicates how namespaces are selected for this target selector. + + All means all namespaces watched by Envoy Gateway. + Selector means namespaces watched by Envoy Gateway that match Selector. enum: - - All - Same + - All + - Selector type: string + selector: + description: Selector selects namespaces when From is set + to Selector. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object required: - kind @@ -34010,6 +34243,13 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' + - message: namespaces.selector must be specified when namespaces.from + is Selector + rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' + || has(self.namespaces.selector)' + - message: at least one of namespaces, matchLabels, or matchExpressions + must be specified + rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) type: array type: object x-kubernetes-validations: From 7dbee3d59a4be35ccbfce6d18d54691c4b735d72 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 7 Apr 2026 12:23:03 +0800 Subject: [PATCH 05/11] update API Signed-off-by: Huabing (Robin) Zhao --- api/v1alpha1/policy_helpers.go | 4 ++-- .../gateway.envoyproxy.io_backendtrafficpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_clienttrafficpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_envoyextensionpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_securitypolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_backendtrafficpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_clienttrafficpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_envoyextensionpolicies.yaml | 9 +++++---- .../gateway.envoyproxy.io_securitypolicies.yaml | 9 +++++---- 9 files changed, 42 insertions(+), 34 deletions(-) diff --git a/api/v1alpha1/policy_helpers.go b/api/v1alpha1/policy_helpers.go index 3913c54988..e6dac99332 100644 --- a/api/v1alpha1/policy_helpers.go +++ b/api/v1alpha1/policy_helpers.go @@ -42,7 +42,6 @@ type PolicyTargetReferences struct { } // +kubebuilder:validation:XValidation:rule="has(self.group) ? self.group == 'gateway.networking.k8s.io' : true ", message="group must be gateway.networking.k8s.io" -// +kubebuilder:validation:XValidation:rule="!has(self.namespaces) || self.namespaces.from != 'Selector' || has(self.namespaces.selector)", message="namespaces.selector must be specified when namespaces.from is Selector" // +kubebuilder:validation:XValidation:rule="has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions)", message="at least one of namespaces, matchLabels, or matchExpressions must be specified" type TargetSelector struct { // Group is the group that this selector targets. Defaults to gateway.networking.k8s.io @@ -87,6 +86,7 @@ const ( ) // TargetSelectorNamespaces determines which namespaces are considered for target selection. +// +kubebuilder:validation:XValidation:rule="self.from != 'Selector' || has(self.selector)", message="selector must be specified when from is Selector" type TargetSelectorNamespaces struct { // From indicates how namespaces are selected for this target selector. // @@ -95,7 +95,7 @@ type TargetSelectorNamespaces struct { // // +kubebuilder:validation:Enum=Same;All;Selector // +kubebuilder:default:=Same - From TargetNamespaceFrom `json:"from,omitempty"` + From TargetNamespaceFrom `json:"from"` // Selector selects namespaces when From is set to Selector. // diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index db6ad2b470..78d9205ab2 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -2684,7 +2684,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -2692,10 +2697,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index baa1413b05..7af30b88f2 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -1240,7 +1240,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -1248,10 +1253,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index ee50963b8c..724f314022 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1834,7 +1834,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -1842,10 +1847,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml index 4e759b0de7..1d45f30df9 100644 --- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -6943,7 +6943,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -6951,10 +6956,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index b609428095..722b1a08f0 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -2683,7 +2683,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -2691,10 +2696,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index aaccaa3c8c..e55b425cea 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -1239,7 +1239,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -1247,10 +1252,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 802527b238..9896764416 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -1833,7 +1833,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -1841,10 +1846,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 227e20c75e..e4d080720d 100644 --- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -6942,7 +6942,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -6950,10 +6955,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) From 7d277a77dd85152cae8d6e6b3abd1617bc07cf5d Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Wed, 15 Apr 2026 18:16:03 +0800 Subject: [PATCH 06/11] implmentation Signed-off-by: Huabing (Robin) Zhao --- api/v1alpha1/shared_types.go | 4 + internal/gatewayapi/backendtrafficpolicy.go | 87 +++- .../gatewayapi/backendtrafficpolicy_test.go | 2 +- internal/gatewayapi/clienttrafficpolicy.go | 51 ++- internal/gatewayapi/envoyextensionpolicy.go | 46 +- internal/gatewayapi/extensionserverpolicy.go | 29 +- .../gatewayapi/extensionserverpolicy_test.go | 32 +- internal/gatewayapi/helpers.go | 304 ++++++++++++- internal/gatewayapi/helpers_test.go | 405 ++++++++++++++++-- internal/gatewayapi/securitypolicy.go | 67 ++- internal/gatewayapi/securitypolicy_test.go | 22 +- ...getselector-invalid-referencegrant.in.yaml | 80 ++++ ...etselector-invalid-referencegrant.out.yaml | 199 +++++++++ ...getselector-partial-referencegrant.in.yaml | 155 +++++++ ...etselector-partial-referencegrant.out.yaml | 382 +++++++++++++++++ ...alid-referencegrant-gateway-target.in.yaml | 126 ++++++ ...lid-referencegrant-gateway-target.out.yaml | 223 ++++++++++ ...-valid-referencegrant-route-target.in.yaml | 126 ++++++ ...valid-referencegrant-route-target.out.yaml | 224 ++++++++++ ...argetselector-valid-referencegrant.in.yaml | 198 +++++++++ ...rgetselector-valid-referencegrant.out.yaml | 369 ++++++++++++++++ internal/gatewayapi/translator.go | 4 +- internal/gatewayapi/validate.go | 39 +- test/helm/gateway-crds-helm/all.out.yaml | 36 +- test/helm/gateway-crds-helm/e2e.out.yaml | 36 +- .../envoy-gateway-crds.out.yaml | 36 +- 26 files changed, 3040 insertions(+), 242 deletions(-) create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.out.yaml diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 39e1850fc1..bedf9843a1 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -67,6 +67,10 @@ const ( // PolicyReasonDeprecatedField is used with the "Warning" condition when the policy // uses deprecated fields that should be migrated to newer alternatives. PolicyReasonDeprecatedField gwapiv1.PolicyConditionReason = "DeprecatedField" + + // PolicyReasonRefNotPermitted is used with the "Accepted" condition when the policy + // targets a cross-namespace object without a matching ReferenceGrant. + PolicyReasonRefNotPermitted gwapiv1.PolicyConditionReason = "RefNotPermitted" ) // GroupVersionKind unambiguously identifies a Kind. diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index c570f764eb..0834ae52ec 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -14,12 +14,14 @@ import ( "time" perr "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" egv1a1validation "github.com/envoyproxy/gateway/api/v1alpha1/validation" @@ -68,6 +70,8 @@ func BuildBTPRoutingTypeIndex( btps []*egv1a1.BackendTrafficPolicy, routes []client.Object, gateways []*GatewayContext, + referenceGrants []*gwapiv1b1.ReferenceGrant, + namespaceLookup func(string) *corev1.Namespace, ) *BTPRoutingTypeIndex { idx := &BTPRoutingTypeIndex{ routeRuleLevel: make(map[btpRoutingKey]*egv1a1.RoutingType), @@ -88,7 +92,18 @@ func BuildBTPRoutingTypeIndex( continue } - refs := getPolicyTargetRefs(btp.Spec.PolicyTargetReferences, allTargets, btp.Namespace) + refs := getPolicyTargetRefs( + btp.Spec.PolicyTargetReferences, + allTargets, + crossNamespaceFrom{ + group: egv1a1.GroupVersion.Group, + kind: "BackendTrafficPolicy", + namespace: btp.Namespace, + }, + referenceGrants, + btp.Namespace, + namespaceLookup, + ) for _, ref := range refs { kind := string(ref.Kind) key := btpRoutingKey{ @@ -257,12 +272,22 @@ func (t *Translator) ProcessBackendTrafficPolicies( // 4. Finally, the policies targeting Gateways // Build gateway policy maps, which are needed when processing the policies targeting xRoutes. - t.buildGatewayPolicyMap(backendTrafficPolicies, gateways, gatewayMap, gatewayPolicyMap) + t.buildGatewayPolicyMap(backendTrafficPolicies, gateways, gatewayMap, gatewayPolicyMap, resources.ReferenceGrants) // Process the policies targeting RouteRules for _, currPolicy := range backendTrafficPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + routeMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(routeMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + handledPolicies[policyName] = policy + res = append(res, policy) + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, routeMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is defined, then it's a route rule. if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil { @@ -282,7 +307,7 @@ func (t *Translator) ProcessBackendTrafficPolicies( // Process the policies targeting Routes for _, currPolicy := range backendTrafficPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is not defined, then it's a route. if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil { @@ -302,7 +327,17 @@ func (t *Translator) ProcessBackendTrafficPolicies( // Process the policies targeting Listeners for _, currPolicy := range backendTrafficPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(gatewayMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + handledPolicies[policyName] = policy + res = append(res, policy) + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, gatewayMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is a gateway and the section name is defined, then it's a listener. if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil { @@ -312,8 +347,9 @@ func (t *Translator) ProcessBackendTrafficPolicies( handledPolicies[policyName] = policy res = append(res, policy) } + targetNamespace := namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed) t.processBackendTrafficPolicyForGateway(xdsIR, - gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) + gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget, targetNamespace) } } } @@ -321,7 +357,8 @@ func (t *Translator) ProcessBackendTrafficPolicies( // Process the policies targeting Gateways for _, currPolicy := range backendTrafficPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is a gateway and the section name is not defined, then it's a gateway. if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil { @@ -331,8 +368,9 @@ func (t *Translator) ProcessBackendTrafficPolicies( handledPolicies[policyName] = policy res = append(res, policy) } + targetNamespace := namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed) t.processBackendTrafficPolicyForGateway(xdsIR, - gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) + gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget, targetNamespace) } } } @@ -351,15 +389,17 @@ func (t *Translator) buildGatewayPolicyMap( gateways []*GatewayContext, gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, gatewayPolicyMap map[NamespacedNameWithSection]*egv1a1.BackendTrafficPolicy, + referenceGrants []*gwapiv1b1.ReferenceGrant, ) { for _, currPolicy := range backendTrafficPolicies { - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, referenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "BackendTrafficPolicy", namespace: currPolicy.Namespace}, referenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { if currTarget.Kind == resource.KindGateway { // Check if the gateway exists key := types.NamespacedName{ Name: string(currTarget.Name), - Namespace: currPolicy.Namespace, + Namespace: namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed), } gateway, ok := gatewayMap[key] if !ok { @@ -397,7 +437,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( gatewayPolicyMergedMap *GatewayPolicyRouteMap, gatewayPolicyMap map[NamespacedNameWithSection]*egv1a1.BackendTrafficPolicy, policy *egv1a1.BackendTrafficPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, ) { var ( targetedRoute RouteContext @@ -568,7 +608,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( key := policyTargetRouteKey{ Kind: string(currTarget.Kind), Name: string(currTarget.Name), - Namespace: policy.Namespace, + Namespace: string(currTarget.Namespace), } overriddenTargetsMessage := getOverriddenTargetsMessageForRoute(routeMap[key], currTarget.SectionName) if overriddenTargetsMessage != "" { @@ -590,7 +630,8 @@ func (t *Translator) processBackendTrafficPolicyForGateway( gatewayRouteMap *GatewayPolicyRouteMap, gatewayPolicyMergedMap *GatewayPolicyRouteMap, policy *egv1a1.BackendTrafficPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, + targetNamespace string, ) { var ( targetedGateway *GatewayContext @@ -598,7 +639,7 @@ func (t *Translator) processBackendTrafficPolicyForGateway( ) // Negative statuses have already been assigned so it's safe to skip - targetedGateway, resolveErr = resolveBackendTrafficPolicyGatewayTargetRef(policy, currTarget, gatewayMap) + targetedGateway, resolveErr = resolveBackendTrafficPolicyGatewayTargetRef(currTarget, targetNamespace, gatewayMap) if targetedGateway == nil { return } @@ -664,14 +705,14 @@ func (t *Translator) processBackendTrafficPolicyForGateway( } func resolveBackendTrafficPolicyGatewayTargetRef( - policy *egv1a1.BackendTrafficPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, + targetNamespace string, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { // Check if the gateway exists key := types.NamespacedName{ Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: targetNamespace, } gateway, ok := gateways[key] @@ -729,14 +770,14 @@ func resolveBackendTrafficPolicyGatewayTargetRef( func resolveBackendTrafficPolicyRouteTargetRef( policy *egv1a1.BackendTrafficPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { // Check if the route exists key := policyTargetRouteKey{ Kind: string(target.Kind), Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: string(target.Namespace), } route, ok := routes[key] @@ -789,7 +830,7 @@ func resolveBackendTrafficPolicyRouteTargetRef( func (t *Translator) translateBackendTrafficPolicyForRoute( policy *egv1a1.BackendTrafficPolicy, route RouteContext, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, xdsIR resource.XdsIRMap, policyTargetGatewayNN *types.NamespacedName, policyTargetListener *gwapiv1.SectionName, @@ -815,7 +856,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute( func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( policy, parentPolicy *egv1a1.BackendTrafficPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, policyTargetGatewayNN types.NamespacedName, policyTargetListener *gwapiv1.SectionName, route RouteContext, xdsIR resource.XdsIRMap, ) error { @@ -876,7 +917,7 @@ func (t *Translator) translateBackendTrafficPolicyForRouteWithMerge( func (t *Translator) applyTrafficFeatureToRoute(route RouteContext, tf *ir.TrafficFeatures, errs error, policy *egv1a1.BackendTrafficPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, x *ir.Xds, policyTargetListener *gwapiv1.SectionName, ) { @@ -1169,7 +1210,7 @@ func buildBackendMetrics(metrics *egv1a1.BackendMetrics) *ir.BackendMetrics { } func (t *Translator) translateBackendTrafficPolicyForGateway( - policy *egv1a1.BackendTrafficPolicy, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + policy *egv1a1.BackendTrafficPolicy, target policyTargetReferenceWithSectionName, gateway *GatewayContext, xdsIR resource.XdsIRMap, ) error { tf, errs := t.buildTrafficFeatures(policy) diff --git a/internal/gatewayapi/backendtrafficpolicy_test.go b/internal/gatewayapi/backendtrafficpolicy_test.go index 4bf8ca9a1f..4267c2fbee 100644 --- a/internal/gatewayapi/backendtrafficpolicy_test.go +++ b/internal/gatewayapi/backendtrafficpolicy_test.go @@ -1744,7 +1744,7 @@ func TestBTPRoutingTypeIndex(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - idx := BuildBTPRoutingTypeIndex(tt.btps, tt.routes, tt.gateways) + idx := BuildBTPRoutingTypeIndex(tt.btps, tt.routes, tt.gateways, nil, nil) got := idx.LookupBTPRoutingType(tt.routeKind, tt.routeNN, tt.gatewayNN, tt.listenerName, tt.routeRuleName) require.Equal(t, tt.expected, got) }) diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index 44138972dd..431a460216 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -32,7 +32,7 @@ const ( AllSections = "/" ) -func hasSectionName(target *gwapiv1.LocalPolicyTargetReferenceWithSectionName) bool { +func hasSectionName(target *policyTargetReferenceWithSectionName) bool { return target.SectionName != nil } @@ -90,7 +90,14 @@ func (t *Translator) ProcessClientTrafficPolicies( // so there's no need to try to match targets with selectors targetRefs := currPolicy.Spec.GetTargetRefs() for _, currTarget := range targetRefs { - if hasSectionName(&currTarget) { + targetRef := policyTargetReferenceWithSectionName{ + Group: currTarget.Group, + Kind: currTarget.Kind, + Name: currTarget.Name, + Namespace: gwapiv1.Namespace(currPolicy.Namespace), + SectionName: currTarget.SectionName, + } + if hasSectionName(&targetRef) { policy, found := handledPolicies[policyName] if !found { policy = currPolicy @@ -98,7 +105,7 @@ func (t *Translator) ProcessClientTrafficPolicies( res = append(res, policy) } - gateway, resolveErr := resolveClientTrafficPolicyTargetRef(policy, &currTarget, gatewayMap) + gateway, resolveErr := resolveClientTrafficPolicyTargetRef(policy, &targetRef, gatewayMap) // Negative statuses have already been assigned so its safe to skip if gateway == nil { @@ -182,7 +189,39 @@ func (t *Translator) ProcessClientTrafficPolicies( // Policy with no section set (targeting all sections) for _, currPolicy := range clientTrafficPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + matches := getPolicySelectorTargetMatches( + currPolicy.Spec.PolicyTargetReferences, + gateways, + crossNamespaceFrom{ + group: egv1a1.GroupVersion.Group, + kind: "ClientTrafficPolicy", + namespace: currPolicy.Namespace, + }, + resources.ReferenceGrants, + currPolicy.Namespace, + t.GetNamespace, + ) + targetRefs := getPolicyTargetRefs( + currPolicy.Spec.PolicyTargetReferences, + gateways, + crossNamespaceFrom{ + group: egv1a1.GroupVersion.Group, + kind: "ClientTrafficPolicy", + namespace: currPolicy.Namespace, + }, + resources.ReferenceGrants, + currPolicy.Namespace, + t.GetNamespace, + ) + if len(matches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + res = append(res, policy) + handledPolicies[policyName] = policy + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, matches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { if !hasSectionName(&currTarget) { @@ -311,13 +350,13 @@ func (t *Translator) ProcessClientTrafficPolicies( func resolveClientTrafficPolicyTargetRef( policy *egv1a1.ClientTrafficPolicy, - targetRef *gwapiv1.LocalPolicyTargetReferenceWithSectionName, + targetRef *policyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { // Check if the gateway exists key := types.NamespacedName{ Name: string(targetRef.Name), - Namespace: policy.Namespace, + Namespace: string(targetRef.Namespace), } gateway, ok := gateways[key] diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index 0ca167d418..15241a9e97 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -114,7 +114,17 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( // Process the policies targeting RouteRules for _, currPolicy := range envoyExtensionPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + routeMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(routeMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + res = append(res, policy) + handledPolicies[policyName] = policy + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, routeMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is defined, then it's a route rule. if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil { @@ -134,7 +144,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( // Process the policies targeting xRoutes for _, currPolicy := range envoyExtensionPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is not defined, then it's a route. if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil { @@ -154,7 +164,17 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( // Process the policies targeting Listeners for _, currPolicy := range envoyExtensionPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(gatewayMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + res = append(res, policy) + handledPolicies[policyName] = policy + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, gatewayMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is a gateway and the section name is defined, then it's a listener. if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil { @@ -174,7 +194,7 @@ func (t *Translator) ProcessEnvoyExtensionPolicies( // Process the policies targeting Gateways for _, currPolicy := range envoyExtensionPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "EnvoyExtensionPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is a gateway and the section name is not defined, then it's a gateway. if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil { @@ -206,7 +226,7 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( routeMap map[policyTargetRouteKey]*policyRouteTargetContext, gatewayRouteMap map[string]map[string]sets.Set[string], policy *egv1a1.EnvoyExtensionPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, ) { var ( targetedRoute RouteContext @@ -291,7 +311,7 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( key := policyTargetRouteKey{ Kind: string(currTarget.Kind), Name: string(currTarget.Name), - Namespace: policy.Namespace, + Namespace: string(currTarget.Namespace), } overriddenTargetsMessage := getOverriddenTargetsMessageForRoute(routeMap[key], currTarget.SectionName) if overriddenTargetsMessage != "" { @@ -313,7 +333,7 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, gatewayRouteMap map[string]map[string]sets.Set[string], policy *egv1a1.EnvoyExtensionPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, ) { var ( targetedGateway *GatewayContext @@ -381,13 +401,13 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( func resolveEnvoyExtensionPolicyGatewayTargetRef( policy *egv1a1.EnvoyExtensionPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { // Check if the gateway exists key := types.NamespacedName{ Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: string(target.Namespace), } gateway, ok := gateways[key] @@ -443,14 +463,14 @@ func resolveEnvoyExtensionPolicyGatewayTargetRef( func resolveEnvoyExtensionPolicyRouteTargetRef( policy *egv1a1.EnvoyExtensionPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { // Check if the route exists key := policyTargetRouteKey{ Kind: string(target.Kind), Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: string(target.Namespace), } route, ok := routes[key] @@ -503,7 +523,7 @@ func resolveEnvoyExtensionPolicyRouteTargetRef( func (t *Translator) translateEnvoyExtensionPolicyForRoute( policy *egv1a1.EnvoyExtensionPolicy, route RouteContext, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, xdsIR resource.XdsIRMap, resources *resource.Resources, ) error { @@ -617,7 +637,7 @@ func (t *Translator) translateEnvoyExtensionPolicyForRoute( func (t *Translator) translateEnvoyExtensionPolicyForGateway( policy *egv1a1.EnvoyExtensionPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, gateway *GatewayContext, xdsIR resource.XdsIRMap, resources *resource.Resources, diff --git a/internal/gatewayapi/extensionserverpolicy.go b/internal/gatewayapi/extensionserverpolicy.go index 9a1b9ff2fc..0b8ed9be47 100644 --- a/internal/gatewayapi/extensionserverpolicy.go +++ b/internal/gatewayapi/extensionserverpolicy.go @@ -11,9 +11,11 @@ import ( "fmt" "strings" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi/resource" @@ -24,6 +26,7 @@ import ( func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unstructured, gateways []*GatewayContext, + referenceGrants []*gwapiv1b1.ReferenceGrant, xdsIR resource.XdsIRMap, ) ([]unstructured.Unstructured, error) { res := []unstructured.Unstructured{} @@ -43,7 +46,7 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst policy := &policy var policyStatus gwapiv1.PolicyStatus accepted := false - targetRefs, err := extractTargetRefs(policy, gateways) + targetRefs, err := extractTargetRefs(policy, gateways, referenceGrants, t.GetNamespace) if err != nil { errs = errors.Join(errs, fmt.Errorf("error finding targetRefs for policy %s: %w", policy.GetName(), err)) continue @@ -88,7 +91,12 @@ func (t *Translator) ProcessExtensionServerPolicies(policies []unstructured.Unst return res, errs } -func extractTargetRefs(policy *unstructured.Unstructured, gateways []*GatewayContext) ([]gwapiv1.LocalPolicyTargetReferenceWithSectionName, error) { +func extractTargetRefs( + policy *unstructured.Unstructured, + gateways []*GatewayContext, + referenceGrants []*gwapiv1b1.ReferenceGrant, + namespaceLookup func(string) *corev1.Namespace, +) ([]policyTargetReferenceWithSectionName, error) { spec, found := policy.Object["spec"].(map[string]any) if !found { return nil, fmt.Errorf("no targets found for the policy") @@ -101,14 +109,25 @@ func extractTargetRefs(policy *unstructured.Unstructured, gateways []*GatewayCon if err := json.Unmarshal(specAsJSON, &targetRefs); err != nil { return nil, fmt.Errorf("no targets found for the policy") } - ret := getPolicyTargetRefs(targetRefs, gateways, policy.GetNamespace()) + ret := getPolicyTargetRefs( + targetRefs, + gateways, + crossNamespaceFrom{ + group: policy.GroupVersionKind().Group, + kind: policy.GroupVersionKind().Kind, + namespace: policy.GetNamespace(), + }, + referenceGrants, + policy.GetNamespace(), + namespaceLookup, + ) if len(ret) == 0 { return nil, fmt.Errorf("no targets found for the policy") } return ret, nil } -func resolveExtServerPolicyGatewayTargetRef(policy *unstructured.Unstructured, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext { +func resolveExtServerPolicyGatewayTargetRef(policy *unstructured.Unstructured, target policyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext { // Check if the gateway exists key := types.NamespacedName{ Name: string(target.Name), @@ -150,7 +169,7 @@ func ExtServerPolicyStatusAsPolicyStatus(policy *unstructured.Unstructured) gwap func (t *Translator) translateExtServerPolicyForGateway( policy *unstructured.Unstructured, gateway *GatewayContext, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, xdsIR resource.XdsIRMap, ) bool { irKey := t.getIRKey(gateway.Gateway) diff --git a/internal/gatewayapi/extensionserverpolicy_test.go b/internal/gatewayapi/extensionserverpolicy_test.go index 751a76af99..8dc8b64098 100644 --- a/internal/gatewayapi/extensionserverpolicy_test.go +++ b/internal/gatewayapi/extensionserverpolicy_test.go @@ -17,7 +17,7 @@ func TestExtractTargetRefs(t *testing.T) { tests := []struct { desc string specInput map[string]any - output []gwapiv1.LocalPolicyTargetReferenceWithSectionName + output []policyTargetReferenceWithSectionName expectedError string }{ { @@ -61,13 +61,11 @@ func TestExtractTargetRefs(t *testing.T) { "name": "name", }, }, - output: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + output: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "some.group", - Kind: "SomeKind", - Name: "name", - }, + Group: "some.group", + Kind: "SomeKind", + Name: "name", }, }, }, @@ -87,20 +85,16 @@ func TestExtractTargetRefs(t *testing.T) { }, }, }, - output: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + output: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "some.group", - Kind: "SomeKind2", - Name: "othername", - }, + Group: "some.group", + Kind: "SomeKind2", + Name: "othername", }, { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "some.group", - Kind: "SomeKind", - Name: "name", - }, + Group: "some.group", + Kind: "SomeKind", + Name: "name", }, }, }, @@ -112,7 +106,7 @@ func TestExtractTargetRefs(t *testing.T) { Object: map[string]any{}, } policy.Object["spec"] = currTest.specInput - targets, err := extractTargetRefs(policy, []*GatewayContext{}) + targets, err := extractTargetRefs(policy, []*GatewayContext{}, nil, nil) if currTest.expectedError != "" { require.EqualError(t, err, currTest.expectedError) diff --git a/internal/gatewayapi/helpers.go b/internal/gatewayapi/helpers.go index 3b44fd9f66..e0e96de365 100644 --- a/internal/gatewayapi/helpers.go +++ b/internal/gatewayapi/helpers.go @@ -23,9 +23,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi/resource" + "github.com/envoyproxy/gateway/internal/gatewayapi/status" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils" ) @@ -727,10 +729,54 @@ func irConfigName(policy client.Object) string { } type targetRefWithTimestamp struct { - gwapiv1.LocalPolicyTargetReferenceWithSectionName + policyTargetReferenceWithSectionName CreationTimestamp metav1.Time } +// policyTargetReferenceWithSectionName extends the Gateway API's LocalPolicyTargetReference to include a Namespace field. +// This is necessary because policies may reference targets in other namespaces. +type policyTargetReferenceWithSectionName struct { + // Group is the group of the target resource. + // +required + Group gwapiv1.Group `json:"group"` + + // Kind is kind of the target resource. + // +required + Kind gwapiv1.Kind `json:"kind"` + + // Name is the name of the target resource. + // +required + Name gwapiv1.ObjectName `json:"name"` + + // Namespace is the namespace of the target resource. When unspecified, it is assumed to be in the same namespace as the policy. + Namespace gwapiv1.Namespace `json:"namespace"` + + // SectionName is the name of a section within the target resource. When + // unspecified, this targetRef targets the entire resource. In the following + // resources, SectionName is interpreted as the following: + // + // * Gateway: Listener name + // * HTTPRoute: HTTPRouteRule name + // * Service: Port name + // + // If a SectionName is specified, but does not exist on the targeted object, + // the Policy must fail to attach, and the policy implementation should record + // a `ResolvedRefs` or similar Condition in the Policy's status. + // + // +optional + SectionName *gwapiv1.SectionName `json:"sectionName,omitempty"` +} + +type policySelectorTargetMatch[T client.Object] struct { + Object T + Ref policyTargetReferenceWithSectionName +} + +type policySelectorTargetMatches[T client.Object] struct { + Allowed []policySelectorTargetMatch[T] + Denied []policySelectorTargetMatch[T] +} + func selectorFromTargetSelector(selector egv1a1.TargetSelector) labels.Selector { l, err := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ MatchLabels: selector.MatchLabels, @@ -743,8 +789,110 @@ func selectorFromTargetSelector(selector egv1a1.TargetSelector) labels.Selector return l } -func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, potentialTargets []T, policyNamespace string) []gwapiv1.LocalPolicyTargetReferenceWithSectionName { - dedup := sets.New[targetRefWithTimestamp]() +func targetNamespaceLabelSelector(namespaces *egv1a1.TargetSelectorNamespaces) labels.Selector { + if namespaces == nil || namespaces.Selector == nil { + return labels.Nothing() + } + + selector, err := metav1.LabelSelectorAsSelector(namespaces.Selector) + if err != nil { + return labels.Nothing() + } + + return selector +} + +func targetSelectorNamespacesMatch( + namespaces *egv1a1.TargetSelectorNamespaces, + policyNamespace, + targetNamespace string, + targetNamespaceLabels map[string]string, +) bool { + if namespaces == nil { + return targetNamespace == policyNamespace + } + + switch namespaces.From { + case "", egv1a1.TargetNamespaceFromSame: + return targetNamespace == policyNamespace + case egv1a1.TargetNamespaceFromAll: + return true + case egv1a1.TargetNamespaceFromSelector: + if targetNamespaceLabels == nil { + return false + } + return targetNamespaceLabelSelector(namespaces).Matches(labels.Set(targetNamespaceLabels)) + default: + return false + } +} + +func targetNamespaceMatches( + selector egv1a1.TargetSelector, + policyNamespace, + targetNamespace string, + namespaceLookup func(string) *corev1.Namespace, +) bool { + var targetNamespaceLabels map[string]string + if namespaceLookup != nil { + if ns := namespaceLookup(targetNamespace); ns != nil { + targetNamespaceLabels = ns.GetLabels() + } + } + + return targetSelectorNamespacesMatch(selector.Namespaces, policyNamespace, targetNamespace, targetNamespaceLabels) +} + +func isCrossNamespacePolicyTargetRefAllowed( + from crossNamespaceFrom, + to crossNamespaceTo, + referenceGrants []*gwapiv1b1.ReferenceGrant, +) bool { + if from.namespace == to.namespace { + return true + } + + for _, referenceGrant := range referenceGrants { + if referenceGrant.Namespace != to.namespace { + continue + } + + var fromAllowed bool + for _, refGrantFrom := range referenceGrant.Spec.From { + if string(refGrantFrom.Namespace) == from.namespace && + string(refGrantFrom.Group) == from.group && + string(refGrantFrom.Kind) == from.kind { + fromAllowed = true + break + } + } + if !fromAllowed { + continue + } + + for _, refGrantTo := range referenceGrant.Spec.To { + if string(refGrantTo.Group) == to.group && + string(refGrantTo.Kind) == to.kind && + (refGrantTo.Name == nil || *refGrantTo.Name == "" || string(*refGrantTo.Name) == to.name) { + return true + } + } + } + + return false +} + +func getPolicySelectorTargetMatches[T client.Object]( + policy egv1a1.PolicyTargetReferences, + potentialTargets []T, + from crossNamespaceFrom, + referenceGrants []*gwapiv1b1.ReferenceGrant, + policyNamespace string, + namespaceLookup func(string) *corev1.Namespace, +) policySelectorTargetMatches[T] { + allowedDedup := sets.New[targetRefWithTimestamp]() + deniedDedup := sets.New[policyTargetReferenceWithSectionName]() + matches := policySelectorTargetMatches[T]{} for _, currSelector := range policy.TargetSelectors { labelSelector := selectorFromTargetSelector(currSelector) for _, obj := range potentialTargets { @@ -754,32 +902,82 @@ func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, continue } - // Skip objects not in the same namespace as the policy - if obj.GetNamespace() != policyNamespace { + if !targetNamespaceMatches(currSelector, policyNamespace, obj.GetNamespace(), namespaceLookup) { continue } - if labelSelector.Matches(labels.Set(obj.GetLabels())) { - dedup.Insert(targetRefWithTimestamp{ - CreationTimestamp: obj.GetCreationTimestamp(), - LocalPolicyTargetReferenceWithSectionName: gwapiv1.LocalPolicyTargetReferenceWithSectionName{ - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: gwapiv1.Group(gvk.Group), - Kind: gwapiv1.Kind(gvk.Kind), - Name: gwapiv1.ObjectName(obj.GetName()), - }, - }, + ref := policyTargetReferenceWithSectionName{ + Group: gwapiv1.Group(gvk.Group), + Kind: gwapiv1.Kind(gvk.Kind), + Name: gwapiv1.ObjectName(obj.GetName()), + Namespace: gwapiv1.Namespace(obj.GetNamespace()), + } + + if !labelSelector.Matches(labels.Set(obj.GetLabels())) { + continue + } + + if !isCrossNamespacePolicyTargetRefAllowed( + from, + crossNamespaceTo{ + group: gvk.Group, + kind: gvk.Kind, + namespace: obj.GetNamespace(), + name: obj.GetName(), + }, + referenceGrants, + ) { + if deniedDedup.Has(ref) { + continue + } + deniedDedup.Insert(ref) + matches.Denied = append(matches.Denied, policySelectorTargetMatch[T]{ + Object: obj, + Ref: ref, }) + continue } + + targetRef := targetRefWithTimestamp{ + CreationTimestamp: obj.GetCreationTimestamp(), + policyTargetReferenceWithSectionName: ref, + } + if allowedDedup.Has(targetRef) { + continue + } + allowedDedup.Insert(targetRef) + matches.Allowed = append(matches.Allowed, policySelectorTargetMatch[T]{ + Object: obj, + Ref: ref, + }) } } - selectorsList := dedup.UnsortedList() + + return matches +} + +func getPolicyTargetRefs[T client.Object]( + policy egv1a1.PolicyTargetReferences, + potentialTargets []T, + from crossNamespaceFrom, + referenceGrants []*gwapiv1b1.ReferenceGrant, + policyNamespace string, + namespaceLookup func(string) *corev1.Namespace, +) []policyTargetReferenceWithSectionName { + matches := getPolicySelectorTargetMatches(policy, potentialTargets, from, referenceGrants, policyNamespace, namespaceLookup) + selectorsList := make([]targetRefWithTimestamp, 0, len(matches.Allowed)) + for _, match := range matches.Allowed { + selectorsList = append(selectorsList, targetRefWithTimestamp{ + CreationTimestamp: match.Object.GetCreationTimestamp(), + policyTargetReferenceWithSectionName: match.Ref, + }) + } slices.SortFunc(selectorsList, func(i, j targetRefWithTimestamp) int { return i.CreationTimestamp.Compare(j.CreationTimestamp.Time) }) - ret := make([]gwapiv1.LocalPolicyTargetReferenceWithSectionName, len(selectorsList)) + ret := make([]policyTargetReferenceWithSectionName, len(selectorsList)) for i, v := range selectorsList { - ret[i] = v.LocalPolicyTargetReferenceWithSectionName + ret[i] = v.policyTargetReferenceWithSectionName } // Plain targetRefs in the policy don't have an associated creation timestamp, but can still refer // to targets that were already found via the selectors. Only add them to the returned list if @@ -791,14 +989,80 @@ func getPolicyTargetRefs[T client.Object](policy egv1a1.PolicyTargetReferences, // This can happen when the targetRef structure is read from extension server policies continue } - if !fastLookup.Has(v) { - ret = append(ret, v) + targetRef := policyTargetReferenceWithSectionName{ + Group: v.Group, + Kind: v.Kind, + Name: v.Name, + Namespace: gwapiv1.Namespace(policyNamespace), + SectionName: v.SectionName, + } + if !fastLookup.Has(targetRef) { + ret = append(ret, targetRef) } } return ret } +func setPolicyTargetRefNotPermittedStatus[T client.Object]( + policyStatus *gwapiv1.PolicyStatus, + denied []policySelectorTargetMatch[T], + controllerName string, + generation int64, +) { + for _, deniedMatch := range denied { + msg := fmt.Sprintf( + "Target %s %s/%s is not permitted by any ReferenceGrant.", + deniedMatch.Object.GetObjectKind().GroupVersionKind().Kind, + deniedMatch.Object.GetNamespace(), + deniedMatch.Object.GetName(), + ) + + switch obj := any(deniedMatch.Object).(type) { + case *GatewayContext: + ancestorRef := getAncestorRefForPolicy(utils.NamespacedName(obj), nil) + status.SetResolveErrorForPolicyAncestor(policyStatus, &ancestorRef, controllerName, generation, &status.PolicyResolveError{ + Reason: egv1a1.PolicyReasonRefNotPermitted, + Message: msg, + }) + case RouteContext: + parentRefs := GetManagedParentReferences(obj) + ancestorRefs := make([]*gwapiv1.ParentReference, 0, len(parentRefs)) + for _, p := range parentRefs { + if p.Kind != nil && *p.Kind != resource.KindGateway { + continue + } + namespace := obj.GetNamespace() + if p.Namespace != nil { + namespace = string(*p.Namespace) + } + ancestorRef := getAncestorRefForPolicy(types.NamespacedName{ + Name: string(p.Name), + Namespace: namespace, + }, p.SectionName) + ancestorRefs = append(ancestorRefs, &ancestorRef) + } + status.SetResolveErrorForPolicyAncestors(policyStatus, ancestorRefs, controllerName, generation, &status.PolicyResolveError{ + Reason: egv1a1.PolicyReasonRefNotPermitted, + Message: msg, + }) + } + } +} + +func namespaceForPolicyTargetRef[T client.Object]( + target policyTargetReferenceWithSectionName, + defaultNamespace string, + matches []policySelectorTargetMatch[T], +) string { + for _, match := range matches { + if match.Ref == target { + return match.Object.GetNamespace() + } + } + return defaultNamespace +} + // Sets *target to value if and only if *target is nil func setIfNil[T any](target **T, value *T) { if *target == nil { diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go index 44940e634f..f6fd0484f4 100644 --- a/internal/gatewayapi/helpers_test.go +++ b/internal/gatewayapi/helpers_test.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/ir" @@ -203,10 +204,12 @@ func TestValidateHTTPFilterRef(t *testing.T) { func TestGetPolicyTargetRefs(t *testing.T) { testCases := []struct { - name string - policy egv1a1.PolicyTargetReferences - targets []*unstructured.Unstructured - results []gwapiv1.LocalPolicyTargetReferenceWithSectionName + name string + policy egv1a1.PolicyTargetReferences + targets []*unstructured.Unstructured + namespaces []*corev1.Namespace + grants []*gwapiv1b1.ReferenceGrant + results []policyTargetReferenceWithSectionName }{ { name: "simple", @@ -262,13 +265,12 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + results: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "gateway.networking.k8s.io", - Kind: "Gateway", - Name: "second", - }, + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "second", + Namespace: "default", }, }, }, @@ -331,20 +333,18 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + results: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "gateway.networking.k8s.io", - Kind: "TLSRoute", - Name: "third", - }, + Group: "gateway.networking.k8s.io", + Kind: "TLSRoute", + Name: "third", + Namespace: "default", }, { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "gateway.networking.k8s.io", - Kind: "Gateway", - Name: "second", - }, + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "second", + Namespace: "default", }, }, }, @@ -410,13 +410,12 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + results: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "gateway.networking.k8s.io", - Kind: "TLSRoute", - Name: "third", - }, + Group: "gateway.networking.k8s.io", + Kind: "TLSRoute", + Name: "third", + Namespace: "default", }, }, }, @@ -474,7 +473,7 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{}, + results: []policyTargetReferenceWithSectionName{}, }, { name: "match expression", @@ -520,13 +519,12 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{ + results: []policyTargetReferenceWithSectionName{ { - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: "gateway.networking.k8s.io", - Kind: "Gateway", - Name: "first", - }, + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "first", + Namespace: "default", }, }, }, @@ -574,13 +572,348 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, }, }, - results: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{}, + results: []policyTargetReferenceWithSectionName{}, + }, + { + name: "namespaces from same", + policy: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Kind: "Gateway", + Namespaces: &egv1a1.TargetSelectorNamespaces{ + From: egv1a1.TargetNamespaceFromSame, + }, + MatchLabels: map[string]string{ + "pick": "me", + }, + }, + }, + }, + targets: []*unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "same-ns", + "namespace": "default", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "other-ns", + "namespace": "other", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + }, + results: []policyTargetReferenceWithSectionName{ + { + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "same-ns", + Namespace: "default", + }, + }, + }, + { + name: "namespaces from all", + policy: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Kind: "Gateway", + Namespaces: &egv1a1.TargetSelectorNamespaces{ + From: egv1a1.TargetNamespaceFromAll, + }, + MatchLabels: map[string]string{ + "pick": "me", + }, + }, + }, + }, + targets: []*unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "same-ns", + "namespace": "default", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "other-ns", + "namespace": "other", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + }, + grants: []*gwapiv1b1.ReferenceGrant{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "allow-default-btp", + Namespace: "other", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1b1.Kind("BackendTrafficPolicy"), + Namespace: gwapiv1b1.Namespace("default"), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Group: gwapiv1b1.Group(gwapiv1.GroupName), + Kind: gwapiv1b1.Kind("Gateway"), + }, + }, + }, + }, + }, + results: []policyTargetReferenceWithSectionName{ + { + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "same-ns", + Namespace: "default", + }, + { + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "other-ns", + Namespace: "other", + }, + }, + }, + { + name: "namespaces from selector", + policy: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Kind: "Gateway", + Namespaces: &egv1a1.TargetSelectorNamespaces{ + From: egv1a1.TargetNamespaceFromSelector, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "team": "blue", + }, + }, + }, + MatchLabels: map[string]string{ + "pick": "me", + }, + }, + }, + }, + targets: []*unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "selected-ns", + "namespace": "selected", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "unselected-ns", + "namespace": "unselected", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + }, + namespaces: []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "selected", + Labels: map[string]string{"team": "blue"}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "unselected", + Labels: map[string]string{"team": "green"}, + }, + }, + }, + grants: []*gwapiv1b1.ReferenceGrant{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "allow-default-btp", + Namespace: "selected", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1b1.Kind("BackendTrafficPolicy"), + Namespace: gwapiv1b1.Namespace("default"), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Group: gwapiv1b1.Group(gwapiv1.GroupName), + Kind: gwapiv1b1.Kind("Gateway"), + }, + }, + }, + }, + }, + results: []policyTargetReferenceWithSectionName{ + { + + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "selected-ns", + Namespace: "selected", + }, + }, + }, + { + name: "namespaces from selector requires known namespace labels", + policy: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Kind: "Gateway", + Namespaces: &egv1a1.TargetSelectorNamespaces{ + From: egv1a1.TargetNamespaceFromSelector, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "team": "blue", + }, + }, + }, + MatchLabels: map[string]string{ + "pick": "me", + }, + }, + }, + }, + targets: []*unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "unknown-ns", + "namespace": "unknown", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + }, + results: []policyTargetReferenceWithSectionName{}, + }, + { + name: "namespaces from all cross-namespace requires reference grant", + policy: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Kind: "Gateway", + Namespaces: &egv1a1.TargetSelectorNamespaces{ + From: egv1a1.TargetNamespaceFromAll, + }, + MatchLabels: map[string]string{ + "pick": "me", + }, + }, + }, + }, + targets: []*unstructured.Unstructured{ + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "same-ns", + "namespace": "default", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + { + Object: map[string]any{ + "apiVersion": "gateway.networking.k8s.io/v1", + "kind": "Gateway", + "metadata": map[string]any{ + "name": "other-ns", + "namespace": "other", + "labels": map[string]any{ + "pick": "me", + }, + }, + }, + }, + }, + results: []policyTargetReferenceWithSectionName{ + { + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "same-ns", + Namespace: "default", + }, + }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - results := getPolicyTargetRefs(tc.policy, tc.targets, "default") + namespaceMap := map[string]*corev1.Namespace{} + for _, ns := range tc.namespaces { + namespaceMap[ns.Name] = ns + } + + results := getPolicyTargetRefs( + tc.policy, + tc.targets, + crossNamespaceFrom{ + group: egv1a1.GroupVersion.Group, + kind: "BackendTrafficPolicy", + namespace: "default", + }, + tc.grants, + "default", + func(name string) *corev1.Namespace { + return namespaceMap[name] + }, + ) require.ElementsMatch(t, results, tc.results) }) } diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 8ed73f90d5..9907c00fac 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -32,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation" "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi/resource" @@ -135,12 +136,22 @@ func (t *Translator) ProcessSecurityPolicies( // 4. Finally, the policies targeting Gateways // Build gateway policy maps, which are needed when processing the policies targeting xRoutes. - t.buildGatewayPolicyMapForSecurity(securityPolicies, gateways, gatewayMap, gatewayPolicyMap) + t.buildGatewayPolicyMapForSecurity(securityPolicies, gateways, gatewayMap, gatewayPolicyMap, resources.ReferenceGrants) // Process the policies targeting RouteRules (HTTP + TCP) for _, currPolicy := range securityPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + routeMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(routeMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + handledPolicies[policyName] = policy + res = append(res, policy) + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, routeMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is defined, then it's a route rule. if currTarget.Kind != resource.KindGateway && currTarget.SectionName != nil { @@ -158,7 +169,7 @@ func (t *Translator) ProcessSecurityPolicies( // Process the policies targeting xRoutes (HTTP + TCP) for _, currPolicy := range securityPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, currPolicy.Namespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, routes, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is not a gateway, then it's an xRoute. If the section name is not defined, then it's a route. if currTarget.Kind != resource.KindGateway && currTarget.SectionName == nil { @@ -176,7 +187,17 @@ func (t *Translator) ProcessSecurityPolicies( // Process the policies targeting Listeners for _, currPolicy := range securityPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + if len(gatewayMatches.Denied) > 0 { + policy, found := handledPolicies[policyName] + if !found { + policy = currPolicy + handledPolicies[policyName] = policy + res = append(res, policy) + } + setPolicyTargetRefNotPermittedStatus(&policy.Status, gatewayMatches.Denied, t.GatewayControllerName, policy.Generation) + } for _, currTarget := range targetRefs { // If the target is a gateway and the section name is defined, then it's a listener. if currTarget.Kind == resource.KindGateway && currTarget.SectionName != nil { @@ -186,15 +207,17 @@ func (t *Translator) ProcessSecurityPolicies( handledPolicies[policyName] = policy res = append(res, policy) } + targetNamespace := namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed) - t.processSecurityPolicyForGateway(resources, xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) + t.processSecurityPolicyForGateway(resources, xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget, targetNamespace) } } } // Process the policies targeting Gateways for _, currPolicy := range securityPolicies { policyName := utils.NamespacedName(currPolicy) - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, resources.ReferenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { // If the target is a gateway and the section name is not defined, then it's a gateway. if currTarget.Kind == resource.KindGateway && currTarget.SectionName == nil { @@ -204,8 +227,9 @@ func (t *Translator) ProcessSecurityPolicies( handledPolicies[policyName] = policy res = append(res, policy) } + targetNamespace := namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed) - t.processSecurityPolicyForGateway(resources, xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget) + t.processSecurityPolicyForGateway(resources, xdsIR, gatewayMap, gatewayRouteMap, gatewayPolicyMerged, policy, currTarget, targetNamespace) } } } @@ -224,15 +248,17 @@ func (t *Translator) buildGatewayPolicyMapForSecurity( gateways []*GatewayContext, gatewayMap map[types.NamespacedName]*policyGatewayTargetContext, gatewayPolicyMap map[NamespacedNameWithSection]*egv1a1.SecurityPolicy, + referenceGrants []*gwapiv1b1.ReferenceGrant, ) { for _, currPolicy := range securityPolicies { - targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, currPolicy.Namespace) + gatewayMatches := getPolicySelectorTargetMatches(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, referenceGrants, currPolicy.Namespace, t.GetNamespace) + targetRefs := getPolicyTargetRefs(currPolicy.Spec.PolicyTargetReferences, gateways, crossNamespaceFrom{group: egv1a1.GroupVersion.Group, kind: "SecurityPolicy", namespace: currPolicy.Namespace}, referenceGrants, currPolicy.Namespace, t.GetNamespace) for _, currTarget := range targetRefs { if currTarget.Kind == resource.KindGateway { // Check if the gateway exists key := types.NamespacedName{ Name: string(currTarget.Name), - Namespace: currPolicy.Namespace, + Namespace: namespaceForPolicyTargetRef(currTarget, currPolicy.Namespace, gatewayMatches.Allowed), } gateway, ok := gatewayMap[key] if !ok { @@ -273,7 +299,7 @@ func (t *Translator) processSecurityPolicyForRoute( gatewayPolicyMerged *GatewayPolicyRouteMap, gatewayPolicyMap map[NamespacedNameWithSection]*egv1a1.SecurityPolicy, policy *egv1a1.SecurityPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, ) { var ( targetedRoute RouteContext @@ -496,7 +522,7 @@ func (t *Translator) processSecurityPolicyForRoute( key := policyTargetRouteKey{ Kind: string(currTarget.Kind), Name: string(currTarget.Name), - Namespace: policy.Namespace, + Namespace: string(currTarget.Namespace), } overriddenTargetsMessage := getOverriddenTargetsMessageForRoute(routeMap[key], currTarget.SectionName) if overriddenTargetsMessage != "" { @@ -519,14 +545,15 @@ func (t *Translator) processSecurityPolicyForGateway( gatewayRouteMap *GatewayPolicyRouteMap, gatewayPolicyMergedMap *GatewayPolicyRouteMap, policy *egv1a1.SecurityPolicy, - currTarget gwapiv1.LocalPolicyTargetReferenceWithSectionName, + currTarget policyTargetReferenceWithSectionName, + targetNamespace string, ) { var ( targetedGateway *GatewayContext resolveErr *status.PolicyResolveError ) - targetedGateway, resolveErr = resolveSecurityPolicyGatewayTargetRef(policy, currTarget, gatewayMap) + targetedGateway, resolveErr = resolveSecurityPolicyGatewayTargetRef(currTarget, targetNamespace, gatewayMap) // Skip if the gateway is not found // It's not necessarily an error because the SecurityPolicy may be // reconciled by multiple controllers. And the other controller may @@ -694,14 +721,14 @@ func validateBasicAuth(_ *egv1a1.BasicAuth) error { } func resolveSecurityPolicyGatewayTargetRef( - policy *egv1a1.SecurityPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, + targetNamespace string, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { // Find the Gateway key := types.NamespacedName{ Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: targetNamespace, } gateway, ok := gateways[key] @@ -760,14 +787,14 @@ func resolveSecurityPolicyGatewayTargetRef( func resolveSecurityPolicyRouteTargetRef( policy *egv1a1.SecurityPolicy, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { // Check if the route exists key := policyTargetRouteKey{ Kind: string(target.Kind), Name: string(target.Name), - Namespace: policy.Namespace, + Namespace: string(target.Namespace), } route, ok := routes[key] @@ -822,7 +849,7 @@ func resolveSecurityPolicyRouteTargetRef( func (t *Translator) translateSecurityPolicyForRoute( policy *egv1a1.SecurityPolicy, route RouteContext, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, resources *resource.Resources, xdsIR resource.XdsIRMap, policyTargetGateway *types.NamespacedName, @@ -1048,7 +1075,7 @@ func (t *Translator) translateSecurityPolicyForRoute( func (t *Translator) translateSecurityPolicyForGateway( policy *egv1a1.SecurityPolicy, gtwCtx *GatewayContext, - target gwapiv1.LocalPolicyTargetReferenceWithSectionName, + target policyTargetReferenceWithSectionName, resources *resource.Resources, xdsIR resource.XdsIRMap, ) error { diff --git a/internal/gatewayapi/securitypolicy_test.go b/internal/gatewayapi/securitypolicy_test.go index f4e91aa897..1171335796 100644 --- a/internal/gatewayapi/securitypolicy_test.go +++ b/internal/gatewayapi/securitypolicy_test.go @@ -948,12 +948,11 @@ func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { SetRouteParentContext(tcpRoute, tcpRoute.Spec.ParentRefs[0]) // Create the target reference - target := gwapiv1.LocalPolicyTargetReferenceWithSectionName{ - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: gwapiv1.Group(gwapiv1.GroupVersion.Group), - Kind: resource.KindTCPRoute, - Name: "tcp-route", - }, + target := policyTargetReferenceWithSectionName{ + Group: gwapiv1.Group(gwapiv1.GroupVersion.Group), + Kind: resource.KindTCPRoute, + Name: "tcp-route", + Namespace: "default", } // Create route map @@ -1034,12 +1033,11 @@ func Test_SecurityPolicy_HTTP_Invalid_setsStatus_and_returns(t *testing.T) { SetRouteParentContext(httpRoute, httpRoute.Spec.ParentRefs[0]) // Create the target reference - target := gwapiv1.LocalPolicyTargetReferenceWithSectionName{ - LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{ - Group: gwapiv1.Group(gwapiv1.GroupVersion.Group), - Kind: resource.KindHTTPRoute, - Name: "http-route", - }, + target := policyTargetReferenceWithSectionName{ + Group: gwapiv1.Group(gwapiv1.GroupVersion.Group), + Kind: resource.KindHTTPRoute, + Name: "http-route", + Namespace: "default", } // Create route map diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml new file mode 100644 index 0000000000..58191cd6bb --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml @@ -0,0 +1,80 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-a + name: target-gateway + labels: + policy: selected + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-a + name: target-route + spec: + parentRefs: + - name: target-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: target-service + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: policy-ns + name: cross-ns-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + namespaces: + from: All + matchLabels: + policy: selected + useClientProtocol: true +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-a +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-a + name: target-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-target-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: target-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml new file mode 100644 index 0000000000..e48f42fba5 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml @@ -0,0 +1,199 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + name: cross-ns-policy + namespace: policy-ns + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + matchLabels: + policy: selected + namespaces: + from: All + useClientProtocol: true + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: target-gateway + namespace: policy-target-a + conditions: + - lastTransitionTime: null + message: Target Gateway policy-target-a/target-gateway is not permitted by + any ReferenceGrant. + reason: RefNotPermitted + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + policy: selected + name: target-gateway + namespace: policy-target-a + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: target-route + namespace: policy-target-a + spec: + parentRefs: + - name: target-gateway + sectionName: http + rules: + - backendRefs: + - name: target-service + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: target-gateway + sectionName: http +infraIR: + policy-target-a/target-gateway: + proxy: + listeners: + - name: policy-target-a/target-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: target-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-a + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-a/target-gateway + namespace: envoy-gateway-system +xdsIR: + policy-target-a/target-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-a-target-gateway-e4ecf2e1 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/target-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-a-target-gateway-e4ecf2e1 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/target-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: target-gateway + namespace: policy-target-a + sectionName: http + name: policy-target-a/target-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: target-route + namespace: policy-target-a + name: httproute/policy-target-a/target-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + metadata: + kind: Service + name: target-service + namespace: policy-target-a + sectionName: "8080" + name: httproute/policy-target-a/target-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: target-route + namespace: policy-target-a + name: httproute/policy-target-a/target-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml new file mode 100644 index 0000000000..b86e657918 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml @@ -0,0 +1,155 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-a + name: allowed-gateway + labels: + policy: selected + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-b + name: denied-gateway + labels: + policy: selected + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-a + name: allowed-route + spec: + parentRefs: + - name: allowed-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: allowed-service + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-b + name: denied-route + spec: + parentRefs: + - name: denied-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: denied-service + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: policy-ns + name: cross-ns-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + namespaces: + from: All + matchLabels: + policy: selected + useClientProtocol: true +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-a +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-b +referenceGrants: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-a + name: allow-btp + spec: + from: + - group: gateway.envoyproxy.io + kind: BackendTrafficPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: Gateway + name: allowed-gateway +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-a + name: allowed-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-b + name: denied-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-allowed-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: allowed-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-denied-service + namespace: policy-target-b + labels: + kubernetes.io/service-name: denied-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.4.4 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml new file mode 100644 index 0000000000..3350d1a7d1 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml @@ -0,0 +1,382 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + name: cross-ns-policy + namespace: policy-ns + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + matchLabels: + policy: selected + namespaces: + from: All + useClientProtocol: true + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: denied-gateway + namespace: policy-target-b + conditions: + - lastTransitionTime: null + message: Target Gateway policy-target-b/denied-gateway is not permitted by + any ReferenceGrant. + reason: RefNotPermitted + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: allowed-gateway + namespace: policy-target-a + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + policy: selected + name: allowed-gateway + namespace: policy-target-a + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + policy: selected + name: denied-gateway + namespace: policy-target-b + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: allowed-route + namespace: policy-target-a + spec: + parentRefs: + - name: allowed-gateway + sectionName: http + rules: + - backendRefs: + - name: allowed-service + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: allowed-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: denied-route + namespace: policy-target-b + spec: + parentRefs: + - name: denied-gateway + sectionName: http + rules: + - backendRefs: + - name: denied-service + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: denied-gateway + sectionName: http +infraIR: + policy-target-a/allowed-gateway: + proxy: + listeners: + - name: policy-target-a/allowed-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: allowed-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-a + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-a/allowed-gateway + namespace: envoy-gateway-system + policy-target-b/denied-gateway: + proxy: + listeners: + - name: policy-target-b/denied-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: denied-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-b + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-b/denied-gateway + namespace: envoy-gateway-system +xdsIR: + policy-target-a/allowed-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-a-allowed-gateway-720a9890 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/allowed-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-a-allowed-gateway-720a9890 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/allowed-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: allowed-gateway + namespace: policy-target-a + sectionName: http + name: policy-target-a/allowed-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: allowed-route + namespace: policy-target-a + name: httproute/policy-target-a/allowed-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + metadata: + kind: Service + name: allowed-service + namespace: policy-target-a + sectionName: "8080" + name: httproute/policy-target-a/allowed-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: allowed-route + namespace: policy-target-a + policies: + - kind: BackendTrafficPolicy + name: cross-ns-policy + namespace: policy-ns + name: httproute/policy-target-a/allowed-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + traffic: {} + useClientProtocol: true + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + policy-target-b/denied-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-b-denied-gateway-56a9c11b + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-b/denied-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-b-denied-gateway-56a9c11b + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-b/denied-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: denied-gateway + namespace: policy-target-b + sectionName: http + name: policy-target-b/denied-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: denied-route + namespace: policy-target-b + name: httproute/policy-target-b/denied-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.4.4 + port: 8080 + metadata: + kind: Service + name: denied-service + namespace: policy-target-b + sectionName: "8080" + name: httproute/policy-target-b/denied-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: denied-route + namespace: policy-target-b + name: httproute/policy-target-b/denied-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml new file mode 100644 index 0000000000..6af0274305 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-ns + name: selector-gateway + labels: + team: blue + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-ns + name: selector-route + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: backend + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: policy-ns + name: cross-ns-selector-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + namespaces: + from: Selector + selector: + matchLabels: + team: blue + matchLabels: + team: blue + cors: + allowOrigins: + - https://example.com + allowMethods: + - GET + allowHeaders: + - x-example + exposeHeaders: + - x-exposed + maxAge: 1000s +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-ns + labels: + team: blue +referenceGrants: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-ns + name: allow-selector-sp + spec: + from: + - group: gateway.envoyproxy.io + kind: SecurityPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: Gateway + name: selector-gateway +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-ns + name: backend + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-target-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: target-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-backend + namespace: policy-target-ns + labels: + kubernetes.io/service-name: backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.4.4 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml new file mode 100644 index 0000000000..ea9d6da291 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml @@ -0,0 +1,223 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + team: blue + name: selector-gateway + namespace: policy-target-ns + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: selector-route + namespace: policy-target-ns + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - backendRefs: + - name: backend + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: selector-gateway + sectionName: http +infraIR: + policy-target-ns/selector-gateway: + proxy: + listeners: + - name: policy-target-ns/selector-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: selector-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-ns + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-ns/selector-gateway + namespace: envoy-gateway-system +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + name: cross-ns-selector-policy + namespace: policy-ns + spec: + cors: + allowHeaders: + - x-example + allowMethods: + - GET + allowOrigins: + - https://example.com + exposeHeaders: + - x-exposed + maxAge: 1000s + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + matchLabels: + team: blue + namespaces: + from: Selector + selector: + matchLabels: + team: blue + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: selector-gateway + namespace: policy-target-ns + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + policy-target-ns/selector-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-ns-selector-gateway-e6838076 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-ns/selector-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-ns-selector-gateway-e6838076 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-ns/selector-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: selector-gateway + namespace: policy-target-ns + sectionName: http + name: policy-target-ns/selector-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-ns + name: httproute/policy-target-ns/selector-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.4.4 + port: 8080 + metadata: + kind: Service + name: backend + namespace: policy-target-ns + sectionName: "8080" + name: httproute/policy-target-ns/selector-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-ns + name: httproute/policy-target-ns/selector-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + security: + cors: + allowHeaders: + - x-example + allowMethods: + - GET + allowOrigins: + - distinct: false + exact: https://example.com + name: "" + exposeHeaders: + - x-exposed + maxAge: 16m40s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml new file mode 100644 index 0000000000..ceaae5bbab --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml @@ -0,0 +1,126 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-ns + name: selector-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-ns + name: selector-route + labels: + team: blue + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: backend + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: policy-ns + name: cross-ns-selector-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: Selector + selector: + matchLabels: + team: blue + matchLabels: + team: blue + cors: + allowOrigins: + - https://example.com + allowMethods: + - GET + allowHeaders: + - x-example + exposeHeaders: + - x-exposed + maxAge: 1000s +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-ns + labels: + team: blue +referenceGrants: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-ns + name: allow-selector-sp + spec: + from: + - group: gateway.envoyproxy.io + kind: SecurityPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: selector-route +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-ns + name: backend + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-target-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: target-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-backend + namespace: policy-target-ns + labels: + kubernetes.io/service-name: backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.4.4 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml new file mode 100644 index 0000000000..8cbfc9f52e --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml @@ -0,0 +1,224 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: selector-gateway + namespace: policy-target-ns + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + labels: + team: blue + name: selector-route + namespace: policy-target-ns + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - backendRefs: + - name: backend + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: selector-gateway + sectionName: http +infraIR: + policy-target-ns/selector-gateway: + proxy: + listeners: + - name: policy-target-ns/selector-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: selector-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-ns + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-ns/selector-gateway + namespace: envoy-gateway-system +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + name: cross-ns-selector-policy + namespace: policy-ns + spec: + cors: + allowHeaders: + - x-example + allowMethods: + - GET + allowOrigins: + - https://example.com + exposeHeaders: + - x-exposed + maxAge: 1000s + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + matchLabels: + team: blue + namespaces: + from: Selector + selector: + matchLabels: + team: blue + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: selector-gateway + namespace: policy-target-ns + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + policy-target-ns/selector-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-ns-selector-gateway-e6838076 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-ns/selector-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-ns-selector-gateway-e6838076 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-ns/selector-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: selector-gateway + namespace: policy-target-ns + sectionName: http + name: policy-target-ns/selector-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-ns + name: httproute/policy-target-ns/selector-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.4.4 + port: 8080 + metadata: + kind: Service + name: backend + namespace: policy-target-ns + sectionName: "8080" + name: httproute/policy-target-ns/selector-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-ns + name: httproute/policy-target-ns/selector-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + security: + cors: + allowHeaders: + - x-example + allowMethods: + - GET + allowOrigins: + - distinct: false + exact: https://example.com + name: "" + exposeHeaders: + - x-exposed + maxAge: 16m40s + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.in.yaml new file mode 100644 index 0000000000..c8d51a76a8 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.in.yaml @@ -0,0 +1,198 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-a + name: target-gateway + labels: + policy: selected + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: policy-target-selector + name: selector-gateway + labels: + policy: namespace-selector + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-a + name: target-route + spec: + parentRefs: + - name: target-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: target-service + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-selector + name: selector-route + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - matches: + - path: + value: / + backendRefs: + - name: selector-service + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: policy-ns + name: cross-ns-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + namespaces: + from: All + matchLabels: + policy: selected + useClientProtocol: true +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: policy-ns + name: cross-ns-selector-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: Selector + selector: + matchLabels: + team: blue + matchLabels: + policy: namespace-selector + cors: + allowOrigins: + - https://example.com + allowMethods: + - GET + allowHeaders: + - x-example + exposeHeaders: + - x-exposed + maxAge: 1000s +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-a +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-selector + labels: + team: blue +referenceGrants: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-a + name: allow-btp + spec: + from: + - group: gateway.envoyproxy.io + kind: BackendTrafficPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: Gateway + name: target-gateway +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-selector + name: allow-selector-btp + spec: + from: + - group: gateway.envoyproxy.io + kind: SecurityPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: Gateway + name: selector-gateway +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-a + name: target-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-selector + name: selector-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-target-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: target-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-selector-service + namespace: policy-target-selector + labels: + kubernetes.io/service-name: selector-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.4.4 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.out.yaml new file mode 100644 index 0000000000..7e4a8cbe6f --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant.out.yaml @@ -0,0 +1,369 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + name: cross-ns-policy + namespace: policy-ns + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: Gateway + matchLabels: + policy: selected + namespaces: + from: All + useClientProtocol: true + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: target-gateway + namespace: policy-target-a + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + policy: selected + name: target-gateway + namespace: policy-target-a + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + labels: + policy: namespace-selector + name: selector-gateway + namespace: policy-target-selector + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: target-route + namespace: policy-target-a + spec: + parentRefs: + - name: target-gateway + sectionName: http + rules: + - backendRefs: + - name: target-service + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: target-gateway + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: selector-route + namespace: policy-target-selector + spec: + parentRefs: + - name: selector-gateway + sectionName: http + rules: + - backendRefs: + - name: selector-service + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: selector-gateway + sectionName: http +infraIR: + policy-target-a/target-gateway: + proxy: + listeners: + - name: policy-target-a/target-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: target-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-a + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-a/target-gateway + namespace: envoy-gateway-system + policy-target-selector/selector-gateway: + proxy: + listeners: + - name: policy-target-selector/selector-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: selector-gateway + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-selector + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: policy-target-selector/selector-gateway + namespace: envoy-gateway-system +xdsIR: + policy-target-a/target-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-a-target-gateway-e4ecf2e1 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/target-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-a-target-gateway-e4ecf2e1 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-a/target-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: target-gateway + namespace: policy-target-a + sectionName: http + name: policy-target-a/target-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: target-route + namespace: policy-target-a + name: httproute/policy-target-a/target-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + metadata: + kind: Service + name: target-service + namespace: policy-target-a + sectionName: "8080" + name: httproute/policy-target-a/target-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: target-route + namespace: policy-target-a + policies: + - kind: BackendTrafficPolicy + name: cross-ns-policy + namespace: policy-ns + name: httproute/policy-target-a/target-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + traffic: {} + useClientProtocol: true + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 + policy-target-selector/selector-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-policy-target-selector-selector-gateway-45fe5771 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-selector/selector-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-policy-target-selector-selector-gateway-45fe5771 + namespace: envoy-gateway-system + sectionName: "8080" + name: policy-target-selector/selector-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: selector-gateway + namespace: policy-target-selector + sectionName: http + name: policy-target-selector/selector-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-selector + name: httproute/policy-target-selector/selector-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.4.4 + port: 8080 + metadata: + kind: Service + name: selector-service + namespace: policy-target-selector + sectionName: "8080" + name: httproute/policy-target-selector/selector-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: selector-route + namespace: policy-target-selector + name: httproute/policy-target-selector/selector-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 1955ef9ab4..cb93c7e684 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -258,6 +258,8 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, resources.BackendTrafficPolicies, routesToObjects(resources), acceptedGateways, + resources.ReferenceGrants, + t.GetNamespace, ) } @@ -334,7 +336,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, resources.EnvoyExtensionPolicies, acceptedGateways, routes, resources, xdsIR) extServerPolicies, err := t.ProcessExtensionServerPolicies( - resources.ExtensionServerPolicies, acceptedGateways, xdsIR) + resources.ExtensionServerPolicies, acceptedGateways, resources.ReferenceGrants, xdsIR) if err != nil { errs = errors.Join(errs, err) } diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index ab9d5bc5ee..4f9d565161 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -833,44 +833,7 @@ func (t *Translator) validateConflictedLayer4Listeners(gateways []*GatewayContex } func (t *Translator) validateCrossNamespaceRef(from crossNamespaceFrom, to crossNamespaceTo, referenceGrants []*gwapiv1b1.ReferenceGrant) bool { - for _, referenceGrant := range referenceGrants { - // The ReferenceGrant must be defined in the namespace of - // the "to" (the referent). - if referenceGrant.Namespace != to.namespace { - continue - } - - // Check if the ReferenceGrant has a matching "from". - var fromAllowed bool - for _, refGrantFrom := range referenceGrant.Spec.From { - if string(refGrantFrom.Namespace) == from.namespace && string(refGrantFrom.Group) == from.group && string(refGrantFrom.Kind) == from.kind { - fromAllowed = true - break - } - } - if !fromAllowed { - continue - } - - // Check if the ReferenceGrant has a matching "to". - var toAllowed bool - for _, refGrantTo := range referenceGrant.Spec.To { - if string(refGrantTo.Group) == to.group && string(refGrantTo.Kind) == to.kind && (refGrantTo.Name == nil || *refGrantTo.Name == "" || string(*refGrantTo.Name) == to.name) { - toAllowed = true - break - } - } - if !toAllowed { - continue - } - - // If we got here, both the "from" and the "to" were allowed by this - // reference grant. - return true - } - - // If we got here, no reference policy or reference grant allowed both the "from" and "to". - return false + return isCrossNamespacePolicyTargetRefAllowed(from, to, referenceGrants) } // Checks if a hostname is valid according to RFC 1123 and gateway API's requirement that it not be an IP address diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml index bfb2881faa..b31adabb4a 100644 --- a/test/helm/gateway-crds-helm/all.out.yaml +++ b/test/helm/gateway-crds-helm/all.out.yaml @@ -25211,7 +25211,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -25219,10 +25224,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -27062,7 +27063,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -27070,10 +27076,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -29794,7 +29796,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -29802,10 +29809,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -56262,7 +56265,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -56270,10 +56278,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml index 1c9e0a5c3d..d181088969 100644 --- a/test/helm/gateway-crds-helm/e2e.out.yaml +++ b/test/helm/gateway-crds-helm/e2e.out.yaml @@ -3184,7 +3184,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -3192,10 +3197,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -5035,7 +5036,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -5043,10 +5049,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -7767,7 +7769,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -7775,10 +7782,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -34235,7 +34238,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -34243,10 +34251,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml index 62580e533c..25f3a5c5ab 100644 --- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml +++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml @@ -3184,7 +3184,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -3192,10 +3197,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -5035,7 +5036,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -5043,10 +5049,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -7767,7 +7769,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -7775,10 +7782,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) @@ -34235,7 +34238,12 @@ spec: type: object type: object x-kubernetes-map-type: atomic + required: + - from type: object + x-kubernetes-validations: + - message: selector must be specified when from is Selector + rule: self.from != 'Selector' || has(self.selector) required: - kind type: object @@ -34243,10 +34251,6 @@ spec: - message: group must be gateway.networking.k8s.io rule: 'has(self.group) ? self.group == ''gateway.networking.k8s.io'' : true ' - - message: namespaces.selector must be specified when namespaces.from - is Selector - rule: '!has(self.namespaces) || self.namespaces.from != ''Selector'' - || has(self.namespaces.selector)' - message: at least one of namespaces, matchLabels, or matchExpressions must be specified rule: has(self.namespaces) || has(self.matchLabels) || has(self.matchExpressions) From 1ac43e26dcd01cb0fb320346d96c4b5b4bf7e0d8 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 21 Apr 2026 10:54:24 +0800 Subject: [PATCH 07/11] add e2e tests for cross ns policy attachment Signed-off-by: Huabing (Robin) Zhao --- .../backendtrafficpolicy-cross-namespace.yaml | 157 ++++++++++++++++++ .../backendtrafficpolicy_cross_namespace.go | 109 ++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml create mode 100644 test/e2e/tests/backendtrafficpolicy_cross_namespace.go diff --git a/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml b/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml new file mode 100644 index 0000000000..794eefb96a --- /dev/null +++ b/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml @@ -0,0 +1,157 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: btp-cross-ns-granted + labels: + policy-target: granted +--- +apiVersion: v1 +kind: Namespace +metadata: + name: btp-cross-ns-denied + labels: + policy-target: denied +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: cross-namespace-btp + namespace: btp-cross-ns-granted +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: http + hostname: "granted.cross-namespace-btp.example.com" + port: 80 + protocol: HTTP +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: cross-namespace-btp + namespace: btp-cross-ns-denied +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: http + hostname: "denied.cross-namespace-btp.example.com" + port: 80 + protocol: HTTP +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: HTTPRouteFilter +metadata: + name: direct-response-ok + namespace: btp-cross-ns-granted +spec: + directResponse: + body: + type: Inline + inline: "ok" +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: HTTPRouteFilter +metadata: + name: direct-response-ok + namespace: btp-cross-ns-denied +spec: + directResponse: + body: + type: Inline + inline: "ok" +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: cross-namespace-btp + namespace: btp-cross-ns-granted +spec: + parentRefs: + - name: cross-namespace-btp + hostnames: + - granted.cross-namespace-btp.example.com + rules: + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: HTTPRouteFilter + name: direct-response-ok +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: cross-namespace-btp + namespace: btp-cross-ns-denied +spec: + parentRefs: + - name: cross-namespace-btp + hostnames: + - denied.cross-namespace-btp.example.com + rules: + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: ExtensionRef + extensionRef: + group: gateway.envoyproxy.io + kind: HTTPRouteFilter + name: direct-response-ok +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: ReferenceGrant +metadata: + name: allow-cross-namespace-btp + namespace: btp-cross-ns-granted +spec: + from: + - group: gateway.envoyproxy.io + kind: BackendTrafficPolicy + namespace: gateway-conformance-infra + to: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: cross-namespace-btp +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: cross-namespace-btp-granted + namespace: gateway-conformance-infra +spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: Selector + selector: + matchLabels: + policy-target: granted + faultInjection: + abort: + httpStatus: 418 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: cross-namespace-btp-denied # this BackendTrafficPolicy will be rejected due to missing ReferenceGrant + namespace: gateway-conformance-infra +spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: Selector + selector: + matchLabels: + policy-target: denied + faultInjection: + abort: + httpStatus: 419 diff --git a/test/e2e/tests/backendtrafficpolicy_cross_namespace.go b/test/e2e/tests/backendtrafficpolicy_cross_namespace.go new file mode 100644 index 0000000000..9b127f4fa9 --- /dev/null +++ b/test/e2e/tests/backendtrafficpolicy_cross_namespace.go @@ -0,0 +1,109 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e + +package tests + +import ( + "testing" + + "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + + "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" +) + +func init() { + ConformanceTests = append(ConformanceTests, BackendTrafficPolicyCrossNamespaceTest) +} + +var BackendTrafficPolicyCrossNamespaceTest = suite.ConformanceTest{ + ShortName: "BackendTrafficPolicyCrossNamespace", + Description: "Test cross-namespace BackendTrafficPolicy attachment to HTTPRoute with and without ReferenceGrant", + Manifests: []string{"testdata/backendtrafficpolicy-cross-namespace.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("BackendTrafficPolicyCrossNamespace", func(t *testing.T) { + policyNS := "gateway-conformance-infra" + grantedNS := "btp-cross-ns-granted" + deniedNS := "btp-cross-ns-denied" + + grantedRouteNN := types.NamespacedName{Name: "cross-namespace-btp", Namespace: grantedNS} + deniedRouteNN := types.NamespacedName{Name: "cross-namespace-btp", Namespace: deniedNS} + grantedGatewayNN := types.NamespacedName{Name: "cross-namespace-btp", Namespace: grantedNS} + deniedGatewayNN := types.NamespacedName{Name: "cross-namespace-btp", Namespace: deniedNS} + + grantedAddr := kubernetes.GatewayAndRoutesMustBeAccepted( + t, suite.Client, suite.TimeoutConfig, suite.ControllerName, + kubernetes.NewGatewayRef(grantedGatewayNN), &gwapiv1.HTTPRoute{}, false, grantedRouteNN, + ) + deniedAddr := kubernetes.GatewayAndRoutesMustBeAccepted( + t, suite.Client, suite.TimeoutConfig, suite.ControllerName, + kubernetes.NewGatewayRef(deniedGatewayNN), &gwapiv1.HTTPRoute{}, false, deniedRouteNN, + ) + + kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, grantedRouteNN, grantedGatewayNN) + kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, deniedRouteNN, deniedGatewayNN) + + grantedAncestorRef := gwapiv1.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(grantedGatewayNN.Namespace), + Name: gwapiv1.ObjectName(grantedGatewayNN.Name), + } + deniedAncestorRef := gwapiv1.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(deniedGatewayNN.Namespace), + Name: gwapiv1.ObjectName(deniedGatewayNN.Name), + } + + BackendTrafficPolicyMustBeAccepted( + t, + suite.Client, + types.NamespacedName{Name: "cross-namespace-btp-granted", Namespace: policyNS}, + suite.ControllerName, + grantedAncestorRef, + ) + + BackendTrafficPolicyMustFail( + t, + suite.Client, + types.NamespacedName{Name: "cross-namespace-btp-denied", Namespace: policyNS}, + suite.ControllerName, + deniedAncestorRef, + "is not permitted by any ReferenceGrant", + ) + + grantedResponse := http.ExpectedResponse{ + Namespace: grantedNS, + Request: http.Request{ + Host: "granted.cross-namespace-btp.example.com", + Path: "/", + }, + Response: http.Response{ + StatusCodes: []int{418}, + }, + } + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, grantedAddr, grantedResponse) + + deniedResponse := http.ExpectedResponse{ + Namespace: deniedNS, + Request: http.Request{ + Host: "denied.cross-namespace-btp.example.com", + Path: "/", + }, + Response: http.Response{ + StatusCodes: []int{200}, + }, + } + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, deniedAddr, deniedResponse) + }) + }, +} From 3066e5459f227d94f3b19677ccb4b8dc5bc74716 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 21 Apr 2026 11:18:47 +0800 Subject: [PATCH 08/11] update test Signed-off-by: Huabing (Robin) Zhao --- ...alid-referencegrant-gateway-target.in.yaml | 50 +++++++------- ...lid-referencegrant-gateway-target.out.yaml | 68 +++++++++++-------- ...-valid-referencegrant-route-target.in.yaml | 48 ++++++------- ...valid-referencegrant-route-target.out.yaml | 68 +++++++++++-------- 4 files changed, 120 insertions(+), 114 deletions(-) diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml index 6af0274305..9926b3880e 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.in.yaml @@ -2,8 +2,8 @@ gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - namespace: policy-target-ns - name: selector-gateway + namespace: gateway-ns + name: test-gateway labels: team: blue spec: @@ -12,15 +12,22 @@ gateways: - name: http protocol: HTTP port: 80 + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + team: blue httpRoutes: - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - namespace: policy-target-ns - name: selector-route + namespace: route-ns + name: test-route spec: parentRefs: - - name: selector-gateway + - name: test-gateway + namespace: gateway-ns sectionName: http rules: - matches: @@ -60,14 +67,20 @@ namespaces: - apiVersion: v1 kind: Namespace metadata: - name: policy-target-ns + name: gateway-ns + labels: + team: blue +- apiVersion: v1 + kind: Namespace + metadata: + name: route-ns labels: team: blue referenceGrants: - apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: - namespace: policy-target-ns + namespace: gateway-ns name: allow-selector-sp spec: from: @@ -77,12 +90,12 @@ referenceGrants: to: - group: gateway.networking.k8s.io kind: Gateway - name: selector-gateway + name: test-gateway services: - apiVersion: v1 kind: Service metadata: - namespace: policy-target-ns + namespace: route-ns name: backend spec: ports: @@ -90,28 +103,11 @@ services: name: http protocol: TCP endpointSlices: -- apiVersion: discovery.k8s.io/v1 - kind: EndpointSlice - metadata: - name: endpointslice-target-service - namespace: policy-target-a - labels: - kubernetes.io/service-name: target-service - addressType: IPv4 - ports: - - name: http - protocol: TCP - port: 8080 - endpoints: - - addresses: - - 8.8.8.8 - conditions: - ready: true - apiVersion: discovery.k8s.io/v1 kind: EndpointSlice metadata: name: endpointslice-backend - namespace: policy-target-ns + namespace: route-ns labels: kubernetes.io/service-name: backend addressType: IPv4 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml index ea9d6da291..21c07ceb69 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-gateway-target.out.yaml @@ -4,12 +4,18 @@ gateways: metadata: labels: team: blue - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns spec: gatewayClassName: envoy-gateway-class listeners: - - name: http + - allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + team: blue + name: http port: 80 protocol: HTTP status: @@ -41,11 +47,12 @@ httpRoutes: - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - name: selector-route - namespace: policy-target-ns + name: test-route + namespace: route-ns spec: parentRefs: - - name: selector-gateway + - name: test-gateway + namespace: gateway-ns sectionName: http rules: - backendRefs: @@ -69,13 +76,14 @@ httpRoutes: type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: - name: selector-gateway + name: test-gateway + namespace: gateway-ns sectionName: http infraIR: - policy-target-ns/selector-gateway: + gateway-ns/test-gateway: proxy: listeners: - - name: policy-target-ns/selector-gateway/http + - name: gateway-ns/test-gateway/http ports: - containerPort: 10080 name: http-80 @@ -83,12 +91,12 @@ infraIR: servicePort: 80 metadata: labels: - gateway.envoyproxy.io/owning-gateway-name: selector-gateway - gateway.envoyproxy.io/owning-gateway-namespace: policy-target-ns + gateway.envoyproxy.io/owning-gateway-name: test-gateway + gateway.envoyproxy.io/owning-gateway-namespace: gateway-ns ownerReference: kind: GatewayClass name: envoy-gateway-class - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway namespace: envoy-gateway-system securityPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 @@ -122,8 +130,8 @@ securityPolicies: - ancestorRef: group: gateway.networking.k8s.io kind: Gateway - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns conditions: - lastTransitionTime: null message: Policy has been accepted. @@ -132,7 +140,7 @@ securityPolicies: type: Accepted controllerName: gateway.envoyproxy.io/gatewayclass-controller xdsIR: - policy-target-ns/selector-gateway: + gateway-ns/test-gateway: accessLog: json: - path: /dev/stdout @@ -140,10 +148,10 @@ xdsIR: proxyServiceCluster: metadata: kind: Service - name: envoy-policy-target-ns-selector-gateway-e6838076 + name: envoy-gateway-ns-test-gateway-85a70075 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway settings: - addressType: IP endpoints: @@ -152,10 +160,10 @@ xdsIR: zone: zone1 metadata: kind: Service - name: envoy-policy-target-ns-selector-gateway-e6838076 + name: envoy-gateway-ns-test-gateway-85a70075 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway protocol: TCP http: - address: 0.0.0.0 @@ -164,10 +172,10 @@ xdsIR: - '*' metadata: kind: Gateway - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns sectionName: http - name: policy-target-ns/selector-gateway/http + name: gateway-ns/test-gateway/http path: escapedSlashesAction: UnescapeAndRedirect mergeSlashes: true @@ -176,9 +184,9 @@ xdsIR: - destination: metadata: kind: HTTPRoute - name: selector-route - namespace: policy-target-ns - name: httproute/policy-target-ns/selector-route/rule/0 + name: test-route + namespace: route-ns + name: httproute/route-ns/test-route/rule/0 settings: - addressType: IP endpoints: @@ -187,18 +195,18 @@ xdsIR: metadata: kind: Service name: backend - namespace: policy-target-ns + namespace: route-ns sectionName: "8080" - name: httproute/policy-target-ns/selector-route/rule/0/backend/0 + name: httproute/route-ns/test-route/rule/0/backend/0 protocol: HTTP weight: 1 hostname: '*' isHTTP2: false metadata: kind: HTTPRoute - name: selector-route - namespace: policy-target-ns - name: httproute/policy-target-ns/selector-route/rule/0/match/0/* + name: test-route + namespace: route-ns + name: httproute/route-ns/test-route/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml index ceaae5bbab..52e973dac5 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.in.yaml @@ -2,25 +2,32 @@ gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - namespace: policy-target-ns - name: selector-gateway + namespace: gateway-ns + name: test-gateway spec: gatewayClassName: envoy-gateway-class listeners: - name: http protocol: HTTP port: 80 + allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + team: blue httpRoutes: - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - namespace: policy-target-ns - name: selector-route + namespace: route-ns + name: test-route labels: team: blue spec: parentRefs: - - name: selector-gateway + - name: test-gateway + namespace: gateway-ns sectionName: http rules: - matches: @@ -60,14 +67,18 @@ namespaces: - apiVersion: v1 kind: Namespace metadata: - name: policy-target-ns + name: route-ns labels: team: blue +- apiVersion: v1 + kind: Namespace + metadata: + name: gateway-ns referenceGrants: - apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: - namespace: policy-target-ns + namespace: route-ns name: allow-selector-sp spec: from: @@ -77,12 +88,12 @@ referenceGrants: to: - group: gateway.networking.k8s.io kind: HTTPRoute - name: selector-route + name: test-route services: - apiVersion: v1 kind: Service metadata: - namespace: policy-target-ns + namespace: route-ns name: backend spec: ports: @@ -90,28 +101,11 @@ services: name: http protocol: TCP endpointSlices: -- apiVersion: discovery.k8s.io/v1 - kind: EndpointSlice - metadata: - name: endpointslice-target-service - namespace: policy-target-a - labels: - kubernetes.io/service-name: target-service - addressType: IPv4 - ports: - - name: http - protocol: TCP - port: 8080 - endpoints: - - addresses: - - 8.8.8.8 - conditions: - ready: true - apiVersion: discovery.k8s.io/v1 kind: EndpointSlice metadata: name: endpointslice-backend - namespace: policy-target-ns + namespace: route-ns labels: kubernetes.io/service-name: backend addressType: IPv4 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml index 8cbfc9f52e..5c1240234d 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-valid-referencegrant-route-target.out.yaml @@ -2,12 +2,18 @@ gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns spec: gatewayClassName: envoy-gateway-class listeners: - - name: http + - allowedRoutes: + namespaces: + from: Selector + selector: + matchLabels: + team: blue + name: http port: 80 protocol: HTTP status: @@ -41,11 +47,12 @@ httpRoutes: metadata: labels: team: blue - name: selector-route - namespace: policy-target-ns + name: test-route + namespace: route-ns spec: parentRefs: - - name: selector-gateway + - name: test-gateway + namespace: gateway-ns sectionName: http rules: - backendRefs: @@ -69,13 +76,14 @@ httpRoutes: type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: - name: selector-gateway + name: test-gateway + namespace: gateway-ns sectionName: http infraIR: - policy-target-ns/selector-gateway: + gateway-ns/test-gateway: proxy: listeners: - - name: policy-target-ns/selector-gateway/http + - name: gateway-ns/test-gateway/http ports: - containerPort: 10080 name: http-80 @@ -83,12 +91,12 @@ infraIR: servicePort: 80 metadata: labels: - gateway.envoyproxy.io/owning-gateway-name: selector-gateway - gateway.envoyproxy.io/owning-gateway-namespace: policy-target-ns + gateway.envoyproxy.io/owning-gateway-name: test-gateway + gateway.envoyproxy.io/owning-gateway-namespace: gateway-ns ownerReference: kind: GatewayClass name: envoy-gateway-class - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway namespace: envoy-gateway-system securityPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 @@ -122,8 +130,8 @@ securityPolicies: - ancestorRef: group: gateway.networking.k8s.io kind: Gateway - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns sectionName: http conditions: - lastTransitionTime: null @@ -133,7 +141,7 @@ securityPolicies: type: Accepted controllerName: gateway.envoyproxy.io/gatewayclass-controller xdsIR: - policy-target-ns/selector-gateway: + gateway-ns/test-gateway: accessLog: json: - path: /dev/stdout @@ -141,10 +149,10 @@ xdsIR: proxyServiceCluster: metadata: kind: Service - name: envoy-policy-target-ns-selector-gateway-e6838076 + name: envoy-gateway-ns-test-gateway-85a70075 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway settings: - addressType: IP endpoints: @@ -153,10 +161,10 @@ xdsIR: zone: zone1 metadata: kind: Service - name: envoy-policy-target-ns-selector-gateway-e6838076 + name: envoy-gateway-ns-test-gateway-85a70075 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-ns/selector-gateway + name: gateway-ns/test-gateway protocol: TCP http: - address: 0.0.0.0 @@ -165,10 +173,10 @@ xdsIR: - '*' metadata: kind: Gateway - name: selector-gateway - namespace: policy-target-ns + name: test-gateway + namespace: gateway-ns sectionName: http - name: policy-target-ns/selector-gateway/http + name: gateway-ns/test-gateway/http path: escapedSlashesAction: UnescapeAndRedirect mergeSlashes: true @@ -177,9 +185,9 @@ xdsIR: - destination: metadata: kind: HTTPRoute - name: selector-route - namespace: policy-target-ns - name: httproute/policy-target-ns/selector-route/rule/0 + name: test-route + namespace: route-ns + name: httproute/route-ns/test-route/rule/0 settings: - addressType: IP endpoints: @@ -188,18 +196,18 @@ xdsIR: metadata: kind: Service name: backend - namespace: policy-target-ns + namespace: route-ns sectionName: "8080" - name: httproute/policy-target-ns/selector-route/rule/0/backend/0 + name: httproute/route-ns/test-route/rule/0/backend/0 protocol: HTTP weight: 1 hostname: '*' isHTTP2: false metadata: kind: HTTPRoute - name: selector-route - namespace: policy-target-ns - name: httproute/policy-target-ns/selector-route/rule/0/match/0/* + name: test-route + namespace: route-ns + name: httproute/route-ns/test-route/rule/0/match/0/* pathMatch: distinct: false name: "" From f4c2f7a16804c80ec2f9ed4e94448a061faaf5de Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 21 Apr 2026 11:24:12 +0800 Subject: [PATCH 09/11] fix lint Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/backendtrafficpolicy.go | 3 +-- internal/gatewayapi/clienttrafficpolicy.go | 5 ++--- internal/gatewayapi/envoyextensionpolicy.go | 6 ++---- internal/gatewayapi/securitypolicy.go | 3 +-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 75f632d47e..7e8c7993ec 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -446,7 +446,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( resolveErr *status.PolicyResolveError ) - targetedRoute, resolveErr = resolveBackendTrafficPolicyRouteTargetRef(policy, currTarget, routeMap) + targetedRoute, resolveErr = resolveBackendTrafficPolicyRouteTargetRef(currTarget, routeMap) // Skip if the route is not found // It's not necessarily an error because the BackendTrafficPolicy may be // reconciled by multiple controllers. And the other controller may @@ -771,7 +771,6 @@ func resolveBackendTrafficPolicyGatewayTargetRef( } func resolveBackendTrafficPolicyRouteTargetRef( - policy *egv1a1.BackendTrafficPolicy, target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index c21757dc97..952cd169d9 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -107,7 +107,7 @@ func (t *Translator) ProcessClientTrafficPolicies( res = append(res, policy) } - gateway, resolveErr := resolveClientTrafficPolicyTargetRef(policy, &targetRef, gatewayMap) + gateway, resolveErr := resolveClientTrafficPolicyTargetRef(&targetRef, gatewayMap) // Negative statuses have already been assigned so its safe to skip if gateway == nil { @@ -247,7 +247,7 @@ func (t *Translator) ProcessClientTrafficPolicies( handledPolicies[policyName] = policy } - gateway, resolveErr := resolveClientTrafficPolicyTargetRef(policy, &currTarget, gatewayMap) + gateway, resolveErr := resolveClientTrafficPolicyTargetRef(&currTarget, gatewayMap) // Negative statuses have already been assigned so its safe to skip if gateway == nil { @@ -378,7 +378,6 @@ func (t *Translator) ProcessClientTrafficPolicies( } func resolveClientTrafficPolicyTargetRef( - policy *egv1a1.ClientTrafficPolicy, targetRef *policyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index e803ce31d3..3ed286cd3d 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -236,7 +236,7 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( resolveErr *status.PolicyResolveError ) - targetedRoute, resolveErr = resolveEnvoyExtensionPolicyRouteTargetRef(policy, currTarget, routeMap) + targetedRoute, resolveErr = resolveEnvoyExtensionPolicyRouteTargetRef(currTarget, routeMap) // Skip if the route is not found // It's not necessarily an error because the EnvoyExtensionPolicy may be // reconciled by multiple controllers. And the other controller may @@ -342,7 +342,7 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( resolveErr *status.PolicyResolveError ) - targetedGateway, resolveErr = resolveEnvoyExtensionPolicyGatewayTargetRef(policy, currTarget, gatewayMap) + targetedGateway, resolveErr = resolveEnvoyExtensionPolicyGatewayTargetRef(currTarget, gatewayMap) // Skip if the gateway is not found // It's not necessarily an error because the EnvoyExtensionPolicy may be // reconciled by multiple controllers. And the other controller may @@ -402,7 +402,6 @@ func (t *Translator) processEnvoyExtensionPolicyForGateway( } func resolveEnvoyExtensionPolicyGatewayTargetRef( - policy *egv1a1.EnvoyExtensionPolicy, target policyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext, ) (*GatewayContext, *status.PolicyResolveError) { @@ -464,7 +463,6 @@ func resolveEnvoyExtensionPolicyGatewayTargetRef( } func resolveEnvoyExtensionPolicyRouteTargetRef( - policy *egv1a1.EnvoyExtensionPolicy, target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 93d639cdab..47a044f0c7 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -308,7 +308,7 @@ func (t *Translator) processSecurityPolicyForRoute( resolveErr *status.PolicyResolveError ) - targetedRoute, resolveErr = resolveSecurityPolicyRouteTargetRef(policy, currTarget, routeMap) + targetedRoute, resolveErr = resolveSecurityPolicyRouteTargetRef(currTarget, routeMap) // Skip if the route is not found // It's not necessarily an error because the SecurityPolicy may be // reconciled by multiple controllers. And the other controller may @@ -788,7 +788,6 @@ func resolveSecurityPolicyGatewayTargetRef( } func resolveSecurityPolicyRouteTargetRef( - policy *egv1a1.SecurityPolicy, target policyTargetReferenceWithSectionName, routes map[policyTargetRouteKey]*policyRouteTargetContext, ) (RouteContext, *status.PolicyResolveError) { From e507c223d1facc6e5f3e3f61244c97401eb81d16 Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 21 Apr 2026 12:10:55 +0800 Subject: [PATCH 10/11] update test Signed-off-by: Huabing (Robin) Zhao --- ...getselector-invalid-referencegrant.in.yaml | 10 +- ...etselector-invalid-referencegrant.out.yaml | 42 +-- ...ial-referencegrant-gateway-target.in.yaml} | 0 ...al-referencegrant-gateway-target.out.yaml} | 0 ...artial-referencegrant-route-target.in.yaml | 149 ++++++++++ ...rtial-referencegrant-route-target.out.yaml | 278 ++++++++++++++++++ 6 files changed, 453 insertions(+), 26 deletions(-) rename internal/gatewayapi/testdata/{policy-cross-namespace-targetselector-partial-referencegrant.in.yaml => policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.in.yaml} (100%) rename internal/gatewayapi/testdata/{policy-cross-namespace-targetselector-partial-referencegrant.out.yaml => policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.out.yaml} (100%) create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.in.yaml create mode 100644 internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.out.yaml diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml index 58191cd6bb..ff05db314b 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.in.yaml @@ -2,7 +2,7 @@ gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: - namespace: policy-target-a + namespace: policy-target-ns name: target-gateway labels: policy: selected @@ -16,7 +16,7 @@ httpRoutes: - apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: - namespace: policy-target-a + namespace: policy-target-ns name: target-route spec: parentRefs: @@ -48,12 +48,12 @@ namespaces: - apiVersion: v1 kind: Namespace metadata: - name: policy-target-a + name: policy-target-ns services: - apiVersion: v1 kind: Service metadata: - namespace: policy-target-a + namespace: policy-target-ns name: target-service spec: ports: @@ -65,7 +65,7 @@ endpointSlices: kind: EndpointSlice metadata: name: endpointslice-target-service - namespace: policy-target-a + namespace: policy-target-ns labels: kubernetes.io/service-name: target-service addressType: IPv4 diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml index e48f42fba5..4a883cbcf0 100644 --- a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-invalid-referencegrant.out.yaml @@ -19,10 +19,10 @@ backendTrafficPolicies: group: gateway.networking.k8s.io kind: Gateway name: target-gateway - namespace: policy-target-a + namespace: policy-target-ns conditions: - lastTransitionTime: null - message: Target Gateway policy-target-a/target-gateway is not permitted by + message: Target Gateway policy-target-ns/target-gateway is not permitted by any ReferenceGrant. reason: RefNotPermitted status: "False" @@ -35,7 +35,7 @@ gateways: labels: policy: selected name: target-gateway - namespace: policy-target-a + namespace: policy-target-ns spec: gatewayClassName: envoy-gateway-class listeners: @@ -72,7 +72,7 @@ httpRoutes: kind: HTTPRoute metadata: name: target-route - namespace: policy-target-a + namespace: policy-target-ns spec: parentRefs: - name: target-gateway @@ -102,10 +102,10 @@ httpRoutes: name: target-gateway sectionName: http infraIR: - policy-target-a/target-gateway: + policy-target-ns/target-gateway: proxy: listeners: - - name: policy-target-a/target-gateway/http + - name: policy-target-ns/target-gateway/http ports: - containerPort: 10080 name: http-80 @@ -114,14 +114,14 @@ infraIR: metadata: labels: gateway.envoyproxy.io/owning-gateway-name: target-gateway - gateway.envoyproxy.io/owning-gateway-namespace: policy-target-a + gateway.envoyproxy.io/owning-gateway-namespace: policy-target-ns ownerReference: kind: GatewayClass name: envoy-gateway-class - name: policy-target-a/target-gateway + name: policy-target-ns/target-gateway namespace: envoy-gateway-system xdsIR: - policy-target-a/target-gateway: + policy-target-ns/target-gateway: accessLog: json: - path: /dev/stdout @@ -129,10 +129,10 @@ xdsIR: proxyServiceCluster: metadata: kind: Service - name: envoy-policy-target-a-target-gateway-e4ecf2e1 + name: envoy-policy-target-ns-target-gateway-9aed6540 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-a/target-gateway + name: policy-target-ns/target-gateway settings: - addressType: IP endpoints: @@ -141,10 +141,10 @@ xdsIR: zone: zone1 metadata: kind: Service - name: envoy-policy-target-a-target-gateway-e4ecf2e1 + name: envoy-policy-target-ns-target-gateway-9aed6540 namespace: envoy-gateway-system sectionName: "8080" - name: policy-target-a/target-gateway + name: policy-target-ns/target-gateway protocol: TCP http: - address: 0.0.0.0 @@ -154,9 +154,9 @@ xdsIR: metadata: kind: Gateway name: target-gateway - namespace: policy-target-a + namespace: policy-target-ns sectionName: http - name: policy-target-a/target-gateway/http + name: policy-target-ns/target-gateway/http path: escapedSlashesAction: UnescapeAndRedirect mergeSlashes: true @@ -166,8 +166,8 @@ xdsIR: metadata: kind: HTTPRoute name: target-route - namespace: policy-target-a - name: httproute/policy-target-a/target-route/rule/0 + namespace: policy-target-ns + name: httproute/policy-target-ns/target-route/rule/0 settings: - addressType: IP endpoints: @@ -176,9 +176,9 @@ xdsIR: metadata: kind: Service name: target-service - namespace: policy-target-a + namespace: policy-target-ns sectionName: "8080" - name: httproute/policy-target-a/target-route/rule/0/backend/0 + name: httproute/policy-target-ns/target-route/rule/0/backend/0 protocol: HTTP weight: 1 hostname: '*' @@ -186,8 +186,8 @@ xdsIR: metadata: kind: HTTPRoute name: target-route - namespace: policy-target-a - name: httproute/policy-target-a/target-route/rule/0/match/0/* + namespace: policy-target-ns + name: httproute/policy-target-ns/target-route/rule/0/match/0/* pathMatch: distinct: false name: "" diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.in.yaml similarity index 100% rename from internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.in.yaml rename to internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.in.yaml diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.out.yaml similarity index 100% rename from internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant.out.yaml rename to internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-gateway-target.out.yaml diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.in.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.in.yaml new file mode 100644 index 0000000000..dbc3564596 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.in.yaml @@ -0,0 +1,149 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: gateway-ns + name: mixed-gateway # this gateway has two attached routes, one with ReferenceGrant and the other without + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-a + name: allowed-route + labels: + policy: selected + spec: + parentRefs: + - name: mixed-gateway + namespace: gateway-ns + sectionName: http + rules: + - matches: + - path: + value: /allowed + backendRefs: + - name: allowed-service + port: 8080 +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: policy-target-b + name: denied-route + labels: + policy: selected + spec: + parentRefs: + - name: mixed-gateway + namespace: gateway-ns + sectionName: http + rules: + - matches: + - path: + value: /denied + backendRefs: + - name: denied-service + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: policy-ns + name: cross-ns-policy + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + namespaces: + from: All + matchLabels: + policy: selected + useClientProtocol: true +namespaces: +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-a +- apiVersion: v1 + kind: Namespace + metadata: + name: policy-target-b +referenceGrants: +- apiVersion: gateway.networking.k8s.io/v1beta1 + kind: ReferenceGrant + metadata: + namespace: policy-target-a + name: allow-btp + spec: + from: + - group: gateway.envoyproxy.io + kind: BackendTrafficPolicy + namespace: policy-ns + to: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: allowed-route +services: +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-a + name: allowed-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +- apiVersion: v1 + kind: Service + metadata: + namespace: policy-target-b + name: denied-service + spec: + ports: + - port: 8080 + name: http + protocol: TCP +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-allowed-service + namespace: policy-target-a + labels: + kubernetes.io/service-name: allowed-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-denied-service + namespace: policy-target-b + labels: + kubernetes.io/service-name: denied-service + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 8080 + endpoints: + - addresses: + - 8.8.4.4 + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.out.yaml b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.out.yaml new file mode 100644 index 0000000000..100f261165 --- /dev/null +++ b/internal/gatewayapi/testdata/policy-cross-namespace-targetselector-partial-referencegrant-route-target.out.yaml @@ -0,0 +1,278 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + name: cross-ns-policy + namespace: policy-ns + spec: + targetSelectors: + - group: gateway.networking.k8s.io + kind: HTTPRoute + matchLabels: + policy: selected + namespaces: + from: All + useClientProtocol: true + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: mixed-gateway + namespace: gateway-ns + sectionName: http + conditions: + - lastTransitionTime: null + message: Target HTTPRoute policy-target-b/denied-route is not permitted by + any ReferenceGrant. + reason: RefNotPermitted + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: mixed-gateway + namespace: gateway-ns + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + labels: + policy: selected + name: allowed-route + namespace: policy-target-a + spec: + parentRefs: + - name: mixed-gateway + namespace: gateway-ns + sectionName: http + rules: + - backendRefs: + - name: allowed-service + port: 8080 + matches: + - path: + value: /allowed + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: mixed-gateway + namespace: gateway-ns + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + labels: + policy: selected + name: denied-route + namespace: policy-target-b + spec: + parentRefs: + - name: mixed-gateway + namespace: gateway-ns + sectionName: http + rules: + - backendRefs: + - name: denied-service + port: 8080 + matches: + - path: + value: /denied + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: mixed-gateway + namespace: gateway-ns + sectionName: http +infraIR: + gateway-ns/mixed-gateway: + proxy: + listeners: + - name: gateway-ns/mixed-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: mixed-gateway + gateway.envoyproxy.io/owning-gateway-namespace: gateway-ns + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: gateway-ns/mixed-gateway + namespace: envoy-gateway-system +xdsIR: + gateway-ns/mixed-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-gateway-ns-mixed-gateway-b951d52e + namespace: envoy-gateway-system + sectionName: "8080" + name: gateway-ns/mixed-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-gateway-ns-mixed-gateway-b951d52e + namespace: envoy-gateway-system + sectionName: "8080" + name: gateway-ns/mixed-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + metadata: + kind: Gateway + name: mixed-gateway + namespace: gateway-ns + sectionName: http + name: gateway-ns/mixed-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: allowed-route + namespace: policy-target-a + name: httproute/policy-target-a/allowed-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 8080 + metadata: + kind: Service + name: allowed-service + namespace: policy-target-a + sectionName: "8080" + name: httproute/policy-target-a/allowed-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: allowed-route + namespace: policy-target-a + policies: + - kind: BackendTrafficPolicy + name: cross-ns-policy + namespace: policy-ns + name: httproute/policy-target-a/allowed-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /allowed + traffic: {} + useClientProtocol: true + - destination: + metadata: + kind: HTTPRoute + name: denied-route + namespace: policy-target-b + name: httproute/policy-target-b/denied-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 8.8.4.4 + port: 8080 + metadata: + kind: Service + name: denied-service + namespace: policy-target-b + sectionName: "8080" + name: httproute/policy-target-b/denied-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: denied-route + namespace: policy-target-b + name: httproute/policy-target-b/denied-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: /denied + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 From 044fc0c42dbcee4d31a29395757553fb5f961f4e Mon Sep 17 00:00:00 2001 From: "Huabing (Robin) Zhao" Date: Tue, 21 Apr 2026 15:37:04 +0800 Subject: [PATCH 11/11] add referencegrants to the provider Signed-off-by: Huabing (Robin) Zhao --- internal/gatewayapi/helpers_test.go | 1 - internal/provider/kubernetes/controller.go | 128 ++++++++++++++ .../provider/kubernetes/controller_test.go | 156 ++++++++++++++++++ .../backendtrafficpolicy-cross-namespace.yaml | 20 +-- 4 files changed, 287 insertions(+), 18 deletions(-) diff --git a/internal/gatewayapi/helpers_test.go b/internal/gatewayapi/helpers_test.go index 04be2c21c7..8f78d0ae12 100644 --- a/internal/gatewayapi/helpers_test.go +++ b/internal/gatewayapi/helpers_test.go @@ -793,7 +793,6 @@ func TestGetPolicyTargetRefs(t *testing.T) { }, results: []policyTargetReferenceWithSectionName{ { - Group: "gateway.networking.k8s.io", Kind: "Gateway", Name: "selected-ns", diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 582bf000e9..9dc145d8a8 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -511,6 +511,14 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques } } + if err = r.processPolicyTargetReferenceGrants(ctx, gwcResource, gwcResourceMapping); err != nil { + if isTransientError(err) { + gcLogger.Error(err, "transient error processing policy target ReferenceGrants") + return reconcile.Result{}, err + } + gcLogger.Error(err, "failed to process policy target ReferenceGrants for GatewayClass") + } + if err = r.processExtensionServerPolicies(ctx, gwcResource); err != nil { if isTransientError(err) { gcLogger.Error(err, "transient error processing ExtensionServerPolicies") @@ -1601,6 +1609,126 @@ func (r *gatewayAPIReconciler) findReferenceGrant(ctx context.Context, from, to return nil, nil } +// processPolicyTargetReferenceGrants finds the ReferenceGrants that allow policies in this GatewayClass to reference resources in other namespaces, and adds them to the resourceTree. +func (r *gatewayAPIReconciler) processPolicyTargetReferenceGrants( + ctx context.Context, + resourceTree *resource.Resources, + resourceMap *resourceMappings, +) error { + targetNamespaces := sets.New[string]() + for _, gateway := range resourceTree.Gateways { + targetNamespaces.Insert(gateway.Namespace) + } + for _, route := range resourceTree.HTTPRoutes { + targetNamespaces.Insert(route.Namespace) + } + for _, route := range resourceTree.GRPCRoutes { + targetNamespaces.Insert(route.Namespace) + } + for _, route := range resourceTree.TLSRoutes { + targetNamespaces.Insert(route.Namespace) + } + for _, route := range resourceTree.TCPRoutes { + targetNamespaces.Insert(route.Namespace) + } + for _, route := range resourceTree.UDPRoutes { + targetNamespaces.Insert(route.Namespace) + } + if targetNamespaces.Len() == 0 { + return nil + } + + policyFromNamespaces := map[string]sets.Set[string]{ + resource.KindClientTrafficPolicy: sets.New[string](), + resource.KindBackendTrafficPolicy: sets.New[string](), + resource.KindEnvoyExtensionPolicy: sets.New[string](), + } + for _, policy := range resourceTree.ClientTrafficPolicies { + policyFromNamespaces[resource.KindClientTrafficPolicy].Insert(policy.Namespace) + } + for _, policy := range resourceTree.BackendTrafficPolicies { + policyFromNamespaces[resource.KindBackendTrafficPolicy].Insert(policy.Namespace) + } + for _, policy := range resourceTree.EnvoyExtensionPolicies { + policyFromNamespaces[resource.KindEnvoyExtensionPolicy].Insert(policy.Namespace) + } + + allowedTargetKinds := map[string]sets.Set[string]{ + resource.KindClientTrafficPolicy: sets.New[string](resource.KindGateway), + resource.KindBackendTrafficPolicy: sets.New[string]( + resource.KindGateway, + resource.KindHTTPRoute, + resource.KindGRPCRoute, + resource.KindTLSRoute, + resource.KindTCPRoute, + resource.KindUDPRoute, + ), + resource.KindEnvoyExtensionPolicy: sets.New[string]( + resource.KindGateway, + resource.KindHTTPRoute, + resource.KindGRPCRoute, + resource.KindTLSRoute, + resource.KindTCPRoute, + resource.KindUDPRoute, + ), + } + + refGrantList := new(gwapiv1b1.ReferenceGrantList) + if err := r.client.List(ctx, refGrantList); err != nil { + return fmt.Errorf("failed to list ReferenceGrants: %w", err) + } + + for i := range refGrantList.Items { + refGrant := &refGrantList.Items[i] + if !targetNamespaces.Has(refGrant.Namespace) { + continue + } + if !policyTargetReferenceGrantAllowed(refGrant, policyFromNamespaces, allowedTargetKinds) { + continue + } + + key := utils.NamespacedName(refGrant).String() + if resourceMap.allAssociatedReferenceGrants.Has(key) { + continue + } + resourceMap.allAssociatedReferenceGrants.Insert(key) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added policy target ReferenceGrant to resource map", "namespace", refGrant.Namespace, "name", refGrant.Name) + } + + return nil +} + +// policyTargetReferenceGrantAllowed checks if the ReferenceGrant allows any of the policies in this GatewayClass to reference resources in other namespaces. +// This is a coarse check that looks at the "from" and "to" of the ReferenceGrant to see if it potentially allows any valid reference from policies to gateways/routes. +// The detailed check is done in the Gateway API IR translation, where we check if the ReferenceGrant allows the specific reference from the specific policy to the specific gateway/route. +func policyTargetReferenceGrantAllowed( + refGrant *gwapiv1b1.ReferenceGrant, + policyFromNamespaces map[string]sets.Set[string], + allowedTargetKinds map[string]sets.Set[string], +) bool { + for _, refGrantFrom := range refGrant.Spec.From { + if string(refGrantFrom.Group) != egv1a1.GroupVersion.Group { + continue + } + fromNamespaces, ok := policyFromNamespaces[string(refGrantFrom.Kind)] + if !ok || !fromNamespaces.Has(string(refGrantFrom.Namespace)) { + continue + } + + for _, refGrantTo := range refGrant.Spec.To { + if string(refGrantTo.Group) != gwapiv1.GroupName { + continue + } + if allowedTargetKinds[string(refGrantFrom.Kind)].Has(string(refGrantTo.Kind)) { + return true + } + } + } + + return false +} + func (r *gatewayAPIReconciler) processGateways(ctx context.Context, managedGC *gwapiv1.GatewayClass, resourceTree *resource.Resources, resourceMap *resourceMappings) error { // Find gateways for the managedGC // Find the Gateways that reference this Class. diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index 6b562da47d..bd7b45d37a 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -2342,6 +2342,162 @@ func setupReferenceGrantReconciler(objs []client.Object) *gatewayAPIReconciler { return r } +func TestProcessPolicyTargetReferenceGrants(t *testing.T) { + const ( + policyNS = "policy" + gatewayNS = "gateway" + routeNS = "route" + otherNS = "other" + ) + + refGrant := func(name, namespace, fromKind, fromNamespace, toGroup, toKind string) *gwapiv1b1.ReferenceGrant { + return &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Group: gwapiv1b1.Group(egv1a1.GroupVersion.Group), + Kind: gwapiv1b1.Kind(fromKind), + Namespace: gwapiv1b1.Namespace(fromNamespace), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Group: gwapiv1b1.Group(toGroup), + Kind: gwapiv1b1.Kind(toKind), + }, + }, + }, + } + } + + resourceTreeWithTargets := func() *resource.Resources { + resourceTree := resource.NewResources() + resourceTree.Gateways = append(resourceTree.Gateways, + test.GetGateway(types.NamespacedName{Namespace: gatewayNS, Name: "gateway"}, "gc", 80)) + resourceTree.HTTPRoutes = append(resourceTree.HTTPRoutes, + test.GetHTTPRoute( + types.NamespacedName{Namespace: routeNS, Name: "route"}, + "gateway", + test.GetServiceBackendRef(types.NamespacedName{Name: "svc"}, 80), + "")) + return resourceTree + } + + testCases := []struct { + name string + mutateTree func(*resource.Resources) + referenceGrants []client.Object + existingGrantName string + expectedNames sets.Set[string] + }{ + { + name: "BackendTrafficPolicy includes gateway and route target grants", + mutateTree: func(resourceTree *resource.Resources) { + resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &egv1a1.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: "btp"}, + }) + }, + referenceGrants: []client.Object{ + refGrant("btp-gateway", gatewayNS, resource.KindBackendTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + refGrant("btp-route", routeNS, resource.KindBackendTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindHTTPRoute), + }, + expectedNames: sets.New("btp-gateway", "btp-route"), + }, + { + name: "ClientTrafficPolicy only includes gateway target grants", + mutateTree: func(resourceTree *resource.Resources) { + resourceTree.ClientTrafficPolicies = append(resourceTree.ClientTrafficPolicies, &egv1a1.ClientTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: "ctp"}, + }) + }, + referenceGrants: []client.Object{ + refGrant("ctp-gateway", gatewayNS, resource.KindClientTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + refGrant("ctp-route", routeNS, resource.KindClientTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindHTTPRoute), + }, + expectedNames: sets.New("ctp-gateway"), + }, + { + name: "EnvoyExtensionPolicy includes gateway and route target grants", + mutateTree: func(resourceTree *resource.Resources) { + resourceTree.EnvoyExtensionPolicies = append(resourceTree.EnvoyExtensionPolicies, &egv1a1.EnvoyExtensionPolicy{ + ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: "eep"}, + }) + }, + referenceGrants: []client.Object{ + refGrant("eep-gateway", gatewayNS, resource.KindEnvoyExtensionPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + refGrant("eep-route", routeNS, resource.KindEnvoyExtensionPolicy, policyNS, gwapiv1.GroupName, resource.KindHTTPRoute), + }, + expectedNames: sets.New("eep-gateway", "eep-route"), + }, + { + name: "ignores non matching grants", + mutateTree: func(resourceTree *resource.Resources) { + resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &egv1a1.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: "btp"}, + }) + }, + referenceGrants: []client.Object{ + refGrant("wrong-from-kind", gatewayNS, resource.KindClientTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + refGrant("wrong-from-namespace", gatewayNS, resource.KindBackendTrafficPolicy, otherNS, gwapiv1.GroupName, resource.KindGateway), + func() *gwapiv1b1.ReferenceGrant { + rg := refGrant("wrong-from-group", gatewayNS, resource.KindBackendTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway) + rg.Spec.From[0].Group = gwapiv1b1.Group(gwapiv1.GroupName) + return rg + }(), + refGrant("wrong-to-group", gatewayNS, resource.KindBackendTrafficPolicy, policyNS, corev1.GroupName, resource.KindGateway), + refGrant("outside-target-namespaces", otherNS, resource.KindBackendTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + }, + expectedNames: sets.New[string](), + }, + { + name: "deduplicates grants already collected", + mutateTree: func(resourceTree *resource.Resources) { + resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &egv1a1.BackendTrafficPolicy{ + ObjectMeta: metav1.ObjectMeta{Namespace: policyNS, Name: "btp"}, + }) + }, + referenceGrants: []client.Object{ + refGrant("btp-gateway", gatewayNS, resource.KindBackendTrafficPolicy, policyNS, gwapiv1.GroupName, resource.KindGateway), + }, + existingGrantName: "btp-gateway", + expectedNames: sets.New("btp-gateway"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resourceTree := resourceTreeWithTargets() + tc.mutateTree(resourceTree) + resourceMap := newResourceMapping() + + if tc.existingGrantName != "" { + for _, obj := range tc.referenceGrants { + refGrant := obj.(*gwapiv1b1.ReferenceGrant) + if refGrant.Name != tc.existingGrantName { + continue + } + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + } + } + + r := setupReferenceGrantReconciler(tc.referenceGrants) + require.NoError(t, r.processPolicyTargetReferenceGrants(t.Context(), resourceTree, resourceMap)) + + actualNames := sets.New[string]() + for _, refGrant := range resourceTree.ReferenceGrants { + actualNames.Insert(refGrant.Name) + } + require.Equal(t, tc.expectedNames, actualNames) + require.Len(t, resourceTree.ReferenceGrants, tc.expectedNames.Len()) + }) + } +} + func TestIsTransientError(t *testing.T) { serverTimeoutErr := kerrors.NewServerTimeout( schema.GroupResource{Group: "core", Resource: "pods"}, "list", 10) diff --git a/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml b/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml index 794eefb96a..8222935caa 100644 --- a/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml +++ b/test/e2e/testdata/backendtrafficpolicy-cross-namespace.yaml @@ -50,17 +50,6 @@ spec: type: Inline inline: "ok" --- -apiVersion: gateway.envoyproxy.io/v1alpha1 -kind: HTTPRouteFilter -metadata: - name: direct-response-ok - namespace: btp-cross-ns-denied -spec: - directResponse: - body: - type: Inline - inline: "ok" ---- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: @@ -98,12 +87,9 @@ spec: - path: type: PathPrefix value: / - filters: - - type: ExtensionRef - extensionRef: - group: gateway.envoyproxy.io - kind: HTTPRouteFilter - name: direct-response-ok + backendRefs: + - name: infra-backend-v1 + port: 8080 --- apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant