CVE-2023-33189: Other (NVD-CWE-Other)
value: Pomerium is an identity and context-aware access proxy. With specially crafted requests, incorrect authorization decisions may be made by Pomerium. This issue has been patched in versions 0.17.4, 0.18.1, 0.19.2, 0.20.1, 0.21.4 and 0.22.2.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 9.8
Published: 2023-05-30T06:16Z
Repository: https://github.com/pomerium/pomerium
Language: Go
Files changed: authorize/evaluator/evaluator.go, authorize/evaluator/headers_evaluator.go, authorize/evaluator/policy_evaluator_test.go, authorize/grpc.go, config/envoyconfig/listeners.go, config/envoyconfig/per_filter_config.go, config/envoyconfig/route_configurations_test.go, config/envoyconfig/routes.go, config/envoyconfig/routes_test.go, config/envoyconfig/testdata/main_http_connection_manager_filter.json, config/policy_ppl.go, config/policy_ppl_test.go
Commit date: 2023-05-26 13:34:21 -0700
Commit message: Merge pull request from GHSA-pvrc-wvj2-f59p

* authorize: use route id from envoy for policy evaluation

* authorize: normalize URL query params

* config: enable envoy normalize_path option

* fix tests

---------

Co-authored-by: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com>
Diff: diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go
index 0b1c313a..454026f2 100644
--- a/authorize/evaluator/evaluator.go
+++ b/authorize/evaluator/evaluator.go
@@ -20,21 +20,16 @@ import (
 	"github.com/pomerium/pomerium/pkg/contextutil"
 	"github.com/pomerium/pomerium/pkg/cryptutil"
 	"github.com/pomerium/pomerium/pkg/policy/criteria"
 )
 
-// notFoundOutput is what's returned if a route isn't found for a policy.
-var notFoundOutput = &Result{
-	Deny:    NewRuleResult(true, criteria.ReasonRouteNotFound),
-	Headers: make(http.Header),
-}
-
 // Request contains the inputs needed for evaluation.
 type Request struct {
-	Policy  *config.Policy
-	HTTP    RequestHTTP
-	Session RequestSession
+	IsInternal bool
+	Policy     *config.Policy
+	HTTP       RequestHTTP
+	Session    RequestSession
 }
 
 // RequestHTTP is the HTTP field in the request.
 type RequestHTTP struct {
 	Method            string            `json:"method"`
@@ -123,22 +118,76 @@ func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator
 // Evaluate evaluates the rego for the given policy and generates the identity headers.
 func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error) {
 	ctx, span := trace.StartSpan(ctx, "authorize.Evaluator.Evaluate")
 	defer span.End()
 
+	eg, ctx := errgroup.WithContext(ctx)
+
+	var policyOutput *PolicyResponse
+	eg.Go(func() error {
+		var err error
+		if req.IsInternal {
+			policyOutput, err = e.evaluateInternal(ctx, req)
+		} else {
+			policyOutput, err = e.evaluatePolicy(ctx, req)
+		}
+		return err
+	})
+
+	var headersOutput *HeadersResponse
+	eg.Go(func() error {
+		var err error
+		headersOutput, err = e.evaluateHeaders(ctx, req)
+		return err
+	})
+
+	err := eg.Wait()
+	if err != nil {
+		return nil, err
+	}
+
+	res := &Result{
+		Allow:   policyOutput.Allow,
+		Deny:    policyOutput.Deny,
+		Headers: headersOutput.Headers,
+		Traces:  policyOutput.Traces,
+	}
+	return res, nil
+}
+
+func (e *Evaluator) evaluateInternal(_ context.Context, req *Request) (*PolicyResponse, error) {
+	// these endpoints require a logged-in user
+	if req.HTTP.Path == "/.pomerium/webauthn" || req.HTTP.Path == "/.pomerium/jwt" {
+		if req.Session.ID == "" {
+			return &PolicyResponse{
+				Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated),
+			}, nil
+		}
+	}
+
+	return &PolicyResponse{
+		Allow: NewRuleResult(true, criteria.ReasonPomeriumRoute),
+	}, nil
+}
+
+func (e *Evaluator) evaluatePolicy(ctx context.Context, req *Request) (*PolicyResponse, error) {
 	if req.Policy == nil {
-		return notFoundOutput, nil
+		return &PolicyResponse{
+			Deny: NewRuleResult(true, criteria.ReasonRouteNotFound),
+		}, nil
 	}
 
 	id, err := req.Policy.RouteID()
 	if err != nil {
 		return nil, fmt.Errorf("authorize: error computing policy route id: %w", err)
 	}
 
 	policyEvaluator, ok := e.policyEvaluators[id]
 	if !ok {
-		return notFoundOutput, nil
+		return &PolicyResponse{
+			Deny: NewRuleResult(true, criteria.ReasonRouteNotFound),
+		}, nil
 	}
 
 	clientCA, err := e.getClientCA(req.Policy)
 	if err != nil {
 		return nil, err
@@ -147,45 +196,27 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
 	isValidClientCertificate, err := isValidClientCertificate(clientCA, req.HTTP.ClientCertificate)
 	if err != nil {
 		return nil, fmt.Errorf("authorize: error validating client certificate: %w", err)
 	}
 
-	eg, ectx := errgroup.WithContext(ctx)
-
-	var policyOutput *PolicyResponse
-	eg.Go(func() error {
-		var err error
-		policyOutput, err = policyEvaluator.Evaluate(ectx, &PolicyRequest{
-			HTTP:                     req.HTTP,
-			Session:                  req.Session,
-			IsValidClientCertificate: isValidClientCertificate,
-		})
-		return err
-	})
-
-	var headersOutput *HeadersResponse
-	eg.Go(func() error {
-		headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname)
-		headersReq.Session = req.Session
-		var err error
-		headersOutput, err = e.headersEvaluators.Evaluate(ectx, headersReq)
-		return err
+	return policyEvaluator.Evaluate(ctx, &PolicyRequest{
+		HTTP:                     req.HTTP,
+		Session:                  req.Session,
+		IsValidClientCertificate: isValidClientCertificate,
 	})
+}
 
-	err = eg.Wait()
+func (e *Evaluator) evaluateHeaders(ctx context.Context, req *Request) (*HeadersResponse, error) {
+	headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname)
+	headersReq.Session = req.Session
+	res, err := e.headersEvaluators.Evaluate(ctx, headersReq)
 	if err != nil {
 		return nil, err
 	}
 
-	carryOverJWTAssertion(headersOutput.Headers, req.HTTP.Headers)
+	carryOverJWTAssertion(res.Headers, req.HTTP.Headers)
 
-	res := &Result{
-		Allow:   policyOutput.Allow,
-		Deny:    policyOutput.Deny,
-		Headers: headersOutput.Headers,
-		Traces:  policyOutput.Traces,
-	}
 	return res, nil
 }
 
 func (e *Evaluator) getClientCA(policy *config.Policy) (string, error) {
 	if policy != nil && policy.TLSDownstreamClientCA != "" {
diff --git a/authorize/evaluator/headers_evaluator.go b/authorize/evaluator/headers_evaluator.go
index 4b16a845..f86a9a28 100644
--- a/authorize/evaluator/headers_evaluator.go
+++ b/authorize/evaluator/headers_evaluator.go
@@ -28,20 +28,22 @@ type HeadersRequest struct {
 }
 
 // NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy.
 func NewHeadersRequestFromPolicy(policy *config.Policy, hostname string) *HeadersRequest {
 	input := new(HeadersRequest)
-	input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
-	input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
-		policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
-	input.Issuer = hostname
-	input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
-	for _, wu := range policy.To {
-		input.ToAudience = "https://" + wu.URL.Hostname()
+	if policy != nil {
+		input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
+		input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
+			policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
+		input.Issuer = hostname
+		input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
+		for _, wu := range policy.To {
+			input.ToAudience = "https://" + wu.URL.Hostname()
+		}
+		input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
+		input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
 	}
-	input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
-	input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
 	return input
 }
 
 // HeadersResponse is the output from the headers.rego script.
 type HeadersResponse struct {
diff --git a/authorize/evaluator/policy_evaluator_test.go b/authorize/evaluator/policy_evaluator_test.go
index 386184c1..7893464b 100644
--- a/authorize/evaluator/policy_evaluator_test.go
+++ b/authorize/evaluator/policy_evaluator_test.go
@@ -109,11 +109,11 @@ func TestPolicyEvaluator(t *testing.T) {
 
 				IsValidClientCertificate: true,
 			})
 		require.NoError(t, err)
 		assert.Equal(t, &PolicyResponse{
-			Allow:  NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthorized),
+			Allow:  NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized),
 			Deny:   NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired),
 			Traces: []contextutil.PolicyEvaluationTrace{{}},
 		}, output)
 	})
 	t.Run("ppl", func(t *testing.T) {
@@ -170,11 +170,11 @@ func TestPolicyEvaluator(t *testing.T) {
 
 					IsValidClientCertificate: true,
 				})
 			require.NoError(t, err)
 			assert.Equal(t, &PolicyResponse{
-				Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
+				Allow:  NewRuleResult(false),
 				Deny:   NewRuleResult(true, criteria.ReasonAccept),
 				Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Deny: true}},
 			}, output)
 		})
 		t.Run("client certificate", func(t *testing.T) {
@@ -201,11 +201,11 @@ func TestPolicyEvaluator(t *testing.T) {
 
 					IsValidClientCertificate: false,
 				})
 			require.NoError(t, err)
 			assert.Equal(t, &PolicyResponse{
-				Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
+				Allow:  NewRuleResult(false),
 				Deny:   NewRuleResult(true, criteria.ReasonAccept, criteria.ReasonInvalidClientCertificate),
 				Traces: []contextutil.PolicyEvaluationTrace{{Deny: true}, {ID: "p1", Deny: true}},
 			}, output)
 		})
 	})
