PROJET AUTOBLOG


Le blog de Seboss666

Site original : Le blog de Seboss666

⇐ retour index

Haproxy : OpenVPN ou SSH ?

vendredi 17 avril 2020 à 18:30

Je n’utilise pas du tout Haproxy à titre personnel, mes contacts n’ont été que professionnels, et principalement pour du load-balancing HTTP/HTTPS. Mais il est tout à fait possible de s’en servir pour d’autres usages, en mode TCP, c’est à dire en mode « brut ». Et là, ça permet de faire des choses de tarés, et l’astuce d’aujourd’hui en est une, partager le port 443 pour faire soit du SSH, soit de l’OpenVPN. Z’êtes prêts ?

Le concept

Oui parce que l’idée ne sort pas de nulle part non plus. Sur le papier déjà, il faut savoir que n’importe quel port peut être utilisé pour n’importe quel protocole. Ça n’empêche pas les RFC de définir des ports par défaut, qui sont la plupart du temps respectés, et qu’on doit laisser passer pour faire un peu de Web et d’Internet, sinon ça n’aurait pas grand intérêt : 53 pour le DNS, 80 et 443 pour HTTP et HTTPS, 22 pour SSH, vous en connaissez surement plusieurs dans ce cas.

Mais dans la pratique, et dans beaucoup de réseaux publics notamment, mais aussi des réseaux d’entreprise, avoir droit à autre chose que du 80 et du 443, c’est pas toujours évident (oui parfois même le DNS public est bloqué, et on doit passer par le point d’accès pour ça –quelle horreur…). Et il n’y a rien de plus frustrant de se retrouver coincé sur un réseau qui n’autorise pas à faire de l’Internet, seulement du Web. Et pour ceux qui ont du mal, prenez quelques instants pour me relire ce billet, et reviendez après.

Donc, pour faire de l’internet sur les réseaux qui ne veulent pas, il est intéressant de faire croire qu’on va faire du Web, soit port 443 en mode TCP, mais d’y héberger notre service qui n’est pas du Web. Le problème qui survient très vite dans ce cas, c’est qu’en théorie, on ne peut exposer qu’un seul service à la fois sur un port donné. C’est là qu’Haproxy va nous aider.

La trousse à outils du load-balancing, et bien plus

J’ai déjà eu l’occasion de parler d’Haproxy par le passé, mais c’était à chaque fois pour évoquer des usages Web. Et il le fait très bien, c’est ce qu’on utilise en général sur les pare-feux « netfilter » qu’on déploie sur des plateformes VMware entre autres, mais c’est aussi le moteur qui est exploité au sein d’NSX Edge, la passerelle virtuelle de VMware pour ses solutions de clustering. À part que dans ce cas, on vous colle devant une interface aussi merdique que possible, où appliquer une configuration relève du miracle tellement tout est contre-intuitif. À tel point que l’astuce que je vous présente aujourd’hui n’est peut-être pas applicable sur NSX, alors que la technologie sous-jacente le permet, c’est dire.

Tout ça pour dire que l’application de load-balancing sait en faire beaucoup plus, notamment grâce au mode TCP, qui permet de traiter les paquets de manière un peu moins spécifique. Encore que là, on va le voir, il est possible d’appliquer des règles en fonction de l’analyse du contenus des paquets. C’est pas du DPI encore à ce niveau, on cherche pas à déchiffrer le contenu, juste les entêtes pour agir en conséquence.

Architecture

L’exemple d’aujourd’hui se base sur une configuration qui peut être simple, à savoir que tout tient sur le même serveur, mais ce n’est pas obligatoire. On a donc trois services, paramétrés de cette manière :

Sur la machine, seul le port 443 est ouvert au monde évidemment, ce qui permet de brouiller les pistes. Celui qui va vouloir scanner les ports pensera tomber sur un serveur web, et aura quelques difficultés. Et le comportement sera un peu bizarre s’il tente une connexion HTTPS classique, au mieux la session TLS va quand même démarrer (sauf si la connexion VPN repose sur un certificat client), au pire il aura une réponse étrange voire une simple déconnexion (RST).

Comme j’ai dit, c’est pour l’exemple, les services en question peuvent tout à fait se trouver sur d’autres machines sur un réseau privé par exemple. Et je ne vais pas plus que ça détailler la configuration de ces services, c’est très facile de trouver des ressources même en français.

Alors oui, je préviens, je déconseille du coup d’utiliser cette configuration pour ajouter aussi le support d’un serveur web. Déjà parce que la connexion TLS est déjà filtrée pour la partie OpenVPN, donc il faudrait encore creuser pour qualifier la différence entre les deux, mais surtout parce qu’en mode TCP, haproxy va certes renvoyer les paquets vers le serveur web, mais du coup ce dernier verra l’adresse IP d’haproxy en tant que source, et plus l’ip du client. Et comme haproxy ne fait pas la terminaison, il n’y a pas d’ajout d’entête HTTP X-Forwarded-For puisque le protocole est toujours chiffré à ce moment-là. Il y a potentiellement moyen de s’en sortir avec le TPROXY, mais ça sort du cadre de l’expérimentation. Vous êtes prévenu, attaquons les choses sérieuses.

