Authentication and Authorization with Istio 1.6

mattk
4 min readNov 2, 2020

After spending a considerable amount of time trying to piece together a combination of outdated advice on various forums with Istio and Envoy documentation I decided to put together everything I learned from this experience into one place.

This was our journey to leverage Envoy ext_authz filters for authentication and authorization to the cluster.

Prerequisites

  • Kubernetes 1.17 cluster
  • Istio 1.6 used for ingress and service mesh
  • APIs that are exposed to the internet and need to be authenticated and authorized at ingress because you want to remove authorization from the responsibilities of the back-end services

Using Istio

The most basic functionality needed for an effective API gateway is:

  • routing specific paths to different services running in the cluster
  • doing URL re-write in case the URL path differs between external API and the the service the request is being routed to
  • performing authentication and authorization
  • performing rate limiting (not discussed here)

As it happens, all of this can be accomplished with Istio ingress and some Envoy filters. And while there are some examples of doing this, we were not able to find any that worked with Istio 1.6 specifically.

Routing API requests

This is pretty straight forward and is actually very well documented. Here is a sample Istio VirtualService using the bookfino example:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
namespace: bookinfo
spec:
hosts:
- "api.bookinfo.demo.app"
gateways:
- istio-system/istio-gateway
http:
- match:
- uri:
prefix: "/api/v1/products"
route:
- destination:
host: productpage
port:
number: 9080

In this file we are creating a new VirtualService in the bookinfo namespace where our services are running. It will expose the APIs on the https://api.bookinfo.demo.app endpoint using an existing Istio ingress gateway (in this example called istio-gateway). It will only accept requests starting with /api/v1/products and route the requests to the destination service productpage on port 9080.

Re-writing request URL

Next we need to satisfy the requirement for rewriting URLs. In this scenario we need to allow 2 additional URL paths to route to the same API endpoint in the service

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
namespace: bookinfo
spec:
hosts:
- "api.bookinfo.demo.app"
gateways:
- istio-system/istio-gateway
http:
- match:
- uri:
prefix: "/api/products"
- uri:
prefix: "/api/v1/products"
- uri:
prefix: "/api/v2/products"
rewrite:
uri: "/api/v1/products"
route:
- destination:
host: productpage
port:
number: 9080

The new lines that were added will match 2 additional URL paths starting with either /api/products or /api/v2/products as well as the original /api/v1/products and rewrite all to /api/v1/products because that is still the URL that our productpage service is expecting.

Creating Envoy filter

Now that we have our API routing figured out, we need to make sure that it’s adequately secured. To accomplish this we will use Envoy ExtAuthz filter. It will look something like this:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: authn-filter
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
failure_mode_allow: false
http_service:
server_uri:
uri: "http://auth.default.svc.cluster.local:8000"
cluster: "outbound|8000||auth.default.svc.cluster.local"
timeout: 3s

Lets break it down. We are going to create this EnvoyFilter in the istio-system namespace and target a specific workload with a label istio: ingressgateway. As the label suggests, this will target Istio's ingress gateway pods. Next we are going to patch HTTP_FILTER configuration. We will insert our new filter before envoy.router sub filter under the envoy.http_connection_manager filter. Our filter is http but a gRPC version is also available. Finally we are going to specify our authorization server location with the URI and cluster index (in this case are using a service named auth in the default namespace).

So what will this do when a request comes into the cluster? This will take the request and forward the header to the auth service. What it expects in response is HTTP 200 status code if the request is allowed to proceed or any other response if the request needs to get rejected.

Creating Exceptions

The Envoy filter that was created above will now apply to all of the traffic coming through the ingress gateway. But what if we have other workloads (or even specific end-points) in the cluster that need to bypass authentication / authorization. We do that by creating exceptions.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: bypass-auth-filter
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: VIRTUAL_HOST
match:
routeConfiguration:
vhost:
name: "unsecured.demo.app:443"
patch:
operation: MERGE
value:
per_filter_config:
envoy.ext_authz:
disabled: true
- applyTo: HTTP_ROUTE
match:
routeConfiguration:
vhost:
name: "api.bookinfo.demo.app:443"
route:
name: "ignore-authz-filter"
patch:
operation: MERGE
value:
per_filter_config:
envoy.ext_authz:
disabled: true

In this example we create another EnvoyFilter following the same pattern as the first one. It will also target Istio's ingress gateway and will patch the configuration to accomplish the following:

  • Disable envoy.ext_authz filter for the unsecured.demo.app:443 virtual host
  • Disable envoy.ext_authz filter for a route named ignore-authz-filter in the api.bookinfo.demo.app:443 virtual host

In order to take advantage of the named route we need to modify our VirtualService and add a name: ignore-authz-filter to the appropriate route

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
namespace: bookinfo
spec:
hosts:
- "api.bookinfo.demo.app"
gateways:
- istio-system/istio-gateway
http:
- match:
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
- match:
- uri:
prefix: /api/unsecured
name: ignore-authz-filter
route:
- destination:
host: productpage
port:
number: 9080

Viewing current Envoy configuration

In order to make sure desired configuration has been applied to the Envoy proxy on the Istio ingress gateway we can take advantage of the Istio dashboards. You can get the name of your Istio ingress gateway pod by running the following command

kubectl get pods -n istio-system -l istio=ingressgateway

Then you can use that pod name to launch Envoy dashboard

istioctl dashboard envoy <istio-ingressgateway-pod-name> -n istio-system

--

--