ExaProxy, un proxy HTTP filtrant

Posté par (page perso) . Édité par Lucas Bonnet et Xavier Claude. Modéré par patrick_g. Licence CC by-sa
26
1
mar.
2012
Technologie

ExaProxy est un nouveau proxy HTTP s'ajoutant à une longue liste de logiciels libres (SQUID, haproxy, varnish, tinyproxy…). Il se place dans le secteur de niche des proxy filtrants principalement desservis commercialement par des entreprises comme BlueCoat ou Fortinet.

Dans ce domaine, SQUID est la plus connue des solutions libre grâce à une interface simple permettant l’écriture rapide de modules de filtrage qui reçoivent l'IP de hôte connecté avec la requête faite. SQUID supporte aussi ICAP/eCAP pour ceux qui veulent modifier la requête et la réponse du serveur.

Cependant, afin d'avoir un filtrage différent par IP d'origine, il est impossible d'utiliser SQUID avec un load balancer de niveau 7, comme haproxy, qui change l'IP d'origine. Seules les solutions de haute disponibilités ré-écrivant l'adresse MAC de destination (comme ipvsadmin) sont adaptées. De plus, SQUID ne présente pas les requêtes de type CONNECT.

ExaProxy est donc un forward proxy permettant de filtrer ou router le trafic transitant dans un cluster de manière très souple, et permet l'utilisation du header X-Forwaded-For quand l'IP de l’hôte est masquée. Une autre utilisation peut être comme passerelle entre machines IPv4 et IPv6.

Le fonctionnement du serveur peut-être observé via une interface web sur le port local 8080.

Ce proxy, bien qu’écrit en Python, a été conçu pour remplacer SQUID dans un cluster de serveurs sous forte charge. Afin d’être performant, l'application utilise un arsenal de techniques de programmation :

  • un réacteur asynchrone (epoll ou select) ;
  • une gestion par événements de la machine d'état (state-machine) ;
  • l'utilisation de co-routines (plus légères que des threads) pour la gestions des connections TCP ;
  • une résolution DNS UDP/TCP interne intégrée dans le coeur (gethostbyname est une fonction bloquante) ;
  • une communication entre threads et le coeur par messages ;
  • une gestion automatique du nombre de threads nécessaire pour une bonne montée en charge.

Beaucoup de scripts (certains compatibles avec le format SQUID) sont présents avec le code comme :

  • comment éditer un cookie pour par exemple, toujours avoir des résultats YouTube filtrés ;
  • comment présenter https://www.wolframalpha.com/ quand https://www.google.com/ a été demandé, sans que le butineur n'affiche de problème de certificat SSL ;
  • comment servir des fichiers locaux à la place de la page demandée ;
  • comment servir un contenu généré depuis le script lui-même.

