Preserve the source IP in Istio with HAProxy as the reverse proxy
Discover strategies for maintaining the source IP of visitors accessing your applications via the Istio Ingress Gateway Controller, especially when employing HAProxy as the reverse proxy in Kubernetes.
This is useful for situations where you want to whitelist/blacklist certain IP addresses with the Istio authorization policy.
By default, when using a reverse proxy, the X-Forwarded-For
header is lost when the request passes through the proxy. This way, Istio will recognize the source IP as the IP of the pod where the request was meant to end. This is problematic if you want to set authorization policies and you are not controlling this in the application itself.
This will also configure HAProxy in a TCP mode which will allow SSL Passthrough, which means that the SSL termination will happen at the Istio Ingress Gateway level inside the cluster.
The solution
The solution for this is to enable the proxy protocol on both HAProxy and Istio. As HAProxy explains it, the proxy protocol is designed to chain proxies or reverse proxies without losing the client information. This is what we need to solve it.
The HAProxy configuration
The HAProxy configuration is done for both :80
and :443
ports. You can replace this if your configuration is done in another way. Also, replace the IP addresses with your own. Here is the configuration, and you will place it in /etc/haproxy/haproxy.cfg
on your host machine:
|
|
Now restart the HAProxy service:
|
|
As you can see from the configuration above, we are setting the frontend
and backend
configurations for both http
and https
, and we are using the send-proxy-v2
HAProxy module which allows for the client information, including the X-Forwarded-For
header to pass through the reverse proxy. Now you have to enable it on Istio level as well so it can receive this information.
The Istio configuration
Setting the number of trusted proxies
First and foremost, you need to add a mesh configuration which lets Istio and Envoy itself know the number of proxies it can trust. We will set the number to 2
. Create a new file named topology.yaml
and paste this inside:
|
|
Save the file, and if you haven’t installed Istio yet, run the following:
|
|
If you already have Istio installed in your cluster, run the following:
|
|
You can check if the setting is applied if the meshConfig
is present in the spec
field of the installed-state
istio operator. You can get it by doing:
|
|
Setting up the proxy protocol
Istio itself doesn’t support the proxy protocol, but we can enable an Envoy Proxy filter which is configured to inspect TLS and allow the client headers to pass through the proxy with the proxy_protocol
and tls_inspector
filters.
After the number of trusted proxies is set up, create a new file named envoy-proxy-filters.yaml
and paste the following:
|
|
This will enable the proxy protocol to listen to incoming information, which will come from HAProxy. Apply this manifest:
|
|
After you have applied it, restart the istio-ingressgateway
pod in the istio-system
namespace:
|
|
Testing the setup
Now it’s time to test if the proxy protocol is working. To do so, you can enable the debug mode of the Envoy proxy logs inside the istio-ingressgateway
pod:
|
|
Now follow the logs:
|
|
Send a request to any application which is routed through the Istio Ingress Gateway:
|
|
And finally, check the logs of the istio-ingressgateway
pod to see that the X-Forwarded-For
header contains your public IP. That means that the setup is complete.
If you find this post helpful, please consider supporting the blog. Your contributions help sustain the development and sharing of great content. Your support is greatly appreciated!
Buy Me a Coffee