diff --git a/api/v1alpha1/ext_auth_types.go b/api/v1alpha1/ext_auth_types.go
index 7ea742c409..40830dc28c 100644
--- a/api/v1alpha1/ext_auth_types.go
+++ b/api/v1alpha1/ext_auth_types.go
@@ -171,6 +171,7 @@ type GRPCExtAuthService struct {
// +kubebuilder:validation:XValidation:message="backendRef or backendRefs needs to be set",rule="has(self.backendRef) || self.backendRefs.size() > 0"
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Service, ServiceImport, and Backend kind.",rule="has(self.backendRefs) ? self.backendRefs.all(f, f.kind == 'Service' || f.kind == 'ServiceImport' || f.kind == 'Backend') : true"
// +kubebuilder:validation:XValidation:message="BackendRefs only supports Core, multicluster.x-k8s.io, and gateway.envoyproxy.io groups.",rule="has(self.backendRefs) ? (self.backendRefs.all(f, f.group == \"\" || f.group == 'multicluster.x-k8s.io' || f.group == 'gateway.envoyproxy.io')) : true"
+// +kubebuilder:validation:XValidation:message="only one of path or pathOverride can be specified",rule="!(has(self.path) && has(self.pathOverride))"
type HTTPExtAuthService struct {
// Only Service kind is supported for now.
BackendCluster `json:",inline"`
@@ -183,9 +184,19 @@ type HTTPExtAuthService struct {
// For example, if the original request path is "/hello", and the path specified here is "/auth",
// then the path of the authorization request will be "/auth/hello". If the path is not specified,
// the path of the authorization request will be "/hello".
+ // Only one of Path or PathOverride can be set.
// +optional
Path *string `json:"path,omitempty"`
+ // PathOverride replaces the original request path in the authorization request.
+ // If set, the path will be overridden to this value during authorization.
+ // For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ // then the path of the authorization request will be "/auth".
+ // Only one of Path or PathOverride can be set.
+ //
+ // +optional
+ PathOverride *string `json:"pathOverride,omitempty"`
+
// 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.
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 746c6c732f..272f419948 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -4299,6 +4299,11 @@ func (in *HTTPExtAuthService) DeepCopyInto(out *HTTPExtAuthService) {
*out = new(string)
**out = **in
}
+ if in.PathOverride != nil {
+ in, out := &in.PathOverride, &out.PathOverride
+ *out = new(string)
+ **out = **in
+ }
if in.HeadersToBackend != nil {
in, out := &in.HeadersToBackend, &out.HeadersToBackend
*out = make([]string, len(*in))
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
index d8797fdcbf..162f7ac520 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -3523,6 +3523,15 @@ spec:
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
+ Only one of Path or PathOverride can be set.
+ type: string
+ pathOverride:
+ description: |-
+ PathOverride replaces the original request path in the authorization request.
+ If set, the path will be overridden to this value during authorization.
+ For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ then the path of the authorization request will be "/auth".
+ Only one of Path or PathOverride can be set.
type: string
type: object
x-kubernetes-validations:
@@ -3538,6 +3547,8 @@ spec:
rule: 'has(self.backendRefs) ? (self.backendRefs.all(f, f.group
== "" || f.group == ''multicluster.x-k8s.io'' || f.group ==
''gateway.envoyproxy.io'')) : true'
+ - message: only one of path or pathOverride can be specified
+ rule: '!(has(self.path) && has(self.pathOverride))'
includeRouteMetadata:
description: |-
IncludeRouteMetadata sends Envoy Gateway's built-in route metadata to the
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 769179c233..a674272b38 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -3522,6 +3522,15 @@ spec:
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
+ Only one of Path or PathOverride can be set.
+ type: string
+ pathOverride:
+ description: |-
+ PathOverride replaces the original request path in the authorization request.
+ If set, the path will be overridden to this value during authorization.
+ For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ then the path of the authorization request will be "/auth".
+ Only one of Path or PathOverride can be set.
type: string
type: object
x-kubernetes-validations:
@@ -3537,6 +3546,8 @@ spec:
rule: 'has(self.backendRefs) ? (self.backendRefs.all(f, f.group
== "" || f.group == ''multicluster.x-k8s.io'' || f.group ==
''gateway.envoyproxy.io'')) : true'
+ - message: only one of path or pathOverride can be specified
+ rule: '!(has(self.path) && has(self.pathOverride))'
includeRouteMetadata:
description: |-
IncludeRouteMetadata sends Envoy Gateway's built-in route metadata to the
diff --git a/examples/envoy-ext-auth/main.go b/examples/envoy-ext-auth/main.go
index 6237512fa0..c7a67cf6f5 100644
--- a/examples/envoy-ext-auth/main.go
+++ b/examples/envoy-ext-auth/main.go
@@ -294,6 +294,7 @@ func authCheckerHandler(w http.ResponseWriter, req *http.Request) {
if len(extracted) == 2 && extracted[0] == "Bearer" {
if user, ok := testUsers[extracted[1]]; ok {
w.Header().Add("x-current-user", user) // this should be set before call WriteHeader
+ w.Header().Add("x-ext-auth-req-path", req.URL.Path)
w.WriteHeader(http.StatusOK)
return
}
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index 6e439ca02f..e2606bc3e0 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -2207,6 +2207,7 @@ func (t *Translator) buildExtAuth(
Destination: *rd,
Authority: authority,
Path: ptr.Deref(http.Path, ""),
+ PathOverride: ptr.Deref(http.PathOverride, ""),
HeadersToBackend: http.HeadersToBackend,
}
} else {
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.in.yaml
new file mode 100644
index 0000000000..591dd71a2c
--- /dev/null
+++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.in.yaml
@@ -0,0 +1,126 @@
+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: /foo
+ backendRefs:
+ - name: service-1
+ 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: service-1
+ spec:
+ ports:
+ - port: 8080
+ name: http
+ 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-service-1
+ namespace: default
+ labels:
+ kubernetes.io/service-name: service-1
+ addressType: IPv4
+ ports:
+ - name: http
+ protocol: TCP
+ port: 8080
+ 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-gateway-1
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ extAuth:
+ failOpen: false
+ http:
+ backendRefs:
+ - Name: http-backend
+ Namespace: envoy-gateway
+ Port: 80
+ pathOverride: /check
+ headersToBackend:
+ - x-user
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.out.yaml
new file mode 100644
index 0000000000..70131570e7
--- /dev/null
+++ b/internal/gatewayapi/testdata/securitypolicy-with-extauth-path-override.out.yaml
@@ -0,0 +1,240 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: default
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: 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: /foo
+ 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:
+ - 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
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: default/gateway-1
+ namespace: envoy-gateway-system
+securityPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: SecurityPolicy
+ metadata:
+ name: policy-for-gateway-1
+ namespace: default
+ spec:
+ extAuth:
+ failOpen: false
+ http:
+ backendRefs:
+ - name: http-backend
+ namespace: envoy-gateway
+ port: 80
+ headersToBackend:
+ - x-user
+ pathOverride: /check
+ 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
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+xdsIR:
+ default/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-default-gateway-1-bfd08ef4
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: default/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-default-gateway-1-bfd08ef4
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: default/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: default
+ sectionName: http
+ name: default/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 8.8.8.8
+ port: 8080
+ - host: 7.7.7.7
+ port: 8080
+ metadata:
+ kind: Service
+ name: service-1
+ namespace: default
+ sectionName: "8080"
+ name: httproute/default/httproute-1/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: www.foo.com
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/www_foo_com
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /foo
+ security:
+ extAuth:
+ failOpen: false
+ http:
+ authority: http-backend.envoy-gateway:80
+ destination:
+ metadata:
+ kind: SecurityPolicy
+ name: policy-for-gateway-1
+ namespace: default
+ name: securitypolicy/default/policy-for-gateway-1/extauth/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 80
+ metadata:
+ kind: Service
+ name: http-backend
+ namespace: envoy-gateway
+ sectionName: "80"
+ name: securitypolicy/default/policy-for-gateway-1/extauth/0/backend/0
+ protocol: HTTP
+ weight: 1
+ headersToBackend:
+ - x-user
+ path: ""
+ pathOverride: /check
+ name: securitypolicy/default/policy-for-gateway-1
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 1e778399b1..22b5e77963 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -1561,6 +1561,9 @@ type HTTPExtAuthService struct {
// or else the authorization request will be sent to the root path.
Path string `json:"path"`
+ // PathOverride replaces the value of authorization request path with this value.
+ PathOverride string `json:"pathOverride,omitempty"`
+
// 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.
diff --git a/internal/xds/translator/extauth.go b/internal/xds/translator/extauth.go
index 8d3d984f5c..2405ba505b 100644
--- a/internal/xds/translator/extauth.go
+++ b/internal/xds/translator/extauth.go
@@ -187,7 +187,8 @@ func httpService(http *ir.HTTPExtAuthService, timeout *durationpb.Duration) *ext
)
service = &extauthv3.HttpService{
- PathPrefix: http.Path,
+ PathPrefix: http.Path,
+ PathOverride: http.PathOverride,
}
u := url.URL{
@@ -196,7 +197,11 @@ func httpService(http *ir.HTTPExtAuthService, timeout *durationpb.Duration) *ext
// uri to make the request. It only uses the cluster.
Scheme: "http",
Host: http.Authority,
- Path: http.Path,
+ }
+ if http.PathOverride != "" {
+ u.Path = http.PathOverride
+ } else {
+ u.Path = http.Path
}
uri = u.String()
diff --git a/internal/xds/translator/testdata/in/xds-ir/ext-auth-path-override.yaml b/internal/xds/translator/testdata/in/xds-ir/ext-auth-path-override.yaml
new file mode 100644
index 0000000000..80ec74f6a9
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/ext-auth-path-override.yaml
@@ -0,0 +1,47 @@
+http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*'
+ name: default/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - name: httproute/default/httproute-2/rule/0/match/0/www_bar_com
+ hostname: www.bar.com
+ isHTTP2: false
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /bar
+ destination:
+ name: httproute/default/httproute-2/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ name: httproute/default/httproute-2/rule/0/backend/0
+ security:
+ extAuth:
+ name: securitypolicy/default/policy-for-gateway-1
+ failOpen: true
+ 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
+ name: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend/backend/0
+ headersToBackend:
+ - header1
+ - header2
+ pathOverride: /auth/hello
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.clusters.yaml
new file mode 100644
index 0000000000..a52765ac67
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.clusters.yaml
@@ -0,0 +1,46 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: httproute/default/httproute-2/rule/0
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: httproute/default/httproute-2/rule/0
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.endpoints.yaml
new file mode 100644
index 0000000000..bdf8531237
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.endpoints.yaml
@@ -0,0 +1,24 @@
+- clusterName: httproute/default/httproute-2/rule/0
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 7.7.7.7
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: httproute/default/httproute-2/rule/0/backend/0
+- clusterName: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 7.7.7.7
+ portValue: 80
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.listeners.yaml
new file mode 100644
index 0000000000..cfd99de7f4
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.listeners.yaml
@@ -0,0 +1,54 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - disabled: true
+ name: envoy.filters.http.ext_authz/securitypolicy/default/policy-for-gateway-1
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
+ failureModeAllow: true
+ httpService:
+ authorizationResponse:
+ allowedUpstreamHeaders:
+ patterns:
+ - exact: header1
+ ignoreCase: true
+ - exact: header2
+ ignoreCase: true
+ pathOverride: /auth/hello
+ serverUri:
+ cluster: securitypolicy/default/policy-for-gateway-1/envoy-gateway/http-backend
+ timeout: 10s
+ uri: http://http-backend.envoy-gateway:80/auth/hello
+ transportApiVersion: V3
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: default/gateway-1/http
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: default/gateway-1/http
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: default/gateway-1/http
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.routes.yaml
new file mode 100644
index 0000000000..de6199af3a
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/ext-auth-path-override.routes.yaml
@@ -0,0 +1,18 @@
+- ignorePortInHostMatching: true
+ name: default/gateway-1/http
+ virtualHosts:
+ - domains:
+ - www.bar.com
+ name: default/gateway-1/http/www_bar_com
+ routes:
+ - match:
+ pathSeparatedPrefix: /bar
+ name: httproute/default/httproute-2/rule/0/match/0/www_bar_com
+ route:
+ cluster: httproute/default/httproute-2/rule/0
+ upgradeConfigs:
+ - upgradeType: websocket
+ typedPerFilterConfig:
+ envoy.filters.http.ext_authz/securitypolicy/default/policy-for-gateway-1:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
+ checkSettings: {}
diff --git a/release-notes/current.yaml b/release-notes/current.yaml
index a77ca0a894..36f3b975aa 100644
--- a/release-notes/current.yaml
+++ b/release-notes/current.yaml
@@ -46,6 +46,7 @@ new features: |
Added support for OpenTelemetry sampler configuration for tracing.
Added support for default EnvoyProxy settings on EnvoyGatewaySpec that can be overridden by GatewayClass or Gateway-level EnvoyProxy configurations. A new MergeType field allows choosing between Replace (default), StrategicMerge, or JSONMerge strategies for combining configurations.
Added support for sending Envoy Gateway route metadata to external authorization backends via `SecurityPolicy.spec.extAuth.includeRouteMetadata`.
+ Added support for path override in ExtAuth HTTP service.
bug fixes: |
Fixed local rate limit rules with identical sourceCIDR client selectors producing conflicting descriptors.
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 01dded902c..33bd5d1825 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -2940,7 +2940,8 @@ _Appears in:_
| `backendRef` | _[BackendObjectReference](https://gateway-api.sigs.k8s.io/reference/1.5/spec/#backendobjectreference)_ | false | | BackendRef references a Kubernetes object that represents the
backend server to which the authorization request will be sent.
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. |
| `backendSettings` | _[ClusterSettings](#clustersettings)_ | false | | BackendSettings holds configuration for managing the connection
to the backend. |
-| `path` | _string_ | false | | 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 use the path of the original request.
Please note that the original request path will be appended to the path specified here.
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello". |
+| `path` | _string_ | false | | 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 use the path of the original request.
Please note that the original request path will be appended to the path specified here.
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
Only one of Path or PathOverride can be set. |
+| `pathOverride` | _string_ | false | | PathOverride replaces the original request path in the authorization request.
If set, the path will be overridden to this value during authorization.
For example, if the original request path is "/hello", and PathOverride is set to "/auth",
then the path of the authorization request will be "/auth".
Only one of Path or PathOverride can be set. |
| `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 e750cbf9d7..5fea0b9c25 100644
--- a/test/cel-validation/securitypolicy_test.go
+++ b/test/cel-validation/securitypolicy_test.go
@@ -605,6 +605,41 @@ func TestSecurityPolicyTarget(t *testing.T) {
},
wantErrors: []string{},
},
+ {
+ desc: "HTTP external auth service with both path and pathOverride",
+ mutate: func(sp *egv1a1.SecurityPolicy) {
+ sp.Spec = egv1a1.SecurityPolicySpec{
+ ExtAuth: &egv1a1.ExtAuth{
+ HTTP: &egv1a1.HTTPExtAuthService{
+ BackendCluster: egv1a1.BackendCluster{
+ BackendRefs: []egv1a1.BackendRef{
+ {
+ BackendObjectReference: gwapiv1.BackendObjectReference{
+ Name: "http-auth-service",
+ Port: new(gwapiv1.PortNumber(15001)),
+ },
+ },
+ },
+ },
+ Path: new("/auth"),
+ PathOverride: new("/check"),
+ },
+ },
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRef: &gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: "gateway.networking.k8s.io",
+ Kind: "Gateway",
+ Name: "eg",
+ },
+ },
+ },
+ }
+ },
+ wantErrors: []string{
+ " only one of path or pathOverride can be specified",
+ },
+ },
{
desc: "HTTP external auth service with backendRefs",
mutate: func(sp *egv1a1.SecurityPolicy) {
diff --git a/test/e2e/testdata/ext-auth-http-securitypolicy.yaml b/test/e2e/testdata/ext-auth-http-securitypolicy.yaml
index 712b5a1eaf..a330169708 100644
--- a/test/e2e/testdata/ext-auth-http-securitypolicy.yaml
+++ b/test/e2e/testdata/ext-auth-http-securitypolicy.yaml
@@ -35,6 +35,24 @@ spec:
- name: infra-backend-v1
port: 8080
---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: http-with-ext-auth-path-override
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ hostnames: ["www.example.com"]
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /myapp-override
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
@@ -52,4 +70,23 @@ spec:
name: envoy-ext-auth
namespace: gateway-conformance-infra
port: 8080
- headersToBackend: ["x-current-user"]
+ headersToBackend: ["x-current-user", "x-ext-auth-req-path"]
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: SecurityPolicy
+metadata:
+ name: ext-auth-path-override-test
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: http-with-ext-auth-path-override
+ extAuth:
+ http:
+ pathOverride: /auth
+ backendRef:
+ name: envoy-ext-auth
+ namespace: gateway-conformance-infra
+ port: 8080
+ headersToBackend: ["x-current-user", "x-ext-auth-req-path"]
diff --git a/test/e2e/tests/ext_auth_http_service.go b/test/e2e/tests/ext_auth_http_service.go
index 364d21aaeb..5affcef34e 100644
--- a/test/e2e/tests/ext_auth_http_service.go
+++ b/test/e2e/tests/ext_auth_http_service.go
@@ -64,7 +64,8 @@ var HTTPExtAuthTest = suite.ConformanceTest{
Host: "www.example.com",
Path: "/myapp",
Headers: map[string]string{
- "x-current-user": "user1",
+ "x-current-user": "user1",
+ "x-ext-auth-req-path": "/auth/myapp",
},
},
},
@@ -140,6 +141,42 @@ var HTTPExtAuthTest = suite.ConformanceTest{
}
})
+ t.Run("http route with ext auth path override", func(t *testing.T) {
+ ancestorRef := gwapiv1.ParentReference{
+ Group: gatewayapi.GroupPtr(gwapiv1.GroupName),
+ Kind: gatewayapi.KindPtr(resource.KindGateway),
+ Namespace: gatewayapi.NamespacePtr(gwNN.Namespace),
+ Name: gwapiv1.ObjectName(gwNN.Name),
+ }
+ SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "ext-auth-path-override-test", Namespace: ns}, suite.ControllerName, ancestorRef)
+
+ expectedResponse := http.ExpectedResponse{
+ Request: http.Request{
+ Host: "www.example.com",
+ Path: "/myapp-override",
+ Headers: map[string]string{
+ "Authorization": "Bearer token1",
+ },
+ },
+ ExpectedRequest: &http.ExpectedRequest{
+ Request: http.Request{
+ Host: "www.example.com",
+ Path: "/myapp-override",
+ Headers: map[string]string{
+ "x-current-user": "user1",
+ "x-ext-auth-req-path": "/auth",
+ },
+ },
+ },
+ Response: http.Response{
+ StatusCodes: []int{200},
+ },
+ Namespace: ns,
+ }
+
+ http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
+ })
+
t.Run("http route without ext auth authentication", func(t *testing.T) {
ancestorRef := gwapiv1.ParentReference{
Group: gatewayapi.GroupPtr(gwapiv1.GroupName),
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index 78b32eb49d..ff753be966 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -53067,6 +53067,15 @@ spec:
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
+ Only one of Path or PathOverride can be set.
+ type: string
+ pathOverride:
+ description: |-
+ PathOverride replaces the original request path in the authorization request.
+ If set, the path will be overridden to this value during authorization.
+ For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ then the path of the authorization request will be "/auth".
+ Only one of Path or PathOverride can be set.
type: string
type: object
x-kubernetes-validations:
@@ -53082,6 +53091,8 @@ spec:
rule: 'has(self.backendRefs) ? (self.backendRefs.all(f, f.group
== "" || f.group == ''multicluster.x-k8s.io'' || f.group ==
''gateway.envoyproxy.io'')) : true'
+ - message: only one of path or pathOverride can be specified
+ rule: '!(has(self.path) && has(self.pathOverride))'
includeRouteMetadata:
description: |-
IncludeRouteMetadata sends Envoy Gateway's built-in route metadata to the
diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml
index c4f6463b73..63b65b44c8 100644
--- a/test/helm/gateway-crds-helm/e2e.out.yaml
+++ b/test/helm/gateway-crds-helm/e2e.out.yaml
@@ -31040,6 +31040,15 @@ spec:
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
+ Only one of Path or PathOverride can be set.
+ type: string
+ pathOverride:
+ description: |-
+ PathOverride replaces the original request path in the authorization request.
+ If set, the path will be overridden to this value during authorization.
+ For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ then the path of the authorization request will be "/auth".
+ Only one of Path or PathOverride can be set.
type: string
type: object
x-kubernetes-validations:
@@ -31055,6 +31064,8 @@ spec:
rule: 'has(self.backendRefs) ? (self.backendRefs.all(f, f.group
== "" || f.group == ''multicluster.x-k8s.io'' || f.group ==
''gateway.envoyproxy.io'')) : true'
+ - message: only one of path or pathOverride can be specified
+ rule: '!(has(self.path) && has(self.pathOverride))'
includeRouteMetadata:
description: |-
IncludeRouteMetadata sends Envoy Gateway's built-in route metadata to the
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index 0561ffb5a1..a3abc73200 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -31040,6 +31040,15 @@ spec:
For example, if the original request path is "/hello", and the path specified here is "/auth",
then the path of the authorization request will be "/auth/hello". If the path is not specified,
the path of the authorization request will be "/hello".
+ Only one of Path or PathOverride can be set.
+ type: string
+ pathOverride:
+ description: |-
+ PathOverride replaces the original request path in the authorization request.
+ If set, the path will be overridden to this value during authorization.
+ For example, if the original request path is "/hello", and PathOverride is set to "/auth",
+ then the path of the authorization request will be "/auth".
+ Only one of Path or PathOverride can be set.
type: string
type: object
x-kubernetes-validations:
@@ -31055,6 +31064,8 @@ spec:
rule: 'has(self.backendRefs) ? (self.backendRefs.all(f, f.group
== "" || f.group == ''multicluster.x-k8s.io'' || f.group ==
''gateway.envoyproxy.io'')) : true'
+ - message: only one of path or pathOverride can be specified
+ rule: '!(has(self.path) && has(self.pathOverride))'
includeRouteMetadata:
description: |-
IncludeRouteMetadata sends Envoy Gateway's built-in route metadata to the