Preserve the source IP in Istio with NGINX as the reverse proxy
Learn the techniques to retain the source IP for external users of applications under the Istio Ingress Gateway Controller, specifically with NGINX as your reverse proxy in a Kubernetes setting.
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 NGINX 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 NGINX and Istio. As NGINX 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 NGINX configuration
The NGINX 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.
First, add this snippet to /etc/nginx/nginx.conf
:
|
|
For the :80
port, we will not create a stream, but a simple http server configuration. You can change it to a stream if you want, I did this because I didn’t want the :80
port to be configured as a stream. Create a new file istio.conf
inside /etc/nginx/sites-enabled
and add the following:
|
|
Then, create the streams
directory:
|
|
Create the file istio.conf
inside /etc/nginx/streams
to configure the :443
port and add the following to it:
|
|
As you can see from the configuration above, we are setting the proxy_protocol
NGINX module to on
for both :80
and :443
, 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 NGINX. 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