Istio JWT Authentication & Authorization at the edge

Jorge Reus
Globant
Published in
5 min readAug 1, 2022

--

Ever wanted to know how you can use a JWT token to authenticate & authorize requests coming from an API gateway. But found it to be confusing and the information you found was scattered, and you wanted to know how it all fits together?3
Fear not! I’ve also experienced those scenarios, and I’ve built my own playground for that, and I will walk you through the process, tips and how you can implement it in your use case.

The following scenarios will be reviewed in the article:

  • First, what is a JWT, and why should you care?
  • Setting up the playground
  • Dissection Istio’s JWT edge authentication & authorization
  • How to build an external authz service for istio

First, what is a JWT, and why should you care?

A JWT (short for JSON Web Token) is a web standard for sharing claims between two parties.
Many systems out there use JWTs, chances are that you go to your favorite website, inspect the persistent stores (local storage, cookies, session storage, etc.) you will find a JWT

They look like this

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NTM4NzU4MDUsImV4cCI6MTY4NTQxMTgwNSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.3KtBCvZAieEJvZou7-49vjcrmd4sU-RypSqlqBGm4v

They consist of 3 base 64 encoded parts, each separated by a dot (.)
1. The header
2. The payload
3. Signature

The beauty of them is that the signature is generated by an algorithm specified in the header, so that we can be sure that the token wasn’t tampered with. You can find more information in here

Enough of this JWT introduction, let’s get our hands dirty.

Setting up the playground

Requirements:

Clone the repo

Run git clone https://github.com/JorgeReus/istio-jwt

Start the playground

You just need to run task setup

Wait for a couple of minutes, and you’ll have a complete k8s playground with istio and all the required services & configuration applied.

Test JWTs

  • You can generate a valid JWT with task get-valid-jwt
  • And an invalid JWT with task get-invalid-jwt
  • Get the istio gateway ip with task get-istio-ip

All together you can run:

As you can see, with the valid JWT you will get an HTML response with a 200 response code.
With the invalid JWT, you will get the message Your role doesn’t have te required permissions with a status code 403.
Let’s break down what happened

Ok, so what happened?

First, task is a task runner (weirdly enough), this will allow us to run commands by simply specifying the task to run, the neat thing is we can set up dependencies between tasks, so by simply one command we can set up the development environment.
The tasks executed by running task setup are the following ones

  • create-cluster
    Creates a k3d cluster with 3 nodes and setups a container registry in the port 5050
  • push-all-images
    Push images of the services we will use in our environment
  • install-istio
    Installs istio via helm charts using terraform
  • configure-istio
    Configures istio gateways and setups up all custom resources required by the environment

Dissecting Istio’s JWT edge authentication & authorization

Authn (Authentication)

Istio has the concept of request authentication, which applies JWT Rules to a request which can come from a workload inside the cluster or a request coming from outside the cluster. You can check the reference for more information.

Let’s now take a look at the request authentication manifest we have defined in the repo, it’s located in terraform/ops/main.tf.

First of all you can see that we have an array of jwtRules in the spec, every jwtRules contains an issuer and a jwksUri.

An issuer maps to a field in the JWT called iss which is the “party” that created the JWT, istio will decode the JWT and compare the iss field with this one.

A jwksUri is a resolvable URL which contains a public JWT Key Set that istio uses to validate that the token was signed by a trusted private JWT key set.

Effectively, this rule states that any JWT evaluated must have the iss field with the value “my.jwt.issuer” and should be signed by any key of the private part of the keys present in http://auth-service.default.svc.cluster.local/jwk/public.
Just remember that this will create the policy but to apply if to the gateway we must use an AuthorizationPolicy

You can find the code responsible for evaluating the rules in here.

Authz (Authorization)

In istio you can configure access control to the mesh, namespace and workloads using an AuthorizationPolicy.

In this CRD we will apply the request authentication in the previous step and, we will go further by decoding the jwt and evaluate other fields.

First of all we’ll take a look at how we can write an application to do custom authorization.
Why?
Because istio’s policies for JWT authorization are static, so pulling data from a database is impossible with vanilla policies.

Istio supports a method called for using an external service to apply our custom authorization logic, useful when we want a dynamic way tomanage access controls.

To configure external authorization, we need to supply a custom mesh config

There are two protocols that istio support to communicate with your custom authz service: http & grpc, for both you need to supply a port, the hostname of the service and optionally in http the headers you want to pass from the request.

How to build an external authz service for istio

Since istio is open source, we can use the same libraries to develop the service, we’ll see a couple of snippets showing the important bits.

For grpc, istio exposes two versions of the API v2 & v3, you can implement both of them if you have different versions of istio and want this to work in any of them.

In here, we can see how to get headers from the request and process them. In this case, we’re getting the Authorization: Bearer <jwt> header, decoding the jwt and apply a custom validator function.

When using plain http, is simpler, we only need to implement a function and apply out

After the service is deployed and ready, you can use by setting a provider in the authorization policy

Effectively, with this configuration, the policy forward the request to the custom authorization service to decide if the request will be allowed or denied.

Authorization policies evaluation rules

Since we’re applying multiple policies to the same path, istio applies some internal rules to know if the request should be allowed or denied, which are the following:

  1. If there are any CUSTOM policies that match the request, evaluate and deny the request if the evaluation result is denied.
  2. If there are any DENY policies that match the request, deny the request.
  3. If there are no ALLOW policies for the workload, allow the request.
  4. If any of the ALLOW policies match the request, allow the request.
  5. Deny the request.

In this specific case, the authorization service will be called first and then request authentication policy. You can find more information here

Conclusion

In this article, we dived into how istio handles authentication & authorization using JWTs, being a widely used standard, JWT pretty important to learn, istio gives us a powerful yet easy way on applying our own rules to authn & authz several types of workloads.

Extra Content!

Wondered how to authn & authz completely serverless in AWS?
Check out this repo

--

--