¿Cómo puedo usar HAproxy con SSL y obtener encabezados X-Forward-For Y decirle a PHP que SSL está en uso?

20

Tengo la siguiente configuración:

(internet) ---> [  pfSense Box  ]    /-> [ Apache / PHP server ]
                [running HAproxy] --+--> [ Apache / PHP server ]
                                    +--> [ Apache / PHP server ]
                                     \-> [ Apache / PHP server ]

Para las solicitudes HTTP, esto funciona muy bien , las solicitudes se distribuyen a mis servidores Apache muy bien. Para las solicitudes SSL, tuve HAproxy distribuyendo las solicitudes usando el equilibrio de carga TCP, y funcionó sin embargo, ya que HAproxy no actuó como un proxy, no agregó el X-Forwarded-Forencabezado HTTP, y los servidores Apache / PHP no conocían el cliente dirección IP real

Entonces, agregué stunnelfrente a HAproxy, leyendo que stunnel podría agregar el X-Forwarded-Forencabezado HTTP. Sin embargo, el paquete que pude instalar en pfSense no agrega este encabezado ... además, esto aparentemente mata mi capacidad de usar las solicitudes KeepAlive , que realmente me gustaría conservar. Pero el mayor problema que mató esa idea fue que el aturdimiento convirtió las solicitudes HTTPS en solicitudes HTTP simples, por lo que PHP no sabía que SSL estaba habilitado e intentó redirigir al sitio SSL.

¿Cómo puedo usar HAproxy para equilibrar la carga en varios servidores SSL, permitiendo que esos servidores conozcan la dirección IP del cliente y sepan que SSL está en uso? Y si es posible, ¿cómo puedo hacerlo en mi servidor pfSense?

¿O debería dejar todo esto y simplemente usar nginx?

Josh
fuente
3
Re: aturdimiento y X-Forwarded-For, mira aquí .
Shane Madden
@ Shane: Gracias. Ahí es exactamente donde leí que pierdo KeepAlive :-)
Josh
2
+1 para una excelente diagramación ASCII. :-)
KyleFarris
@AlanHamlett, su enlace es 404.
luckydonald
@luckydonald gracias, aquí hay un enlace actualizado. Puede usar el protocolo proxy agregando la palabra clave send-proxy a su configuración de haproxy. Escribí una publicación de blog con ejemplos aquí: wakatime.com/blog/23-how-to-scale-ssl-with-haproxy-and-nginx
Alan Hamlett el

Respuestas:

17

No necesita soltarlo todo, simplemente puede usar nginx frente a haproxy para soporte SSL, manteniendo toda su configuración de equilibrio de carga. Ni siquiera necesita usar nginx para HTTP si no lo desea. Nginx puede pasar tanto X-Fordered-For como un encabezado personalizado que indica que SSL está en uso (y la información del certificado del cliente si lo desea). Fragmento de configuración de Nginx que envía la información requerida:

proxy_set_header SCHEME $scheme;      # http/https
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header CLIENT_CERT $ssl_client_raw_cert;
Ochoto
fuente
37

Solo para el registro, ya que a menudo se hace referencia a este hilo con respecto a HAProxy + SSL, HAProxy admite SSL nativo en ambos lados desde 1.5-dev12. Entonces, tener X-Fordered-For, HTTP keep-alive y un encabezado que le dice al servidor que la conexión se realizó a través de SSL es tan simple como lo siguiente:

listen front
    bind :80
    bind :443 ssl crt /etc/haproxy/haproxy.pem
    mode http
    option http-server-close
    option forwardfor
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    server srv1 1.1.1.1:80 check ...
    ...

Estoy seguro de que para cuando se te ocurra algo diferente, pero al menos los nuevos visitantes obtendrán la solución fácil ahora :-)

Willy Tarreau
fuente
Gracias, esta es una buena información general ... mi pregunta era sobre HAproxy ejecutándose en pfSense, así que por ahora necesito usar nginx frente a HAproxy, ya que pfSense no es compatible con esta versión de HAProxy (todavía)
Josh
Lo siento, Josh, no sé lo suficiente sobre pfSense para saber si puedes actualizar componentes en él o no, y dado que hablabas de instalar un paquete, creí que era el caso. La última vez que lo intenté fue hace unos 5 años, así que no recuerdo todos los detalles.
Willy Tarreau
1
No entiendo mucho sobre la configuración de haproxy por ahora, pero con la última versión, tuve que agregar un acl: acl is-ssl dst_port 443y reescribir una línea: reqadd X-Forwarded-Proto:\ https if is-sslNginx parece manejar este encabezado bastante bien
greg0ire
Esto funcionó a las mil maravillas. No se requiere nginx.
Jay Taylor
1
@ greg0ire eso es porque con el último haproxy no hay is_ssl sino ssl_fc en su lugar
josch
12

