Search code examples
istioistio-gateway

Istio: AuthorizationPolicy executed before RequestAuthentication?


I'd like to understand in which order RequestAuthentications and AuthorizationPolicies are executed for an istio-ingressgateway.

In a PoC, I'm defining the following RequestAuthentication and AuthorizationPolicy for the istio-ingressgateway, where the AuthorizationPolicy uses the CUSTOM action (external authorizer):

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata: 
  name: ingress-reguest-authentication
  namespace: istio-system
spec: 
  jwtRules: 
    - 
      issuer: "https://<my-issuer>"
      jwksUri: "https://<my-issuer>/oauth2/jwks"
      forwardOriginalToken: true
      outputPayloadToHeader: x-forwarded-jwt-payload
  selector:
    matchLabels:
      istio: ingressgateway
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ingress-ext-authz
  namespace: istio-system
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  action: CUSTOM
  provider:
    name: ext-authz-http
  rules: [{}]

With the RequestPolicy I'd like to let Istio handle the basic token validation before the external authorizer performs additional checks, for which Istio doesn't provide configuration (in my case I want to verify that the mTLS client certificate and JWT token for an incoming request match according to RFC 8705). Since I don't want to validate the token again in the external authorizer, I'm using the outputPayloadToHeader to forward the payload of the validated JWT.

However, to my surprise, it looks like the external authorizer is actually called before the request authentication happens:

  • The x-forwarded-jwt-payload header defined in the RequestAuthentication is not arriving at the external authorizer (I ended up using the Authorization header instead to extract the JWT payload myself).
  • A request with an expired access token that also didn't match the used client cert resulted in a deny response from the external authorizer instead of the expired token being rejected by the request authentication before.

This is not what I had expected, given that ALLOW/DENY actions for authorization policies support rules which inspect JWT claims, which I assumed would be evaluated for an already validated JWT.

Is this the order in which Istio processes RequestAuthentication and AuthorizationPolicy in general or is it specific to the CUSTOM action?

Or is there simply something wrong in my configuration?


Solution

  • In the end, what helped to get the order right was configuring the external authorizer through an envoy filter that is plugged into the http_connection_manager filter chain right before http router filter.

    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      name: ingress-ext-authz
      namespace: istio-system
    spec:
      workloadSelector:
        labels:
          istio: ingressgateway
      configPatches:
      - applyTo: HTTP_FILTER
        match:
          context: GATEWAY
          listener:
            filterChain:
              filter:
                name: envoy.filters.network.http_connection_manager
                subFilter:
                  name: envoy.filters.http.router
        patch:
          operation: INSERT_BEFORE
          value:
            name: envoy.filters.http.ext_authz
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
              failureModeAllow: false
              httpService:
                serverUri:
                  cluster: outbound|8000||ext-authz.default.svc.cluster.local
                  timeout: 1s
                  uri: http://ext-authz.default.svc.cluster.local
                authorizationRequest:
                  allowedHeaders:
                    patterns:
                      - exact: x-forwarded-jwt-payload
                      - exact: x-forwarded-client-cert