diff --git a/apis/v1alpha2/policy_types.go b/apis/v1alpha2/policy_types.go index cf151ea23d..83c8107a2c 100644 --- a/apis/v1alpha2/policy_types.go +++ b/apis/v1alpha2/policy_types.go @@ -16,11 +16,11 @@ limitations under the License. package v1alpha2 -// PolicyTargetReference identifies an API object to apply policy to. This -// should be used as part of Policy resources that can target Gateway API -// resources. For more information on how this policy attachment model works, -// and a sample Policy resource, refer to the policy attachment documentation -// for Gateway API. +// PolicyTargetReference identifies an API object to apply a direct or +// inherited policy to. This should be used as part of Policy resources +// that can target Gateway API resources. For more information on how this +// policy attachment model works, and a sample Policy resource, refer to +// the policy attachment documentation for Gateway API. type PolicyTargetReference struct { // Group is the group of the target resource. Group Group `json:"group"` @@ -40,6 +40,33 @@ type PolicyTargetReference struct { Namespace *Namespace `json:"namespace,omitempty"` } +// PolicyTargetReferenceWithSectionName identifies an API object to apply a direct +// policy to. This should be used as part of Policy resources that can target +// single resources. For more information on how this policy attachment mode +// works, and a sample Policy resource, refer to the policy attachment documentation +// for Gateway API. +// +// Note: This should only be used for direct policy attachment when references +// to SectionName are actually needed. In all other cases, PolicyTargetReference +// should be used. +type PolicyTargetReferenceWithSectionName struct { + PolicyTargetReference `json:",inline"` + + // 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 + // * 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 *SectionName `json:"sectionName,omitempty"` +} + // PolicyConditionType is a type of condition for a policy. This type should be // used with a Policy resource Status.Conditions field. type PolicyConditionType string diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index f14f4c0984..0c88caa292 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -497,6 +497,27 @@ func (in *PolicyTargetReference) DeepCopy() *PolicyTargetReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PolicyTargetReferenceWithSectionName) DeepCopyInto(out *PolicyTargetReferenceWithSectionName) { + *out = *in + in.PolicyTargetReference.DeepCopyInto(&out.PolicyTargetReference) + if in.SectionName != nil { + in, out := &in.SectionName, &out.SectionName + *out = new(v1beta1.SectionName) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PolicyTargetReferenceWithSectionName. +func (in *PolicyTargetReferenceWithSectionName) DeepCopy() *PolicyTargetReferenceWithSectionName { + if in == nil { + return nil + } + out := new(PolicyTargetReferenceWithSectionName) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ReferenceGrant) DeepCopyInto(out *ReferenceGrant) { *out = *in diff --git a/geps/gep-713.md b/geps/gep-713.md index cef7af9805..c677541c76 100644 --- a/geps/gep-713.md +++ b/geps/gep-713.md @@ -1244,20 +1244,44 @@ level. The implementations that support this policy attachment model will have the same behavior and semantics, although they may not be able to support attachment of all types of policy at all potential attachment points. -### Apply Policies to Sections of a Resource (Future Extension) -Although initially out of scope, it would be helpful to be able to target -specific matches within nested objects. For example, it may be useful to attach -policies to a specific Gateway listener or Route rule. This section explores -what that could look like. - -Each Route rule or Gateway listener should be expanded with an optional name -field. The target ref would be expanded with an optional sectionName field that -could be used to refer to that specific section of the resource. It would refer -to the following concepts on these resources: - -* Gateway.Listeners.Name -* xRoute.Rules.Name +### Apply Policies to Sections of a Resource +Policies can target specific matches within nested objects. For instance, rather than +applying a policy to the entire Gateway, we may want to attach it to a particular Gateway listener. + +To achieve this, an optional `sectionName` field can be set in the `targetRef` of a policy +to refer to a specific listener within the target Gateway. + +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: foo-gateway +spec: + gatewayClassName: foo-lb + listeners: + - name: bar + ... +--- +apiVersion: networking.acme.io/v1alpha2 +kind: AuthenticationPolicy +metadata: + name: foo +spec: + provider: + issuer: "https://oidc.example.com" + targetRef: + name: foo-gateway + group: gateway.networking.k8s.io + kind: Gateway + sectionName: bar +``` + +The `sectionName` field can also be used to target a specific section of other resources: + * Service.Ports.Name +* xRoute.Rules.Name + +For example, the RetryPolicy below applies to a RouteRule inside an HTTPRoute. ```yaml apiVersion: gateway.networking.k8s.io/v1alpha2 @@ -1292,43 +1316,8 @@ spec: sectionName: bar ``` -This would require adding a `SectionName` field to the PolicyTargetReference: -```go -type PolicyTargetReference struct { - // SectionName is the name of a section within the target resource. When - // unspecified, this targets the entire resource. In the following - // resources, SectionName is interpreted as the following: - // * Gateway: Listener Name - // * Route: Rule Name - // * Service: Port Name - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +optional - SectionName string `json:"sectionName,omitempty"` - // ... -} -``` - -This would also require adding a `Name` field to Gateway listeners and Route -rules: - -```go -type Listener struct { - // Name is the name of the Listener. If more than one Listener is present - // each Listener MUST specify a name. The names of Listeners MUST be unique - // within a Gateway. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +optional - Name string `json:"name,omitempty"` - // ... -} -``` - +This would require adding a `name` field to those sub-resources that currently lack a name. For example, +a `name` field could be added to the `RouteRule` object: ```go type RouteRule struct { // Name is the name of the Route rule. If more than one Route Rule is @@ -1345,6 +1334,17 @@ type RouteRule struct { } ``` +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. + +When multiple Policies of the same type target the same object, one with a `sectionName` specified, and one without, +the one with a `sectionName` is more specific, and so will have all its settings apply. The less-specific Policy will +not attach to the target. + +Note that the `sectionName` is currently intended to be used only for Direct Policy Attachment when references to +SectionName are actually needed. Inherited Policies are always applied to the entire object. +The `PolicyTargetReferenceWithSectionName` API can be used to apply a direct Policy to a section of an object. + ### Advantages * Incredibly flexible approach that should work well for both ingress and mesh * Conceptually similar to existing ServicePolicy proposal and BackendPolicy