HAProxy con SNI y diferentes configuraciones de SSL

9

Tengo HAProxy para mis dos sitios, uno público y otro privado.

www.mysite.com private.mysite.com

Atm, estoy usando haproxy como este:

frontend mysite_https
  bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3
  mode http
  acl domain_www     hdr_beg(host) -i www.
  acl domain_private hdr_beg(host) -i private.
  acl path_ghost     path_beg         /ghost/
  acl clientcert     ssl_c_used

  redirect location https://www.example.com if path_ghost !clientcert
  redirect location https://www.example.com if !domain_www !clientcert

  use_backend bknd_private if domain_private
  use_backend bknd_www     if domain_www

  default_backend bknd_www

Lo que debe hacer es solicitar un certificado de cliente (opcionalmente) y continuar. Si el dominio no es www.example.com y el visitante no puede proporcionar el certificado correcto o la ruta es / ghost / y el visitante no puede proporcionar el certificado correcto, debe redirigirse a https://www.example.com

Hasta ahora, esto funciona bien. Sin embargo, recibí quejas de los usuarios de Mac que navegan en mi sitio con Safari de que se les sigue pidiendo el certificado cuando navegan en https://www.example.com/ mientras que, por ejemplo, Firefox solo pregunta cuando navega en https: //private.example .com / o https://www.example.com/ghost/ .

Aparentemente así es como funciona Safari, así que no puedo solucionarlo. Mi idea era usar SNI para dividir entre diferentes interfaces

frontend mysite_https
  bind *.443 ssl crt /etc/mycert.pem no-sslv3

frontend private_https
  bind *.443 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3

Por supuesto que eso no funciona porque

a. No puedo tener dos frontends escuchando en el puerto 443 con una sola IP pública b. Todavía no he encontrado una manera de decir "use_frontend if domain_www" o algo así. (Solo use_backend o use-server)

También intenté hacerlo con tres servidores haproxy

frontend haproxy-sni
bind *:443 ssl crt /etc/mycert.pem no-sslv3
mode tcp

tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }

acl domain_www ssl_fc_sni_end -i www.example.com

use-server server1 haproxy-private.lan  if !domain_www
use-server server2 haproxy-public.lan   if domain_www

Esto funciona, sin embargo, el problema aquí es que haproxy-private solicita el certificado del cliente, pero la solicitud no llega al navegador. De alguna manera, haproxy-sni deja caer la solicitud.

Además, ahora tengo tres servidores haproxy que no es deseable (aunque es una opción posible si no puedo encontrar una solución mejor).

Preferiblemente me gustaría algo como esto (inventado ... no sé las opciones reales)

frontend mysite_https
  bind *.443 ssl crt /etc/mycert.pem no-sslv3
  mode http

  acl domain_www     hdr_beg(host) -i www.
  acl domain_private hdr_beg(host) -i private.
  acl path_ghost     path_beg         /ghost/

  ssl_options ca-file /etc/myca.pem verify optional if !www_domain          # made up!
  ssl_options ca-file /etc/myca.pem verify optional if !path_ghost          # made up!

  acl clientcert     ssl_c_used

  redirect location https://www.example.com if path_ghost !clientcert
  redirect location https://www.example.com if !domain_www !clientcert
  ...

Espero que alguien pueda ayudarme con esto...

mohrphium
fuente

Respuestas:

13

Encontré una solución a este problema, que no requiere servidores o servicios adicionales. Sin embargo, no estoy completamente seguro de si esto no genera nuevos problemas. Para mí parece funcionar en este momento.

La forma en que lo hice fue crear una interfaz para cada dominio que requería diferentes configuraciones de SSL. Luego configuro la opción de vinculación de esas interfaces a puertos altos (¡estos no son accesibles desde el público!).

Creé otro frontend escuchando en el puerto: 443 para dividir el tráfico basado en SNI, y configuré los servidores de backend en 127.0.0.1:high-port.

