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
…
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…)
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 !