From aa0b7bbe07c542f3c0613059730bb76328479b85 Mon Sep 17 00:00:00 2001 From: ALESIA_TARAIKOVICH Date: Mon, 21 Oct 2024 12:57:32 +0000 Subject: [PATCH] add gcpAuthzExtension CRD --- .../backend_ref_invalid_group.yaml | 11 + .../backend_ref_invalid_kind.yaml | 11 + .../invalid_metadata_key.yaml | 14 + .../invalid_metadata_value.yaml | 14 + authz/authz-cr-validation/no_authority.yaml | 11 + authz/authz-cr-validation/no_timeout.yaml | 11 + authz/authz-cr-validation/run-test.sh | 11 + .../authz-cr-validation/too_big_timeout.yaml | 12 + .../too_small_timeout.yaml | 12 + .../authz-cr-validation/valid_extension.yaml | 18 + .../wrong_wire_format.yaml | 13 + .../crd/experimental/gcpauthzextension.yaml | 472 ++++++++++++++++++ 12 files changed, 610 insertions(+) create mode 100644 authz/authz-cr-validation/backend_ref_invalid_group.yaml create mode 100644 authz/authz-cr-validation/backend_ref_invalid_kind.yaml create mode 100644 authz/authz-cr-validation/invalid_metadata_key.yaml create mode 100644 authz/authz-cr-validation/invalid_metadata_value.yaml create mode 100644 authz/authz-cr-validation/no_authority.yaml create mode 100644 authz/authz-cr-validation/no_timeout.yaml create mode 100644 authz/authz-cr-validation/too_big_timeout.yaml create mode 100644 authz/authz-cr-validation/too_small_timeout.yaml create mode 100644 authz/authz-cr-validation/valid_extension.yaml create mode 100644 authz/authz-cr-validation/wrong_wire_format.yaml create mode 100644 gateway-api/config/mesh/crd/experimental/gcpauthzextension.yaml diff --git a/authz/authz-cr-validation/backend_ref_invalid_group.yaml b/authz/authz-cr-validation/backend_ref_invalid_group.yaml new file mode 100644 index 00000000..3078a5da --- /dev/null +++ b/authz/authz-cr-validation/backend_ref_invalid_group.yaml @@ -0,0 +1,11 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "fake-group" + kind: Service + name: test-service + port: 80 + authority: hello.com diff --git a/authz/authz-cr-validation/backend_ref_invalid_kind.yaml b/authz/authz-cr-validation/backend_ref_invalid_kind.yaml new file mode 100644 index 00000000..5ab57581 --- /dev/null +++ b/authz/authz-cr-validation/backend_ref_invalid_kind.yaml @@ -0,0 +1,11 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Gateway + name: test-service + port: 80 + authority: hello.com diff --git a/authz/authz-cr-validation/invalid_metadata_key.yaml b/authz/authz-cr-validation/invalid_metadata_key.yaml new file mode 100644 index 00000000..55fadba3 --- /dev/null +++ b/authz/authz-cr-validation/invalid_metadata_key.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com + timeout: 1s + metadata: + "???": "value" diff --git a/authz/authz-cr-validation/invalid_metadata_value.yaml b/authz/authz-cr-validation/invalid_metadata_value.yaml new file mode 100644 index 00000000..093e0efd --- /dev/null +++ b/authz/authz-cr-validation/invalid_metadata_value.yaml @@ -0,0 +1,14 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com + timeout: 1s + metadata: + "key": "" diff --git a/authz/authz-cr-validation/no_authority.yaml b/authz/authz-cr-validation/no_authority.yaml new file mode 100644 index 00000000..2cb889b2 --- /dev/null +++ b/authz/authz-cr-validation/no_authority.yaml @@ -0,0 +1,11 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + timeout: 1s diff --git a/authz/authz-cr-validation/no_timeout.yaml b/authz/authz-cr-validation/no_timeout.yaml new file mode 100644 index 00000000..b1a10b4a --- /dev/null +++ b/authz/authz-cr-validation/no_timeout.yaml @@ -0,0 +1,11 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com diff --git a/authz/authz-cr-validation/run-test.sh b/authz/authz-cr-validation/run-test.sh index 96b52569..898adfd6 100755 --- a/authz/authz-cr-validation/run-test.sh +++ b/authz/authz-cr-validation/run-test.sh @@ -45,3 +45,14 @@ check_cr_validation "./authz/authz-cr-validation/invalid_provider_deny.yaml" "tr check_cr_validation "./authz/authz-cr-validation/invalid_provider_custom.yaml" "true" check_cr_validation "./authz/authz-cr-validation/invalid_multiple_providers.yaml" "true" check_cr_validation "./authz/authz-cr-validation/valid.yaml" "false" + +check_cr_validation "./authz/authz-cr-validation/backend_ref_invalid_group.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/backend_ref_invalid_kind.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/invalid_metadata_key.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/invalid_metadata_value.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/no_authority.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/no_timeout.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/too_big_timeout.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/too_small_timeout.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/wrong_wire_format.yaml" "true" +check_cr_validation "./authz/authz-cr-validation/valid_extension.yaml" "false" diff --git a/authz/authz-cr-validation/too_big_timeout.yaml b/authz/authz-cr-validation/too_big_timeout.yaml new file mode 100644 index 00000000..9c34f521 --- /dev/null +++ b/authz/authz-cr-validation/too_big_timeout.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com + timeout: 11s diff --git a/authz/authz-cr-validation/too_small_timeout.yaml b/authz/authz-cr-validation/too_small_timeout.yaml new file mode 100644 index 00000000..444ce6ba --- /dev/null +++ b/authz/authz-cr-validation/too_small_timeout.yaml @@ -0,0 +1,12 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com + timeout: 1ms diff --git a/authz/authz-cr-validation/valid_extension.yaml b/authz/authz-cr-validation/valid_extension.yaml new file mode 100644 index 00000000..43979b0d --- /dev/null +++ b/authz/authz-cr-validation/valid_extension.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + authority: hello.com + timeout: 1s + failOpen: true + metadata: + "key": "value" + forwardHeaders: + - hello.com + wireFormat: ExtProcGRPC diff --git a/authz/authz-cr-validation/wrong_wire_format.yaml b/authz/authz-cr-validation/wrong_wire_format.yaml new file mode 100644 index 00000000..0fff6756 --- /dev/null +++ b/authz/authz-cr-validation/wrong_wire_format.yaml @@ -0,0 +1,13 @@ +apiVersion: networking.gke.io/v1 +kind: GCPAuthzExtension +metadata: + name: "test" +spec: + backendRef: + group: "" + kind: Service + name: test-service + port: 80 + timeout: 1s + authority: hello.com + wireFormat: ExtProc diff --git a/gateway-api/config/mesh/crd/experimental/gcpauthzextension.yaml b/gateway-api/config/mesh/crd/experimental/gcpauthzextension.yaml new file mode 100644 index 00000000..c13a4b15 --- /dev/null +++ b/gateway-api/config/mesh/crd/experimental/gcpauthzextension.yaml @@ -0,0 +1,472 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: unapproved + controller-gen.kubebuilder.io/version: (unknown) + name: gcpauthzextensions.networking.gke.io +spec: + conversion: + strategy: None + group: networking.gke.io + names: + kind: GCPAuthzExtension + listKind: GCPAuthzExtensionList + plural: gcpauthzextensions + singular: gcpauthzextension + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: |- + GCPAuthzExtension is the CRD for the Authz Extension. + It allows the traffic forwarding to a callout backend + to make an authorization decision. + 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 GCPAuthzExtension. + properties: + authority: + description: |- + The `:authority` header in the gRPC request sent from Envoy to the + callout extension backend service. + maxLength: 1000 + pattern: ^[A-Za-z0-9-_:%\.\[\]]*$ + type: string + backendRef: + description: BackendRef identifies an API object that runs the extension. + properties: + group: + default: "" + description: Group is the group of the referent. + enum: + - "" + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: Kind is kind of the referent. + enum: + - Service + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + port: + description: Port is the port of the referent. + format: int32 + type: integer + required: + - group + - kind + - name + - port + type: object + failOpen: + description: |- + Determines how the proxy behaves if the call to the extension + fails or times out. If not specified, this defaults to false. + + When set to true, request or response processing continues without + error. Any subsequent extensions in the extension chain are also + executed. When set to false or the default setting of false is used, + one of the following happens: + + * If response headers have not been delivered to the downstream client, + a generic 500 error is returned to the client. The error response can be + tailored by configuring a custom error response in the load balancer. + + * If response headers have been delivered, then the HTTP stream to the + downstream client is reset. + type: boolean + forwardHeaders: + description: |- + List of the HTTP headers to forward to the extension + (from the client or backend). If omitted, all headers are sent. + Each element is a string indicating the header name. + items: + description: HTTPHeaderName is the name of the HTTP header. + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 50 + type: array + metadata: + additionalProperties: + description: MetadataValue is the value of an metadata in GCP Extensions. + maxLength: 63 + minLength: 1 + pattern: ^([A-Za-z0-9\/\-._~%!$&'()*+,;=:])*$ + type: string + description: |- + The metadata provided here is included as part of the + `metadata_context` (of type `google.protobuf.Struct`) in the + `ProcessingRequest` message sent to the extension + server. The metadata is available under the namespace + `com.google....`. + For example: + `com.google.lb_traffic_extension.lbtrafficextension1.chain1.ext1`. + + The following variables are supported in the metadata: + + `{forwarding_rule_id}` - substituted with the forwarding rule's fully + qualified resource name. + maxProperties: 100 + type: object + x-kubernetes-validations: + - message: Metadata keys must only contain valid characters (matching + ^([A-Za-z0-9\/\-._~%!$&'()*+,;=:]{1,63})$) and must be up to 63 + characters long. + rule: self.all(key, key.matches(r"""^([A-Za-z0-9\/\-._~%!$&'()*+,;=:]{1,63})$""")) + timeout: + description: |- + Specifies the timeout for each individual message on the stream. + The timeout must be between 10-10000 milliseconds. + If omitted, the default timeout is 10000 milliseconds. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + wireFormat: + description: The format of communication supported by the extension. + enum: + - ExtProcGRPC + type: string + required: + - backendRef + type: object + x-kubernetes-validations: + - message: timeout must be between 10-10000 milliseconds + rule: 'has(self.backendRef) ? has(self.timeout) && duration(self.timeout) + >= duration(''10ms'') && duration(self.timeout) <= duration(''10000ms'') + : true' + - message: authority must be set if backendRef is set + rule: 'has(self.backendRef) ? has(self.authority) && size(self.authority) + != 0 : true' + status: + description: Status defines the current state of GCPAuthzExtension. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: networking.gke.io + description: |- + Group is the group of the referent. + When unspecified, "networking.gke.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {}