From 3362e3d5baba481424c6f4ff9891490ff0cd1184 Mon Sep 17 00:00:00 2001 From: Nick Young Date: Thu, 29 Jul 2021 06:34:08 +0000 Subject: [PATCH 1/7] Implement GEP-709, ReferencePolicy Signed-off-by: Nick Young --- ...nce_types.go => object_reference_types.go} | 30 +++++ apis/v1alpha2/referencepolicy_types.go | 114 ++++++++++++++++ apis/v1alpha2/zz_generated.deepcopy.go | 93 +++++++++++++ apis/v1alpha2/zz_generated.register.go | 1 + ...y.networking.k8s.io_referencepolicies.yaml | 122 ++++++++++++++++++ site-src/geps/gep-709.md | 2 +- 6 files changed, 361 insertions(+), 1 deletion(-) rename apis/v1alpha2/{local_object_reference_types.go => object_reference_types.go} (58%) create mode 100644 apis/v1alpha2/referencepolicy_types.go create mode 100644 config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml diff --git a/apis/v1alpha2/local_object_reference_types.go b/apis/v1alpha2/object_reference_types.go similarity index 58% rename from apis/v1alpha2/local_object_reference_types.go rename to apis/v1alpha2/object_reference_types.go index 078a82d6c1..5164462ae2 100644 --- a/apis/v1alpha2/local_object_reference_types.go +++ b/apis/v1alpha2/object_reference_types.go @@ -36,3 +36,33 @@ type LocalObjectReference struct { // +kubebuilder:validation:MaxLength=253 Name string `json:"name"` } + +// ObjectReference identifies an API object including its namespace. +type ObjectReference struct { + // Group is the group of the referent. + // + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is kind of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` + + // Name is the name of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // Namespace is the namespace of the backend. When unspecified, the local + // namespace is inferred. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Namespace *string `json:"namespace,omitempty"` +} diff --git a/apis/v1alpha2/referencepolicy_types.go b/apis/v1alpha2/referencepolicy_types.go new file mode 100644 index 0000000000..73c7134eb4 --- /dev/null +++ b/apis/v1alpha2/referencepolicy_types.go @@ -0,0 +1,114 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha2 + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// ReferencePolicy identifies kinds of resources in other namespaces that are +// trusted to reference the specified kinds of resources in the local namespace. +// Each ReferencePolicy can be used to represent a unique trust relationship. +// Additional Reference Policies can be used to add to the set of trusted +// sources of inbound references for the namespace they are defined within. +type ReferencePolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of ReferencePolicy. + Spec ReferencePolicySpec `json:"spec,omitempty"` +} + +// ReferencePolicySpec identifies a cross namespace relationship that is trusted +// for Gateway API. +type ReferencePolicySpec struct { + // From describes the trusted namespaces and kinds that can reference the + // resources described in "To". Each entry in this list must be considered + // to be an additional place that references can be valid from, or to put + // this another way, entries must be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + From []ReferencePolicyFrom `json:"from"` + + // To describes the resources that may be referenced by the resources + // described in "From". Each entry in this list must be considered to be an + // additional place that references can be valid to, or to put this another + // way, entries must be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + To []ReferencePolicyTo `json:"to"` +} + +// ReferencePolicyFrom describes trusted namespaces and kinds. +type ReferencePolicyFrom struct { + // Group is the group of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following Route types are part of the "Core" + // support level for this field: + // + // * HTTPRoute + // * TCPRoute + // * TLSRoute + // * UDPRoute + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` + + // Namespace is the namespace of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Namespace string `json:"namespace,omitempty"` +} + +// ReferencePolicyTo describes what Kinds are allowed as targets of the +// references. +type ReferencePolicyTo struct { + // Group is the group of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following types are part of the "Core" + // support level for this field: + // + // * Service + // * HTTPRoute + // * TCPRoute + // * TLSRoute + // * UDPRoute + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` +} diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index e097759f38..eb120adbb3 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -861,6 +861,26 @@ func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { + *out = *in + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference. +func (in *ObjectReference) DeepCopy() *ObjectReference { + if in == nil { + return nil + } + out := new(ObjectReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ParametersReference) DeepCopyInto(out *ParametersReference) { *out = *in @@ -886,6 +906,79 @@ func (in *ParametersReference) DeepCopy() *ParametersReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferencePolicy) DeepCopyInto(out *ReferencePolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferencePolicy. +func (in *ReferencePolicy) DeepCopy() *ReferencePolicy { + if in == nil { + return nil + } + out := new(ReferencePolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferencePolicyFrom) DeepCopyInto(out *ReferencePolicyFrom) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferencePolicyFrom. +func (in *ReferencePolicyFrom) DeepCopy() *ReferencePolicyFrom { + if in == nil { + return nil + } + out := new(ReferencePolicyFrom) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferencePolicySpec) DeepCopyInto(out *ReferencePolicySpec) { + *out = *in + if in.From != nil { + in, out := &in.From, &out.From + *out = make([]ReferencePolicyFrom, len(*in)) + copy(*out, *in) + } + if in.To != nil { + in, out := &in.To, &out.To + *out = make([]ReferencePolicyTo, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferencePolicySpec. +func (in *ReferencePolicySpec) DeepCopy() *ReferencePolicySpec { + if in == nil { + return nil + } + out := new(ReferencePolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferencePolicyTo) DeepCopyInto(out *ReferencePolicyTo) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferencePolicyTo. +func (in *ReferencePolicyTo) DeepCopy() *ReferencePolicyTo { + if in == nil { + return nil + } + out := new(ReferencePolicyTo) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteBindingSelector) DeepCopyInto(out *RouteBindingSelector) { *out = *in diff --git a/apis/v1alpha2/zz_generated.register.go b/apis/v1alpha2/zz_generated.register.go index 0cd582861d..1e03c7e6bd 100644 --- a/apis/v1alpha2/zz_generated.register.go +++ b/apis/v1alpha2/zz_generated.register.go @@ -64,6 +64,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { &GatewayList{}, &HTTPRoute{}, &HTTPRouteList{}, + &ReferencePolicy{}, &TCPRoute{}, &TCPRouteList{}, &TLSRoute{}, diff --git a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml new file mode 100644 index 0000000000..7ddf7b99a6 --- /dev/null +++ b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml @@ -0,0 +1,122 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: unapproved + creationTimestamp: null + name: referencepolicies.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + kind: ReferencePolicy + listKind: ReferencePolicyList + plural: referencepolicies + singular: referencepolicy + scope: Namespaced + versions: + - name: v1alpha2 + schema: + openAPIV3Schema: + description: ReferencePolicy identifies kinds of resources in other namespaces + that are trusted to reference the specified kinds of resources in the local + namespace. Each ReferencePolicy can be used to represent a unique trust + relationship. Additional Reference Policies can be used to add to the set + of trusted sources of inbound references for the namespace they are defined + within. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ReferencePolicy. + properties: + from: + description: "From describes the trusted namespaces and kinds that + can reference the resources described in \"To\". Each entry in this + list must be considered to be an additional place that references + can be valid from, or to put this another way, entries must be combined + using OR. \n Support: Core" + items: + description: ReferencePolicyFrom describes trusted namespaces and + kinds. + properties: + group: + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + kind: + description: "Kind is the kind of the referent. Although implementations + may support additional resources, the following Route types + are part of the \"Core\" support level for this field: \n + * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 253 + minLength: 1 + type: string + namespace: + description: "Namespace is the namespace of the referent. \n + Support: Core" + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + minItems: 1 + type: array + to: + description: "To describes the resources that may be referenced by + the resources described in \"From\". Each entry in this list must + be considered to be an additional place that references can be valid + to, or to put this another way, entries must be combined using OR. + \n Support: Core" + items: + description: ReferencePolicyTo describes what Kinds are allowed + as targets of the references. + properties: + group: + description: "Group is the group of the referent. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string + kind: + description: "Kind is the kind of the referent. Although implementations + may support additional resources, the following types are + part of the \"Core\" support level for this field: \n * Service + * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + type: object + minItems: 1 + type: array + required: + - from + - to + type: object + type: object + served: true + storage: true +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/site-src/geps/gep-709.md b/site-src/geps/gep-709.md index 86d8892e68..10f1094749 100644 --- a/site-src/geps/gep-709.md +++ b/site-src/geps/gep-709.md @@ -1,7 +1,7 @@ # GEP-709: Cross Namespace References from Routes * Issue: [#709](https://github.com/kubernetes-sigs/gateway-api/issues/709) -* Status: Implementable +* Status: Implemented ## TLDR From 0aee0fc945393917cd176b66cc3e2b61b7b475eb Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 2 Aug 2021 03:58:19 +0000 Subject: [PATCH 2/7] Complete documentation and add Gateway status info Signed-off-by: Nick Young --- apis/v1alpha2/gateway_types.go | 7 + apis/v1alpha2/httproute_types.go | 23 +- apis/v1alpha2/referencepolicy_types.go | 18 ++ apis/v1alpha2/zz_generated.deepcopy.go | 40 +++ apis/v1alpha2/zz_generated.register.go | 1 + ...y.networking.k8s.io_referencepolicies.yaml | 11 +- .../typed/apis/v1alpha2/apis_client.go | 5 + .../apis/v1alpha2/fake/fake_apis_client.go | 4 + .../v1alpha2/fake/fake_referencepolicy.go | 130 +++++++++ .../apis/v1alpha2/generated_expansion.go | 2 + .../typed/apis/v1alpha2/referencepolicy.go | 178 ++++++++++++ .../apis/v1alpha2/interface.go | 7 + .../apis/v1alpha2/referencepolicy.go | 90 ++++++ .../gateway/externalversions/generic.go | 2 + .../apis/v1alpha2/expansion_generated.go | 8 + .../gateway/apis/v1alpha2/referencepolicy.go | 99 +++++++ .../references/cross-namespace-references.md | 273 ++++++++++++++++++ .../references/images/referencepolicy.png | Bin 0 -> 39133 bytes 18 files changed, 891 insertions(+), 7 deletions(-) create mode 100644 pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_referencepolicy.go create mode 100644 pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/referencepolicy.go create mode 100644 pkg/client/informers/gateway/externalversions/apis/v1alpha2/referencepolicy.go create mode 100644 pkg/client/listers/gateway/apis/v1alpha2/referencepolicy.go create mode 100644 site-src/v1alpha2/references/cross-namespace-references.md create mode 100644 site-src/v1alpha2/references/images/referencepolicy.png diff --git a/apis/v1alpha2/gateway_types.go b/apis/v1alpha2/gateway_types.go index 6cb91ae534..0c19963e67 100644 --- a/apis/v1alpha2/gateway_types.go +++ b/apis/v1alpha2/gateway_types.go @@ -812,6 +812,7 @@ const ( // * "DegradedRoutes" // * "InvalidCertificateRef" // * "InvalidRoutesRef" + // * "ReferenceNotPermitted" // // Controllers may raise this condition with other reasons, // but should prefer to use the reasons listed above to improve @@ -839,6 +840,12 @@ const ( // not resolve any Routes, and the "ResolvedRefs" status condition // should not be raised in that case. ListenerReasonInvalidRoutesRef ListenerConditionReason = "InvalidRoutesRef" + + // This reason is used with the "ResolvedRefs" condition when + // one of the Listener's Routes has a BackendRef to an object in + // another namespace, where the object in the other namespace does + // not have a ReferencePolicy explicitly allowing the reference. + ListenerReasonRefNotPermitted ListenerConditionReason = "RefNotPermitted" ) const ( diff --git a/apis/v1alpha2/httproute_types.go b/apis/v1alpha2/httproute_types.go index b8ec846480..cd518fe17e 100644 --- a/apis/v1alpha2/httproute_types.go +++ b/apis/v1alpha2/httproute_types.go @@ -737,13 +737,24 @@ type HTTPRequestMirrorFilter struct { // HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. type HTTPBackendRef struct { - // BackendRef defines how a Route should forward a request to a Kubernetes - // resource. + // BackendRef is a reference to a backend to forward matched requests to. If + // both BackendRef and ServiceName are specified, ServiceName will be given + // precedence. // - // If the referent cannot be found, the rule is not included in the route. - // The controller should raise the "ResolvedRefs" condition on the Gateway - // with the "DegradedRoutes" reason. The gateway status for this route should - // be updated with a condition that describes the error more specifically. + // If the referent cannot be found, the route must be dropped + // from the Gateway. The controller should raise the "ResolvedRefs" + // condition on the Gateway with the "DegradedRoutes" reason. + // The gateway status for this route should be updated with a + // condition that describes the error more specifically. + // + // If there is a cross-namespace reference to an *existing* object + // with no ReferencePolicy, the controller must ensure the "ResolvedRefs" + // condition on the Gateway is set to `status: true`, with the "RefNotPermitted" + // reason. + // + // Support: Custom + // + // +optional BackendRef `json:",inline"` // Filters defined at this-level should be executed if and only if the diff --git a/apis/v1alpha2/referencepolicy_types.go b/apis/v1alpha2/referencepolicy_types.go index 73c7134eb4..4a24822bbf 100644 --- a/apis/v1alpha2/referencepolicy_types.go +++ b/apis/v1alpha2/referencepolicy_types.go @@ -18,6 +18,12 @@ package v1alpha2 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:resource:categories=gateway-api,shortName=refpol +// +kubebuilder:storageversion +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` + // ReferencePolicy identifies kinds of resources in other namespaces that are // trusted to reference the specified kinds of resources in the local namespace. // Each ReferencePolicy can be used to represent a unique trust relationship. @@ -29,6 +35,18 @@ type ReferencePolicy struct { // Spec defines the desired state of ReferencePolicy. Spec ReferencePolicySpec `json:"spec,omitempty"` + + // Note that we are explicitly *excluding* ReferencePolicy status at the + // moment, as designing it is more difficult than it would seem. + // As it is an additive change, we can make changes later. +} + +// +kubebuilder:object:root=true +// ReferencePolicyList contains a list of ReferencePolicy. +type ReferencePolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ReferencePolicy `json:"items"` } // ReferencePolicySpec identifies a cross namespace relationship that is trusted diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index eb120adbb3..38f0263861 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -924,6 +924,14 @@ func (in *ReferencePolicy) DeepCopy() *ReferencePolicy { return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ReferencePolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ReferencePolicyFrom) DeepCopyInto(out *ReferencePolicyFrom) { *out = *in @@ -939,6 +947,38 @@ func (in *ReferencePolicyFrom) DeepCopy() *ReferencePolicyFrom { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReferencePolicyList) DeepCopyInto(out *ReferencePolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ReferencePolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReferencePolicyList. +func (in *ReferencePolicyList) DeepCopy() *ReferencePolicyList { + if in == nil { + return nil + } + out := new(ReferencePolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ReferencePolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ReferencePolicySpec) DeepCopyInto(out *ReferencePolicySpec) { *out = *in diff --git a/apis/v1alpha2/zz_generated.register.go b/apis/v1alpha2/zz_generated.register.go index 1e03c7e6bd..94dc2ff41b 100644 --- a/apis/v1alpha2/zz_generated.register.go +++ b/apis/v1alpha2/zz_generated.register.go @@ -65,6 +65,7 @@ func addKnownTypes(scheme *runtime.Scheme) error { &HTTPRoute{}, &HTTPRouteList{}, &ReferencePolicy{}, + &ReferencePolicyList{}, &TCPRoute{}, &TCPRouteList{}, &TLSRoute{}, diff --git a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml index 7ddf7b99a6..befe29877a 100644 --- a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml @@ -10,13 +10,21 @@ metadata: spec: group: gateway.networking.k8s.io names: + categories: + - gateway-api kind: ReferencePolicy listKind: ReferencePolicyList plural: referencepolicies + shortNames: + - refpol singular: referencepolicy scope: Namespaced versions: - - name: v1alpha2 + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 schema: openAPIV3Schema: description: ReferencePolicy identifies kinds of resources in other namespaces @@ -114,6 +122,7 @@ spec: type: object served: true storage: true + subresources: {} status: acceptedNames: kind: "" diff --git a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/apis_client.go b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/apis_client.go index d124f85192..e036c11ee4 100644 --- a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/apis_client.go +++ b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/apis_client.go @@ -29,6 +29,7 @@ type GatewayV1alpha2Interface interface { GatewaysGetter GatewayClassesGetter HTTPRoutesGetter + ReferencePoliciesGetter TCPRoutesGetter TLSRoutesGetter UDPRoutesGetter @@ -51,6 +52,10 @@ func (c *GatewayV1alpha2Client) HTTPRoutes(namespace string) HTTPRouteInterface return newHTTPRoutes(c, namespace) } +func (c *GatewayV1alpha2Client) ReferencePolicies(namespace string) ReferencePolicyInterface { + return newReferencePolicies(c, namespace) +} + func (c *GatewayV1alpha2Client) TCPRoutes(namespace string) TCPRouteInterface { return newTCPRoutes(c, namespace) } diff --git a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_apis_client.go b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_apis_client.go index c308ec56eb..4b07afaf0e 100644 --- a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_apis_client.go +++ b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_apis_client.go @@ -40,6 +40,10 @@ func (c *FakeGatewayV1alpha2) HTTPRoutes(namespace string) v1alpha2.HTTPRouteInt return &FakeHTTPRoutes{c, namespace} } +func (c *FakeGatewayV1alpha2) ReferencePolicies(namespace string) v1alpha2.ReferencePolicyInterface { + return &FakeReferencePolicies{c, namespace} +} + func (c *FakeGatewayV1alpha2) TCPRoutes(namespace string) v1alpha2.TCPRouteInterface { return &FakeTCPRoutes{c, namespace} } diff --git a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_referencepolicy.go b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_referencepolicy.go new file mode 100644 index 0000000000..414a985f2c --- /dev/null +++ b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/fake/fake_referencepolicy.go @@ -0,0 +1,130 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" + v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// FakeReferencePolicies implements ReferencePolicyInterface +type FakeReferencePolicies struct { + Fake *FakeGatewayV1alpha2 + ns string +} + +var referencepoliciesResource = schema.GroupVersionResource{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Resource: "referencepolicies"} + +var referencepoliciesKind = schema.GroupVersionKind{Group: "gateway.networking.k8s.io", Version: "v1alpha2", Kind: "ReferencePolicy"} + +// Get takes name of the referencePolicy, and returns the corresponding referencePolicy object, and an error if there is any. +func (c *FakeReferencePolicies) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.ReferencePolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(referencepoliciesResource, c.ns, name), &v1alpha2.ReferencePolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ReferencePolicy), err +} + +// List takes label and field selectors, and returns the list of ReferencePolicies that match those selectors. +func (c *FakeReferencePolicies) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.ReferencePolicyList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(referencepoliciesResource, referencepoliciesKind, c.ns, opts), &v1alpha2.ReferencePolicyList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha2.ReferencePolicyList{ListMeta: obj.(*v1alpha2.ReferencePolicyList).ListMeta} + for _, item := range obj.(*v1alpha2.ReferencePolicyList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested referencePolicies. +func (c *FakeReferencePolicies) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(referencepoliciesResource, c.ns, opts)) + +} + +// Create takes the representation of a referencePolicy and creates it. Returns the server's representation of the referencePolicy, and an error, if there is any. +func (c *FakeReferencePolicies) Create(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.CreateOptions) (result *v1alpha2.ReferencePolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(referencepoliciesResource, c.ns, referencePolicy), &v1alpha2.ReferencePolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ReferencePolicy), err +} + +// Update takes the representation of a referencePolicy and updates it. Returns the server's representation of the referencePolicy, and an error, if there is any. +func (c *FakeReferencePolicies) Update(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.UpdateOptions) (result *v1alpha2.ReferencePolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(referencepoliciesResource, c.ns, referencePolicy), &v1alpha2.ReferencePolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ReferencePolicy), err +} + +// Delete takes name of the referencePolicy and deletes it. Returns an error if one occurs. +func (c *FakeReferencePolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(referencepoliciesResource, c.ns, name), &v1alpha2.ReferencePolicy{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeReferencePolicies) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(referencepoliciesResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha2.ReferencePolicyList{}) + return err +} + +// Patch applies the patch and returns the patched referencePolicy. +func (c *FakeReferencePolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ReferencePolicy, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(referencepoliciesResource, c.ns, name, pt, data, subresources...), &v1alpha2.ReferencePolicy{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha2.ReferencePolicy), err +} diff --git a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/generated_expansion.go b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/generated_expansion.go index 1952cc3bc6..bf9c1d9718 100644 --- a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/generated_expansion.go +++ b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/generated_expansion.go @@ -24,6 +24,8 @@ type GatewayClassExpansion interface{} type HTTPRouteExpansion interface{} +type ReferencePolicyExpansion interface{} + type TCPRouteExpansion interface{} type TLSRouteExpansion interface{} diff --git a/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/referencepolicy.go b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/referencepolicy.go new file mode 100644 index 0000000000..5f29f3a641 --- /dev/null +++ b/pkg/client/clientset/gateway/versioned/typed/apis/v1alpha2/referencepolicy.go @@ -0,0 +1,178 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" + v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + scheme "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned/scheme" +) + +// ReferencePoliciesGetter has a method to return a ReferencePolicyInterface. +// A group's client should implement this interface. +type ReferencePoliciesGetter interface { + ReferencePolicies(namespace string) ReferencePolicyInterface +} + +// ReferencePolicyInterface has methods to work with ReferencePolicy resources. +type ReferencePolicyInterface interface { + Create(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.CreateOptions) (*v1alpha2.ReferencePolicy, error) + Update(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.UpdateOptions) (*v1alpha2.ReferencePolicy, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha2.ReferencePolicy, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha2.ReferencePolicyList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ReferencePolicy, err error) + ReferencePolicyExpansion +} + +// referencePolicies implements ReferencePolicyInterface +type referencePolicies struct { + client rest.Interface + ns string +} + +// newReferencePolicies returns a ReferencePolicies +func newReferencePolicies(c *GatewayV1alpha2Client, namespace string) *referencePolicies { + return &referencePolicies{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the referencePolicy, and returns the corresponding referencePolicy object, and an error if there is any. +func (c *referencePolicies) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha2.ReferencePolicy, err error) { + result = &v1alpha2.ReferencePolicy{} + err = c.client.Get(). + Namespace(c.ns). + Resource("referencepolicies"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ReferencePolicies that match those selectors. +func (c *referencePolicies) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha2.ReferencePolicyList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha2.ReferencePolicyList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("referencepolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested referencePolicies. +func (c *referencePolicies) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("referencepolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a referencePolicy and creates it. Returns the server's representation of the referencePolicy, and an error, if there is any. +func (c *referencePolicies) Create(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.CreateOptions) (result *v1alpha2.ReferencePolicy, err error) { + result = &v1alpha2.ReferencePolicy{} + err = c.client.Post(). + Namespace(c.ns). + Resource("referencepolicies"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(referencePolicy). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a referencePolicy and updates it. Returns the server's representation of the referencePolicy, and an error, if there is any. +func (c *referencePolicies) Update(ctx context.Context, referencePolicy *v1alpha2.ReferencePolicy, opts v1.UpdateOptions) (result *v1alpha2.ReferencePolicy, err error) { + result = &v1alpha2.ReferencePolicy{} + err = c.client.Put(). + Namespace(c.ns). + Resource("referencepolicies"). + Name(referencePolicy.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(referencePolicy). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the referencePolicy and deletes it. Returns an error if one occurs. +func (c *referencePolicies) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("referencepolicies"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *referencePolicies) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("referencepolicies"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched referencePolicy. +func (c *referencePolicies) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha2.ReferencePolicy, err error) { + result = &v1alpha2.ReferencePolicy{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("referencepolicies"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/informers/gateway/externalversions/apis/v1alpha2/interface.go b/pkg/client/informers/gateway/externalversions/apis/v1alpha2/interface.go index c43fec8654..6d75f2128c 100644 --- a/pkg/client/informers/gateway/externalversions/apis/v1alpha2/interface.go +++ b/pkg/client/informers/gateway/externalversions/apis/v1alpha2/interface.go @@ -30,6 +30,8 @@ type Interface interface { GatewayClasses() GatewayClassInformer // HTTPRoutes returns a HTTPRouteInformer. HTTPRoutes() HTTPRouteInformer + // ReferencePolicies returns a ReferencePolicyInformer. + ReferencePolicies() ReferencePolicyInformer // TCPRoutes returns a TCPRouteInformer. TCPRoutes() TCPRouteInformer // TLSRoutes returns a TLSRouteInformer. @@ -64,6 +66,11 @@ func (v *version) HTTPRoutes() HTTPRouteInformer { return &hTTPRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// ReferencePolicies returns a ReferencePolicyInformer. +func (v *version) ReferencePolicies() ReferencePolicyInformer { + return &referencePolicyInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // TCPRoutes returns a TCPRouteInformer. func (v *version) TCPRoutes() TCPRouteInformer { return &tCPRouteInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/gateway/externalversions/apis/v1alpha2/referencepolicy.go b/pkg/client/informers/gateway/externalversions/apis/v1alpha2/referencepolicy.go new file mode 100644 index 0000000000..070b7c55d6 --- /dev/null +++ b/pkg/client/informers/gateway/externalversions/apis/v1alpha2/referencepolicy.go @@ -0,0 +1,90 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "context" + time "time" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" + apisv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + versioned "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned" + internalinterfaces "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions/internalinterfaces" + v1alpha2 "sigs.k8s.io/gateway-api/pkg/client/listers/gateway/apis/v1alpha2" +) + +// ReferencePolicyInformer provides access to a shared informer and lister for +// ReferencePolicies. +type ReferencePolicyInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha2.ReferencePolicyLister +} + +type referencePolicyInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewReferencePolicyInformer constructs a new informer for ReferencePolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewReferencePolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredReferencePolicyInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredReferencePolicyInformer constructs a new informer for ReferencePolicy type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredReferencePolicyInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayV1alpha2().ReferencePolicies(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.GatewayV1alpha2().ReferencePolicies(namespace).Watch(context.TODO(), options) + }, + }, + &apisv1alpha2.ReferencePolicy{}, + resyncPeriod, + indexers, + ) +} + +func (f *referencePolicyInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredReferencePolicyInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *referencePolicyInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&apisv1alpha2.ReferencePolicy{}, f.defaultInformer) +} + +func (f *referencePolicyInformer) Lister() v1alpha2.ReferencePolicyLister { + return v1alpha2.NewReferencePolicyLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/gateway/externalversions/generic.go b/pkg/client/informers/gateway/externalversions/generic.go index 0925ef97eb..9caba89b7a 100644 --- a/pkg/client/informers/gateway/externalversions/generic.go +++ b/pkg/client/informers/gateway/externalversions/generic.go @@ -59,6 +59,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1alpha2().GatewayClasses().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("httproutes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1alpha2().HTTPRoutes().Informer()}, nil + case v1alpha2.SchemeGroupVersion.WithResource("referencepolicies"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1alpha2().ReferencePolicies().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("tcproutes"): return &genericInformer{resource: resource.GroupResource(), informer: f.Gateway().V1alpha2().TCPRoutes().Informer()}, nil case v1alpha2.SchemeGroupVersion.WithResource("tlsroutes"): diff --git a/pkg/client/listers/gateway/apis/v1alpha2/expansion_generated.go b/pkg/client/listers/gateway/apis/v1alpha2/expansion_generated.go index 4c9bab0f5b..1e8f44a0af 100644 --- a/pkg/client/listers/gateway/apis/v1alpha2/expansion_generated.go +++ b/pkg/client/listers/gateway/apis/v1alpha2/expansion_generated.go @@ -38,6 +38,14 @@ type HTTPRouteListerExpansion interface{} // HTTPRouteNamespaceLister. type HTTPRouteNamespaceListerExpansion interface{} +// ReferencePolicyListerExpansion allows custom methods to be added to +// ReferencePolicyLister. +type ReferencePolicyListerExpansion interface{} + +// ReferencePolicyNamespaceListerExpansion allows custom methods to be added to +// ReferencePolicyNamespaceLister. +type ReferencePolicyNamespaceListerExpansion interface{} + // TCPRouteListerExpansion allows custom methods to be added to // TCPRouteLister. type TCPRouteListerExpansion interface{} diff --git a/pkg/client/listers/gateway/apis/v1alpha2/referencepolicy.go b/pkg/client/listers/gateway/apis/v1alpha2/referencepolicy.go new file mode 100644 index 0000000000..5a1f994b9d --- /dev/null +++ b/pkg/client/listers/gateway/apis/v1alpha2/referencepolicy.go @@ -0,0 +1,99 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha2 + +import ( + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" + v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// ReferencePolicyLister helps list ReferencePolicies. +// All objects returned here must be treated as read-only. +type ReferencePolicyLister interface { + // List lists all ReferencePolicies in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha2.ReferencePolicy, err error) + // ReferencePolicies returns an object that can list and get ReferencePolicies. + ReferencePolicies(namespace string) ReferencePolicyNamespaceLister + ReferencePolicyListerExpansion +} + +// referencePolicyLister implements the ReferencePolicyLister interface. +type referencePolicyLister struct { + indexer cache.Indexer +} + +// NewReferencePolicyLister returns a new ReferencePolicyLister. +func NewReferencePolicyLister(indexer cache.Indexer) ReferencePolicyLister { + return &referencePolicyLister{indexer: indexer} +} + +// List lists all ReferencePolicies in the indexer. +func (s *referencePolicyLister) List(selector labels.Selector) (ret []*v1alpha2.ReferencePolicy, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.ReferencePolicy)) + }) + return ret, err +} + +// ReferencePolicies returns an object that can list and get ReferencePolicies. +func (s *referencePolicyLister) ReferencePolicies(namespace string) ReferencePolicyNamespaceLister { + return referencePolicyNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// ReferencePolicyNamespaceLister helps list and get ReferencePolicies. +// All objects returned here must be treated as read-only. +type ReferencePolicyNamespaceLister interface { + // List lists all ReferencePolicies in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha2.ReferencePolicy, err error) + // Get retrieves the ReferencePolicy from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha2.ReferencePolicy, error) + ReferencePolicyNamespaceListerExpansion +} + +// referencePolicyNamespaceLister implements the ReferencePolicyNamespaceLister +// interface. +type referencePolicyNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all ReferencePolicies in the indexer for a given namespace. +func (s referencePolicyNamespaceLister) List(selector labels.Selector) (ret []*v1alpha2.ReferencePolicy, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha2.ReferencePolicy)) + }) + return ret, err +} + +// Get retrieves the ReferencePolicy from the indexer for a given namespace and name. +func (s referencePolicyNamespaceLister) Get(name string) (*v1alpha2.ReferencePolicy, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha2.Resource("referencepolicy"), name) + } + return obj.(*v1alpha2.ReferencePolicy), nil +} diff --git a/site-src/v1alpha2/references/cross-namespace-references.md b/site-src/v1alpha2/references/cross-namespace-references.md new file mode 100644 index 0000000000..9e9e556f07 --- /dev/null +++ b/site-src/v1alpha2/references/cross-namespace-references.md @@ -0,0 +1,273 @@ +# Cross namespace references and ReferencePolicy + +In the Gateway API, it is possible to have references between objects cross namespace boundaries. +In particular, it's expected that Gateways and Routes may exist in different namespaces, +or that Services may be referred to by Routes in a another namespace. + +However, this significantly violates the idea of a namespace as the edge of a trust domain. +In order to bring cross-namespace references under the control of the owner of the referent object's namespace, +the Gateway API has a ReferencePolicy object that must be created in the referent namespace for the reference to be successful. + +To put this another way, if an object is referred to from outside its namespace, +the object's owner must create a ReferencePolicy object that describes how that reference is allowed. +This page explains how this process works. + +In the past, we've seen that forwarding traffic across namespace boundaries is a desired feature, +but without the kinds of safeguards proposed here, [vulnerabilities](https://github.com/kubernetes/kubernetes/issues/103675) +can emerge. + +## ReferencePolicy + +To ensure that Gateway API is able to safely provide this functionality, +we need to enforce a handshake mechanism that requires resources in both namespaces to agree to this reference. +To accomplish that, a ReferencePolicy resource has been be introduced. + +![Reference Policy](images/referencepolicy.png) + +With this model, Routes are able to directly reference Routes and Services in other namespaces. +These references are only considered valid if a ReferencePolicy in the target namespace explicitly allows it. + +The following example shows how a HTTPRoute in namespace `foo` can reference a Service in namespace `bar`. +In this example a ReferencePolicy in the `bar` namespace explicitly allows references to Services from HTTPRoutes in the `foo` namespace. + +```yaml +kind: HTTPRoute +metadata: + name: foo + namespace: foo +spec: + rules: + - matches: + - path: /bar + forwardTo: + backend: + - name: bar + namespace: bar +--- +kind: ReferencePolicy +metadata: + name: bar + namespace: bar +spec: + from: + - group: networking.gateway.k8s.io + kind: HTTPRoute + namespace: foo + to: + - group: core + kind: Service +``` + +### API design decisions +This proposed API is fairly straightforward, but comes with a few notable decisions: + +1. Each ReferencePolicy only supports a single From and To section. + Additional trust relationships must be modeled with additional ReferencePolicy resources. +1. Resource names are intentionally excluded from this policy for simplicity and because they rarely provide any meaningful protection. + A user that is able to write to resources of a certain kind within a namespace can always rename + resources or change the structure of the resources to match a given policy. +1. A single Namespace is allowed per "From" struct. + Although a selector would be more powerful it may encourage unnecessarily insecure configuration. + +```go +// ReferencePolicy identifies kinds of resources in other namespaces that are +// trusted to reference the specified kinds of resources in the local namespace. +// Each ReferencePolicy can be used to represent a unique trust relationship. +// Additional Reference Policies can be used to add to the set of trusted +// sources of inbound references for the namespace they are defined within. +type ReferencePolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of ReferencePolicy. + Spec ReferencePolicySpec `json:"spec,omitempty"` +} + + +// ReferencePolicySpec identifies a cross namespace relationship that is trusted +// for Gateway API. +type ReferencePolicySpec struct { + // From describes the trusted namespaces and kinds that can reference the + // resources described in "To". Each entry in this list must be considered + // to be an additional place that references can be valid from, or to put + // this another way, entries must be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + From []ReferencePolicyFrom `json:"from"` + + // To describes the resources that may be referenced by the resources + // described in "From". Each entry in this list must be considered to be an + // additional place that references can be valid to, or to put this another + // way, entries must be combined using OR. + // + // Support: Core + // + // +kubebuilder:validation:MinItems=1 + To []ReferencePolicyTo `json:"to"` +} + +// ReferencePolicyFrom describes trusted namespaces and kinds. +type ReferencePolicyFrom struct { + // Group is the group of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following Route types are part of the "Core" + // support level for this field: + // + // * HTTPRoute + // * TCPRoute + // * TLSRoute + // * UDPRoute + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` + + // Namespace is the namespace of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Namespace string `json:"namespace,omitempty"` +} + +// ReferencePolicyTo describes what Kinds are allowed as targets of the +// references. +type ReferencePolicyTo struct { + // Group is the group of the referent. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is the kind of the referent. Although implementations may support + // additional resources, the following types are part of the "Core" + // support level for this field: + // + // * Service + // * HTTPRoute + // * TCPRoute + // * TLSRoute + // * UDPRoute + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` +} +``` + +### Benefits + +* Conceptually similar to NetworkPolicy. +* A separate resource enables admins to restrict who can allow cross namespace + references. +* Provides consistent way to control references to any resource from a Route. +* Can be extended in the future for additional use cases. +* A single ReferencePolicy resource can be used for a namespace in place of + separate handshake config on each Service or Route resource. + +### Exceptions +There are some situations where it MAY be acceptable to ignore ReferencePolicy +in favor of some other security mechanism. This MAY only be done if other +mechanisms like NetworkPolicy can effectively limit cross-namespace references +by the implementation. + +An implementation choosing to make this exception MUST clearly document that +ReferencePolicy is not honored by their implementations and detail which +alternative safeguards are available. Note that this is unlikely to apply to +ingress implementations of the API and will not apply to all mesh +implementations. + +For an example of the risks involved in cross-namespace references, refer to +[CVE-2021-25740](https://github.com/kubernetes/kubernetes/issues/103675). +Implementations of this API need to be very careful to avoid confused deputy +attacks. ReferencePolicy provides a safeguard for that. Exceptions MUST only +be made by implementations that are absolutely certain that other equally +effective safeguards are in place. + +## ForwardTo + +To enable cross-namespace forwarding, we have created a new `ObjectReference` struct that can be used in places +where cross-namespace references are possible, and have updated the HTTPRoute ForwardTo field `BackendRef` to use the new type. + +```go + +// Fron object_reference_types.go + +// ObjectReference identifies an API object including its namespace. +type ObjectReference struct { + // Group is the group of the referent. + // + // +kubebuilder:validation:MaxLength=253 + Group string `json:"group"` + + // Kind is kind of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind string `json:"kind"` + + // Name is the name of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // Namespace is the namespace of the backend. When unspecified, the local + // namespace is inferred. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Namespace *string `json:"namespace,omitempty"` +} + + +// From httproute_types.go + + // BackendRef is a reference to a backend to forward matched requests to. If + // both BackendRef and ServiceName are specified, ServiceName will be given + // precedence. + // + // If the referent cannot be found, the route must be dropped + // from the Gateway. The controller should raise the "ResolvedRefs" + // condition on the Gateway with the "DegradedRoutes" reason. + // The gateway status for this route should be updated with a + // condition that describes the error more specifically. + // + // If there is a cross-namespace reference to an *existing* object + // with no ReferencePolicy, the controller must ensure the "ResolvedRefs" + // condition on the Gateway is set to `status: true`, with the "RefNotPermitted" + // reason. + // + // Support: Custom + // + // +optional + BackendRef *ObjectReference `json:"backendRef,omitempty"` + + // The BackendRef in the more general RouteForwardTo object in shared_types.go + // has also been updated. +``` +### Conformance Level + +ReferencePolicy support is a "CORE" coformance level for the following +objects: +- HTTPRoute +- TLSRoute +- TCPRoute +- UDPRoute + +Other "ImplementationSpecific" objects and references are *strongly recommended* to also use this flow for cross-namespace references. \ No newline at end of file diff --git a/site-src/v1alpha2/references/images/referencepolicy.png b/site-src/v1alpha2/references/images/referencepolicy.png new file mode 100644 index 0000000000000000000000000000000000000000..e211764a8b8760df943f1dd0ad8c1716860c801f GIT binary patch literal 39133 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 z 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 U+P4Srn^F Date: Fri, 6 Aug 2021 07:45:55 +0000 Subject: [PATCH 3/7] Update after PR review Signed-off-by: Nick Young --- apis/v1alpha2/gateway_types.go | 2 +- apis/v1alpha2/httproute_types.go | 4 +- apis/v1alpha2/object_reference_types.go | 1 + apis/v1alpha2/referencepolicy_types.go | 19 +- ...y.networking.k8s.io_referencepolicies.yaml | 24 +- .../references/cross-namespace-references.md | 252 ++++-------------- 6 files changed, 85 insertions(+), 217 deletions(-) diff --git a/apis/v1alpha2/gateway_types.go b/apis/v1alpha2/gateway_types.go index 0c19963e67..8f224e8e90 100644 --- a/apis/v1alpha2/gateway_types.go +++ b/apis/v1alpha2/gateway_types.go @@ -812,7 +812,7 @@ const ( // * "DegradedRoutes" // * "InvalidCertificateRef" // * "InvalidRoutesRef" - // * "ReferenceNotPermitted" + // * "RefNotPermitted" // // Controllers may raise this condition with other reasons, // but should prefer to use the reasons listed above to improve diff --git a/apis/v1alpha2/httproute_types.go b/apis/v1alpha2/httproute_types.go index cd518fe17e..dd17e1406b 100644 --- a/apis/v1alpha2/httproute_types.go +++ b/apis/v1alpha2/httproute_types.go @@ -749,8 +749,8 @@ type HTTPBackendRef struct { // // If there is a cross-namespace reference to an *existing* object // with no ReferencePolicy, the controller must ensure the "ResolvedRefs" - // condition on the Gateway is set to `status: true`, with the "RefNotPermitted" - // reason. + // condition on the Gateway is set to `status: false`, with the "RefNotPermitted" + // reason and not configure this route in the underlying implementation. // // Support: Custom // diff --git a/apis/v1alpha2/object_reference_types.go b/apis/v1alpha2/object_reference_types.go index 5164462ae2..ee0825bb53 100644 --- a/apis/v1alpha2/object_reference_types.go +++ b/apis/v1alpha2/object_reference_types.go @@ -40,6 +40,7 @@ type LocalObjectReference struct { // ObjectReference identifies an API object including its namespace. type ObjectReference struct { // Group is the group of the referent. + // When empty, the "core" API group is inferred. // // +kubebuilder:validation:MaxLength=253 Group string `json:"group"` diff --git a/apis/v1alpha2/referencepolicy_types.go b/apis/v1alpha2/referencepolicy_types.go index 4a24822bbf..41228387e9 100644 --- a/apis/v1alpha2/referencepolicy_types.go +++ b/apis/v1alpha2/referencepolicy_types.go @@ -1,5 +1,5 @@ /* -Copyright 2020 The Kubernetes Authors. +Copyright 2021 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -25,10 +25,15 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` // ReferencePolicy identifies kinds of resources in other namespaces that are -// trusted to reference the specified kinds of resources in the local namespace. +// trusted to reference the specified kinds of resources in the same namespace +// as the policy. +// // Each ReferencePolicy can be used to represent a unique trust relationship. // Additional Reference Policies can be used to add to the set of trusted // sources of inbound references for the namespace they are defined within. +// +// All cross-namespace references in Gateway API (with the exception of cross-namespace +// Gateway-route attachment) require a ReferencePolicy. type ReferencePolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -36,9 +41,9 @@ type ReferencePolicy struct { // Spec defines the desired state of ReferencePolicy. Spec ReferencePolicySpec `json:"spec,omitempty"` - // Note that we are explicitly *excluding* ReferencePolicy status at the - // moment, as designing it is more difficult than it would seem. - // As it is an additive change, we can make changes later. + // Note that `Status` sub-resource has been excluded at the + // moment as it was difficult to work out the design. + // `Status` sub-resource may be added in future. } // +kubebuilder:object:root=true @@ -60,6 +65,7 @@ type ReferencePolicySpec struct { // Support: Core // // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=16 From []ReferencePolicyFrom `json:"from"` // To describes the resources that may be referenced by the resources @@ -70,12 +76,14 @@ type ReferencePolicySpec struct { // Support: Core // // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=16 To []ReferencePolicyTo `json:"to"` } // ReferencePolicyFrom describes trusted namespaces and kinds. type ReferencePolicyFrom struct { // Group is the group of the referent. + // When empty, the "core" API group is inferred. // // Support: Core // @@ -109,6 +117,7 @@ type ReferencePolicyFrom struct { // references. type ReferencePolicyTo struct { // Group is the group of the referent. + // When empty, the "core" API group is inferred. // // Support: Core // diff --git a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml index befe29877a..f8e023fc86 100644 --- a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml @@ -27,12 +27,14 @@ spec: name: v1alpha2 schema: openAPIV3Schema: - description: ReferencePolicy identifies kinds of resources in other namespaces - that are trusted to reference the specified kinds of resources in the local - namespace. Each ReferencePolicy can be used to represent a unique trust - relationship. Additional Reference Policies can be used to add to the set - of trusted sources of inbound references for the namespace they are defined - within. + description: "ReferencePolicy identifies kinds of resources in other namespaces + that are trusted to reference the specified kinds of resources in the same + namespace as the policy. \n Each ReferencePolicy can be used to represent + a unique trust relationship. Additional Reference Policies can be used to + add to the set of trusted sources of inbound references for the namespace + they are defined within. \n All cross-namespace references in Gateway API + (with the exception of cross-namespace Gateway-route attachment) require + a ReferencePolicy." properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -60,8 +62,8 @@ spec: kinds. properties: group: - description: "Group is the group of the referent. \n Support: - Core" + description: "Group is the group of the referent. When empty, + the \"core\" API group is inferred. \n Support: Core" maxLength: 253 minLength: 1 type: string @@ -83,6 +85,7 @@ spec: - group - kind type: object + maxItems: 16 minItems: 1 type: array to: @@ -96,8 +99,8 @@ spec: as targets of the references. properties: group: - description: "Group is the group of the referent. \n Support: - Core" + description: "Group is the group of the referent. When empty, + the \"core\" API group is inferred. \n Support: Core" maxLength: 253 minLength: 1 type: string @@ -113,6 +116,7 @@ spec: - group - kind type: object + maxItems: 16 minItems: 1 type: array required: diff --git a/site-src/v1alpha2/references/cross-namespace-references.md b/site-src/v1alpha2/references/cross-namespace-references.md index 9e9e556f07..1235750cce 100644 --- a/site-src/v1alpha2/references/cross-namespace-references.md +++ b/site-src/v1alpha2/references/cross-namespace-references.md @@ -1,34 +1,48 @@ # Cross namespace references and ReferencePolicy -In the Gateway API, it is possible to have references between objects cross namespace boundaries. -In particular, it's expected that Gateways and Routes may exist in different namespaces, -or that Services may be referred to by Routes in a another namespace. - -However, this significantly violates the idea of a namespace as the edge of a trust domain. -In order to bring cross-namespace references under the control of the owner of the referent object's namespace, -the Gateway API has a ReferencePolicy object that must be created in the referent namespace for the reference to be successful. +### Terminology note +When discussing the process of creating cross-namespace object references, this +document and the documentation on the API itself refers to the object being +referred to as "the referent object", using the meaning of "referent" to be +"the person, thing, or idea that a word, phrase, or object refers to".[1](https://dictionary.cambridge.org/dictionary/english/referent) + +## Introduction +In the Gateway API, it is possible to have references between objects cross +namespace boundaries. In particular, that Services may be referred to by Routes +in a another namespace, or possibly Secrets may be referred to by Gateways or +Routes in another namespace. + +In the past, we've seen that forwarding traffic across namespace boundaries is a +desired feature, but without the kinds of safeguards proposed here, +[vulnerabilities](https://github.com/kubernetes/kubernetes/issues/103675) can emerge. + +In order to bring cross-namespace references under the control +of the owner of the referent object's namespace, the Gateway API has a +ReferencePolicy object that must be created in the referent namespace for the +reference to be successful. To put this another way, if an object is referred to from outside its namespace, -the object's owner must create a ReferencePolicy object that describes how that reference is allowed. -This page explains how this process works. +the object's owner must create a ReferencePolicy object that describes how that +reference is allowed. This page explains how this process works. + -In the past, we've seen that forwarding traffic across namespace boundaries is a desired feature, -but without the kinds of safeguards proposed here, [vulnerabilities](https://github.com/kubernetes/kubernetes/issues/103675) -can emerge. ## ReferencePolicy To ensure that Gateway API is able to safely provide this functionality, -we need to enforce a handshake mechanism that requires resources in both namespaces to agree to this reference. -To accomplish that, a ReferencePolicy resource has been be introduced. +we need to enforce a handshake mechanism that requires resources in both +namespaces to agree to this reference. To accomplish that, a ReferencePolicy +resource has been introduced. ![Reference Policy](images/referencepolicy.png) -With this model, Routes are able to directly reference Routes and Services in other namespaces. -These references are only considered valid if a ReferencePolicy in the target namespace explicitly allows it. +With this model, Routes are able to directly reference Services in other namespaces. +These references are only considered valid if a ReferencePolicy in the target +namespace explicitly allows it. -The following example shows how a HTTPRoute in namespace `foo` can reference a Service in namespace `bar`. -In this example a ReferencePolicy in the `bar` namespace explicitly allows references to Services from HTTPRoutes in the `foo` namespace. +The following example shows how a HTTPRoute in namespace `foo` can reference a +Service in namespace `bar`. In this example a ReferencePolicy in the `bar` namespace +explicitly allows references to Services from HTTPRoutes in the `foo` namespace. ```yaml kind: HTTPRoute @@ -59,123 +73,22 @@ spec: ``` ### API design decisions -This proposed API is fairly straightforward, but comes with a few notable decisions: +While the API is simplistic in nature, it comes with a few notable decisions: 1. Each ReferencePolicy only supports a single From and To section. - Additional trust relationships must be modeled with additional ReferencePolicy resources. -1. Resource names are intentionally excluded from this policy for simplicity and because they rarely provide any meaningful protection. - A user that is able to write to resources of a certain kind within a namespace can always rename - resources or change the structure of the resources to match a given policy. + Additional trust relationships must be modeled with additional ReferencePolicy + resources. +1. Resource names are intentionally excluded from this policy for simplicity + and because they rarely provide any meaningful protection. A user that is + able to write to resources of a certain kind within a namespace can always + rename resources or change the structure of the resources to match a given + policy. 1. A single Namespace is allowed per "From" struct. - Although a selector would be more powerful it may encourage unnecessarily insecure configuration. - -```go -// ReferencePolicy identifies kinds of resources in other namespaces that are -// trusted to reference the specified kinds of resources in the local namespace. -// Each ReferencePolicy can be used to represent a unique trust relationship. -// Additional Reference Policies can be used to add to the set of trusted -// sources of inbound references for the namespace they are defined within. -type ReferencePolicy struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the desired state of ReferencePolicy. - Spec ReferencePolicySpec `json:"spec,omitempty"` -} - - -// ReferencePolicySpec identifies a cross namespace relationship that is trusted -// for Gateway API. -type ReferencePolicySpec struct { - // From describes the trusted namespaces and kinds that can reference the - // resources described in "To". Each entry in this list must be considered - // to be an additional place that references can be valid from, or to put - // this another way, entries must be combined using OR. - // - // Support: Core - // - // +kubebuilder:validation:MinItems=1 - From []ReferencePolicyFrom `json:"from"` - - // To describes the resources that may be referenced by the resources - // described in "From". Each entry in this list must be considered to be an - // additional place that references can be valid to, or to put this another - // way, entries must be combined using OR. - // - // Support: Core - // - // +kubebuilder:validation:MinItems=1 - To []ReferencePolicyTo `json:"to"` -} - -// ReferencePolicyFrom describes trusted namespaces and kinds. -type ReferencePolicyFrom struct { - // Group is the group of the referent. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Group string `json:"group"` - - // Kind is the kind of the referent. Although implementations may support - // additional resources, the following Route types are part of the "Core" - // support level for this field: - // - // * HTTPRoute - // * TCPRoute - // * TLSRoute - // * UDPRoute - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Kind string `json:"kind"` - - // Namespace is the namespace of the referent. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Namespace string `json:"namespace,omitempty"` -} - -// ReferencePolicyTo describes what Kinds are allowed as targets of the -// references. -type ReferencePolicyTo struct { - // Group is the group of the referent. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Group string `json:"group"` - - // Kind is the kind of the referent. Although implementations may support - // additional resources, the following types are part of the "Core" - // support level for this field: - // - // * Service - // * HTTPRoute - // * TCPRoute - // * TLSRoute - // * UDPRoute - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Kind string `json:"kind"` -} -``` - -### Benefits + Although a selector would be more powerful, it encourages unnecessarily + insecure configuration. -* Conceptually similar to NetworkPolicy. -* A separate resource enables admins to restrict who can allow cross namespace - references. -* Provides consistent way to control references to any resource from a Route. -* Can be extended in the future for additional use cases. -* A single ReferencePolicy resource can be used for a namespace in place of - separate handshake config on each Service or Route resource. +Please see the [API Specification](https://gateway-api.sigs.k8s.io/references/spec/) +for the details of the object's behavior. ### Exceptions There are some situations where it MAY be acceptable to ignore ReferencePolicy @@ -196,78 +109,19 @@ attacks. ReferencePolicy provides a safeguard for that. Exceptions MUST only be made by implementations that are absolutely certain that other equally effective safeguards are in place. -## ForwardTo - -To enable cross-namespace forwarding, we have created a new `ObjectReference` struct that can be used in places -where cross-namespace references are possible, and have updated the HTTPRoute ForwardTo field `BackendRef` to use the new type. - -```go - -// Fron object_reference_types.go - -// ObjectReference identifies an API object including its namespace. -type ObjectReference struct { - // Group is the group of the referent. - // - // +kubebuilder:validation:MaxLength=253 - Group string `json:"group"` - - // Kind is kind of the referent. - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Kind string `json:"kind"` - - // Name is the name of the referent. - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Name string `json:"name"` - - // Namespace is the namespace of the backend. When unspecified, the local - // namespace is inferred. - // - // Support: Core - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - // +optional - Namespace *string `json:"namespace,omitempty"` -} - - -// From httproute_types.go - - // BackendRef is a reference to a backend to forward matched requests to. If - // both BackendRef and ServiceName are specified, ServiceName will be given - // precedence. - // - // If the referent cannot be found, the route must be dropped - // from the Gateway. The controller should raise the "ResolvedRefs" - // condition on the Gateway with the "DegradedRoutes" reason. - // The gateway status for this route should be updated with a - // condition that describes the error more specifically. - // - // If there is a cross-namespace reference to an *existing* object - // with no ReferencePolicy, the controller must ensure the "ResolvedRefs" - // condition on the Gateway is set to `status: true`, with the "RefNotPermitted" - // reason. - // - // Support: Custom - // - // +optional - BackendRef *ObjectReference `json:"backendRef,omitempty"` - - // The BackendRef in the more general RouteForwardTo object in shared_types.go - // has also been updated. -``` ### Conformance Level -ReferencePolicy support is a "CORE" coformance level for the following -objects: +ReferencePolicy support is a "CORE" conformance level requirement for +cross-namespace references inside the following objects: - HTTPRoute - TLSRoute - TCPRoute - UDPRoute -Other "ImplementationSpecific" objects and references are *strongly recommended* to also use this flow for cross-namespace references. \ No newline at end of file +That is, all implemenations MUST use this flow for any cross namespaces +in any of the core xRoute types, except as noted in the Exceptions section +above. + +Other "ImplementationSpecific" objects and references MUST also use this flow +for cross-namespace references, except as noted in the Exceptions section +above. \ No newline at end of file From 0ad53af92dd0f478e458e6afd3da7736d9c09a0c Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 9 Aug 2021 00:27:28 +0000 Subject: [PATCH 4/7] Refactor after GEP-718 changes Signed-off-by: Nick Young --- apis/v1alpha2/httproute_types.go | 33 ++++++++---- apis/v1alpha2/object_reference_types.go | 67 +++++++++++++++++++++++-- apis/v1alpha2/shared_types.go | 39 ++------------ 3 files changed, 90 insertions(+), 49 deletions(-) diff --git a/apis/v1alpha2/httproute_types.go b/apis/v1alpha2/httproute_types.go index dd17e1406b..1357fcbb3f 100644 --- a/apis/v1alpha2/httproute_types.go +++ b/apis/v1alpha2/httproute_types.go @@ -728,6 +728,15 @@ type HTTPRequestMirrorFilter struct { // with the "DegradedRoutes" reason. The gateway status for this route should // be updated with a condition that describes the error more specifically. // + // If there is a cross-namespace reference to an *existing* object + // that is not covered by a ReferencePolicy, the controller must ensure the + // "ResolvedRefs" condition on the Gateway is set to `status: true`, + // with the "RefNotPermitted" reason and not configure this backend in the + // underlying implementation. + // + // In either error case, the Message of the `ResolvedRefs` Condition + // should be used to provide more detail about the problem. + // // Support: Extended for Kubernetes Service // Support: Custom for any other resource // @@ -737,20 +746,22 @@ type HTTPRequestMirrorFilter struct { // HTTPBackendRef defines how a HTTPRoute should forward an HTTP request. type HTTPBackendRef struct { - // BackendRef is a reference to a backend to forward matched requests to. If - // both BackendRef and ServiceName are specified, ServiceName will be given - // precedence. + // BackendRef is a reference to a backend to forward matched requests to. // - // If the referent cannot be found, the route must be dropped - // from the Gateway. The controller should raise the "ResolvedRefs" - // condition on the Gateway with the "DegradedRoutes" reason. - // The gateway status for this route should be updated with a - // condition that describes the error more specifically. + // If the referent cannot be found, this HTTPBackendRef is invalid + // and must be dropped from the Gateway. The controller must ensure the + // "ResolvedRefs" condition on the Gateway is set to `status: true` + // with the "DegradedRoutes" reason, and not configure this backend in the + // underlying implemenation. // // If there is a cross-namespace reference to an *existing* object - // with no ReferencePolicy, the controller must ensure the "ResolvedRefs" - // condition on the Gateway is set to `status: false`, with the "RefNotPermitted" - // reason and not configure this route in the underlying implementation. + // that is not covered by a ReferencePolicy, the controller must ensure the + // "ResolvedRefs" condition on the Gateway is set to `status: true`, + // with the "RefNotPermitted" reason and not configure this backend in the + // underlying implementation. + // + // In either error case, the Message of the `ResolvedRefs` Condition + // should be used to provide more detail about the problem. // // Support: Custom // diff --git a/apis/v1alpha2/object_reference_types.go b/apis/v1alpha2/object_reference_types.go index ee0825bb53..16e429f89d 100644 --- a/apis/v1alpha2/object_reference_types.go +++ b/apis/v1alpha2/object_reference_types.go @@ -40,16 +40,20 @@ type LocalObjectReference struct { // ObjectReference identifies an API object including its namespace. type ObjectReference struct { // Group is the group of the referent. - // When empty, the "core" API group is inferred. + // When unspecified (empty string), core API group is inferred. // + // +optional + // +kubebuilder:default="" // +kubebuilder:validation:MaxLength=253 - Group string `json:"group"` + Group *string `json:"group"` // Kind is kind of the referent. // + // +optional + // +kubebuilder:default=Service // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - Kind string `json:"kind"` + Kind *string `json:"kind"` // Name is the name of the referent. // @@ -60,6 +64,10 @@ type ObjectReference struct { // Namespace is the namespace of the backend. When unspecified, the local // namespace is inferred. // + // Note that when a namespace is specified, a ReferencePolicy object + // is required in the referent namespace to allow that namespace's + // owner to accept the reference. See the ReferencePolicy object for details. + // // Support: Core // // +kubebuilder:validation:MinLength=1 @@ -67,3 +75,56 @@ type ObjectReference struct { // +optional Namespace *string `json:"namespace,omitempty"` } + +// BackendObjectReference defines how an ObjectReference that is +// specific to BackendRef. It includes a few additional fields and features +// than a regular ObjectReference. +// +// Note that when a namespace is specified, a ReferencePolicy object +// is required in the referent namespace to allow that namespace's +// owner to accept the reference. See the ReferencePolicy object for details. +type BackendObjectReference struct { + // Group is the group of the referent. + // When unspecified (empty string), core API group is inferred. + // + // +optional + // +kubebuilder:default="" + // +kubebuilder:validation:MaxLength=253 + Group *string `json:"group"` + + // Kind is kind of the referent. + // + // +optional + // +kubebuilder:default=Service + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Kind *string `json:"kind"` + + // Name is the name of the referent. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Name string `json:"name"` + + // Namespace is the namespace of the backend. When unspecified, the local + // namespace is inferred. + // + // Note that when a namespace is specified, a ReferencePolicy object + // is required in the referent namespace to allow that namespace's + // owner to accept the reference. See the ReferencePolicy object for details. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +optional + Namespace *string `json:"namespace,omitempty"` + + // Port specifies the destination port number to use for this resource. + // Port is required when the referent is a Kubernetes Service. + // For other resources, destination port can be derived from the referent + // resource or this field. + // + // +optional + Port *PortNumber `json:"port,omitempty"` +} diff --git a/apis/v1alpha2/shared_types.go b/apis/v1alpha2/shared_types.go index bdbc7abc1c..5bc2112c54 100644 --- a/apis/v1alpha2/shared_types.go +++ b/apis/v1alpha2/shared_types.go @@ -77,43 +77,12 @@ type GatewayReference struct { Namespace string `json:"namespace"` } -// BackendObjectReference defines how an ObjectReference that is -// specific to BackendRef. It includes a few additional fields and features -// than a regular ObjectReference. -type BackendObjectReference struct { - // Group is the group of the referent. - // When unspecified (empty string), core API group is inferred. - // - // +optional - // +kubebuilder:default="" - // +kubebuilder:validation:MaxLength=253 - Group *string `json:"group"` - - // Kind is kind of the referent. - // - // +optional - // +kubebuilder:default=Service - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Kind *string `json:"kind"` - - // Name is the name of the referent. - // - // +kubebuilder:validation:MinLength=1 - // +kubebuilder:validation:MaxLength=253 - Name string `json:"name"` - - // Port specifies the destination port number to use for this resource. - // Port is required when the referent is a Kubernetes Service. - // For other resources, destination port can be derived from the referent - // resource or this field. - // - // +optional - Port *PortNumber `json:"port,omitempty"` -} - // BackendRef defines how a Route should forward a request to a Kubernetes // resource. +// +// Note that when a namespace is specified, a ReferencePolicy object +// is required in the referent namespace to allow that namespace's +// owner to accept the reference. See the ReferencePolicy object for details. type BackendRef struct { // BackendObjectReference references a Kubernetes object. BackendObjectReference `json:",inline"` From e4bfd3ae2bd5e1cff9196fa9881badce016926ee Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 9 Aug 2021 01:31:14 +0000 Subject: [PATCH 5/7] Fix last round of PR comments Signed-off-by: Nick Young --- apis/v1alpha2/httproute_types.go | 9 +- apis/v1alpha2/referencepolicy_types.go | 9 +- apis/v1alpha2/zz_generated.deepcopy.go | 15 ++++ .../gateway.networking.k8s.io_httproutes.yaml | 84 +++++++++++++++---- ...y.networking.k8s.io_referencepolicies.yaml | 7 +- .../gateway.networking.k8s.io_tcproutes.yaml | 19 ++++- .../gateway.networking.k8s.io_tlsroutes.yaml | 19 ++++- .../gateway.networking.k8s.io_udproutes.yaml | 19 ++++- .../references/cross-namespace-references.md | 2 +- 9 files changed, 145 insertions(+), 38 deletions(-) diff --git a/apis/v1alpha2/httproute_types.go b/apis/v1alpha2/httproute_types.go index 1357fcbb3f..40987d81fb 100644 --- a/apis/v1alpha2/httproute_types.go +++ b/apis/v1alpha2/httproute_types.go @@ -723,10 +723,11 @@ type HTTPRequestRedirect struct { type HTTPRequestMirrorFilter struct { // BackendRef references a resource where mirrored requests are sent. // - // If the referent cannot be found, the rule is not included in the route. - // The controller should raise the "ResolvedRefs" condition on the Gateway - // with the "DegradedRoutes" reason. The gateway status for this route should - // be updated with a condition that describes the error more specifically. + // If the referent cannot be found, this HTTPBackendRef is invalid + // and must be dropped from the Gateway. The controller must ensure the + // "ResolvedRefs" condition on the Gateway is set to `status: true` + // with the "DegradedRoutes" reason, and not configure this backend in the + // underlying implemenation. // // If there is a cross-namespace reference to an *existing* object // that is not covered by a ReferencePolicy, the controller must ensure the diff --git a/apis/v1alpha2/referencepolicy_types.go b/apis/v1alpha2/referencepolicy_types.go index 41228387e9..4d93c0cdd8 100644 --- a/apis/v1alpha2/referencepolicy_types.go +++ b/apis/v1alpha2/referencepolicy_types.go @@ -34,6 +34,9 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // // All cross-namespace references in Gateway API (with the exception of cross-namespace // Gateway-route attachment) require a ReferencePolicy. +// +// Support: Core +// type ReferencePolicy struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` @@ -87,7 +90,6 @@ type ReferencePolicyFrom struct { // // Support: Core // - // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 Group string `json:"group"` @@ -121,7 +123,6 @@ type ReferencePolicyTo struct { // // Support: Core // - // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 Group string `json:"group"` @@ -130,10 +131,6 @@ type ReferencePolicyTo struct { // support level for this field: // // * Service - // * HTTPRoute - // * TCPRoute - // * TLSRoute - // * UDPRoute // // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index 38f0263861..ce785297e1 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -38,6 +38,11 @@ func (in *BackendObjectReference) DeepCopyInto(out *BackendObjectReference) { *out = new(string) **out = **in } + if in.Namespace != nil { + in, out := &in.Namespace, &out.Namespace + *out = new(string) + **out = **in + } if in.Port != nil { in, out := &in.Port, &out.Port *out = new(PortNumber) @@ -864,6 +869,16 @@ func (in *LocalObjectReference) DeepCopy() *LocalObjectReference { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ObjectReference) DeepCopyInto(out *ObjectReference) { *out = *in + if in.Group != nil { + in, out := &in.Group, &out.Group + *out = new(string) + **out = **in + } + if in.Kind != nil { + in, out := &in.Kind, &out.Kind + *out = new(string) + **out = **in + } if in.Namespace != nil { in, out := &in.Namespace, &out.Namespace *out = new(string) diff --git a/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml index 05a4882730..22e7ed6dba 100644 --- a/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml @@ -318,15 +318,24 @@ spec: backendRef: description: "BackendRef references a resource where mirrored requests are sent. \n If the - referent cannot be found, the rule is not - included in the route. The controller should - raise the \"ResolvedRefs\" condition on the - Gateway with the \"DegradedRoutes\" reason. - The gateway status for this route should be - updated with a condition that describes the - error more specifically. \n Support: Extended - for Kubernetes Service Support: Custom for - any other resource" + referent cannot be found, this HTTPBackendRef + is invalid and must be dropped from the Gateway. + The controller must ensure the \"ResolvedRefs\" + condition on the Gateway is set to `status: + true` with the \"DegradedRoutes\" reason, + and not configure this backend in the underlying + implemenation. \n If there is a cross-namespace + reference to an *existing* object that is + not covered by a ReferencePolicy, the controller + must ensure the \"ResolvedRefs\" condition + on the Gateway is set to `status: true`, with + the \"RefNotPermitted\" reason and not configure + this backend in the underlying implementation. + \n In either error case, the Message of the + `ResolvedRefs` Condition should be used to + provide more detail about the problem. \n + Support: Extended for Kubernetes Service Support: + Custom for any other resource" properties: group: default: "" @@ -346,6 +355,18 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace + of the backend. When unspecified, the + local namespace is inferred. \n Note that + when a namespace is specified, a ReferencePolicy + object is required in the referent namespace + to allow that namespace's owner to accept + the reference. See the ReferencePolicy + object for details. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. @@ -442,6 +463,17 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferencePolicy + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferencePolicy object for details. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. Port is required when the @@ -624,13 +656,22 @@ spec: backendRef: description: "BackendRef references a resource where mirrored requests are sent. \n If the referent cannot - be found, the rule is not included in the route. - The controller should raise the \"ResolvedRefs\" - condition on the Gateway with the \"DegradedRoutes\" - reason. The gateway status for this route should - be updated with a condition that describes the error - more specifically. \n Support: Extended for Kubernetes - Service Support: Custom for any other resource" + be found, this HTTPBackendRef is invalid and must + be dropped from the Gateway. The controller must + ensure the \"ResolvedRefs\" condition on the Gateway + is set to `status: true` with the \"DegradedRoutes\" + reason, and not configure this backend in the underlying + implemenation. \n If there is a cross-namespace + reference to an *existing* object that is not covered + by a ReferencePolicy, the controller must ensure + the \"ResolvedRefs\" condition on the Gateway is + set to `status: true`, with the \"RefNotPermitted\" + reason and not configure this backend in the underlying + implementation. \n In either error case, the Message + of the `ResolvedRefs` Condition should be used to + provide more detail about the problem. \n Support: + Extended for Kubernetes Service Support: Custom + for any other resource" properties: group: default: "" @@ -650,6 +691,17 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace of the + backend. When unspecified, the local namespace + is inferred. \n Note that when a namespace is + specified, a ReferencePolicy object is required + in the referent namespace to allow that namespace's + owner to accept the reference. See the ReferencePolicy + object for details. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. Port is required diff --git a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml index f8e023fc86..d873c3cd94 100644 --- a/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_referencepolicies.yaml @@ -34,7 +34,7 @@ spec: add to the set of trusted sources of inbound references for the namespace they are defined within. \n All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require - a ReferencePolicy." + a ReferencePolicy. \n Support: Core" properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation @@ -65,7 +65,6 @@ spec: description: "Group is the group of the referent. When empty, the \"core\" API group is inferred. \n Support: Core" maxLength: 253 - minLength: 1 type: string kind: description: "Kind is the kind of the referent. Although implementations @@ -102,13 +101,11 @@ spec: description: "Group is the group of the referent. When empty, the \"core\" API group is inferred. \n Support: Core" maxLength: 253 - minLength: 1 type: string kind: description: "Kind is the kind of the referent. Although implementations may support additional resources, the following types are - part of the \"Core\" support level for this field: \n * Service - * HTTPRoute * TCPRoute * TLSRoute * UDPRoute" + part of the \"Core\" support level for this field: \n * Service" maxLength: 253 minLength: 1 type: string diff --git a/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml index 97002843a1..a143be94c2 100644 --- a/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml @@ -95,8 +95,12 @@ spec: Support: Custom for any other resource \n Support for weight: Extended" items: - description: BackendRef defines how a Route should forward - a request to a Kubernetes resource. + description: "BackendRef defines how a Route should forward + a request to a Kubernetes resource. \n Note that when a + namespace is specified, a ReferencePolicy object is required + in the referent namespace to allow that namespace's owner + to accept the reference. See the ReferencePolicy object + for details." properties: group: default: "" @@ -115,6 +119,17 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferencePolicy + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferencePolicy object for details. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. Port is required when the diff --git a/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml b/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml index 0f90b2c914..d351d7c863 100644 --- a/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml @@ -135,8 +135,12 @@ spec: Support: Custom for any other resource \n Support for weight: Extended" items: - description: BackendRef defines how a Route should forward - a request to a Kubernetes resource. + description: "BackendRef defines how a Route should forward + a request to a Kubernetes resource. \n Note that when a + namespace is specified, a ReferencePolicy object is required + in the referent namespace to allow that namespace's owner + to accept the reference. See the ReferencePolicy object + for details." properties: group: default: "" @@ -155,6 +159,17 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferencePolicy + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferencePolicy object for details. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. Port is required when the diff --git a/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml index 9284ee6de5..c938988dac 100644 --- a/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml @@ -96,8 +96,12 @@ spec: Support: Custom for any other resource \n Support for weight: Extended" items: - description: BackendRef defines how a Route should forward - a request to a Kubernetes resource. + description: "BackendRef defines how a Route should forward + a request to a Kubernetes resource. \n Note that when a + namespace is specified, a ReferencePolicy object is required + in the referent namespace to allow that namespace's owner + to accept the reference. See the ReferencePolicy object + for details." properties: group: default: "" @@ -116,6 +120,17 @@ spec: maxLength: 253 minLength: 1 type: string + namespace: + description: "Namespace is the namespace of the backend. + When unspecified, the local namespace is inferred. \n + Note that when a namespace is specified, a ReferencePolicy + object is required in the referent namespace to allow + that namespace's owner to accept the reference. See + the ReferencePolicy object for details. \n Support: + Core" + maxLength: 253 + minLength: 1 + type: string port: description: Port specifies the destination port number to use for this resource. Port is required when the diff --git a/site-src/v1alpha2/references/cross-namespace-references.md b/site-src/v1alpha2/references/cross-namespace-references.md index 1235750cce..e12b41e268 100644 --- a/site-src/v1alpha2/references/cross-namespace-references.md +++ b/site-src/v1alpha2/references/cross-namespace-references.md @@ -112,7 +112,7 @@ effective safeguards are in place. ### Conformance Level ReferencePolicy support is a "CORE" conformance level requirement for -cross-namespace references inside the following objects: +cross-namespace references that originate from the following objects: - HTTPRoute - TLSRoute - TCPRoute From 4038057212a63bcc6657939410a6a9b9c9b106c7 Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 9 Aug 2021 05:06:47 +0000 Subject: [PATCH 6/7] Fix another round of PR comments Signed-off-by: Nick Young --- apis/v1alpha2/httproute_types.go | 2 +- apis/v1alpha2/object_reference_types.go | 13 +++++++---- apis/v1alpha2/shared_types.go | 3 ++- .../gateway.networking.k8s.io_httproutes.yaml | 11 +++++---- .../gateway.networking.k8s.io_tcproutes.yaml | 4 ++-- .../gateway.networking.k8s.io_tlsroutes.yaml | 4 ++-- .../gateway.networking.k8s.io_udproutes.yaml | 4 ++-- .../references/cross-namespace-references.md | 23 ++++++++++--------- 8 files changed, 35 insertions(+), 29 deletions(-) diff --git a/apis/v1alpha2/httproute_types.go b/apis/v1alpha2/httproute_types.go index 40987d81fb..c533f16441 100644 --- a/apis/v1alpha2/httproute_types.go +++ b/apis/v1alpha2/httproute_types.go @@ -730,7 +730,7 @@ type HTTPRequestMirrorFilter struct { // underlying implemenation. // // If there is a cross-namespace reference to an *existing* object - // that is not covered by a ReferencePolicy, the controller must ensure the + // that is not allowed by a ReferencePolicy, the controller must ensure the // "ResolvedRefs" condition on the Gateway is set to `status: true`, // with the "RefNotPermitted" reason and not configure this backend in the // underlying implementation. diff --git a/apis/v1alpha2/object_reference_types.go b/apis/v1alpha2/object_reference_types.go index 16e429f89d..9ef8b9b122 100644 --- a/apis/v1alpha2/object_reference_types.go +++ b/apis/v1alpha2/object_reference_types.go @@ -66,7 +66,8 @@ type ObjectReference struct { // // Note that when a namespace is specified, a ReferencePolicy object // is required in the referent namespace to allow that namespace's - // owner to accept the reference. See the ReferencePolicy object for details. + // owner to accept the reference. See the ReferencePolicy documentation + // for details. // // Support: Core // @@ -82,7 +83,8 @@ type ObjectReference struct { // // Note that when a namespace is specified, a ReferencePolicy object // is required in the referent namespace to allow that namespace's -// owner to accept the reference. See the ReferencePolicy object for details. +// owner to accept the reference. See the ReferencePolicy documentation +// for details. type BackendObjectReference struct { // Group is the group of the referent. // When unspecified (empty string), core API group is inferred. @@ -90,7 +92,7 @@ type BackendObjectReference struct { // +optional // +kubebuilder:default="" // +kubebuilder:validation:MaxLength=253 - Group *string `json:"group"` + Group *string `json:"group,omitempty"` // Kind is kind of the referent. // @@ -98,7 +100,7 @@ type BackendObjectReference struct { // +kubebuilder:default=Service // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 - Kind *string `json:"kind"` + Kind *string `json:"kind,omitempty"` // Name is the name of the referent. // @@ -111,7 +113,8 @@ type BackendObjectReference struct { // // Note that when a namespace is specified, a ReferencePolicy object // is required in the referent namespace to allow that namespace's - // owner to accept the reference. See the ReferencePolicy object for details. + // owner to accept the reference. See the ReferencePolicy documentation + // for details. // // Support: Core // diff --git a/apis/v1alpha2/shared_types.go b/apis/v1alpha2/shared_types.go index 5bc2112c54..610298bc97 100644 --- a/apis/v1alpha2/shared_types.go +++ b/apis/v1alpha2/shared_types.go @@ -82,7 +82,8 @@ type GatewayReference struct { // // Note that when a namespace is specified, a ReferencePolicy object // is required in the referent namespace to allow that namespace's -// owner to accept the reference. See the ReferencePolicy object for details. +// owner to accept the reference. See the ReferencePolicy documentation +// for details. type BackendRef struct { // BackendObjectReference references a Kubernetes object. BackendObjectReference `json:",inline"` diff --git a/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml index 22e7ed6dba..73415a4c3d 100644 --- a/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_httproutes.yaml @@ -326,7 +326,7 @@ spec: and not configure this backend in the underlying implemenation. \n If there is a cross-namespace reference to an *existing* object that is - not covered by a ReferencePolicy, the controller + not allowed by a ReferencePolicy, the controller must ensure the \"ResolvedRefs\" condition on the Gateway is set to `status: true`, with the \"RefNotPermitted\" reason and not configure @@ -363,7 +363,8 @@ spec: object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferencePolicy - object for details. \n Support: Core" + documentation for details. \n Support: + Core" maxLength: 253 minLength: 1 type: string @@ -469,7 +470,7 @@ spec: Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner to accept the reference. See - the ReferencePolicy object for details. \n Support: + the ReferencePolicy documentation for details. \n Support: Core" maxLength: 253 minLength: 1 @@ -662,7 +663,7 @@ spec: is set to `status: true` with the \"DegradedRoutes\" reason, and not configure this backend in the underlying implemenation. \n If there is a cross-namespace - reference to an *existing* object that is not covered + reference to an *existing* object that is not allowed by a ReferencePolicy, the controller must ensure the \"ResolvedRefs\" condition on the Gateway is set to `status: true`, with the \"RefNotPermitted\" @@ -698,7 +699,7 @@ spec: specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferencePolicy - object for details. \n Support: Core" + documentation for details. \n Support: Core" maxLength: 253 minLength: 1 type: string diff --git a/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml index a143be94c2..d4e48ce808 100644 --- a/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_tcproutes.yaml @@ -99,7 +99,7 @@ spec: a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner - to accept the reference. See the ReferencePolicy object + to accept the reference. See the ReferencePolicy documentation for details." properties: group: @@ -125,7 +125,7 @@ spec: Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner to accept the reference. See - the ReferencePolicy object for details. \n Support: + the ReferencePolicy documentation for details. \n Support: Core" maxLength: 253 minLength: 1 diff --git a/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml b/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml index d351d7c863..7bf06164b2 100644 --- a/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_tlsroutes.yaml @@ -139,7 +139,7 @@ spec: a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner - to accept the reference. See the ReferencePolicy object + to accept the reference. See the ReferencePolicy documentation for details." properties: group: @@ -165,7 +165,7 @@ spec: Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner to accept the reference. See - the ReferencePolicy object for details. \n Support: + the ReferencePolicy documentation for details. \n Support: Core" maxLength: 253 minLength: 1 diff --git a/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml b/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml index c938988dac..f20a1b161f 100644 --- a/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/bases/gateway.networking.k8s.io_udproutes.yaml @@ -100,7 +100,7 @@ spec: a request to a Kubernetes resource. \n Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner - to accept the reference. See the ReferencePolicy object + to accept the reference. See the ReferencePolicy documentation for details." properties: group: @@ -126,7 +126,7 @@ spec: Note that when a namespace is specified, a ReferencePolicy object is required in the referent namespace to allow that namespace's owner to accept the reference. See - the ReferencePolicy object for details. \n Support: + the ReferencePolicy documentation for details. \n Support: Core" maxLength: 253 minLength: 1 diff --git a/site-src/v1alpha2/references/cross-namespace-references.md b/site-src/v1alpha2/references/cross-namespace-references.md index e12b41e268..4d599d24f2 100644 --- a/site-src/v1alpha2/references/cross-namespace-references.md +++ b/site-src/v1alpha2/references/cross-namespace-references.md @@ -1,21 +1,24 @@ # Cross namespace references and ReferencePolicy -### Terminology note -When discussing the process of creating cross-namespace object references, this -document and the documentation on the API itself refers to the object being -referred to as "the referent object", using the meaning of "referent" to be -"the person, thing, or idea that a word, phrase, or object refers to".[1](https://dictionary.cambridge.org/dictionary/english/referent) - ## Introduction In the Gateway API, it is possible to have references between objects cross -namespace boundaries. In particular, that Services may be referred to by Routes +namespace boundaries. In particular, Services may be referred to by Routes in a another namespace, or possibly Secrets may be referred to by Gateways or Routes in another namespace. In the past, we've seen that forwarding traffic across namespace boundaries is a -desired feature, but without the kinds of safeguards proposed here, +desired feature, but a safeguard like ReferencePolicy, [vulnerabilities](https://github.com/kubernetes/kubernetes/issues/103675) can emerge. +!!!note + + When discussing the process of creating cross-namespace object references, this + document and the documentation on the API itself refers to the object being + referred to as "the referent object", using the + [meaning](https://dictionary.cambridge.org/dictionary/english/referent) + of "referent" to be "the person, thing, or idea that a word, phrase, or object + refers to". + In order to bring cross-namespace references under the control of the owner of the referent object's namespace, the Gateway API has a ReferencePolicy object that must be created in the referent namespace for the @@ -25,8 +28,6 @@ To put this another way, if an object is referred to from outside its namespace, the object's owner must create a ReferencePolicy object that describes how that reference is allowed. This page explains how this process works. - - ## ReferencePolicy To ensure that Gateway API is able to safely provide this functionality, @@ -68,7 +69,7 @@ spec: kind: HTTPRoute namespace: foo to: - - group: core + - group: "" kind: Service ``` From 677a3c05659d066d7ce2322ce753b2df4c7a9bfd Mon Sep 17 00:00:00 2001 From: Nick Young Date: Mon, 9 Aug 2021 05:59:32 +0000 Subject: [PATCH 7/7] Add admonitions to mkdocs and note to cross-namespace-references.md Signed-off-by: Nick Young --- mkdocs.yml | 2 ++ site-src/v1alpha2/references/cross-namespace-references.md | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 37f4d00b0a..e26c0ff90f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,6 +12,8 @@ plugins: - awesome-pages - macros: include_dir: examples +markdown_extensions: + - admonition nav: - Introduction: index.md - Concepts: diff --git a/site-src/v1alpha2/references/cross-namespace-references.md b/site-src/v1alpha2/references/cross-namespace-references.md index 4d599d24f2..7fd3246efa 100644 --- a/site-src/v1alpha2/references/cross-namespace-references.md +++ b/site-src/v1alpha2/references/cross-namespace-references.md @@ -10,11 +10,10 @@ In the past, we've seen that forwarding traffic across namespace boundaries is a desired feature, but a safeguard like ReferencePolicy, [vulnerabilities](https://github.com/kubernetes/kubernetes/issues/103675) can emerge. -!!!note - +!!! note When discussing the process of creating cross-namespace object references, this - document and the documentation on the API itself refers to the object being - referred to as "the referent object", using the + document and the documentation on the API itself talk about the object being + referred to using the name "the referent object", using the [meaning](https://dictionary.cambridge.org/dictionary/english/referent) of "referent" to be "the person, thing, or idea that a word, phrase, or object refers to".