HAProxy avec SNI et différents paramètres SSL

9

J'ai HAProxy pour mes deux sites, l'un public et l'autre privé.

www.mysite.com private.mysite.com

Atm, j'utilise haproxy comme ceci:

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

Ce que cela devrait faire, c'est demander un certificat client (facultatif) et continuer. Si le domaine n'est pas www.example.com et que le visiteur ne peut pas fournir le bon certificat ou que le chemin est / ghost / et que le visiteur ne peut pas fournir le bon certificat, il doit être redirigé vers https://www.example.com

Jusqu'à présent, cela fonctionne bien. Cependant, j'ai reçu des plaintes d'utilisateurs de Mac naviguant sur mon site avec Safari, qui leur demandaient constamment le certificat lorsqu'ils naviguaient sur https://www.example.com/ alors que, par exemple, Firefox ne demandait que lors de la navigation sur https: //private.example .com / ou https://www.example.com/ghost/ .

Apparemment, c'est comme ça que Safari fonctionne, donc je ne peux pas résoudre ce problème. Mon idée était d'utiliser SNI pour diviser les différents frontends

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

Bien sûr, cela ne fonctionne pas car

une. Je ne peux pas avoir deux frontends écoutant sur le port 443 avec une seule IP publique b. Je n'ai pas encore trouvé un moyen de dire "use_frontend if domain_www" ou quelque chose comme ça. (Uniquement use_backend ou use-server)

J'ai également essayé de le faire avec trois serveurs 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

Cela fonctionne, le problème ici est cependant que haproxy-private demande le certificat client, mais la demande n'atteint pas le navigateur. D'une manière ou d'une autre, haproxy-sni abandonne la demande.

De plus, j'ai maintenant trois serveurs haproxy ce qui n'est pas souhaitable (bien qu'une option possible si je ne peux pas trouver une meilleure solution).

De préférence, je voudrais quelque chose comme ça (composé .. ne sais pas les vraies options)

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
  ...

J'espère que quelqu'un peut me aider...

mohrphium
la source

Réponses:

13

J'ai trouvé une solution à ce problème, qui ne nécessite pas de serveurs ou de services supplémentaires. Je ne suis pas tout à fait sûr que cela n'engendre pas de nouveaux problèmes. Pour moi, cela semble fonctionner en ce moment.

La façon dont je l'ai fait était de créer un frontend pour chaque domaine nécessitant des paramètres SSL différents. J'ai ensuite défini l'option de liaison de ces frontends sur des ports élevés (ceux-ci ne sont pas accessibles depuis le public!).

J'ai créé une autre écoute frontale sur le port: 443 pour diviser le trafic en fonction de SNI, et définir les serveurs backend sur 127.0.0.1:high-port.

De cette façon, j'ai créé une sorte de boucle dans haproxy

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

Voici la partie config.

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 quelqu'un a des idées à ce sujet, ou si vous savez pourquoi cela pourrait être une mauvaise idée, faites-le moi savoir. Cela fonctionne, mais je me demande pourquoi use_frontend n'est pas une option. Peut-être parce que c'est quelque chose qui ne devrait pas être fait pour quelque raison que ce soit.

mohrphium
la source
Bonne idée. Je n'ai pas non plus pu trouver de documentation sur cette configuration. Les performances sont-elles similaires avec cette boucle HAProxy?
JB. Avec Monica.
Sry, je ne sais pas à quel point c'est performant parce que A: ne l'utilise pas depuis longtemps (à cause des filtres ip source), B: n'ai pas de site à fort trafic, où l'optimisation des performances serait plus intéressante ...
mohrphium
Je mets juste apache2 devant haproxy, ce qui fonctionne mais est en quelque sorte stupide car un point de défaillance unique devant le cluster hapeoxy et (je pense) un goulot d'étranglement des performances (je pense que hap est plus rapide qu'ap2, je n'ai pas de données réelles sur cela cependant.)
mohrphium
3

les versions récentes de haproxy prennent en charge un paramètre appelé crt-listqui vous permet de spécifier différents paramètres TLS en fonction du certificat correspondant

vous pouvez l'utiliser comme ceci:

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]

plus d'informations: https://cbonte.github.io/haproxy-dconv/1.9/configuration.html#5.1-crt-list

remarque sur la sécurité: toujours faire correspondre vos noms d'hôtes (sensibles) avec SNI ssl_fc_sni, pas le nom d'hôte HTTP. Sinon, un attaquant pourrait éventuellement contourner l'authentification de votre client en envoyant le TLS SNI de www.example.orgmais définissez le nom d'hôte HTTP sur private.example.org!

freaker
la source
OP utilise le même certificat pour les deux. La question portait davantage sur un ca-filecadre différent .
gre_gor