Reenviar IP real a un NGINX detrás de un load balancer GCP

Este artículo se centra en los load balancer GCP, pero puede aplicarse a otros proveedores cloud / servidores proxy.

Introducción

En Geko trabajamos en un proyecto que requería un servidor nginx para poder incluir en la lista blanca algunas direcciones IP públicas mientras negaba todas las demás conexiones. Si bien esto puede abordarse utilizando las reglas de firewall de GCP, hubo otras razones por las cuales fue necesario hacerlo a través de la configuración de nginx en lugar de usar las reglas de GCP.

El problema es que nginx no mostraba la dirección IP real correcta de la solicitud en el encabezado REMOTE_ADDR.

130.211.0.230 - - [23/Jan/2020:09:44:51 +0000] "GET / HTTP/1.1" 404 "108.26.106.168, 36.129.221.25"

Escenario

Usaré un escenario muy simple:

  • Instancia privada de Google Compute que ejecuta dos contenedores docker: nginx y una aplicación basada en php
  • Public GCP Load Balancer dirigido al contenedor nginx.

Direcciones IP falsas utilizadas en esta publicación:

  • Solicitudes de origen del usuario: 108.26.106.168
  • IP pública de GCP Load Balancer: 36.129.221.25
  • Rango privado de GCP Load Balancer: 130.211.0.0/22 and 35.191.0.0/16

Eso es.

El problema

Repasemos rápidamente cómo funciona un load balancer: cuando se recibe una solicitud de un cliente remoto, se finaliza y se emite una nueva solicitud desde el LB contra el backend, reenviando un conjunto de headers (haz clic en GCP y AWS para obtener detalles específicos) son atrapados por el servicio subyacente.

Cuando colocas un servidor nginx detrás del LB, recibe la IP privada del Load Balancer como dirección remota en lugar de la IP pública real del usuario.

Si echamos un vistazo a los registros de nginx, podemos ver esto:

130.211.0.130 - - [23/Jan/2020:09:02:51 +0000] "GET / HTTP/1.1" 200 "108.26.106.168, 36.129.221.25"

¡Espera! La dirección IP del usuario aparece en esa cadena al final del registro. ¿Por qué c*j***s está tomando nginx el rango privado de LB como la dirección IP de origen? Bueno, esto se debe a que el LB está haciendo una nueva solicitud.

¿Cómo resolvimos el problema?

La primera IP en el registro proviene del encabezado REMOTE_ADDR. Necesitamos reemplazar el valor de este header con la dirección IP real recibida en el header X-Fordered-For.

Pero hay algo más que debemos tratar con este segundo header: en realidad no solo viene con la IP real del usuario sino también con la dirección pública de Load Balancer.

Para resolver todo esto, utilizaremos el real_ip module. Vamos a aplicar la siguiente configuración en nginx.conf dentro del bloque «server»:

set_real_ip_from 36.129.221.25/32; // LB Public IP address
set_real_ip_from 130.211.0.0/22; // Private IP range for GCP Load Balancers
set_real_ip_from 35.191.0.0/16; //Private IP range for GCP Load Balancers
real_ip_header X-Forwarded-For;
real_ip_recursive on;

Vamos a dividirlo:

  • set_real_ip_from: Indicar a nginx que confíe en las IP públicas y privadas de GCP LB.
  • real_ip_header: Reemplaza el encabezado REMOTE_ADDR con los valores de X-Fordered-For.
  • real_ip_recursive on: Filtra las IPs confiables de la cadena, por lo tanto, la última dirección no confiable de la cadena se utilizará como la dirección remota.

Finalmente pruébalo:

108.26.106.168 - - [23/Jan/2020:09:44:51 +0000] "GET / HTTP/1.1" 200 "108.26.106.168, 36.129.221.25"

Conclusión

Como podemos ver, la solución fue bastante sencilla, pero aun así llevó un tiempo rebuscar en la documentación para comprender cómo Load Balancer reenviaba los encabezados y cómo podemos ajustar nginx para reescribir los encabezados para lograr el comportamiento esperado.

Sin embargo, fue una buena experiencia de aprendizaje.

Fuentes: https://nginx.org/en/docs/http/ngx_http_realip_module.html


Espero que hayas disfrutado de este post y te animo a que revises nuestro blog para leer otros posts que puedan ser de tu interés, por ejemplo «Qué es el cloud?«. No dudes en contactarnos si deseas que te ayudemos en tus proyectos.

¡Nos vemos en la próxima entrada!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *