Istio IP Whitelists

Rich Marshall
Wealth Wizards Engineering
4 min readSep 12, 2018

The Kubernetes Nginx ingress controller makes it really easy to enforce IP restrictions on a per ingress resource level. This means that for each of our services, we can provide fine-grained IP access controls. Something which is really important for us on a number of components.

As part of our process of adopting the Istio service mesh we’ve been looking to replicate this function in Istio. In the long run we’d like to migrate all ingress functions to Istio, but the IP whitelist is realy the first thing we’d like to move becuase it’s implemeneted as part of the ingress resource definitions and therefore we treat it as part of our application layer rather than infrastructure.

Unfortunatley documentation on this is very light and so having spent a good day or so trying to figure it out, having cracked it, I thought I’d share!

Unlike Kubernetes where the IP whitelist is managed as an annotation on the ingress resource and applied by the controller, Istio manages this type of behaviour as part of it’s policies and is distinct from the virtual service or gateways we have defined previously.

There are 3 parts to an Istio IP whitelist policy; a handler, an instance and finally a rule. The handler is implemented through an adapter. The instance is implemented through a template and the rule is a rule. I don’t know why there is this complication. It doesn’t make understanding it any easier. To add to the complication, one element which is described as most suitable for implementing an IP whitelist, a list is actually referred to as a listchecker and you must use this term for it in the policies. Finally, it appears the IP_ADDRESS type is broken.

So, on with the policy. This is what you need:

Handler; how should these be handled? i.e. whitelisted.

This is the list of IPs you wish to whitelist. You can easily turn this into a blacklist by switching “blacklist” to true.
the listchecker spec offers an IP_ADDRESSES type but it appears to be broken currently.

UPDATE:: Please see the comments below. Craig Skelton has reported that the docs are broken (which define IP_ADDRESS) but the add does support IP_ADDRESSES. I’ve not had a chance to validate this.

--- 
apiVersion: "config.istio.io/v1alpha2"
kind: listchecker
metadata:
name: whitelist
spec:
overrides: [ "8.8.8.8", "8.8.4.4" ]
blacklist: false
---

Instance; on what instance of the data should the handler be applied?

Here we decide what part of the object we want to apply the handler too. In this case we’re going to use the http request header “x-real-ip” which is injected by the nginx ingress controller. We could use x-forwarded-for instead, but this header often has multiple IPs in it which makes the mathing more diffifult. The is also the option of source.ip but this value is typically managed by the routing in Kubernetes (If you’ve not come across it yet, kubernetes, by default, relies on a SNAT rule on each host. This makes any request entering a container appear to come from the Kubernetes host it’s running on.) this means unless you’ve setup your Kube clusters in what is currently a failry non-typical way, then the source.ip value is of little use.

---
apiVersion: config.istio.io/v1alpha2
kind: listentry
metadata:
name: x-real
spec:
value: request.headers["x-real-ip"]
---

Rule; When do we apply this rule and what do we plan on doing

In this instance we apply the rule to any traffic which matches ingressgateway in the Istio label field. We then specify the action and reference the handler and instances in DNS format. i.e. least significant element on the left.

---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: ip-rule
spec:
match: source.labels["istio"] == "ingressgateway"
actions:
- handler: whitelist.listchecker
instances:
- x-real.listentry
---

Now apply these 3 policies to the namespace where the services reside and you should find whitelisting is applied. It actually takes a few seconds to apply, and is intermittent initially.

Havng finaly figured out how to do this, we’ve decided we’re not going to use it yet. The main reason being that the response back to the client is not great. Instead of a 403 the client recives a 404 with a message saying they don’t match the istio whitelist. Not really what we want.

There is an alternative way of applying white lists which is described here but it’s also not very elegant but it does at least provide a better response to the client in so far as it’s a 403.

apiVersion: "config.istio.io/v1alpha2"
kind: denier
metadata:
name: denyreviewsv3handler
spec:
status:
code: 7
message: Not allowed
---
apiVersion: "config.istio.io/v1alpha2"
kind: checknothing
metadata:
name: denyreviewsv3request
spec:
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: denyreviewsv3
spec:
match: source.ip != ip("192.168.0.1")
actions:
- handler: denyreviewsv3handler.denier
instances: [ denyreviewsv3request.checknothing ]

We’ll keep an eye out for imporvements in this area but it certainly feels rather immature at present.

--

--