La configuration

Pour la mise en place, ça va « simplement » reposer sur un frontend, celui qui écoutera sur le port 443, et deux backends, un pour ssh, un pour openvpn. Là encore, pas la peine de s’attarder sur la configuration des backends, car toute la beauté du fonctionnement réside dans le frontend, et de plusieurs concepts, dont les acl et l’analyse de contenus des paquets. On va y aller étape par étape avec une explication à chaque fois.

On commence par une détection d’une connexion TLS :

frontend 443
  tcp-request inspect-delay 15s
  tcp-request content accept if { req.ssl_hello_type 1 }

tcp-request permet de spécifier dans quel mode on bosse, et surtout comment on analyse les paquets (TCP et pas HTTPS directement), ici on laisse passer les paquets TLS de type « client hello » (type 1, si vous voulez laisser passer un paquet entrant de type « server hello » — c’est bizarre mais passons –, c’est type 2), et de ce que j’ai compris le délai permet d’éviter de confondre OpenVPN et HTTPS. Cette première étape est nécessaire pour pouvoir ensuite filter les paquets OpenVPN spécifiquement :

acl acl_proto_openvpn payload(0,2) -m bin 003c
  tcp-request content accept if acl_proto_openvpn

On retrouve ici la déclaration d’une ACL, comme on l’a fait par le passé, mais cette fois, le critère est assez velu. On analyse le contenu brut d’un paquet pour faire correspondre une séquence binaire au début du paquet. En effet, la charge d’un paquet TCP commence par un code qui correspond au type de protocole, 003c correspondant donc ici à OpenVPN.

Je vous l’ai dit, c’est velu. Fort heureusement, la détection d’OpenSSH est plus simple. Si vous avez déjà fait un coup de telnet sur un serveur OpenSSH, vous avez déjà vu que l’annonce du serveur est en clair, et il y a un élément facilement identifiable, qui est la version du protocole SSH. On peut dès lors non pas taper sur de la reconnaissance binaire, mais du plus humain avec une chaine de caractères :

acl acl_proto_ssh payload(0,7) -m str SSH-2.0
  tcp-request content accept if acl_proto_ssh

Et c’est tout. Pour chaque protocole on a défini un filtre avec l’ACL sur le contenu du paquet, et on force Haproxy à n’accepter que ce type de paquet avec tcp-request content accept. Ne reste plus qu’à « router » vers le service concerné :

use_backend host_ssh if acl_proto_ssh
  use_backend openvpn if acl_proto_openvpn

Et voilà, vous avez votre routeur maison de protocole pour contourner les limitations un peu pénibles de certains réseaux.

Je vous remet la configuration complète ici, pour que ce soit plus digeste si vous voulez copier/coller :

frontend 443
  #ssl client hello
  tcp-request inspect-delay 15s
  tcp-request content accept if { req.ssl_hello_type 1 }
  #OpenVPN
  acl acl_proto_openvpn payload(0,2) -m bin 003c
  tcp-request content accept if acl_proto_openvpn
  #OpenSSH
  acl acl_proto_ssh payload(0,7) -m str SSH-2.0
  tcp-request content accept if acl_proto_ssh

L’alternative SSLH

J’ai présenté haproxy parce que je le connais bien, et que j’ai kiffé quand un collègue m’a expliqué qu’il faisait ça sur son serveur. Alors évidemment avec mon organisation, j’ai mis plus de trois mois pour vous rédiger tout ça, mais voilà, c’est fait. Mais Haproxy n’est pas seul à permettre ce multiplexage de protocole.

Pour faire du SSH et du HTTPS, il y a SSLH, contraction d’SSL (nom original d’un protocole qui s’appelle désormais TLS, à la base du chiffrement d’HTTPS et d’OpenVPN) et d’SSH, pour l’ouverture sécurisée d’un shell. Et cerise sur le gâteau, chose que je ne savais pas avant d’avoir pratiquement fini d’écrire cet article, il semble qu’il supporte désormais aussi OpenVPN et même plusieurs autres protocoles. J’ai pas creusé, du coup, je vous laisse découvrir, mais pour des accès à une infra derrière une livebox, ça pourrait être plus intéressant que de multiplier les ouvertures de ports et les solutions type rebond/bastion. Reste qu’avec le HTTPS il y a toujours ces questions d’adresses IP source dans les logs, et de ce que j’ai lu, SSLH traite le problème de la même façon qu’Haproxy, à savoir TPROXY.

Vous l’avez peut-être déjà utilisé, ou alors, si vous le mettez en place, n’hésitez pas à partager vos retours, que ce soit ici en commentaire ou votre propre espace d’expression, c’est toujours sympa à voir en français 🙂