@@ -287,11 +287,11 @@ func TestPolicyEvaluator(t *testing.T) {
 
 				IsValidClientCertificate: true,
 			})
 		require.NoError(t, err)
 		assert.Equal(t, &PolicyResponse{
-			Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthenticated),
+			Allow:  NewRuleResult(false, criteria.ReasonUserUnauthenticated),
 			Deny:   NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired),
 			Traces: []contextutil.PolicyEvaluationTrace{{Allow: false}},
 		}, output)
 	})
 }
diff --git a/authorize/grpc.go b/authorize/grpc.go
index 2496ef84..d18cc365 100644
--- a/authorize/grpc.go
+++ b/authorize/grpc.go
@@ -9,10 +9,11 @@ import (
 
 	envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
 
 	"github.com/pomerium/pomerium/authorize/evaluator"
 	"github.com/pomerium/pomerium/config"
+	"github.com/pomerium/pomerium/config/envoyconfig"
 	"github.com/pomerium/pomerium/internal/log"
 	"github.com/pomerium/pomerium/internal/sessions"
 	"github.com/pomerium/pomerium/internal/telemetry/requestid"
 	"github.com/pomerium/pomerium/internal/telemetry/trace"
 	"github.com/pomerium/pomerium/internal/urlutil"
@@ -91,10 +92,11 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
 	in *envoy_service_auth_v3.CheckRequest,
 	sessionState *sessions.State,
 ) (*evaluator.Request, error) {
 	requestURL := getCheckRequestURL(in)
 	req := &evaluator.Request{
+		IsInternal: envoyconfig.ExtAuthzContextExtensionsIsInternal(in.GetAttributes().GetContextExtensions()),
 		HTTP: evaluator.NewRequestHTTP(
 			in.GetAttributes().GetRequest().GetHttp().GetMethod(),
 			requestURL,
 			getCheckRequestHeaders(in),
 			getPeerCertificate(in),
@@ -104,19 +106,20 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
 	if sessionState != nil {
 		req.Session = evaluator.RequestSession{
 			ID: sessionState.ID,
 		}
 	}
-	req.Policy = a.getMatchingPolicy(requestURL)
+	req.Policy = a.getMatchingPolicy(envoyconfig.ExtAuthzContextExtensionsRouteID(in.Attributes.GetContextExtensions()))
 	return req, nil
 }
 
-func (a *Authorize) getMatchingPolicy(requestURL url.URL) *config.Policy {
+func (a *Authorize) getMatchingPolicy(routeID uint64) *config.Policy {
 	options := a.currentOptions.Load()
 
 	for _, p := range options.GetAllPolicies() {
-		if p.Matches(requestURL) {
+		id, _ := p.RouteID()
+		if id == routeID {
 			return &p
 		}
 	}
 
 	return nil
@@ -157,10 +160,11 @@ func getCheckRequestURL(req *envoy_service_auth_v3.CheckRequest) url.URL {
 	u.Host = urlutil.GetDomainsForURL(&u)[0]
 	// envoy sends the query string as part of the path
 	path := h.GetPath()
 	if idx := strings.Index(path, "?"); idx != -1 {
 		u.RawPath, u.RawQuery = path[:idx], path[idx+1:]
+		u.RawQuery = u.Query().Encode()
 	} else {
 		u.RawPath = path
 	}
 	u.Path, _ = url.PathUnescape(u.RawPath)
 	return u
diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go
index aa8e4213..b8c5005e 100644
--- a/config/envoyconfig/listeners.go
+++ b/config/envoyconfig/listeners.go
@@ -11,15 +11,13 @@ import (
 	"time"
 
 	envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
 	envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
-	envoy_extensions_filters_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
 	envoy_http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
 	envoy_extensions_transport_sockets_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
 	envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
-	"github.com/golang/protobuf/ptypes/any"
 	"github.com/golang/protobuf/ptypes/wrappers"
 	"google.golang.org/protobuf/types/known/durationpb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
 
 	"github.com/pomerium/pomerium/config"
@@ -30,31 +28,20 @@ import (
 	"github.com/pomerium/pomerium/internal/urlutil"
 )
 
 const listenerBufferLimit uint32 = 32 * 1024
 
-var (
-	disableExtAuthz *any.Any
-	tlsParams       = &envoy_extensions_transport_sockets_tls_v3.TlsParameters{
-		CipherSuites: []string{
-			"ECDHE-ECDSA-AES256-GCM-SHA384",
-			"ECDHE-RSA-AES256-GCM-SHA384",
-			"ECDHE-ECDSA-AES128-GCM-SHA256",
-			"ECDHE-RSA-AES128-GCM-SHA256",
-			"ECDHE-ECDSA-CHACHA20-POLY1305",
-			"ECDHE-RSA-CHACHA20-POLY1305",
-		},
-		TlsMinimumProtocolVersion: envoy_extensions_transport_sockets_tls_v3.TlsParameters_TLSv1_2,
-	}
-)
-
-func init() {
-	disableExtAuthz = marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
-		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{
-			Disabled: true,
-		},
-	})
+var tlsParams = &envoy_extensions_transport_sockets_tls_v3.TlsParameters{
+	CipherSuites: []string{
+		"ECDHE-ECDSA-AES256-GCM-SHA384",
+		"ECDHE-RSA-AES256-GCM-SHA384",
+		"ECDHE-ECDSA-AES128-GCM-SHA256",
+		"ECDHE-RSA-AES128-GCM-SHA256",
+		"ECDHE-ECDSA-CHACHA20-POLY1305",
+		"ECDHE-RSA-CHACHA20-POLY1305",
+	},
+	TlsMinimumProtocolVersion: envoy_extensions_transport_sockets_tls_v3.TlsParameters_TLSv1_2,
 }
 
 // BuildListeners builds envoy listeners from the given config.
 func (b *Builder) BuildListeners(
 	ctx context.Context,
@@ -310,10 +297,11 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
 		// See https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for
 		UseRemoteAddress:  &wrappers.BoolValue{Value: true},
 		SkipXffAppend:     cfg.Options.SkipXffAppend,
 		XffNumTrustedHops: cfg.Options.XffNumTrustedHops,
 		LocalReplyConfig:  b.buildLocalReplyConfig(cfg.Options, false),
+		NormalizePath:     wrapperspb.Bool(true),
 	}
 
 	if fullyStatic {
 		routeConfiguration, err := b.buildMainRouteConfiguration(ctx, cfg)
 		if err != nil {
diff --git a/config/envoyconfig/per_filter_config.go b/config/envoyconfig/per_filter_config.go
new file mode 100644
index 00000000..02fc65ea
--- /dev/null
+++ b/config/envoyconfig/per_filter_config.go
@@ -0,0 +1,53 @@
+package envoyconfig
+
+import (
+	"strconv"
+
+	envoy_extensions_filters_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
+	"github.com/golang/protobuf/ptypes/any"
+)
+
+// PerFilterConfigExtAuthzName is the name of the ext authz filter to apply config to
+const PerFilterConfigExtAuthzName = "envoy.filters.http.ext_authz"
+
+// PerFilterConfigExtAuthzContextExtensions returns a per-filter config for ext authz that disables ext-authz.
+func PerFilterConfigExtAuthzContextExtensions(authzContextExtensions map[string]string) *any.Any {
+	return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
+		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{
+			CheckSettings: &envoy_extensions_filters_http_ext_authz_v3.CheckSettings{
+				ContextExtensions: authzContextExtensions,
+			},
+		},
+	})
+}
+
+// PerFilterConfigExtAuthzDisabled returns a per-filter config for ext authz that disables ext-authz.
+func PerFilterConfigExtAuthzDisabled() *any.Any {
+	return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
+		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{
+			Disabled: true,
+		},
+	})
+}
+
+// MakeExtAuthzContextExtensions makes the ext authz context extensions.
+func MakeExtAuthzContextExtensions(internal bool, routeID uint64) map[string]string {
+	return map[string]string{
+		"internal": strconv.FormatBool(internal),
+		"route_id": strconv.FormatUint(routeID, 10),
+	}
+}
+
+// ExtAuthzContextExtensionsIsInternal returns true if the context extensions indicates the route is internal.
+func ExtAuthzContextExtensionsIsInternal(extAuthzContextExtensions map[string]string) bool {
+	return extAuthzContextExtensions != nil && extAuthzContextExtensions["internal"] == "true"
+}
+
+// ExtAuthzContextExtensionsRouteID returns the route id for the context extensions.
+func ExtAuthzContextExtensionsRouteID(extAuthzContextExtensions map[string]string) uint64 {
+	if extAuthzContextExtensions == nil {
+		return 0
+	}
+	routeID, _ := strconv.ParseUint(extAuthzContextExtensions["route_id"], 10, 64)
+	return routeID
+}
diff --git a/config/envoyconfig/route_configurations_test.go b/config/envoyconfig/route_configurations_test.go
index e91f561b..b0b2f475 100644
--- a/config/envoyconfig/route_configurations_test.go
+++ b/config/envoyconfig/route_configurations_test.go
@@ -24,10 +24,11 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
 		SharedKey:              cryptutil.NewBase64Key(),
 		Services:               "proxy",
 		Policies: []config.Policy{
 			{
 				From: "https://*.example.com",
+				To:   mustParseWeightedURLs(t, "https://www.example.com"),
 			},
 		},
 	}}
 	b := New("grpc", "http", "metrics", filemgr.NewManager(), nil)
 	routeConfiguration, err := b.buildMainRouteConfiguration(ctx, cfg)
@@ -38,19 +39,17 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
 		"virtualHosts": [
 			{
 				"name": "catch-all",
 				"domains": ["*"],
 				"routes": [
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/jwt", true, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/webauthn", true, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/ping", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/healthz", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.pomerium/", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.well-known/pomerium", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.well-known/pomerium/", false, false))+`,
-					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/robots.txt", false, false))+`,
+					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/ping", false))+`,
+					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/healthz", false))+`,
+					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium", false))+`,
+					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.pomerium/", false))+`,
+					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.well-known/pomerium", false))+`,
+					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.well-known/pomerium/", false))+`,
+					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/robots.txt", false))+`,
 					{
 						"name": "policy-0",
 						"match": {
 							"headers": [
 								{ "name": ":authority", "stringMatch": { "safeRegex": { "regex": "^(.*)\\.example\\.com$" } }}
@@ -77,20 +76,31 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
 							{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-Frame-Options", "value": "SAMEORIGIN" } },
 							{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-XSS-Protection", "value": "1; mode=block" } }
 						],
 						"route": {
 							"autoHostRewrite": true,
-							"cluster": "route-0",
+							"cluster": "route-5feb9fe8bd89aa97",
 							"hashPolicy": [
 								{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
 								{ "connectionProperties": { "sourceIp": true }, "terminal": true }
 							],
 							"timeout": "3s",
 							"upgradeConfigs": [
 								{ "enabled": false, "upgradeType": "websocket" },
 								{ "enabled": false, "upgradeType": "spdy/3.1" }
 							]
+						},
+						"typedPerFilterConfig": {
+							"envoy.filters.http.ext_authz": {
+								"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+								"checkSettings": {
+									"contextExtensions": {
+										"internal": "false",
+										"route_id": "6911793875091303063"
+									}
+								}
+							}
 						}
 					},
 					{
 						"name": "policy-0",
 						"match": {
@@ -119,20 +129,31 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
 							{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-Frame-Options", "value": "SAMEORIGIN" } },
 							{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-XSS-Protection", "value": "1; mode=block" } }
 						],
 						"route": {
 							"autoHostRewrite": true,
-							"cluster": "route-0",
+							"cluster": "route-5feb9fe8bd89aa97",
 							"hashPolicy": [
 								{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
 								{ "connectionProperties": { "sourceIp": true }, "terminal": true }
 							],
 							"timeout": "3s",
 							"upgradeConfigs": [
 								{ "enabled": false, "upgradeType": "websocket" },
 								{ "enabled": false, "upgradeType": "spdy/3.1" }
 							]
+						},
+						"typedPerFilterConfig": {
+							"envoy.filters.http.ext_authz": {
+								"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+								"checkSettings": {
+									"contextExtensions": {
+										"internal": "false",
+										"route_id": "6911793875091303063"
+									}
+								}
+							}
 						}
 					}
 				]
 			}
 		]
diff --git a/config/envoyconfig/routes.go b/config/envoyconfig/routes.go
index 900d840f..28a1c701 100644
--- a/config/envoyconfig/routes.go
+++ b/config/envoyconfig/routes.go
@@ -43,11 +43,11 @@ func (b *Builder) buildGRPCRoutes() ([]*envoy_config_route_v3.Route, error) {
 			},
 			Grpc: &envoy_config_route_v3.RouteMatch_GrpcRouteMatchOptions{},
 		},
 		Action: action,
 		TypedPerFilterConfig: map[string]*any.Any{
-			"envoy.filters.http.ext_authz": disableExtAuthz,
+			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
 		},
 	}}, nil
 }
 
 func (b *Builder) buildPomeriumHTTPRoutes(
@@ -63,24 +63,20 @@ func (b *Builder) buildPomeriumHTTPRoutes(
 	if err != nil {
 		return nil, err
 	}
 	if !isFrontingAuthenticate {
 		routes = append(routes,
-			// enable ext_authz
-			b.buildControlPlanePathRoute(options, "/.pomerium/jwt", true, requireStrictTransportSecurity),
-			b.buildControlPlanePathRoute(options, urlutil.WebAuthnURLPath, true, requireStrictTransportSecurity),
-			// disable ext_authz and passthrough to proxy handlers
-			b.buildControlPlanePathRoute(options, "/ping", false, requireStrictTransportSecurity),
-			b.buildControlPlanePathRoute(options, "/healthz", false, requireStrictTransportSecurity),
-			b.buildControlPlanePathRoute(options, "/.pomerium", false, requireStrictTransportSecurity),
-			b.buildControlPlanePrefixRoute(options, "/.pomerium/", false, requireStrictTransportSecurity),
-			b.buildControlPlanePathRoute(options, "/.well-known/pomerium", false, requireStrictTransportSecurity),
-			b.buildControlPlanePrefixRoute(options, "/.well-known/pomerium/", false, requireStrictTransportSecurity),
+			b.buildControlPlanePathRoute(options, "/ping", requireStrictTransportSecurity),
+			b.buildControlPlanePathRoute(options, "/healthz", requireStrictTransportSecurity),
+			b.buildControlPlanePathRoute(options, "/.pomerium", requireStrictTransportSecurity),
+			b.buildControlPlanePrefixRoute(options, "/.pomerium/", requireStrictTransportSecurity),
+			b.buildControlPlanePathRoute(options, "/.well-known/pomerium", requireStrictTransportSecurity),
+			b.buildControlPlanePrefixRoute(options, "/.well-known/pomerium/", requireStrictTransportSecurity),
 		)
 		// per #837, only add robots.txt if there are no unauthenticated routes
 		if !hasPublicPolicyMatchingURL(options, url.URL{Scheme: "https", Host: host, Path: "/robots.txt"}) {
-			routes = append(routes, b.buildControlPlanePathRoute(options, "/robots.txt", false, requireStrictTransportSecurity))
+			routes = append(routes, b.buildControlPlanePathRoute(options, "/robots.txt", requireStrictTransportSecurity))
 		}
 	}
 
 	authRoutes, err := b.buildPomeriumAuthenticateHTTPRoutes(options, host, requireStrictTransportSecurity)
 	if err != nil {
@@ -107,22 +103,21 @@ func (b *Builder) buildPomeriumAuthenticateHTTPRoutes(
 		if err != nil {
 			return nil, err
 		}
 		if urlMatchesHost(u, host) {
 			return []*envoy_config_route_v3.Route{
-				b.buildControlPlanePathRoute(options, options.AuthenticateCallbackPath, false, requireStrictTransportSecurity),
-				b.buildControlPlanePathRoute(options, "/", false, requireStrictTransportSecurity),
+				b.buildControlPlanePathRoute(options, options.AuthenticateCallbackPath, requireStrictTransportSecurity),
+				b.buildControlPlanePathRoute(options, "/", requireStrictTransportSecurity),
 			}, nil
 		}
 	}
 	return nil, nil
 }
 
 func (b *Builder) buildControlPlanePathRoute(
 	options *config.Options,
 	path string,
-	protected bool,
 	requireStrictTransportSecurity bool,
 ) *envoy_config_route_v3.Route {
 	r := &envoy_config_route_v3.Route{
 		Name: "pomerium-path-" + path,
 		Match: &envoy_config_route_v3.RouteMatch{
@@ -134,23 +129,20 @@ func (b *Builder) buildControlPlanePathRoute(
 					Cluster: httpCluster,
 				},
 			},
 		},
 		ResponseHeadersToAdd: toEnvoyHeaders(options.GetSetResponseHeaders(requireStrictTransportSecurity)),
-	}
-	if !protected {
-		r.TypedPerFilterConfig = map[string]*any.Any{
-			"envoy.filters.http.ext_authz": disableExtAuthz,
-		}
+		TypedPerFilterConfig: map[string]*any.Any{
+			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(true, 0)),
+		},
 	}
 	return r
 }
 
 func (b *Builder) buildControlPlanePrefixRoute(
 	options *config.Options,
 	prefix string,
-	protected bool,
 	requireStrictTransportSecurity bool,
 ) *envoy_config_route_v3.Route {
 	r := &envoy_config_route_v3.Route{
 		Name: "pomerium-prefix-" + prefix,
 		Match: &envoy_config_route_v3.RouteMatch{
@@ -162,15 +154,13 @@ func (b *Builder) buildControlPlanePrefixRoute(
 					Cluster: httpCluster,
 				},
 			},
 		},
 		ResponseHeadersToAdd: toEnvoyHeaders(options.GetSetResponseHeaders(requireStrictTransportSecurity)),
-	}
-	if !protected {
-		r.TypedPerFilterConfig = map[string]*any.Any{
-			"envoy.filters.http.ext_authz": disableExtAuthz,
-		}
+		TypedPerFilterConfig: map[string]*any.Any{
+			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(true, 0)),
+		},
 	}
 	return r
 }
 
 // getClusterID returns a cluster ID
@@ -286,10 +276,15 @@ func (b *Builder) buildRouteForPolicyAndMatch(
 	fromURL, err := urlutil.ParseAndValidateURL(policy.From)
 	if err != nil {
 		return nil, err
 	}
 
+	routeID, err := policy.RouteID()
+	if err != nil {
+		return nil, err
+	}
+
 	requireStrictTransportSecurity := cryptutil.HasCertificateForServerName(certs, fromURL.Hostname())
 
 	route := &envoy_config_route_v3.Route{
 		Name:                   name,
 		Match:                  match,
@@ -321,13 +316,16 @@ func (b *Builder) buildRouteForPolicyAndMatch(
 	if err != nil {
 		return nil, err
 	}
 	if isFrontingAuthenticate {
 		route.TypedPerFilterConfig = map[string]*any.Any{
-			"envoy.filters.http.ext_authz": disableExtAuthz,
+			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
 		}
 	} else {
+		route.TypedPerFilterConfig = map[string]*any.Any{
+			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(false, routeID)),
+		}
 		luaMetadata["remove_pomerium_cookie"] = &structpb.Value{
 			Kind: &structpb.Value_StringValue{
 				StringValue: cfg.Options.CookieName,
 			},
 		}
@@ -342,12 +340,11 @@ func (b *Builder) buildRouteForPolicyAndMatch(
 			},
 		}
 	}
 
 	if policy.IsForKubernetes() {
-		policyID, _ := policy.RouteID()
-		for _, hdr := range b.reproxy.GetPolicyIDHeaders(policyID) {
+		for _, hdr := range b.reproxy.GetPolicyIDHeaders(routeID) {
 			route.RequestHeadersToAdd = append(route.RequestHeadersToAdd,
 				&envoy_config_core_v3.HeaderValueOption{
 					Header: &envoy_config_core_v3.HeaderValue{
 						Key:   hdr[0],
 						Value: hdr[1],
diff --git a/config/envoyconfig/routes_test.go b/config/envoyconfig/routes_test.go
index 6b8e4a45..28e68bce 100644
--- a/config/envoyconfig/routes_test.go
+++ b/config/envoyconfig/routes_test.go
@@ -14,11 +14,10 @@ import (
 	"google.golang.org/protobuf/proto"
 
 	"github.com/pomerium/pomerium/config"
 	"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
 	"github.com/pomerium/pomerium/internal/testutil"
-	"github.com/pomerium/pomerium/internal/urlutil"
 	"github.com/pomerium/pomerium/pkg/cryptutil"
 )
 
 func policyNameFunc() func(*config.Policy) string {
 	i := 0
@@ -54,47 +53,47 @@ func Test_buildGRPCRoutes(t *testing.T) {
 	`, routes)
 }
 
 func Test_buildPomeriumHTTPRoutes(t *testing.T) {
 	b := &Builder{filemgr: filemgr.NewManager()}
-	routeString := func(typ, name string, protected bool) string {
+	routeString := func(typ, name string) string {
 		str := `{
-				"name": "pomerium-` + typ + `-` + name + `",
-				"match": {
-					"` + typ + `": "` + name + `"
+			"name": "pomerium-` + typ + `-` + name + `",
+			"match": {
+				"` + typ + `": "` + name + `"
+			},
+			"responseHeadersToAdd": [
+				{
+					"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
+					"header": {
+						"key": "X-Frame-Options",
+						"value": "SAMEORIGIN"
+					}
 				},
-				"responseHeadersToAdd": [
-					{
-						"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
-						"header": {
-						  "key": "X-Frame-Options",
-						  "value": "SAMEORIGIN"
-						}
-					},
-					{
-						"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
-						"header": {
-						  "key": "X-XSS-Protection",
-						  "value": "1; mode=block"
-						}
+				{
+					"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
+					"header": {
+						"key": "X-XSS-Protection",
+						"value": "1; mode=block"
 					}
-				],
-				"route": {
-					"cluster": "pomerium-control-plane-http"
 				}
-			`
-		if !protected {
-			str += `,
-				"typedPerFilterConfig": {
-					"envoy.filters.http.ext_authz": {
-						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
-						"disabled": true
+			],
+			"route": {
+				"cluster": "pomerium-control-plane-http"
+			},
+			"typedPerFilterConfig": {
+				"envoy.filters.http.ext_authz": {
+					"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+					"checkSettings": {
+						"contextExtensions": {
+							"internal": "true",
+							"route_id": "0"
+						}
 					}
 				}
-			`
-		}
-		str += "}"
+			}
+		}`
 		return str
 	}
 	t.Run("authenticate", func(t *testing.T) {
 		options := &config.Options{
 			Services:                 "all",
@@ -103,21 +102,19 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
 		}
 		routes, err := b.buildPomeriumHTTPRoutes(options, "authenticate.example.com", false)
 		require.NoError(t, err)
 
 		testutil.AssertProtoJSONEqual(t, `[
-			`+routeString("path", "/.pomerium/jwt", true)+`,
-			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
-			`+routeString("path", "/ping", false)+`,
-			`+routeString("path", "/healthz", false)+`,
-			`+routeString("path", "/.pomerium", false)+`,
-			`+routeString("prefix", "/.pomerium/", false)+`,
-			`+routeString("path", "/.well-known/pomerium", false)+`,
-			`+routeString("prefix", "/.well-known/pomerium/", false)+`,
-			`+routeString("path", "/robots.txt", false)+`,
-			`+routeString("path", "/oauth2/callback", false)+`,
-			`+routeString("path", "/", false)+`
+			`+routeString("path", "/ping")+`,
+			`+routeString("path", "/healthz")+`,
+			`+routeString("path", "/.pomerium")+`,
+			`+routeString("prefix", "/.pomerium/")+`,
+			`+routeString("path", "/.well-known/pomerium")+`,
+			`+routeString("prefix", "/.well-known/pomerium/")+`,
+			`+routeString("path", "/robots.txt")+`,
+			`+routeString("path", "/oauth2/callback")+`,
+			`+routeString("path", "/")+`
 		]`, routes)
 	})
 	t.Run("proxy fronting authenticate", func(t *testing.T) {
 		options := &config.Options{
 			Services:                 "proxy",
@@ -142,19 +139,17 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
 		_ = options.Policies[0].Validate()
 		routes, err := b.buildPomeriumHTTPRoutes(options, "from.example.com", false)
 		require.NoError(t, err)
 
 		testutil.AssertProtoJSONEqual(t, `[
-			`+routeString("path", "/.pomerium/jwt", true)+`,
-			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
-			`+routeString("path", "/ping", false)+`,
-			`+routeString("path", "/healthz", false)+`,
-			`+routeString("path", "/.pomerium", false)+`,
-			`+routeString("prefix", "/.pomerium/", false)+`,
-			`+routeString("path", "/.well-known/pomerium", false)+`,
-			`+routeString("prefix", "/.well-known/pomerium/", false)+`,
-			`+routeString("path", "/robots.txt", false)+`
+			`+routeString("path", "/ping")+`,
+			`+routeString("path", "/healthz")+`,
+			`+routeString("path", "/.pomerium")+`,
+			`+routeString("prefix", "/.pomerium/")+`,
+			`+routeString("path", "/.well-known/pomerium")+`,
+			`+routeString("prefix", "/.well-known/pomerium/")+`,
+			`+routeString("path", "/robots.txt")+`
 		]`, routes)
 	})
 
 	t.Run("without robots", func(t *testing.T) {
 		options := &config.Options{
@@ -170,26 +165,24 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
 		_ = options.Policies[0].Validate()
 		routes, err := b.buildPomeriumHTTPRoutes(options, "from.example.com", false)
 		require.NoError(t, err)
 
 		testutil.AssertProtoJSONEqual(t, `[
-			`+routeString("path", "/.pomerium/jwt", true)+`,
-			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
-			`+routeString("path", "/ping", false)+`,
-			`+routeString("path", "/healthz", false)+`,
-			`+routeString("path", "/.pomerium", false)+`,
-			`+routeString("prefix", "/.pomerium/", false)+`,
-			`+routeString("path", "/.well-known/pomerium", false)+`,
-			`+routeString("prefix", "/.well-known/pomerium/", false)+`
+			`+routeString("path", "/ping")+`,
+			`+routeString("path", "/healthz")+`,
+			`+routeString("path", "/.pomerium")+`,
+			`+routeString("prefix", "/.pomerium/")+`,
+			`+routeString("path", "/.well-known/pomerium")+`,
+			`+routeString("prefix", "/.well-known/pomerium/")+`
 		]`, routes)
 	})
 }
 
 func Test_buildControlPlanePathRoute(t *testing.T) {
 	options := config.NewDefaultOptions()
 	b := &Builder{filemgr: filemgr.NewManager()}
-	route := b.buildControlPlanePathRoute(options, "/hello/world", false, false)
+	route := b.buildControlPlanePathRoute(options, "/hello/world", false)
 	testutil.AssertProtoJSONEqual(t, `
 		{
 			"name": "pomerium-path-/hello/world",
 			"match": {
 				"path": "/hello/world"
@@ -214,21 +207,26 @@ func Test_buildControlPlanePathRoute(t *testing.T) {
 				"cluster": "pomerium-control-plane-http"
 			},
 			"typedPerFilterConfig": {
 				"envoy.filters.http.ext_authz": {
 					"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
-					"disabled": true
+					"checkSettings": {
+						"contextExtensions": {
+							"internal": "true",
+							"route_id": "0"
+						}
+					}
 				}
 			}
 		}
 	`, route)
 }
 
 func Test_buildControlPlanePrefixRoute(t *testing.T) {
 	options := config.NewDefaultOptions()
 	b := &Builder{filemgr: filemgr.NewManager()}
-	route := b.buildControlPlanePrefixRoute(options, "/hello/world/", false, false)
+	route := b.buildControlPlanePrefixRoute(options, "/hello/world/", false)
 	testutil.AssertProtoJSONEqual(t, `
 		{
 			"name": "pomerium-prefix-/hello/world/",
 			"match": {
 				"prefix": "/hello/world/"
@@ -253,11 +251,16 @@ func Test_buildControlPlanePrefixRoute(t *testing.T) {
 				"cluster": "pomerium-control-plane-http"
 			},
 			"typedPerFilterConfig": {
 				"envoy.filters.http.ext_authz": {
 					"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
-					"disabled": true
+					"checkSettings": {
+						"contextExtensions": {
+							"internal": "true",
+							"route_id": "0"
+						}
+					}
 				}
 			}
 		}
 	`, route)
 }
@@ -299,10 +302,11 @@ func TestTimeouts(t *testing.T) {
 			DefaultUpstreamTimeout: time.Second * 3,
 			SharedKey:              cryptutil.NewBase64Key(),
 			Policies: []config.Policy{
 				{
 					From:            "https://example.com",
+					To:              mustParseWeightedURLs(t, "https://to.example.com"),
 					Path:            "/test",
 					UpstreamTimeout: getDuration(tc.upstream),
 					IdleTimeout:     getDuration(tc.idle),
 					AllowWebsockets: tc.allowWebsockets,
 				},
@@ -354,59 +358,68 @@ func Test_buildPolicyRoutes(t *testing.T) {
 		DefaultUpstreamTimeout: time.Second * 3,
 		SharedKey:              cryptutil.NewBase64Key(),
 		Policies: []config.Policy{
 			{
 				From:                "https://ignore.example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Path:                "/some/path",
 				AllowWebsockets:     true,
 				PreserveHostHeader:  true,
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Prefix:              "/some/prefix/",
 				SetRequestHeaders:   map[string]string{"HEADER-KEY": "HEADER-VALUE"},
 				UpstreamTimeout:     &oneMinute,
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Regex:               `^/[a]+$`,
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                 "https://example.com",
+				To:                   mustParseWeightedURLs(t, "https://to.example.com"),
 				Prefix:               "/some/prefix/",
 				RemoveRequestHeaders: []string{"HEADER-KEY"},
 				UpstreamTimeout:      &oneMinute,
 				PassIdentityHeaders:  true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Path:                "/some/path",
 				AllowSPDY:           true,
 				PreserveHostHeader:  true,
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Path:                "/some/path",
 				AllowSPDY:           true,
 				AllowWebsockets:     true,
 				PreserveHostHeader:  true,
 				PassIdentityHeaders: true,
 			},
 			{
 				From:                "https://example.com",
+				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 				Path:                "/websocket-timeout",
 				AllowWebsockets:     true,
 				PreserveHostHeader:  true,
 				PassIdentityHeaders: true,
 				UpstreamTimeout:     &ten,
@@ -472,11 +485,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "11444765232398592404"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-2",
 				"match": {
 					"path": "/some/path"
@@ -532,11 +556,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2990091139764155677"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-3",
 				"match": {
 					"prefix": "/some/prefix/"
@@ -598,11 +633,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2544588842279234006"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-4",
 				"match": {
 					"safeRegex": {
@@ -659,11 +705,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "10244970664102670752"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-5",
 				"match": {
 					"prefix": "/some/prefix/"
@@ -719,11 +776,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2544588842279234006"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-6",
 				"match": {
 					"path": "/some/path"
@@ -778,11 +846,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2990091139764155677"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-7",
 				"match": {
 					"path": "/some/path"
@@ -838,11 +917,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2990091139764155677"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-8",
 				"match": {
 					"path": "/websocket-timeout"
@@ -898,11 +988,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "1052418080698022187"
+							}
+						}
+					}
+				}
 			}
 		]
 	`, routes)
 
 	t.Run("fronting-authenticate", func(t *testing.T) {
@@ -913,10 +1014,11 @@ func Test_buildPolicyRoutes(t *testing.T) {
 			DefaultUpstreamTimeout: time.Second * 3,
 			SharedKey:              cryptutil.NewBase64Key(),
 			Policies: []config.Policy{
 				{
 					From:                "https://authenticate.example.com",
+					To:                  mustParseWeightedURLs(t, "https://authenticate.internal"),
 					PassIdentityHeaders: true,
 				},
 			},
 		}}, nil, "authenticate.example.com")
 		require.NoError(t, err)
@@ -995,14 +1097,16 @@ func Test_buildPolicyRoutes(t *testing.T) {
 			DefaultUpstreamTimeout: time.Second * 3,
 			SharedKey:              cryptutil.NewBase64Key(),
 			Policies: []config.Policy{
 				{
 					From:                "tcp+https://example.com:22",
+					To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 					PassIdentityHeaders: true,
 				},
 				{
 					From:                "tcp+https://example.com:22",
+					To:                  mustParseWeightedURLs(t, "https://to.example.com"),
 					PassIdentityHeaders: true,
 					UpstreamTimeout:     &ten,
 				},
 			},
 		}}, nil, "example.com:22")
@@ -1067,11 +1171,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2226589900561460978"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-1",
 				"match": {
 					"connectMatcher": {}
@@ -1128,11 +1243,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "2226589900561460978"
+							}
+						}
+					}
+				}
 			}
 		]
 	`, routes)
 	})
 
@@ -1147,10 +1273,11 @@ func Test_buildPolicyRoutes(t *testing.T) {
 				"x-email": "email",
 			},
 			Policies: []config.Policy{
 				{
 					From: "https://from.example.com",
+					To:   mustParseWeightedURLs(t, "https://to.example.com"),
 				},
 			},
 		}}, nil, "from.example.com")
 		require.NoError(t, err)
 
@@ -1214,11 +1341,22 @@ func Test_buildPolicyRoutes(t *testing.T) {
 							"header": {
 							  "key": "X-XSS-Protection",
 							  "value": "1; mode=block"
 							}
 						}
-					]
+					],
+					"typedPerFilterConfig": {
+						"envoy.filters.http.ext_authz": {
+							"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+							"checkSettings": {
+								"contextExtensions": {
+									"internal": "false",
+									"route_id": "15508081512033148378"
+								}
+							}
+						}
+					}
 				}
 			]
 		`, routes)
 	})
 }
@@ -1333,11 +1471,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-1",
 				"match": {
 					"prefix": "/"
@@ -1393,11 +1542,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-2",
 				"match": {
 					"prefix": "/"
@@ -1458,11 +1618,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-3",
 				"match": {
 					"prefix": "/"
@@ -1518,11 +1689,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-4",
 				"match": {
 					"prefix": "/"
@@ -1578,11 +1760,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			},
 			{
 				"name": "policy-5",
 				"match": {
 					"prefix": "/"
@@ -1643,11 +1836,22 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
 						"header": {
 						  "key": "X-XSS-Protection",
 						  "value": "1; mode=block"
 						}
 					}
-				]
+				],
+				"typedPerFilterConfig": {
+					"envoy.filters.http.ext_authz": {
+						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
+						"checkSettings": {
+							"contextExtensions": {
+								"internal": "false",
+								"route_id": "285016060542193864"
+							}
+						}
+					}
+				}
 			}
 		]
 	`, routes)
 }
 
diff --git a/config/envoyconfig/testdata/main_http_connection_manager_filter.json b/config/envoyconfig/testdata/main_http_connection_manager_filter.json
index 661b36b1..097eb957 100644
--- a/config/envoyconfig/testdata/main_http_connection_manager_filter.json
+++ b/config/envoyconfig/testdata/main_http_connection_manager_filter.json
@@ -118,10 +118,11 @@
           ]
         }
       ]
     },
     "requestTimeout": "30s",
+    "normalizePath": true,
     "rds": {
       "configSource": {
         "ads": {},
         "resourceApiVersion": "V3"
       },
diff --git a/config/policy_ppl.go b/config/policy_ppl.go
index 20001909..e791e626 100644
--- a/config/policy_ppl.go
+++ b/config/policy_ppl.go
@@ -11,14 +11,10 @@ import (
 // ToPPL converts a policy into Pomerium Policy Language.
 func (p *Policy) ToPPL() *parser.Policy {
 	ppl := &parser.Policy{}
 
 	allowRule := parser.Rule{Action: parser.ActionAllow}
-	allowRule.Or = append(allowRule.Or,
-		parser.Criterion{
-			Name: "pomerium_routes",
-		})
 	if p.AllowPublicUnauthenticatedAccess {
 		allowRule.Or = append(allowRule.Or,
 			parser.Criterion{
 				Name: "accept",
 				Data: parser.Boolean(true),
diff --git a/config/policy_ppl_test.go b/config/policy_ppl_test.go
index 9c9d9954..38d876a8 100644
--- a/config/policy_ppl_test.go
+++ b/config/policy_ppl_test.go
@@ -55,28 +55,10 @@ func TestPolicy_ToPPL(t *testing.T) {
 
 default allow = [false, set()]
 
 default deny = [false, set()]
 
-pomerium_routes_0 = [true, {"pomerium-route"}] {
-	session := get_session(input.session.id)
-	session.id != ""
-	contains(input.http.url, "/.pomerium/")
-}
-
-else = [true, {"pomerium-route"}] {
-	contains(input.http.url, "/.pomerium/")
-	not contains(input.http.url, "/.pomerium/jwt")
-	not contains(input.http.url, "/.pomerium/webauthn")
-}
-
-else = [false, {"user-unauthenticated"}] {
-	contains(input.http.url, "/.pomerium/")
-}
-
-else = [false, {"non-pomerium-route"}]
-
 accept_0 = [true, {"accept"}]
 
 cors_preflight_0 = [true, {"cors-request"}] {
 	input.http.method == "OPTIONS"
 	count(object.get(input.http.headers, "Access-Control-Request-Method", [])) > 0
@@ -378,11 +360,11 @@ else = [false, {"email-unauthorized"}] {
 }
 
 else = [false, {"user-unauthenticated"}]
 
 or_0 = v {
-	results := [pomerium_routes_0, accept_0, cors_preflight_0, authenticated_user_0, domain_0, domain_1, domain_2, domain_3, domain_4, claim_0, claim_1, claim_2, claim_3, user_0, email_0, user_1, email_1, user_2, email_2, user_3, email_3, user_4, email_4]
+	results := [accept_0, cors_preflight_0, authenticated_user_0, domain_0, domain_1, domain_2, domain_3, domain_4, claim_0, claim_1, claim_2, claim_3, user_0, email_0, user_1, email_1, user_2, email_2, user_3, email_3, user_4, email_4]
 	normalized := [normalize_criterion_result(x) | x := results[i]]
 	v := merge_with_or(normalized)
 }
 
 user_5 = [true, {"user-ok"}] {
diff --git a/pkg/policy/criteria/pomerium_routes.go b/pkg/policy/criteria/pomerium_routes.go
deleted file mode 100644
index a1b6d6ed..00000000
--- a/pkg/policy/criteria/pomerium_routes.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package criteria
-
-import (
-	"github.com/open-policy-agent/opa/ast"
-
-	"github.com/pomerium/pomerium/internal/urlutil"
-	"github.com/pomerium/pomerium/pkg/policy/generator"
-	"github.com/pomerium/pomerium/pkg/policy/parser"
-	"github.com/pomerium/pomerium/pkg/policy/rules"
-)
-
-type pomeriumRoutesCriterion struct {
-	g *Generator
-}
-
-func (pomeriumRoutesCriterion) DataType() generator.CriterionDataType {
-	return generator.CriterionDataTypeUnused
-}
-
-func (pomeriumRoutesCriterion) Name() string {
-	return "pomerium_routes"
-}
-
-func (c pomeriumRoutesCriterion) GenerateRule(_ string, _ parser.Value) (*ast.Rule, []*ast.Rule, error) {
-	r1 := c.g.NewRule(c.Name())
-	r1.Head.Value = NewCriterionTerm(true, ReasonPomeriumRoute)
-	r1.Body = ast.Body{
-		ast.MustParseExpr(`session := get_session(input.session.id)`),
-		ast.MustParseExpr(`session.id != ""`),
-		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
-	}
-
-	r2 := c.g.NewRule(c.Name())
-	r2.Head.Value = NewCriterionTerm(true, ReasonPomeriumRoute)
-	r2.Body = ast.Body{
-		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
-		ast.MustParseExpr(`not contains(input.http.url, "/.pomerium/jwt")`),
-		ast.MustParseExpr(`not contains(input.http.url, "` + urlutil.WebAuthnURLPath + `")`),
-	}
-	r1.Else = r2
-
-	r3 := c.g.NewRule(c.Name())
-	r3.Head.Value = NewCriterionTerm(false, ReasonUserUnauthenticated)
-	r3.Body = ast.Body{
-		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
-	}
-	r2.Else = r3
-
-	r4 := c.g.NewRule(c.Name())
-	r4.Head.Value = NewCriterionTerm(false, ReasonNonPomeriumRoute)
-	r3.Else = r4
-
-	return r1, []*ast.Rule{
-		rules.GetSession(),
-	}, nil
-}
-
-// PomeriumRoutes returns a Criterion on that allows access to pomerium routes.
-func PomeriumRoutes(generator *Generator) Criterion {
-	return pomeriumRoutesCriterion{g: generator}
-}
-
-func init() {
-	Register(PomeriumRoutes)
-}

Vulnerable code: // notFoundOutput is what's returned if a route isn't found for a policy.
var notFoundOutput = &Result{
	Deny:    NewRuleResult(true, criteria.ReasonRouteNotFound),
	Headers: make(http.Header),
}

	Policy  *config.Policy
	HTTP    RequestHTTP
	Session RequestSession
		return notFoundOutput, nil
		return notFoundOutput, nil
	eg, ectx := errgroup.WithContext(ctx)

	var policyOutput *PolicyResponse
	eg.Go(func() error {
		var err error
		policyOutput, err = policyEvaluator.Evaluate(ectx, &PolicyRequest{
			HTTP:                     req.HTTP,
			Session:                  req.Session,
			IsValidClientCertificate: isValidClientCertificate,
		})
		return err
	})

	var headersOutput *HeadersResponse
	eg.Go(func() error {
		headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname)
		headersReq.Session = req.Session
		var err error
		headersOutput, err = e.headersEvaluators.Evaluate(ectx, headersReq)
		return err
	err = eg.Wait()
	carryOverJWTAssertion(headersOutput.Headers, req.HTTP.Headers)
	res := &Result{
		Allow:   policyOutput.Allow,
		Deny:    policyOutput.Deny,
		Headers: headersOutput.Headers,
		Traces:  policyOutput.Traces,
	}
	input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
	input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
		policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
	input.Issuer = hostname
	input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
	for _, wu := range policy.To {
		input.ToAudience = "https://" + wu.URL.Hostname()
	input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
	input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
			Allow:  NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthorized),
				Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
				Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
			Allow:  NewRuleResult(false, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthenticated),
	req.Policy = a.getMatchingPolicy(requestURL)
func (a *Authorize) getMatchingPolicy(requestURL url.URL) *config.Policy {
		if p.Matches(requestURL) {
	envoy_extensions_filters_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
	"github.com/golang/protobuf/ptypes/any"
var (
	disableExtAuthz *any.Any
	tlsParams       = &envoy_extensions_transport_sockets_tls_v3.TlsParameters{
		CipherSuites: []string{
			"ECDHE-ECDSA-AES256-GCM-SHA384",
			"ECDHE-RSA-AES256-GCM-SHA384",
			"ECDHE-ECDSA-AES128-GCM-SHA256",
			"ECDHE-RSA-AES128-GCM-SHA256",
			"ECDHE-ECDSA-CHACHA20-POLY1305",
			"ECDHE-RSA-CHACHA20-POLY1305",
		},
		TlsMinimumProtocolVersion: envoy_extensions_transport_sockets_tls_v3.TlsParameters_TLSv1_2,
	}
)

func init() {
	disableExtAuthz = marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{
			Disabled: true,
		},
	})
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/jwt", true, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/webauthn", true, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/ping", false, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/healthz", false, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium", false, false))+`,
					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.pomerium/", false, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.well-known/pomerium", false, false))+`,
					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.well-known/pomerium/", false, false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/robots.txt", false, false))+`,
							"cluster": "route-0",
							"cluster": "route-0",
			"envoy.filters.http.ext_authz": disableExtAuthz,
			// enable ext_authz
			b.buildControlPlanePathRoute(options, "/.pomerium/jwt", true, requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, urlutil.WebAuthnURLPath, true, requireStrictTransportSecurity),
			// disable ext_authz and passthrough to proxy handlers
			b.buildControlPlanePathRoute(options, "/ping", false, requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/healthz", false, requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/.pomerium", false, requireStrictTransportSecurity),
			b.buildControlPlanePrefixRoute(options, "/.pomerium/", false, requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/.well-known/pomerium", false, requireStrictTransportSecurity),
			b.buildControlPlanePrefixRoute(options, "/.well-known/pomerium/", false, requireStrictTransportSecurity),
			routes = append(routes, b.buildControlPlanePathRoute(options, "/robots.txt", false, requireStrictTransportSecurity))
				b.buildControlPlanePathRoute(options, options.AuthenticateCallbackPath, false, requireStrictTransportSecurity),
				b.buildControlPlanePathRoute(options, "/", false, requireStrictTransportSecurity),
	protected bool,
	}
	if !protected {
		r.TypedPerFilterConfig = map[string]*any.Any{
			"envoy.filters.http.ext_authz": disableExtAuthz,
		}
	protected bool,
	}
	if !protected {
		r.TypedPerFilterConfig = map[string]*any.Any{
			"envoy.filters.http.ext_authz": disableExtAuthz,
		}
			"envoy.filters.http.ext_authz": disableExtAuthz,
		policyID, _ := policy.RouteID()
		for _, hdr := range b.reproxy.GetPolicyIDHeaders(policyID) {
	"github.com/pomerium/pomerium/internal/urlutil"
	routeString := func(typ, name string, protected bool) string {
				"name": "pomerium-` + typ + `-` + name + `",
				"match": {
					"` + typ + `": "` + name + `"
				"responseHeadersToAdd": [
					{
						"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
						"header": {
						  "key": "X-Frame-Options",
						  "value": "SAMEORIGIN"
						}
					},
					{
						"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
						"header": {
						  "key": "X-XSS-Protection",
						  "value": "1; mode=block"
						}
				],
				"route": {
					"cluster": "pomerium-control-plane-http"
			`
		if !protected {
			str += `,
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"disabled": true
			`
		}
		str += "}"
			`+routeString("path", "/.pomerium/jwt", true)+`,
			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
			`+routeString("path", "/ping", false)+`,
			`+routeString("path", "/healthz", false)+`,
			`+routeString("path", "/.pomerium", false)+`,
			`+routeString("prefix", "/.pomerium/", false)+`,
			`+routeString("path", "/.well-known/pomerium", false)+`,
			`+routeString("prefix", "/.well-known/pomerium/", false)+`,
			`+routeString("path", "/robots.txt", false)+`,
			`+routeString("path", "/oauth2/callback", false)+`,
			`+routeString("path", "/", false)+`
			`+routeString("path", "/.pomerium/jwt", true)+`,
			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
			`+routeString("path", "/ping", false)+`,
			`+routeString("path", "/healthz", false)+`,
			`+routeString("path", "/.pomerium", false)+`,
			`+routeString("prefix", "/.pomerium/", false)+`,
			`+routeString("path", "/.well-known/pomerium", false)+`,
			`+routeString("prefix", "/.well-known/pomerium/", false)+`,
			`+routeString("path", "/robots.txt", false)+`
			`+routeString("path", "/.pomerium/jwt", true)+`,
			`+routeString("path", urlutil.WebAuthnURLPath, true)+`,
			`+routeString("path", "/ping", false)+`,
			`+routeString("path", "/healthz", false)+`,
			`+routeString("path", "/.pomerium", false)+`,
			`+routeString("prefix", "/.pomerium/", false)+`,
			`+routeString("path", "/.well-known/pomerium", false)+`,
			`+routeString("prefix", "/.well-known/pomerium/", false)+`
	route := b.buildControlPlanePathRoute(options, "/hello/world", false, false)
					"disabled": true
	route := b.buildControlPlanePrefixRoute(options, "/hello/world/", false, false)
					"disabled": true
				]
				]
				]
				]
				]
				]
				]
				]
				]
				]
					]
				]
				]
				]
				]
				]
				]
	allowRule.Or = append(allowRule.Or,
		parser.Criterion{
			Name: "pomerium_routes",
		})
pomerium_routes_0 = [true, {"pomerium-route"}] {
	session := get_session(input.session.id)
	session.id != ""
	contains(input.http.url, "/.pomerium/")
}

else = [true, {"pomerium-route"}] {
	contains(input.http.url, "/.pomerium/")
	not contains(input.http.url, "/.pomerium/jwt")
	not contains(input.http.url, "/.pomerium/webauthn")
}

else = [false, {"user-unauthenticated"}] {
	contains(input.http.url, "/.pomerium/")
}

else = [false, {"non-pomerium-route"}]

	results := [pomerium_routes_0, accept_0, cors_preflight_0, authenticated_user_0, domain_0, domain_1, domain_2, domain_3, domain_4, claim_0, claim_1, claim_2, claim_3, user_0, email_0, user_1, email_1, user_2, email_2, user_3, email_3, user_4, email_4]
package criteria

import (
	"github.com/open-policy-agent/opa/ast"

	"github.com/pomerium/pomerium/internal/urlutil"
	"github.com/pomerium/pomerium/pkg/policy/generator"
	"github.com/pomerium/pomerium/pkg/policy/parser"
	"github.com/pomerium/pomerium/pkg/policy/rules"
)

type pomeriumRoutesCriterion struct {
	g *Generator
}

func (pomeriumRoutesCriterion) DataType() generator.CriterionDataType {
	return generator.CriterionDataTypeUnused
}

func (pomeriumRoutesCriterion) Name() string {
	return "pomerium_routes"
}

func (c pomeriumRoutesCriterion) GenerateRule(_ string, _ parser.Value) (*ast.Rule, []*ast.Rule, error) {
	r1 := c.g.NewRule(c.Name())
	r1.Head.Value = NewCriterionTerm(true, ReasonPomeriumRoute)
	r1.Body = ast.Body{
		ast.MustParseExpr(`session := get_session(input.session.id)`),
		ast.MustParseExpr(`session.id != ""`),
		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
	}

	r2 := c.g.NewRule(c.Name())
	r2.Head.Value = NewCriterionTerm(true, ReasonPomeriumRoute)
	r2.Body = ast.Body{
		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
		ast.MustParseExpr(`not contains(input.http.url, "/.pomerium/jwt")`),
		ast.MustParseExpr(`not contains(input.http.url, "` + urlutil.WebAuthnURLPath + `")`),
	}
	r1.Else = r2

	r3 := c.g.NewRule(c.Name())
	r3.Head.Value = NewCriterionTerm(false, ReasonUserUnauthenticated)
	r3.Body = ast.Body{
		ast.MustParseExpr(`contains(input.http.url, "/.pomerium/")`),
	}
	r2.Else = r3

	r4 := c.g.NewRule(c.Name())
	r4.Head.Value = NewCriterionTerm(false, ReasonNonPomeriumRoute)
	r3.Else = r4

	return r1, []*ast.Rule{
		rules.GetSession(),
	}, nil
}

// PomeriumRoutes returns a Criterion on that allows access to pomerium routes.
func PomeriumRoutes(generator *Generator) Criterion {
	return pomeriumRoutesCriterion{g: generator}
}

func init() {
	Register(PomeriumRoutes)
}
Fixed code: 	IsInternal bool
	Policy     *config.Policy
	HTTP       RequestHTTP
	Session    RequestSession
	eg, ctx := errgroup.WithContext(ctx)

	var policyOutput *PolicyResponse
	eg.Go(func() error {
		var err error
		if req.IsInternal {
			policyOutput, err = e.evaluateInternal(ctx, req)
		} else {
			policyOutput, err = e.evaluatePolicy(ctx, req)
		}
		return err
	})

	var headersOutput *HeadersResponse
	eg.Go(func() error {
		var err error
		headersOutput, err = e.evaluateHeaders(ctx, req)
		return err
	})

	err := eg.Wait()
	if err != nil {
		return nil, err
	}

	res := &Result{
		Allow:   policyOutput.Allow,
		Deny:    policyOutput.Deny,
		Headers: headersOutput.Headers,
		Traces:  policyOutput.Traces,
	}
	return res, nil
}

func (e *Evaluator) evaluateInternal(_ context.Context, req *Request) (*PolicyResponse, error) {
	// these endpoints require a logged-in user
	if req.HTTP.Path == "/.pomerium/webauthn" || req.HTTP.Path == "/.pomerium/jwt" {
		if req.Session.ID == "" {
			return &PolicyResponse{
				Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated),
			}, nil
		}
	}

	return &PolicyResponse{
		Allow: NewRuleResult(true, criteria.ReasonPomeriumRoute),
	}, nil
}

func (e *Evaluator) evaluatePolicy(ctx context.Context, req *Request) (*PolicyResponse, error) {
		return &PolicyResponse{
			Deny: NewRuleResult(true, criteria.ReasonRouteNotFound),
		}, nil
		return &PolicyResponse{
			Deny: NewRuleResult(true, criteria.ReasonRouteNotFound),
		}, nil
	return policyEvaluator.Evaluate(ctx, &PolicyRequest{
		HTTP:                     req.HTTP,
		Session:                  req.Session,
		IsValidClientCertificate: isValidClientCertificate,
}
func (e *Evaluator) evaluateHeaders(ctx context.Context, req *Request) (*HeadersResponse, error) {
	headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname)
	headersReq.Session = req.Session
	res, err := e.headersEvaluators.Evaluate(ctx, headersReq)
	carryOverJWTAssertion(res.Headers, req.HTTP.Headers)
	if policy != nil {
		input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
		input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
			policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
		input.Issuer = hostname
		input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
		for _, wu := range policy.To {
			input.ToAudience = "https://" + wu.URL.Hostname()
		}
		input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
		input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
			Allow:  NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized),
				Allow:  NewRuleResult(false),
				Allow:  NewRuleResult(false),
			Allow:  NewRuleResult(false, criteria.ReasonUserUnauthenticated),
	"github.com/pomerium/pomerium/config/envoyconfig"
		IsInternal: envoyconfig.ExtAuthzContextExtensionsIsInternal(in.GetAttributes().GetContextExtensions()),
	req.Policy = a.getMatchingPolicy(envoyconfig.ExtAuthzContextExtensionsRouteID(in.Attributes.GetContextExtensions()))
func (a *Authorize) getMatchingPolicy(routeID uint64) *config.Policy {
		id, _ := p.RouteID()
		if id == routeID {
		u.RawQuery = u.Query().Encode()
var tlsParams = &envoy_extensions_transport_sockets_tls_v3.TlsParameters{
	CipherSuites: []string{
		"ECDHE-ECDSA-AES256-GCM-SHA384",
		"ECDHE-RSA-AES256-GCM-SHA384",
		"ECDHE-ECDSA-AES128-GCM-SHA256",
		"ECDHE-RSA-AES128-GCM-SHA256",
		"ECDHE-ECDSA-CHACHA20-POLY1305",
		"ECDHE-RSA-CHACHA20-POLY1305",
	},
	TlsMinimumProtocolVersion: envoy_extensions_transport_sockets_tls_v3.TlsParameters_TLSv1_2,
		NormalizePath:     wrapperspb.Bool(true),
package envoyconfig

import (
	"strconv"

	envoy_extensions_filters_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
	"github.com/golang/protobuf/ptypes/any"
)

// PerFilterConfigExtAuthzName is the name of the ext authz filter to apply config to
const PerFilterConfigExtAuthzName = "envoy.filters.http.ext_authz"

// PerFilterConfigExtAuthzContextExtensions returns a per-filter config for ext authz that disables ext-authz.
func PerFilterConfigExtAuthzContextExtensions(authzContextExtensions map[string]string) *any.Any {
	return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{
			CheckSettings: &envoy_extensions_filters_http_ext_authz_v3.CheckSettings{
				ContextExtensions: authzContextExtensions,
			},
		},
	})
}

// PerFilterConfigExtAuthzDisabled returns a per-filter config for ext authz that disables ext-authz.
func PerFilterConfigExtAuthzDisabled() *any.Any {
	return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
		Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_Disabled{
			Disabled: true,
		},
	})
}

// MakeExtAuthzContextExtensions makes the ext authz context extensions.
func MakeExtAuthzContextExtensions(internal bool, routeID uint64) map[string]string {
	return map[string]string{
		"internal": strconv.FormatBool(internal),
		"route_id": strconv.FormatUint(routeID, 10),
	}
}

// ExtAuthzContextExtensionsIsInternal returns true if the context extensions indicates the route is internal.
func ExtAuthzContextExtensionsIsInternal(extAuthzContextExtensions map[string]string) bool {
	return extAuthzContextExtensions != nil && extAuthzContextExtensions["internal"] == "true"
}

// ExtAuthzContextExtensionsRouteID returns the route id for the context extensions.
func ExtAuthzContextExtensionsRouteID(extAuthzContextExtensions map[string]string) uint64 {
	if extAuthzContextExtensions == nil {
		return 0
	}
	routeID, _ := strconv.ParseUint(extAuthzContextExtensions["route_id"], 10, 64)
	return routeID
}
				To:   mustParseWeightedURLs(t, "https://www.example.com"),
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/ping", false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/healthz", false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium", false))+`,
					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.pomerium/", false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.well-known/pomerium", false))+`,
					`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.well-known/pomerium/", false))+`,
					`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/robots.txt", false))+`,
							"cluster": "route-5feb9fe8bd89aa97",
						},
						"typedPerFilterConfig": {
							"envoy.filters.http.ext_authz": {
								"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
								"checkSettings": {
									"contextExtensions": {
										"internal": "false",
										"route_id": "6911793875091303063"
									}
								}
							}
							"cluster": "route-5feb9fe8bd89aa97",
						},
						"typedPerFilterConfig": {
							"envoy.filters.http.ext_authz": {
								"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
								"checkSettings": {
									"contextExtensions": {
										"internal": "false",
										"route_id": "6911793875091303063"
									}
								}
							}
			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
			b.buildControlPlanePathRoute(options, "/ping", requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/healthz", requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/.pomerium", requireStrictTransportSecurity),
			b.buildControlPlanePrefixRoute(options, "/.pomerium/", requireStrictTransportSecurity),
			b.buildControlPlanePathRoute(options, "/.well-known/pomerium", requireStrictTransportSecurity),
			b.buildControlPlanePrefixRoute(options, "/.well-known/pomerium/", requireStrictTransportSecurity),
			routes = append(routes, b.buildControlPlanePathRoute(options, "/robots.txt", requireStrictTransportSecurity))
				b.buildControlPlanePathRoute(options, options.AuthenticateCallbackPath, requireStrictTransportSecurity),
				b.buildControlPlanePathRoute(options, "/", requireStrictTransportSecurity),
		TypedPerFilterConfig: map[string]*any.Any{
			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(true, 0)),
		},
		TypedPerFilterConfig: map[string]*any.Any{
			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(true, 0)),
		},
	routeID, err := policy.RouteID()
	if err != nil {
		return nil, err
	}

			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
		route.TypedPerFilterConfig = map[string]*any.Any{
			PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(false, routeID)),
		}
		for _, hdr := range b.reproxy.GetPolicyIDHeaders(routeID) {
	routeString := func(typ, name string) string {
			"name": "pomerium-` + typ + `-` + name + `",
			"match": {
				"` + typ + `": "` + name + `"
			},
			"responseHeadersToAdd": [
				{
					"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
					"header": {
						"key": "X-Frame-Options",
						"value": "SAMEORIGIN"
					}
				{
					"appendAction": "OVERWRITE_IF_EXISTS_OR_ADD",
					"header": {
						"key": "X-XSS-Protection",
						"value": "1; mode=block"
			],
			"route": {
				"cluster": "pomerium-control-plane-http"
			},
			"typedPerFilterConfig": {
				"envoy.filters.http.ext_authz": {
					"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
					"checkSettings": {
						"contextExtensions": {
							"internal": "true",
							"route_id": "0"
						}
			}
		}`
			`+routeString("path", "/ping")+`,
			`+routeString("path", "/healthz")+`,
			`+routeString("path", "/.pomerium")+`,
			`+routeString("prefix", "/.pomerium/")+`,
			`+routeString("path", "/.well-known/pomerium")+`,
			`+routeString("prefix", "/.well-known/pomerium/")+`,
			`+routeString("path", "/robots.txt")+`,
			`+routeString("path", "/oauth2/callback")+`,
			`+routeString("path", "/")+`
			`+routeString("path", "/ping")+`,
			`+routeString("path", "/healthz")+`,
			`+routeString("path", "/.pomerium")+`,
			`+routeString("prefix", "/.pomerium/")+`,
			`+routeString("path", "/.well-known/pomerium")+`,
			`+routeString("prefix", "/.well-known/pomerium/")+`,
			`+routeString("path", "/robots.txt")+`
			`+routeString("path", "/ping")+`,
			`+routeString("path", "/healthz")+`,
			`+routeString("path", "/.pomerium")+`,
			`+routeString("prefix", "/.pomerium/")+`,
			`+routeString("path", "/.well-known/pomerium")+`,
			`+routeString("prefix", "/.well-known/pomerium/")+`
	route := b.buildControlPlanePathRoute(options, "/hello/world", false)
					"checkSettings": {
						"contextExtensions": {
							"internal": "true",
							"route_id": "0"
						}
					}
	route := b.buildControlPlanePrefixRoute(options, "/hello/world/", false)
					"checkSettings": {
						"contextExtensions": {
							"internal": "true",
							"route_id": "0"
						}
					}
					To:              mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                   mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "11444765232398592404"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2990091139764155677"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2544588842279234006"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "10244970664102670752"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2544588842279234006"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2990091139764155677"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2990091139764155677"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "1052418080698022187"
							}
						}
					}
				}
					To:                  mustParseWeightedURLs(t, "https://authenticate.internal"),
					To:                  mustParseWeightedURLs(t, "https://to.example.com"),
					To:                  mustParseWeightedURLs(t, "https://to.example.com"),
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2226589900561460978"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "2226589900561460978"
							}
						}
					}
				}
					To:   mustParseWeightedURLs(t, "https://to.example.com"),
					],
					"typedPerFilterConfig": {
						"envoy.filters.http.ext_authz": {
							"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
							"checkSettings": {
								"contextExtensions": {
									"internal": "false",
									"route_id": "15508081512033148378"
								}
							}
						}
					}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
				],
				"typedPerFilterConfig": {
					"envoy.filters.http.ext_authz": {
						"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
						"checkSettings": {
							"contextExtensions": {
								"internal": "false",
								"route_id": "285016060542193864"
							}
						}
					}
				}
    "normalizePath": true,
	results := [accept_0, cors_preflight_0, authenticated_user_0, domain_0, domain_1, domain_2, domain_3, domain_4, claim_0, claim_1, claim_2, claim_3, user_0, email_0, user_1, email_1, user_2, email_2, user_3, email_3, user_4, email_4]