diff --git a/api/v1alpha1/ext_auth_types.go b/api/v1alpha1/ext_auth_types.go index 6e455aa42d1..906f13c4075 100644 --- a/api/v1alpha1/ext_auth_types.go +++ b/api/v1alpha1/ext_auth_types.go @@ -9,14 +9,14 @@ import ( gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) +// ExtAuth defines the configuration for External Authorization. +// // +kubebuilder:validation:XValidation:rule="(has(self.grpc) || has(self.http))",message="one of grpc or http must be specified" // +kubebuilder:validation:XValidation:rule="(has(self.grpc) && !has(self.http)) || (!has(self.grpc) && has(self.http))",message="only one of grpc or http can be specified" -// +kubebuilder:validation:XValidation:rule="has(self.grpc) ? (!has(self.grpc.backendRef.group) || self.grpc.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" -// +kubebuilder:validation:XValidation:rule="has(self.grpc) ? (!has(self.grpc.backendRef.kind) || self.grpc.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" -// +kubebuilder:validation:XValidation:rule="has(self.http) ? (!has(self.http.backendRef.group) || self.http.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" -// +kubebuilder:validation:XValidation:rule="has(self.http) ? (!has(self.http.backendRef.kind) || self.http.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" -// -// ExtAuth defines the configuration for External Authorization. +// +kubebuilder:validation:XValidation:rule="has(self.grpc) ? (!has(self.grpc.backendRef) || !has(self.grpc.backendRef.group) || self.grpc.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" +// +kubebuilder:validation:XValidation:rule="has(self.grpc) ? (!has(self.grpc.backendRef) || !has(self.grpc.backendRef.kind) || self.grpc.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" +// +kubebuilder:validation:XValidation:rule="has(self.http) ? (!has(self.http.backendRef) || !has(self.http.backendRef.group) || self.http.backendRef.group == \"\") : true", message="group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported" +// +kubebuilder:validation:XValidation:rule="has(self.http) ? (!has(self.http.backendRef) || !has(self.http.backendRef.kind) || self.http.backendRef.kind == 'Service') : true", message="kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported" type ExtAuth struct { // GRPC defines the gRPC External Authorization service. // Either GRPCService or HTTPService must be specified, @@ -55,19 +55,42 @@ type ExtAuth struct { // GRPCExtAuthService defines the gRPC External Authorization service // The authorization request message is defined in // https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto +// +kubebuilder:validation:XValidation:message="backendRef or backendRefs needs to be set",rule="has(self.backendRef) || self.backendRefs.size() > 0" type GRPCExtAuthService struct { // BackendRef references a Kubernetes object that represents the // backend server to which the authorization request will be sent. // Only service Kind is supported for now. - BackendRef gwapiv1.BackendObjectReference `json:"backendRef"` + // Deprecated: Use BackendRefs instead. + BackendRef *gwapiv1.BackendObjectReference `json:"backendRef,omitempty"` + + // BackendRefs references a Kubernetes object that represents the + // backend server to which the authorization request will be sent. + // Only service Kind is supported for now. + // + // +optional + // +kubebuilder:validation:MaxItems=1 + // +kubebuilder:validation:XValidation:message="only support Service kind.",rule="self.all(f, f.kind == 'Service')" + BackendRefs []BackendRef `json:"backendRefs,omitempty"` } // HTTPExtAuthService defines the HTTP External Authorization service +// +// +kubebuilder:validation:XValidation:message="backendRef or backendRefs needs to be set",rule="has(self.backendRef) || self.backendRefs.size() > 0" type HTTPExtAuthService struct { // BackendRef references a Kubernetes object that represents the // backend server to which the authorization request will be sent. // Only service Kind is supported for now. - BackendRef gwapiv1.BackendObjectReference `json:"backendRef"` + // Deprecated: Use BackendRefs instead. + BackendRef *gwapiv1.BackendObjectReference `json:"backendRef,omitempty"` + + // BackendRefs references a Kubernetes object that represents the + // backend server to which the authorization request will be sent. + // Only service Kind is supported for now. + // + // +optional + // +kubebuilder:validation:MaxItems=1 + // +kubebuilder:validation:XValidation:message="only support Service kind.",rule="self.all(f, f.kind == 'Service')" + BackendRefs []BackendRef `json:"backendRefs,omitempty"` // Path is the path of the HTTP External Authorization service. // If path is specified, the authorization request will be sent to that path, diff --git a/api/v1alpha1/share_types_helper.go b/api/v1alpha1/share_types_helper.go new file mode 100644 index 00000000000..ab055535636 --- /dev/null +++ b/api/v1alpha1/share_types_helper.go @@ -0,0 +1,18 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package v1alpha1 + +import gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + +func ToBackendObjectReference(ref BackendRef) *gwapiv1.BackendObjectReference { + return &gwapiv1.BackendObjectReference{ + Group: ref.Group, + Kind: ref.Kind, + Namespace: ref.Namespace, + Name: ref.Name, + Port: ref.Port, + } +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index c3163b73a83..67918dabdfb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2164,7 +2164,18 @@ func (in *FilterPosition) DeepCopy() *FilterPosition { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GRPCExtAuthService) DeepCopyInto(out *GRPCExtAuthService) { *out = *in - in.BackendRef.DeepCopyInto(&out.BackendRef) + if in.BackendRef != nil { + in, out := &in.BackendRef, &out.BackendRef + *out = new(apisv1.BackendObjectReference) + (*in).DeepCopyInto(*out) + } + if in.BackendRefs != nil { + in, out := &in.BackendRefs, &out.BackendRefs + *out = make([]BackendRef, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCExtAuthService. @@ -2397,7 +2408,18 @@ func (in *HTTPClientTimeout) DeepCopy() *HTTPClientTimeout { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPExtAuthService) DeepCopyInto(out *HTTPExtAuthService) { *out = *in - in.BackendRef.DeepCopyInto(&out.BackendRef) + if in.BackendRef != nil { + in, out := &in.BackendRef, &out.BackendRef + *out = new(apisv1.BackendObjectReference) + (*in).DeepCopyInto(*out) + } + if in.BackendRefs != nil { + in, out := &in.BackendRefs, &out.BackendRefs + *out = make([]BackendRef, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Path != nil { in, out := &in.Path, &out.Path *out = new(string) diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 919d272cf89..00e1b791e68 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -247,6 +247,7 @@ spec: BackendRef references a Kubernetes object that represents the backend server to which the authorization request will be sent. Only service Kind is supported for now. + Deprecated: Use BackendRefs instead. properties: group: default: "" @@ -321,9 +322,97 @@ spec: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' - required: - - backendRef + backendRefs: + description: |- + BackendRefs references a Kubernetes object that represents the + backend server to which the authorization request will be sent. + Only service Kind is supported for now. + items: + description: BackendRef defines how an ObjectReference that + is specific to BackendRef. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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 + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 1 + type: array + x-kubernetes-validations: + - message: only support Service kind. + rule: self.all(f, f.kind == 'Service') type: object + x-kubernetes-validations: + - message: backendRef or backendRefs needs to be set + rule: has(self.backendRef) || self.backendRefs.size() > 0 headersToExtAuth: description: |- HeadersToExtAuth defines the client request headers that will be included @@ -350,6 +439,7 @@ spec: BackendRef references a Kubernetes object that represents the backend server to which the authorization request will be sent. Only service Kind is supported for now. + Deprecated: Use BackendRefs instead. properties: group: default: "" @@ -424,6 +514,93 @@ spec: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' + backendRefs: + description: |- + BackendRefs references a Kubernetes object that represents the + backend server to which the authorization request will be sent. + Only service Kind is supported for now. + items: + description: BackendRef defines how an ObjectReference that + is specific to BackendRef. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + 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 the Kubernetes resource kind of the referent. For example + "Service". + + + Defaults to "Service" when not specified. + + + ExternalName services can refer to CNAME DNS records that may live + outside of the cluster and as such are difficult to reason about in + terms of conformance. They also may not be safe to forward to (see + CVE-2021-25740 for more information). Implementations SHOULD NOT + support ExternalName Services. + + + Support: Core (Services with a type other than ExternalName) + + + Support: Implementation-specific (Services with type ExternalName) + 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 + namespace: + description: |- + Namespace is the namespace of the backend. When unspecified, the local + namespace is inferred. + + + Note that when a namespace different than the local namespace is specified, + a ReferenceGrant object is required in the referent namespace to allow that + namespace's owner to accept the reference. See the ReferenceGrant + documentation for details. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 1 + type: array + x-kubernetes-validations: + - message: only support Service kind. + rule: self.all(f, f.kind == 'Service') headersToBackend: description: |- HeadersToBackend are the authorization response headers that will be added @@ -440,9 +617,10 @@ spec: If path is specified, the authorization request will be sent to that path, or else the authorization request will be sent to the root path. type: string - required: - - backendRef type: object + x-kubernetes-validations: + - message: backendRef or backendRefs needs to be set + rule: has(self.backendRef) || self.backendRefs.size() > 0 type: object x-kubernetes-validations: - message: one of grpc or http must be specified @@ -453,21 +631,21 @@ spec: - message: group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported - rule: 'has(self.grpc) ? (!has(self.grpc.backendRef.group) || self.grpc.backendRef.group - == "") : true' + rule: 'has(self.grpc) ? (!has(self.grpc.backendRef) || !has(self.grpc.backendRef.group) + || self.grpc.backendRef.group == "") : true' - message: kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported - rule: 'has(self.grpc) ? (!has(self.grpc.backendRef.kind) || self.grpc.backendRef.kind - == ''Service'') : true' + rule: 'has(self.grpc) ? (!has(self.grpc.backendRef) || !has(self.grpc.backendRef.kind) + || self.grpc.backendRef.kind == ''Service'') : true' - message: group is invalid, only the core API group (specified by omitting the group field or setting it to an empty string) is supported - rule: 'has(self.http) ? (!has(self.http.backendRef.group) || self.http.backendRef.group - == "") : true' + rule: 'has(self.http) ? (!has(self.http.backendRef) || !has(self.http.backendRef.group) + || self.http.backendRef.group == "") : true' - message: kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported - rule: 'has(self.http) ? (!has(self.http.backendRef.kind) || self.http.backendRef.kind - == ''Service'') : true' + rule: 'has(self.http) ? (!has(self.http.backendRef) || !has(self.http.backendRef.kind) + || self.http.backendRef.kind == ''Service'') : true' jwt: description: JWT defines the configuration for JSON Web Token (JWT) authentication. diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 8cdca113bd0..29305d067d5 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -758,10 +758,7 @@ func (t *Translator) buildBasicAuth( }, nil } -func (t *Translator) buildExtAuth( - policy *egv1a1.SecurityPolicy, - resources *Resources, -) (*ir.ExtAuth, error) { +func (t *Translator) buildExtAuth(policy *egv1a1.SecurityPolicy, resources *Resources) (*ir.ExtAuth, error) { var ( http = policy.Spec.ExtAuth.HTTP grpc = policy.Spec.ExtAuth.GRPC @@ -775,16 +772,24 @@ func (t *Translator) buildExtAuth( switch { // These are sanity checks, they should never happen because the API server // should have caught them - case http == nil && grpc == nil: - return nil, errors.New("one of grpc or http must be specified") case http != nil && grpc != nil: return nil, errors.New("only one of grpc or http can be specified") case http != nil: - backendRef = &http.BackendRef + backendRef = http.BackendRef + if len(http.BackendRefs) != 0 { + backendRef = egv1a1.ToBackendObjectReference(http.BackendRefs[0]) + } protocol = ir.HTTP case grpc != nil: - backendRef = &grpc.BackendRef + backendRef = grpc.BackendRef + if len(grpc.BackendRefs) != 0 { + backendRef = egv1a1.ToBackendObjectReference(grpc.BackendRefs[0]) + } protocol = ir.GRPC + // These are sanity checks, they should never happen because the API server + // should have caught them + default: // http == nil && grpc == nil: + return nil, errors.New("one of grpc or http must be specified") } if err = t.validateExtServiceBackendReference( @@ -794,8 +799,7 @@ func (t *Translator) buildExtAuth( resources); err != nil { return nil, err } - authority = fmt.Sprintf( - "%s.%s:%d", + authority = fmt.Sprintf("%s.%s:%d", backendRef.Name, NamespaceDerefOr(backendRef.Namespace, policy.Namespace), *backendRef.Port) diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.in.yaml new file mode 100644 index 00000000000..e362583125b --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.in.yaml @@ -0,0 +1,171 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.foo.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /foo1 + backendRefs: + - name: service-1 + port: 8080 + - matches: + - path: + value: /foo2 + backendRefs: + - name: service-2 + port: 8080 + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-2 + spec: + hostnames: + - www.bar.com + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: /bar + backendRefs: + - name: service-3 + port: 8080 +services: + - apiVersion: v1 + kind: Service + metadata: + namespace: envoy-gateway + name: http-backend + spec: + ports: + - port: 80 + name: http + protocol: TCP + - apiVersion: v1 + kind: Service + metadata: + namespace: default + name: grpc-backend + spec: + ports: + - port: 9000 + name: grpc + protocol: TCP +endpointSlices: + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-http-backend + namespace: envoy-gateway + labels: + kubernetes.io/service-name: http-backend + addressType: IPv4 + ports: + - name: http + protocol: TCP + port: 80 + endpoints: + - addresses: + - 7.7.7.7 + conditions: + ready: true + - apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-grpc-backend + namespace: default + labels: + kubernetes.io/service-name: grpc-backend + addressType: IPv4 + ports: + - name: grpc + protocol: TCP + port: 9000 + endpoints: + - addresses: + - 8.8.8.8 + conditions: + ready: true +referenceGrants: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: ReferenceGrant + metadata: + namespace: envoy-gateway + name: referencegrant-1 + spec: + from: + - group: gateway.envoyproxy.io + kind: SecurityPolicy + namespace: default + to: + - group: '' + kind: Service +securityPolicies: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-http-route-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + extAuth: + failOpen: true + headersToExtAuth: + - header1 + - header2 + grpc: + backendRef: + name: grpc-backend + port: 9000 + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: policy-for-gateway-1 # This will only apply to the httproute-2 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + extAuth: + failOpen: false + http: + backendRef: + Name: http-backend + Namespace: envoy-gateway + Port: 80 + Path: /auth + headersToBackend: + - header1 + - header2 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.out.yaml new file mode 100755 index 00000000000..4d7686f8fd7 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-backendref.out.yaml @@ -0,0 +1,337 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 2 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.foo.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo1 + - backendRefs: + - name: service-2 + port: 8080 + matches: + - path: + value: /foo2 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-2 + namespace: default + spec: + hostnames: + - www.bar.com + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-3 + port: 8080 + matches: + - path: + value: /bar + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-http-route-1 + namespace: default + spec: + extAuth: + failOpen: true + grpc: + backendRef: + name: grpc-backend + port: 9000 + headersToExtAuth: + - header1 + - header2 + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: default + spec: + extAuth: + failOpen: false + http: + backendRef: + name: http-backend + namespace: envoy-gateway + port: 80 + headersToBackend: + - header1 + - header2 + path: /auth + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: 'This policy is being overridden by other securityPolicies for these + routes: [default/httproute-1]' + reason: Overridden + status: "True" + type: Overridden + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/0/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo1 + security: + extAuth: + failOpen: true + grpc: + authority: grpc-backend.default:9000 + destination: + name: securitypolicy/default/policy-for-http-route-1/default/grpc-backend + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 9000 + protocol: GRPC + weight: 1 + headersToExtAuth: + - header1 + - header2 + name: securitypolicy/default/policy-for-http-route-1 + - destination: + name: httproute/default/httproute-1/rule/1 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.foo.com + isHTTP2: false + name: httproute/default/httproute-1/rule/1/match/0/www_foo_com + pathMatch: + distinct: false + name: "" + prefix: /foo2 + security: + extAuth: + failOpen: true + grpc: + authority: grpc-backend.default:9000 + destination: + name: securitypolicy/default/policy-for-http-route-1/default/grpc-backend + settings: + - addressType: IP + endpoints: + - host: 8.8.8.8 + port: 9000 + protocol: GRPC + weight: 1 + headersToExtAuth: + - header1 + - header2 + name: securitypolicy/default/policy-for-http-route-1 + - destination: + name: httproute/default/httproute-2/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.bar.com + isHTTP2: false + name: httproute/default/httproute-2/rule/0/match/0/www_bar_com + pathMatch: + distinct: false + name: "" + prefix: /bar + security: + extAuth: + failOpen: false + http: + authority: http-backend.envoy-gateway:80 + destination: + name: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 80 + protocol: HTTP + weight: 1 + headersToBackend: + - header1 + - header2 + path: /auth + name: securitypolicy/default/policy-for-gateway-1 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml index 1ffcb302787..d562b1c7e1e 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.in.yaml @@ -55,10 +55,10 @@ securityPolicies: name: gateway-1 extAuth: http: - backendRef: - name: http-backend - namespace: default - port: 80 + backendRefs: + - name: http-backend + namespace: default + port: 80 headersToBackend: - header1 - header2 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml index 582db7cf245..14bbc4ea0a4 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-matching-port.out.yaml @@ -105,8 +105,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: default port: 80 headersToBackend: diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml index 75c777585a6..3f743d18067 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.in.yaml @@ -55,9 +55,9 @@ securityPolicies: name: gateway-1 extAuth: http: - backendRef: - name: http-backend - namespace: default + backendRefs: + - name: http-backend + namespace: default headersToBackend: - header1 - header2 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml index fa9d38b2572..f7cbe8322d1 100755 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-port.out.yaml @@ -105,8 +105,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: default headersToBackend: - header1 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml index c0397d6784f..06cb843241b 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.in.yaml @@ -55,10 +55,10 @@ securityPolicies: name: gateway-1 extAuth: http: - backendRef: - name: http-backend - namespace: envoy-gateway - port: 80 + backendRefs: + - name: http-backend + namespace: envoy-gateway + port: 80 headersToBackend: - header1 - header2 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml index 9afc90b0970..e1317710c17 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-reference-grant.out.yaml @@ -105,8 +105,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: envoy-gateway port: 80 headersToBackend: diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml index 3c2fba3e700..e47a1b0f527 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.in.yaml @@ -46,10 +46,10 @@ securityPolicies: name: gateway-1 extAuth: http: - backendRef: - name: http-backend - namespace: default - port: 80 + backendRefs: + - name: http-backend + namespace: default + port: 80 headersToBackend: - header1 - header2 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml index cc079ddf6cd..fba4578b377 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-invalid-no-service.out.yaml @@ -105,8 +105,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: default port: 80 headersToBackend: diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml index 669e9443f66..d2aee51b27e 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.in.yaml @@ -197,10 +197,10 @@ securityPolicies: name: gateway-1 extAuth: http: - backendRef: - Name: http-backend - Namespace: envoy-gateway - Port: 80 + backendRefs: + - Name: http-backend + Namespace: envoy-gateway + Port: 80 Path: /auth headersToBackend: - header1 @@ -221,7 +221,7 @@ securityPolicies: - header1 - header2 grpc: - backendRef: - name: grpc-backend - port: 9000 + backendRefs: + - name: grpc-backend + port: 9000 failOpen: true diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml index 6ac00a0e565..b750c2d61b8 100755 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-with-backendtlspolicy.out.yaml @@ -208,8 +208,8 @@ securityPolicies: extAuth: failOpen: true grpc: - backendRef: - name: grpc-backend + backendRefs: + - name: grpc-backend port: 9000 headersToExtAuth: - header1 @@ -243,8 +243,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: envoy-gateway port: 80 headersToBackend: diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth.in.yaml index e362583125b..ada3900ed60 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth.in.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth.in.yaml @@ -126,7 +126,7 @@ referenceGrants: kind: SecurityPolicy namespace: default to: - - group: '' + - group: "" kind: Service securityPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 @@ -145,14 +145,15 @@ securityPolicies: - header1 - header2 grpc: - backendRef: - name: grpc-backend - port: 9000 + backendRefs: + - name: grpc-backend + port: 9000 - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: SecurityPolicy metadata: namespace: default - name: policy-for-gateway-1 # This will only apply to the httproute-2 + # This will only apply to the httproute-2 + name: policy-for-gateway-1 spec: targetRef: group: gateway.networking.k8s.io @@ -161,10 +162,10 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - Name: http-backend - Namespace: envoy-gateway - Port: 80 + backendRefs: + - Name: http-backend + Namespace: envoy-gateway + Port: 80 Path: /auth headersToBackend: - header1 diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml index 4d7686f8fd7..2e0ca006768 100644 --- a/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml +++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth.out.yaml @@ -149,8 +149,8 @@ securityPolicies: extAuth: failOpen: true grpc: - backendRef: - name: grpc-backend + backendRefs: + - name: grpc-backend port: 9000 headersToExtAuth: - header1 @@ -184,8 +184,8 @@ securityPolicies: extAuth: failOpen: false http: - backendRef: - name: http-backend + backendRefs: + - name: http-backend namespace: envoy-gateway port: 80 headersToBackend: diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 7051d21f4d2..791b7fcbfa6 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -464,11 +464,21 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( // Add the referenced BackendRefs and ReferenceGrants in ExtAuth to Maps for later processing extAuth := policy.Spec.ExtAuth if extAuth != nil { - var backendRef gwapiv1.BackendObjectReference + var backendRef *gwapiv1.BackendObjectReference if extAuth.GRPC != nil { backendRef = extAuth.GRPC.BackendRef + if len(extAuth.GRPC.BackendRefs) > 0 { + if len(extAuth.GRPC.BackendRefs) != 0 { + backendRef = egv1a1.ToBackendObjectReference(extAuth.GRPC.BackendRefs[0]) + } + } } else { backendRef = extAuth.HTTP.BackendRef + if len(extAuth.HTTP.BackendRefs) > 0 { + if len(extAuth.HTTP.BackendRefs) != 0 { + backendRef = egv1a1.ToBackendObjectReference(extAuth.HTTP.BackendRefs[0]) + } + } } backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, policy.Namespace) diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 57c13997fe5..d2abc6a25bc 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -507,9 +507,17 @@ func backendSecurityPolicyIndexFunc(rawObj client.Object) []string { if securityPolicy.Spec.ExtAuth != nil { if securityPolicy.Spec.ExtAuth.HTTP != nil { - backendRef = &securityPolicy.Spec.ExtAuth.HTTP.BackendRef + http := securityPolicy.Spec.ExtAuth.HTTP + backendRef = http.BackendRef + if len(http.BackendRefs) > 0 { + backendRef = v1alpha1.ToBackendObjectReference(http.BackendRefs[0]) + } } else if securityPolicy.Spec.ExtAuth.GRPC != nil { - backendRef = &securityPolicy.Spec.ExtAuth.GRPC.BackendRef + grpc := securityPolicy.Spec.ExtAuth.GRPC + backendRef = grpc.BackendRef + if len(grpc.BackendRefs) > 0 { + backendRef = v1alpha1.ToBackendObjectReference(grpc.BackendRefs[0]) + } } } diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go index f5416c198d0..c7b6054e504 100644 --- a/internal/provider/kubernetes/predicates_test.go +++ b/internal/provider/kubernetes/predicates_test.go @@ -595,8 +595,12 @@ func TestValidateServiceForReconcile(t *testing.T) { }, ExtAuth: &v1alpha1.ExtAuth{ HTTP: &v1alpha1.HTTPExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ - Name: "ext-auth-http-service", + BackendRefs: []v1alpha1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "ext-auth-http-service", + }, + }, }, }, }, @@ -606,6 +610,37 @@ func TestValidateServiceForReconcile(t *testing.T) { service: test.GetService(types.NamespacedName{Name: "ext-auth-http-service"}, nil, nil), expect: true, }, + { + name: "service referenced by SecurityPolicy ExtAuth GRPC service", + configs: []client.Object{ + &v1alpha1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "ext-auth-http", + }, + Spec: v1alpha1.SecurityPolicySpec{ + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Kind: "Gateway", + Name: "scheduled-status-test", + }, + }, + ExtAuth: &v1alpha1.ExtAuth{ + GRPC: &v1alpha1.GRPCExtAuthService{ + BackendRefs: []v1alpha1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "ext-auth-grpc-service", + }, + }, + }, + }, + }, + }, + }, + }, + service: test.GetService(types.NamespacedName{Name: "ext-auth-grpc-service"}, nil, nil), + expect: true, + }, { name: "service referenced by EnvoyExtensionPolicy ExtPrc GRPC service", configs: []client.Object{ diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index b4bfec6a2ba..d28b28339dd 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -284,6 +284,8 @@ BackendRef defines how an ObjectReference that is specific to BackendRef. _Appears in:_ - [ALSEnvoyProxyAccessLog](#alsenvoyproxyaccesslog) - [ExtProc](#extproc) +- [GRPCExtAuthService](#grpcextauthservice) +- [HTTPExtAuthService](#httpextauthservice) - [OpenTelemetryEnvoyProxyAccessLog](#opentelemetryenvoyproxyaccesslog) - [ProxyOpenTelemetrySink](#proxyopentelemetrysink) - [TracingProvider](#tracingprovider) @@ -1589,7 +1591,8 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `backendRef` | _[BackendObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.BackendObjectReference)_ | true | BackendRef references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now. | +| `backendRef` | _[BackendObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.BackendObjectReference)_ | true | BackendRef references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now.
Deprecated: Use BackendRefs instead. | +| `backendRefs` | _[BackendRef](#backendref) array_ | false | BackendRefs references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now. | #### Gateway @@ -1751,7 +1754,8 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `backendRef` | _[BackendObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.BackendObjectReference)_ | true | BackendRef references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now. | +| `backendRef` | _[BackendObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.BackendObjectReference)_ | true | BackendRef references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now.
Deprecated: Use BackendRefs instead. | +| `backendRefs` | _[BackendRef](#backendref) array_ | false | BackendRefs references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
Only service Kind is supported for now. | | `path` | _string_ | true | Path is the path of the HTTP External Authorization service.
If path is specified, the authorization request will be sent to that path,
or else the authorization request will be sent to the root path. | | `headersToBackend` | _string array_ | false | HeadersToBackend are the authorization response headers that will be added
to the original client request before sending it to the backend server.
Note that coexisting headers will be overridden.
If not specified, no authorization response headers will be added to the
original client request. | diff --git a/test/cel-validation/securitypolicy_test.go b/test/cel-validation/securitypolicy_test.go index 984903cf72d..9c2466565ad 100644 --- a/test/cel-validation/securitypolicy_test.go +++ b/test/cel-validation/securitypolicy_test.go @@ -354,7 +354,7 @@ func TestSecurityPolicyTarget(t *testing.T) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ GRPC: &egv1a1.GRPCExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Name: "grpc-auth-service", Port: ptr.To(gwapiv1.PortNumber(80)), }, @@ -371,13 +371,59 @@ func TestSecurityPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "GRPC external auth service with backendRefs", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "grpc-auth-service", + Kind: ptr.To(gwapiv1a2.Kind("Service")), + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + }, + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "empty GRPC external auth service", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{}, + }, + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{" backendRef or backendRefs needs to be set"}, + }, { desc: "HTTP external auth service", mutate: func(sp *egv1a1.SecurityPolicy) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ HTTP: &egv1a1.HTTPExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), }, @@ -394,6 +440,34 @@ func TestSecurityPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "HTTP external auth service with backendRefs", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + HTTP: &egv1a1.HTTPExtAuthService{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "grpc-auth-service", + Kind: ptr.To(gwapiv1a2.Kind("Service")), + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + }, + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{}, + }, { desc: "no extAuth", mutate: func(sp *egv1a1.SecurityPolicy) { @@ -418,13 +492,13 @@ func TestSecurityPolicyTarget(t *testing.T) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ GRPC: &egv1a1.GRPCExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Name: "grpc-auth-service", Port: ptr.To(gwapiv1.PortNumber(80)), }, }, HTTP: &egv1a1.HTTPExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), }, @@ -449,7 +523,7 @@ func TestSecurityPolicyTarget(t *testing.T) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ HTTP: &egv1a1.HTTPExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Group: ptr.To(gwapiv1.Group("unsupported")), Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), @@ -475,7 +549,7 @@ func TestSecurityPolicyTarget(t *testing.T) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ HTTP: &egv1a1.HTTPExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Kind: ptr.To(gwapiv1.Kind("unsupported")), Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), @@ -495,13 +569,41 @@ func TestSecurityPolicyTarget(t *testing.T) { "spec.extAuth: Invalid value: \"object\": kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported", }, }, + { + desc: "http extAuth service backendRefs invalid Kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + HTTP: &egv1a1.HTTPExtAuthService{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "grpc-auth-service", + Kind: ptr.To(gwapiv1a2.Kind("unsupported")), + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + }, + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{" only support Service kind"}, + }, { desc: "grpc extAuth service invalid Group", mutate: func(sp *egv1a1.SecurityPolicy) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ GRPC: &egv1a1.GRPCExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Group: ptr.To(gwapiv1.Group("unsupported")), Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), @@ -527,7 +629,7 @@ func TestSecurityPolicyTarget(t *testing.T) { sp.Spec = egv1a1.SecurityPolicySpec{ ExtAuth: &egv1a1.ExtAuth{ GRPC: &egv1a1.GRPCExtAuthService{ - BackendRef: gwapiv1.BackendObjectReference{ + BackendRef: &gwapiv1.BackendObjectReference{ Kind: ptr.To(gwapiv1.Kind("unsupported")), Name: "http-auth-service", Port: ptr.To(gwapiv1.PortNumber(15001)), @@ -547,6 +649,35 @@ func TestSecurityPolicyTarget(t *testing.T) { "spec.extAuth: Invalid value: \"object\": kind is invalid, only Service (specified by omitting the kind field or setting it to 'Service') is supported", }, }, + { + desc: "grpc extAuth service backendRefs invalid Kind", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Name: "grpc-auth-service", + Kind: ptr.To(gwapiv1a2.Kind("unsupported")), + Port: ptr.To(gwapiv1.PortNumber(80)), + }, + }, + }, + }, + }, + TargetRef: gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: "eg", + }, + }, + } + }, + wantErrors: []string{" only support Service kind"}, + }, + // JWT { desc: "valid jwt", diff --git a/test/e2e/testdata/ext-auth-grpc-securitypolicy.yaml b/test/e2e/testdata/ext-auth-grpc-securitypolicy.yaml index 840977aa60a..3f40fc5830a 100644 --- a/test/e2e/testdata/ext-auth-grpc-securitypolicy.yaml +++ b/test/e2e/testdata/ext-auth-grpc-securitypolicy.yaml @@ -47,8 +47,8 @@ spec: name: http-with-ext-auth extAuth: grpc: - backendRef: - name: grpc-ext-auth + backendRefs: + - name: grpc-ext-auth namespace: gateway-conformance-infra port: 9002 --- diff --git a/test/e2e/testdata/ext-auth-http-securitypolicy.yaml b/test/e2e/testdata/ext-auth-http-securitypolicy.yaml index 1ebcc9e34f9..e77dbde80e5 100644 --- a/test/e2e/testdata/ext-auth-http-securitypolicy.yaml +++ b/test/e2e/testdata/ext-auth-http-securitypolicy.yaml @@ -47,8 +47,8 @@ spec: name: http-with-ext-auth extAuth: http: - backendRef: - name: http-ext-auth + backendRefs: + - name: http-ext-auth namespace: gateway-conformance-infra port: 9002 headersToBackend: ["x-current-user"]