Para cualquiera que encuentre esta pregunta, seguí el consejo de Ochoto y usé nginx. Estos son los pasos específicos que utilicé para que esto funcione en mi enrutador pfSense :

  1. Utilizando la interfaz web de pfsense, instalé el paquete pfsense PfJailctl y el paquete "jail_template" en Sistema> Paquetes para poder crear una cárcel de FreeBSD para compilar e instalar nginx en el sistema pfsense.

  2. Configuré una cárcel para mi servidor nginx en Servicios> Cárceles , dándole a la nueva cárcel el mismo nombre de host y dirección IP del alias de IP virtual en el que tenía ejecutado HAproxy. Até la cárcel a la interfaz WAN. Usé la plantilla de cárcel predeterminada y habilité unionfs en lugar de nullfs.

  3. Una vez que se inició la cárcel, ingresé a la caja de pfsense y corrí jlsa buscar el número de la cárcel. Luego corrí jexec 1 sha buscar un proyectil dentro de la cárcel. Desde allí configuré puertos BSD e instalé nginx usando:

    portsnap extract
    portsnap fetch update
    cd /usr/ports/www/nginx
    make install clean
    
  4. Luego configuré nginx para escuchar en el puerto 443 y pasar todas las solicitudes a HAproxy en el puerto 80, incluida la IP real y el estado SSL dentro de los encabezados HTTP. Mi se usr/local/etc/nginx/nginx.confparece a:

    worker_processes  1;
    
    events {
        worker_connections  2048;
    }
    
    http {
        upstream haproxy {
            server 209.59.186.35:80;
        }
    
        server {
            listen       443;
            server_name  my.host.name default_server;
            ssl                  on;
            ssl_certificate      my.crt;
            ssl_certificate_key  my.key;
            ssl_session_timeout  5m;
    
            ssl_protocols  SSLv3 TLSv1;
            ssl_ciphers  HIGH:!aNULL:!MD5;
            ssl_prefer_server_ciphers   on;
    
            location / {
                proxy_pass http://haproxy;
    
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
                proxy_set_header X-Forwarded-Proto https;
            }
        }
    
    }
    
  5. Luego modifiqué mi aplicación PHP para detectar el X-Forwarded-Protoencabezado HTTP:

    function usingSSL()
    {
        return (
           (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' )
            || (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
                   && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https' ));
    }
    

Entonces la configuración final es:

(internet) ---> [ -> nginx -> haproxy -]--> (pool of apache servers)
                [    (pfSense server)  ]
Josh
fuente
2
Debe deshabilitar SSLv2 a menos que realmente lo necesite. gnu.org/software/gnutls/manual/html_node/… No sé por qué Nginx todavía lo admite en su configuración predeterminada.
Ochoto
También tenga en cuenta que con 1024 conexiones de trabajo admitirá como máximo 512 clientes simultáneos.
Ochoto
@Ochoto: ¡Gracias por ambos consejos! Soy nuevo en HAproxy pero aún menos familiarizado con nignx ...
Josh
7

Mi configuración para una versión 1.5-dev-17 de haproxy:

global
        log 127.0.0.1   local0
        log 127.0.0.1   local1 notice
        #log loghost    local0 info
        maxconn 4096
        #chroot /usr/share/haproxy
        user haproxy
        group haproxy
        daemon
        #debug
        #quiet

defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        option  http-server-close
        retries 3
        option redispatch
        fullconn 1000        
        maxconn 1000
        timeout queue 600s
        timeout connect 5s
        timeout client 600s
        timeout server 600s

frontend http-in
        bind *:80
        bind *:443 ssl crt /usr/local/etc/ssl/certs
        reqadd X-Forwarded-Proto:\ https if { ssl_fc }
        default_backend varnish-ha
        option forwardfor
backend varnish-ha
  server hafront1 10.1.69.1:6081  minconn 100 maxqueue 10000

Utiliza la ssl_fcACL. Tenga en cuenta que la option http-server-closeparte es muy importante.

greg0ire
fuente
¡Gracias! Estoy ejecutando HAProxy v1.4, así que no creo que pueda hacer esto, pero podría ayudar a otros.
Josh
Sí, y el 1.5 debería salir pronto.
greg0ire
5

HAProxy no puede alcanzar un backend SSL sin usar el modo TCP sin procesar y perder X-Forwarded-For, pero podría volver a cifrar el tráfico con un stunnel de escucha para el tránsito del backend. Feo, sin embargo.

Me gusta más el enfoque de Ochoto, con una advertencia: nginx es un equilibrador de carga perfectamente capaz; si lo está usando, yo diría que lo use para todo. Proxy sus HTTPS entrantes para cargar backends HTTPS equilibrados, y de esa manera, no hay necesidad de encabezados personalizados para la información SSL (a menos que necesite el certificado del cliente).

Shane Madden
fuente
No estoy seguro de por qué me aferro a HAproxy. Creo que es porque pfSense tiene un paquete y SOIS lo usa. Ninguno de los dos es una gran razón. :-)
Josh
Me estoy desviando de nginx con un equilibrador de carga capaz, a menos que use el módulo no estándar upstream_fair, hace un simple round robin (o hash de ip del cliente) sin tener en cuenta si el backend de destino ya está ocupado con solicitudes y, por lo tanto, aumenta la cola en ese backend cuando Hay otros backends gratis y esperando un trabajo. HAProxy también monitorea muy bien los backends y muestra estadísticas sobre ellos.
Ochoto
Si solo uno de los siguientes se hiciera realidad a) Nginx obtiene un seguimiento decente del estado y un equilibrio de carga justo b) HAProxy obtiene un soporte SSL decente Uno solo puede esperar
Yavor Shahpasov
Acabo de implementar una configuración usando nginx -> haproxy -> nginx -> backend para SSL, esto se debe a la falta de soporte HTTPS en haproxy como se discutió aquí, pero también porque nginx no admite scripts de comprobación de estado http.
Geoffrey
2

El año pasado implementé una solución para integrar HAProxy con pfSense de manera que aproveche todas las características de HAProxy y mantenga un buen aislamiento con pfSense. Para que sea una opción viable para entornos de producción . SSL termina en HAProxy . Instalé HAProxy dentro de una cárcel en pfSense usando ezjail y Ports Collection . De esa manera es muy fácil mantener ambos componentes de forma independiente. Y puede instalar la versión que desee. Empecé con 1.5-dev13. Y desde entonces está funcionando perfectamente para mí. He documentado todo el asunto aquí.

Instalar HAProxy en pfSense

Por cierto, Willy, muchas gracias por un producto tan excelente.

Dinesh Sharma
fuente