De esta manera, creé una especie de bucle en haproxy

[incoming]->[haproxy:443]->[haproxy:7000]->[www.intern.lan]
[incoming]->[haproxy:443]->[haproxy:8000]->[private.intern.lan]

Aquí está la parte de configuración.

frontend frnd_snipt                                             # Frontend_SNI-PassThrough (snipt)
  bind *:443                                                    # Do not use bind *:8443 ssl crt etc....!
  option tcplog
  mode tcp 

  tcp-request inspect-delay 5s
  tcp-request content accept if { req_ssl_hello_type 1 } 

  acl subdomain_is_www   req_ssl_sni -i www.example.com
  acl subdomain_is_www   req_ssl_sni -i example.com
  acl subdomain_is_private req_ssl_sni -i private.example.com

  use_backend bknd_snipt_private if subdomain_is_private
  use_backend bknd_snipt_www  if subdomain_is_www

backend bknd_snipt_www
  mode tcp                                              # tcp mode must match the frontend mode - already set as default in [global]
  server snipt-www 127.0.0.1:7000                       # run without "check", otherwise haproxy checks itself all the time!

backend bknd_snipt_private
  mode tcp     
  server snipt-private 127.0.0.1:8000                   # also, don't add "ssl" when in tcp mode. "ssl" is an http mode option (result in "NO-SRV" when set in tcp)

##### NORMAL HAPROXY PART #####
frontend www_example_com                                # this frontend can be in tcp or http mode...
  bind *:7000 ssl crt /etc/mycert.pem no-sslv3          # www. frontend with normal https
  mode http
  option httplog


frontend private_example_com
  bind *:8000 ssl crt /etc/mycert.pem ca-file /etc/myca.pem verify optional no-sslv3        # private. frontend with client certificate request.
  mode http
  option httplog
  ... # whatever you have in your frontend

Si alguien tiene pensamientos sobre esto, o alguna idea de por qué podría ser una mala idea, hágamelo saber. Funciona, pero me pregunto por qué use_frontend no es una opción. Quizás porque es algo que no debe hacerse por cualquier motivo.

mohrphium
fuente
Buena idea. Tampoco pude encontrar documentación sobre esta configuración. ¿El rendimiento es similar con este bucle HAProxy?
JB. Con monica.
Sry, no sé qué tan eficiente es porque A: no lo usó por mucho tiempo (debido a los filtros IP de origen), B: no tengo un sitio de alto tráfico, donde la optimización del rendimiento sería más interesante ...
mohrphium
Acabo de poner apache2 frente a haproxy, que funciona, pero es un poco estúpido porque el punto único de falla frente al clúster de hapeoxy y (creo) el cuello de botella de rendimiento (creo que hap es más rápido que ap2, no obtuve datos reales sobre eso aunque.)
mohrphium
3

Las versiones recientes de haproxy admiten una configuración llamada crt-listque le permite especificar diferentes configuraciones de TLS en función del certificado coincidente

puedes usarlo así:

haproxy.conf:

frontend https
    mode http
    bind *:443 ssl crt-list /etc/haproxy/crt-list.conf ca-file ca.pem

    use_backend test if { ssl_fc_sni -i test.area.example.org }
    use_backend private if { ssl_fc_sni -i private.example.org }
    default_backend www

crt-list.conf:

www.pem [verify none]
www.pem [verify required] *.area.example.org
private.pem [verify required]

Más información: https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#5.1-crt-list

nota sobre seguridad: siempre haga coincidir sus nombres de host (confidenciales) con SNI ssl_fc_sni, no el nombre de host HTTP. De lo contrario, un atacante podría omitir la autenticación de su certificado de cliente enviando el SNI de TLS www.example.orgpero estableciendo el nombre de host HTTP en private.example.org!

freaker
fuente
OP está utilizando el mismo certificado para ambos. La pregunta era más sobre un ca-fileentorno diferente .
gre_gor