Le proxy est considéré assez stable pour être utilisé en production par quelques beta-testeurs, une version 1.0.0 devrait voir le jour début mars. Les auteurs recherchent donc activement des retours d’expérience autre que la leur.

  • # c'est pas un peu compliqué tout ca

    Posté par . Évalué à 3.

    je sais pour vous, mais sincèrement à la 1ere lecture j'ai pas compris grand chose

    je ne pense pas que tous les lecteurs de linuxfr soit des experts en proxy, c'est dommage le domaine m’intéresse mais pour moi cette news est trop complexe

    • [^] # Re: c'est pas un peu compliqué tout ca

      Posté par (page perso) . Évalué à 3.

      C'est surtout très verbeux. Par exemple :

      • un réacteur asynchrone (EPool ou select) ;
      • une gestion par événements de la machine d'état (state-machine) ;
      • l'utilisation de co-routines (plus légères que des threads) pour la gestions des connections TCP ;
      • une résolution DNS UDP/TCP interne intégrée dans le coeur (gethostbyname est une fonction bloquante) ;
      • une communication entre threads et le coeur par messages ;
      • une gestion automatique du nombre de threads nécessaire pour une bonne montée en charge.

      se résume tout bêtement à :

      ExaProxy est écrit en style à événements (entrées-sorties non bloquantes) ce qui lui permet de gérer un grand nombre de connexions simultanément en limitant la consommation mémoire.

      Pas besoin de rentrer dans les détails, ceux qui savent ce qu'est du code à événement infèrent les détails à partir de la simple information « style à événements », et ceux qui ne savent pas ont au moins une vague idée de ce que ça implique.

      (Et en plus, la dépêche induit le doute avec tous ces détails ; par exemple, même si c'est ma spécialité, je ne suis pas sûr de comprendre le lien entre les événements, coroutines et threads mentionnés dans cette énumération.)

      • [^] # Re: c'est pas un peu compliqué tout ca

        Posté par (page perso) . Évalué à 2.

        Les threads sont necessaires pour gerer les programmes forkes qui contiennent la logique de filtrage. Sans elles, le code serait bloquant quand le programme de filtrage est lent. Ces threads communiquent via messages a la thread principale afin de ne pas avoir besoin de lock dans le code. Les coroutines sont gere par le reacteur et s'occupent des connections TCP.

        trop verbeux mais pas assez pour etre clair :)

  • # redirection HTTPS

    Posté par . Évalué à 2.

    Je suis curieux : comment fait on pour remplacer un site https par un autre sans alerte de certificat SSL ?

    • [^] # Re: redirection HTTPS

      Posté par (page perso) . Évalué à 3.

      Google propose https://nosslsearch.google.com/ avec un certificat google valide, qui redirige vers son site http://www.google.com/ . En redirigant la premiere requete vers ce site, la second requete peut alors etre redirige / repondu par le proxy.

      Alternativement si le browser utilise le proxy directement et demande un tunneling pour HTTPS, il est possible de retourner un 302 redirect comme reponse au CONNECT et rediriger le browser autre part.

      • [^] # Re: redirection HTTPS

        Posté par . Évalué à 3.

        En redirigant la premiere requete vers ce site

        c'est justement à ce moment que le navigateur va gueuler, car le proxy va lui présenter un certificat qui ne correspond pas à "nosslsearch.google.com", non ?

        je ne vois pas où le proxy s'insère dans la séquence suivante:

        curl -vvv "https://nosslsearch.google.com/"
        * About to connect() to nosslsearch.google.com port 443 (#0)
        *   Trying 216.239.32.20... connected
        * Connected to nosslsearch.google.com (216.239.32.20) port 443 (#0)
        * successfully set certificate verify locations:
        *   CAfile: none
          CApath: /etc/ssl/certs
        * SSLv3, TLS handshake, Client hello (1):
        * SSLv3, TLS handshake, Server hello (2):
        * SSLv3, TLS handshake, CERT (11):
        * SSLv3, TLS handshake, Server finished (14):
        * SSLv3, TLS handshake, Client key exchange (16):
        * SSLv3, TLS change cipher, Client hello (1):
        * SSLv3, TLS handshake, Finished (20):
        * SSLv3, TLS change cipher, Client hello (1):
        * SSLv3, TLS handshake, Finished (20):
        * SSL connection using RC4-SHA
        * Server certificate:
        *        subject: C=US; ST=California; L=Mountain View; O=Google Inc; CN=*.google.com
        *        start date: 2012-02-16 10:38:09 GMT
        *        expire date: 2013-02-16 10:48:09 GMT
        *        subjectAltName: nosslsearch.google.com matched
        *        issuer: C=US; O=Google Inc; CN=Google Internet Authority
        *        SSL certificate verify ok.
        > GET / HTTP/1.1
        > User-Agent: curl/7.21.0
        > Host: nosslsearch.google.com
        > Accept: */*
        >
        < HTTP/1.1 302 Found
        < Cache-Control: private
        < Content-Type: text/html; charset=UTF-8
        < Location: http://www.google.com
        < Content-Length: 218
        < Date: Fri, 02 Mar 2012 00:04:41 GMT
        < Server: GFE/2.0
        <
        <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
        <TITLE>302 Moved</TITLE></HEAD><BODY>
        <H1>302 Moved</H1>
        The document has moved
        <A HREF="http://www.google.com">here</A>.
        </BODY></HTML>
        * Connection #0 to host nosslsearch.google.com left intact
        * Closing connection #0
        
        
        • [^] # Re: redirection HTTPS

          Posté par (page perso) . Évalué à 3.

          Non, il ne va pas geuler, car google presente un star certificate (CN=*.google.com) qui marche pour tous les sous domaines google.com. Pour verifier verifier :

          hg clone http://code.google.com/p/exaproxy
          cd exaproxy
          env \
          exaproxy.redirector.enable=true \
          exaproxy.redirector.protocol=url \
          exaproxy.redirector.program=./etc/exaproxy/redirector/url-google-to-others \
          ./sbin/exaproxy

          changer ses proxies HTTP/HTTPS pour 127.0.0.1, entrer l'url https://www.google.com/ regarder le contenu de la page.

          • [^] # Re: redirection HTTPS

            Posté par . Évalué à 2.

            Je vois un

            "Unable to connect"
            
            

            dans mon navigateur
            Et un

            "ERROR     31285  header        could not parse header <class 'exaproxy.http.message.HostMismatch'> Make up your mind: bing.com - www.google.fr"
            
            

            dans les logs du proxy.

            • [^] # Re: redirection HTTPS

              Posté par (page perso) . Évalué à 2.

              cd exaproxy
              hg log --limit 1

              La version doit etre superieure ou egale a 533 (version de Vendredi).
              Si ce n'est pas le cas, re-essayer apres un :

              hg pull -u

              Si c'est le cas, peux-tu m'envoyer par email les logs complets (car je n'ai pas ce probleme).

  • # X-Forwarded-For

    Posté par . Évalué à 2.

    A priori un proxy filtrant c'est fait pour ne pas être contourné, alors attention aux headers X-Forwarded-For qui sont très simples à générer.

    Imaginons un proxy qui laisse passer certaines requetes web selon la station cliente en se basant sur le champ X-Forwarded-For. Il suffit que la machine cliente injecte un header X-Forwarded-For pour se faire passer pour une autre …

    Il faut donc absolument
    - être sur un réseau sur lequel on maitrise les IP
    - que le proxy filtrant valide que l'adresse IP de laquelle vient la requete est bien un "trusted proxy". Si on autorise les cascades de proxys, il faut valider toute la chaine des proxy qui ont été traversés. Cette chaine doit être présente dans le (ou les) header(s) X-Forwarded-For.

    Car il y a 2 méthodes possibles dans la RFC: chainer les adresses IP dans le même header (séparées par des virgules), ou ajouter un header a chaque fois que l'on traverse un proxy. Du coup les produits (apache, jboss, haproxy, …) implementent rarement les deux solutions et ne sont pas forcement compatibles entre eux :-(

    Bref le X-Forwarded-For pour gérer de la sécurité c'est vraiment quand on n'a pas trouvé mieux !

    • [^] # Re: X-Forwarded-For

      Posté par (page perso) . Évalué à 1. Dernière modification le 02/03/12 à 01:01.

      Ce n'est pas faux, il faut bien faire attention a ce header. ExaProxy utilise la dernière valeur du header comme le demande haproxy dans sa documentation (http://haproxy.1wt.eu/download/1.3/doc/configuration.txt
      "option forwardfor")

      Comme haproxy donne le choix d'un autre nom pour ce leader, j'ajouterai demain une option pour changer ce nom et aussi enlever ce header de la requête quand elle passe a travers le proxy.

      J'espère que cette sécurité par l'obscurité satisfera les utilisateurs les plus exigeants.

    • [^] # Re: X-Forwarded-For

      Posté par (page perso) . Évalué à 2. Dernière modification le 02/03/12 à 08:44.

      Willy a proposé une solution élégante (je trouve) au problème. Il s'agit d'un protocole de communication entre les diverses couches de proxy décrit ici : http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt

      Le problème originel consiste à passer l'information sur l'ip réelle du client depuis stunnel vers haproxy. Mais cela peut avoir d'autres applications bien sympas, comme remplacer le X-Forwarded-For par exemple.

      Pour l'heure c'est implémenté (que je sache) dans stunnel, haproxy et stud. Mais je pense que c'est un mécanisme qui gagnerai à être déployé largement

    • [^] # Re: X-Forwarded-For

      Posté par . Évalué à 2.

      Les proxys ça ne devrait pas exister, enfin, les PALC :

      « Pour la fin des P.A.L.C. (Proxys à la con) » http://blog.ronez.net/?p=708
      « Le proxy à la con (PALC) » http://padawan.info/fr/2007/09/le-proxy-a-la-con.html

      • [^] # Re: X-Forwarded-For

        Posté par (page perso) . Évalué à 2.

        LOL !

        Regardons le trafic de ma societe (un FAI pour entreprise) vers Facebook (la partie qui ne va pas chez Akamai). Le trafic correspond bien aux heures de travail detente d'entreprise.

        Le proxy a la *** est une consequence des actions des employees. Il y a d'autres facteurs comme des certifications ISO toutes aussi stupides.

        Maintenant mon proxy a ete concu pour les ecoles ou le filtrage est obligatoire, c'est pas ma faute si on vit dans des pays liberticides.

  • # EPool ?

    Posté par (page perso) . Évalué à 4.

    Ave,

    Sous linux, c'est epoll le remplaçant de select. J'ai l'impression que le site du projet a une typo.

    http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html

    • [^] # Re: EPool ?

      Posté par (page perso) . Évalué à 2.

      Ils codent en python, alors forcément, il y a des majuscules partout ;-)

      http://code.google.com/p/exaproxy/source/browse/lib/exaproxy/network/async/epoll.py

      • [^] # Re: EPool ?

        Posté par . Évalué à 2.

        Je ne crois pas que la remarque portait sur la majuscule mais plutôt sur la confusion entre poll et pool. Les deux mots sont utilisés en informatique et sont souvent confondus.

    • [^] # Re: EPool ?

      Posté par (page perso) . Évalué à 4.

      C'est corrigé, merci.

      « Rappelez-vous toujours que si la Gestapo avait les moyens de vous faire parler, les politiciens ont, eux, les moyens de vous faire taire. » Coluche

      • [^] # libevent ?

        Posté par (page perso) . Évalué à 1.

        Pourquoi ne pas utiliser libevent au lieu d'attaquer directement epoll ?

        • [^] # Re: libevent ?

          Posté par (page perso) . Évalué à 2.

          ExaProxy doit être facile à installer sur un serveur, même embarqué, nous ne voulons donc pas avoir de dépendances autre que python lui-meme.

          libevent sous python est une librarie (http://code.google.com/p/pyevent/) qui nécessite d'être compilée et installée ou un paquet pour la distribution, alors qu'epoll est disponible dans la librairie standard (from select import epoll).

          • [^] # Re: libevent ?

            Posté par (page perso) . Évalué à 2.

            alors qu'epoll est disponible dans la librairie standard (from select import epoll).

            libevent n'est pas qu'une abstraction pour epoll. Il gère la portabilité (kqueue, /dev/poll…). Du coup sous *BSD ou Solaris ton proxy utilise select ce qui est … lent

            • [^] # Re: libevent ?

              Posté par (page perso) . Évalué à 1.

              kqueue est disponible pour les versions BSD et MAC de python, ecrire un reacteur kqueue est donc possible. Nous ne l'avons pas fait car ce n'est pas une priorite pour nous et je veux pouvoir utiliser le logiciel en production sous peu.
              select est clairement plus lent que kqueue mais ce n'est pas non plus la fin du monde …
              Si quelqu'un veut ecrire le patch pour ajouter kqueue, je suis prenneur ou si des utilisateurs me font savoir que kqueue est important pour eux, nous l'ajouterons.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.