VirtualService header routing from AuthorizationPolicy

So I’m trying to set up a custom authz plugin which works with a PKI infrastructure. This plugin injects some headers which I have some VirtualServices that route to different resources based on the injected headers.

$ istioctl version
client version: 1.14.1
control plane version: 1.15.0
data plane version: 1.15.0 (8 proxies)

For the sake of example, lets say my auth plugin talks over grpc with envoyproxy v3 and injects the header X-TEST-HEADER: SUCCESS. I’m using the httpbin example, with the following mesh config.

extensionProviders:
  - name: "ext-authz-plugin"
    envoyExtAuthzGrpc:
      service: "my-plugin-svc.istio-system.svc.cluster.local"
      port: "50051"
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: httpbin
  namespace: default
spec:
  gateways:
  - istio-system/my-gateway
  hosts:
  - '*'
  http:
  - match:
    - headers:
        X-TEST-HEADER:
          regex: (?:.*?\bSUCCESS\b).*
      uri:
        prefix: /headers
    route:
    - destination:
        host: httpbin
        port:
          number: 8000

and the following gateway

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-gateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      credentialName: istio-ingress-credential
      mode: MUTUAL

I’ve tried creating an AuthorizationPolicy against the ingress gateway itself to attempt to apply a global auth.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ext-authz
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  action: CUSTOM
  provider:
    name: ext-authz-plugin
  rules:
  - {}

I’ve also tried applying that policy on a namespace basis, and workload basis for my deployment, but my logs never show an auth request being received. Instead what I see in the ingress gateway logs are

"GET /headers HTTP/2" 404 NR route_not_found - ... "

That makes sense if the plugin isn’t being called, and based on this it appears that routing happens before auth. So then I tried to remove the AuthorizationPolicy, and instead use an EnvoyFilter to inject the authz before the routing filter. This was built based on istio docs and envoy docs.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: authz-envoyfilter
  namespace: istio-system
spec:
  workloadSelector:
    labels:
      istio: ingressgateway
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      listener:
        filterChain:
          filter:
            name: envoy.filters.network.http_connection_manager
            subFilter:
              name: envoy.filters.http.router
    patch:
      operation: INSERT_FIRST
      value:
        name: envoy.filters.http.ext_authz
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
          grpc_service:
            envoy_grpc:
              cluster_name: ext-authz-plugin-filter
            timeout: 0.5s
          transport_api_version: V3
          include_peer_certificate: true
  - applyTo: CLUSTER
    patch:
      operation: ADD
      value: # cluster specification
        name: ext-authz-plugin-filter
        connect_timeout: 0.25s
        typed_extension_protocol_options:
          envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
            "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
            explicit_http_config:
              http2_protocol_options: {}
        type: LOGICAL_DNS
        lb_policy: ROUND_ROBIN
        load_assignment:
          cluster_name: ext-authz-plugin-filter
          endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    protocol: TCP
                    address: my-plugin-svc.istio-system.svc.cluster.local
                    port_value: 50051

But still, authz fires after routing with the gateway showing the same logs as above (also note that I’ve tried adding match contexts with no luck). The only way I’ve gotten this working is either converting the above EnvoyFilter to a NETWORK_FILTER, or using a nearly identical EnvoyFilter with some Lua that makes an http request to my plugin instead. I’ve also had luck with WASM plugins, but that’s not a desirable choice based on some custom auditing and monitoring.

I’d rather not use the NETWORK_FILTER, because there isn’t as much metadata (ex. x-forwarded-client-cert) and I’ll have to do a lot of refactoring. I’d rather not use the Lua filter, because I’ve run into issues with things like the content-length header being overriden and it’s just more difficult/inconvenient to manage.

I’m hoping it’s just a config issue, is there any way to achieve what I’m wanting with the flow AuthZ header injectVirtual service match?

1 Like