Search code examples
jwtistio

Not able to get JWT header in Istio custom AuthorizationPolicy passed via outputPayloadToHeader of RequestAuthentication


We have kubernetese cluster deployed on AWS EKS with Istio 1.11.4. We are using JWT for authentication and passing it in the header x-jwt-assertion. To validate the JWT we are using Istio RequestAuthentication. Here is the definition

apiVersion: "security.istio.io/v1beta1"
kind: "RequestAuthentication"
metadata:
  name: "graphql-jwt"
  namespace: graphql
spec:
  selector:
    matchLabels:
      app: graphql
  jwtRules:
    - issuer: "https://login.<mycompany>.io/"
      jwksUri: "https://<mycompany>.us.auth0.com/.well-known/jwks.json"
      outputPayloadToHeader: "my-data"
      fromHeaders:
        - name: x-jwt-assertion
          prefix: "Bearer "

We have another service deployed in the cluster that determines based on the request headers and body whether the request should be forwarded or not. We are using Istio CUSTOM Authorization Policy for this. The definition for the AuthorizationPolicy is as following

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: ext-auth-my
spec:
    selector:
      matchLabels:
        app: graphql
  action: CUSTOM
  provider:
    name: my-ext-auth
  rules:
    - { }

The configurations for this AuthorizationPolicy are defined in the istio configmap under the istio-system namespace. The configurations are specified below

apiVersion: v1
data:
  mesh: |-
    extensionProviders:
    - name: "my-ext-auth"
      envoyExtAuthzHttp:
        service: "my-api-svc.foo.svc.cluster.local"
        port: "8080"
        pathPrefix: "/hasaccess"
        includeRequestHeadersInCheck: ["my-data", "content-type"]
        includeRequestBodyInCheck:
          maxRequestBytes: 1000000

Now in my service (my-api-svc.foo.svc.cluster.local:8080/hasaccess) I am not getting the my-data header. If I change the value of includeRequestHeadersInCheck and remove content-type, my service throws an error of invalid mime type, and if I replace my-data with x-jwt-assertion the request doesn't make it through to my service and I get Jwt issuer is not configured. So this validates that the headers are getting detected, its for some reason just not making through to my service.

The headers that make it to my service are

x-request-id
x-b3-parentspanid
x-b3-traceid
x-b3-spanid
x-forwarded-client-cert
x-b3-sampled
X-Forwarded-Proto
X-Forwarded-For
Host
Content-Length
Content-Type

I am not able to figure out why my-dataheader is not making its way to my api which is to Authorize the request. This header does reach the destination service for which the request was intended for, so it's not getting lost.


Solution

  • I was able to resolve the issue by adding my JWT token in Authorization header instead of x-jwt-assertion and using the forwardOriginalToken property in my RequestAuthentication.

    So now RequestAuthentication is

    apiVersion: "security.istio.io/v1beta1"
    kind: "RequestAuthentication"
    metadata:
      name: "graphql-jwt"
      namespace: graphql
    spec:
      selector:
        matchLabels:
          app: graphql
      jwtRules:
        - issuer: "https://login.<mycompany>.io/"
          jwksUri: "https://<mycompany>.us.auth0.com/.well-known/jwks.json"
          outputPayloadToHeader: "my-data"
          forwardOriginalToken: true
          fromHeaders:
            - name: Authorization
              prefix: "Bearer "
    

    and my istio configmap is

    apiVersion: v1
    data:
      mesh: |-
        extensionProviders:
        - name: "my-ext-auth"
          envoyExtAuthzHttp:
            service: "my-api-svc.foo.svc.cluster.local"
            port: "8080"
            pathPrefix: "/hasaccess"
            includeRequestHeadersInCheck: ["Authorization", "content-type"]
            includeRequestBodyInCheck:
              maxRequestBytes: 1000000
    

    For further details here is the link to the github discussion.