J’ai un client qui a un client qui a un (in|ex)tranet. Il veut qu’on puisse y accéder depuis partout dehors, et il veux que ce soit simple pour les gens qui y accèdent.

J’ai donc exclu d’emblée un proxy basique, puisqu’il faudrait que les “gens” aillent changer la configuration de leur navigateur selon s’il veulent accéder à l’extranet, ou au reste du net (parce que, non, j’allais pas faire un proxy ouvert à tous vents, sisi, faut que ce soit simple, donc, pas de login/pass).

Donc, fort de ces informations, je m’en vais faire un reverse proxy avec apache. Il y a tout ce qu’il faut, les directives ProxyPass, ProxyPassReverse et même ProxyPassReverseCookieDomain me plaisent beaucoup, et donc je tente :

<VirtualHost *:80>
        ServerName truc.com
        ProxyPass / http://extranet.com/
        ProxyPassReverse / http://extranet.com/
        ProxyPassReverseCookieDomain extranet.com truc.com
</VirtualHost>

Ça marche impeccable pour la page d’accueil, et là, oh horreur, le formulaire de login contient <form action="http://extranet.com/login.cgi"> et donc, dès la première page, ça ne va plus marcher.

Après quelques petites recherches, je tombe sur un mod_proxy_html qui me semble encore une fois être tout à fait ce dont j’ai besoin… Je transforme ma configuration, d’abord en en faisant qu’a ma tête (ce qui ne marche pas) puis en suivant la doc (ce qui marche) et j’arrive à :

LoadFile   /usr/local/lib/libxml2.so
LoadModule proxy_html_module libexec/apache22/mod_proxy_html.so

ProxyRequests off

<VirtualHost *:80>
        ServerName truc.com
        ProxyPass / http://extranet.com/
        ProxyHTMLURLMap http://extranet.com/ /

        <Location />
                ProxyPassReverse /
                SetOutputFilter  proxy-html
                ProxyPassReverseCookieDomain extranet.com truc.com
                ProxyHTMLURLMap / /
                RequestHeader    unset  Accept-Encoding
        </Location>
</VirtualHost>

Et le formulaire de la page de démarrage est modifié en <form action="/login.cgi">. Miracle, joie, bonheur, tout ça.

Et là, je regarde de plus près, et une partie de la page a été réécrite (mais elle s’affiche correctement), le <!DOCTYPE ...> en haut de la page a été viré. Je retourne sur le site de mon mod_proxy_html et je découvre dans la faq que ça va virer le dtd ou en mettre un, réécrire le html, tout convertir en utf-8. Déception, haine, tout ça.

Si ça ne fonctionne pas comme il faut, j’en serais réduit à regarder comment faire ça avec un OutputFilter et mod_perl

Update

Alors, bon, dans le site, y’a du javascript partout, alors, mod_proxy_html il roxe beaucoup moins déjà… J’ai donc lu un peu de doc sur mod_perl et un des exemples qui fait un reverse ligne par ligne m’a convaincu, ça donne donc :

LoadModule perl_module libexec/apache22/mod_perl.so

PerlRequire /usr/local/etc/apache22/perl/inc.pl

<VirtualHost *:80>
        ServerName truc.com
        ProxyPass / http://extranet.com/

        <Location />
                ProxyPassReverse /
                SetHandler modperl
                PerlOutputFilterHandler ReverseProxy
        </Location>
</VirtualHost>

et

package ReverseProxy;

use strict;
use warnings;

use Apache2::RequestRec ();
use Apache2::RequestIO  ();
use Apache2::Const -compile => qw(OK);

use base qw(Apache2::Filter);

use Apache2::Const -compile => qw(OK);

use constant BUFF_LEN => 100;

sub transform($)
{
        my $s = shift;
        $s =~ s/extranet\.com/truc.com/og;
        return $s;
}

sub handler : FilterRequestHandler
{
        my $f = shift;

        my $leftover = $f->ctx;
        while ( $f->read( my $buffer, BUFF_LEN ) ) {
                $buffer = $leftover . $buffer if defined $leftover;
                $leftover = undef;
                while ( $buffer =~ /([^\r\n]*)([\r\n]*)/g ) {
                        $leftover = $1, last unless $2;
                        $f->print( transform($1), $2 );
                }
        }

        if ( defined($leftover) ) {
                if ( $f->seen_eos ) {
                        $f->print( transform($leftover) );
                        $f->ctx(undef);
                } else {
                        $f->ctx($leftover);
                }
        } else {
                $f->ctx(undef);
        }

        return Apache2::Const::OK;
}

1;

Et miracle, ça fonctionne tout comme il faut… (Pour l’instant…)

Update, le retours

Alors, dans mon exemple, extranet.com et truc.com ne font pas la même taille, et ça pose problème, parce que le navigateur, va demander la page, la récupérer, mais elle sera plus petite que l’originale, et lors d’une requête ultérieure, avec toutes les en-têtes qui vont bien pour ne pas réenvoyer les pages pour rien, le serveur va juste répondre wé, c’est bon, elle a pas changé, et elle fait telle taille, sauf que, la taille que le navigateur il a déjà récupérée, elle est plus petite, et comme pour lui, la taille compte, il va jouer au con, et demander les X octets manquant de la page.

Pour une page web, il va résulter que un petit ml>\n se retrouvera ajouté a la fin (dans mon exemple ou ça fait 4 caractères de moins), dans un javascript, ça va être beaucoup plus problèmatique, parce que on va genre récupérer }\n}\n, et bien sur, ça ne sera plus valide, kakaboum…

Ouin, bon, vous me direz, suffit de prendre un domaine qui fait la même taille… C’est exact, c’est ce que j’ai fait, mais avouez, quand même, c’est d’un crétin !

Article précédent Article suivant