I. Types et rôles d'un proxy

Cette section est détaillée dans l'article "HTTP : le protocole du Web passé en revue", dont je vous conseille la lecture de la rubrique appropriée.
Nous allons ici nous intéresser au cas du forward proxy. Un proxy de type forward est une machine s'intercalant entre le client web (typiquement un navigateur) et un serveur web (la page demandée). Petite remarque : il peut y avoir une succession de proxies qui se relayent la requête avant que celle-ci n'atteigne finalement le serveur final.
En entreprise (université, ou plus généralement petit à moyen espace fermé et limité), un proxy forward est utilisé dans plusieurs buts :

  1. Surveiller l'accès au web, chaque requête étant enregistrée (logguée)
  2. Empêcher l'accès à certains sites en les filtrant et les interdisant
  3. Créer un cache des ressources HTTP demandées
  4. Analyser la source de la page à la volée afin d'en chercher des éventuels malwares, de type javascript par exemple

Et bien plus encore... Ces points étant les principaux.
Dans cet article, nous allons nous intéresser à quelques facettes du proxy. Dans un premier temps, LA raison d'être d'un proxy web (HTTP) : le cache. Imaginez qu'un utilisateur en entreprise télécharge un ficher de 350Mo. C'est un fichier relativement lourd (oui oui, même pour une connexion haut ou très haut débit). Imaginons maintenant que ce soit un manager qui ait demandé à toute une équipe dans l'entreprise, de télécharger ce fichier. Le poids total transféré sur la ligne sera multiplié par le nombre de personnes le téléchargeant. Si tout le monde se connecte via un proxy, une première personne va télécharger ledit fichier et le proxy va stocker celui-ci en cache. Les personnes suivantes vont alors télécharger beaucoup plus vite ce même fichier, car le proxy est généralement situé dans l'entreprise et bénéficie de son réseau, souvent d'une qualité bien supérieure à celle du réseau Internet. Le proxy va jouer le rôle de serveur vis-à-vis des clients et va lui-même délivrer le contenu demandé à la place du serveur original. Aussi, on évite de surcharger le tronçon Internet, car toutes les requêtes HTTP vers ce même fichier peuvent être évitées par le proxy, ou alors fortement diminuées.
En réalité le proxy peut tout de même contacter le serveur d'origine en effectuant une requête conditionnelle de validation de cache, lui permettant de savoir si l'objet (le fichier si vous préferez) qu'il possède en cache est toujours valide et peut être servi par ses soins, ou si au contraire celui-ci a évolué sur le serveur d'origine entre temps, et doit donc être re-téléchargé. Ce processus est simple en apparance mais peut rapidement devenir très complexe de par la diversité des serveurs Web sur la planète, et leur manière assez différente d'implémenter les standards du protocole HTTP.
Vous trouverez beaucoup d'informations à ce sujet dans le paragraphe dédié de l'article "HTTP: le protocole du Web passé en revue"

Autre rôle du proxy : le routeur du tronçon sur lequel nous nous connectons va interdire l'accès au Web de manière directe, en bloquant tout simplement les requêtes à destination d'un port 80. Le seul moyen d'accéder au Web (supposant une sécurité élevée du réseau) sera alors la machine proxy, qui se situe proche physiquement des clients (typiquement dans l'entreprise, si possible sur le même domaine de diffusion IP c'est-à-dire sans routeurs intermédiaires). Toutes les requêtes webs sont donc centralisées et passent par un point (dont il faudra donc assurer la stabilité).
Ceci permet également d'enregistrer (de journaliser), les requêtes. Sachez que si vous êtes derrière un proxy, le propriétaire (l'administrateur) de celui-ci peut absolument tout savoir sur votre surf web (dates, pages consultées, données envoyées, fichiers reçus et leur contenu, etc...), sauf si celui-ci passe par HTTPS qui ne peut pas être utilisé via un proxy (le proxy se contentera de relayer la requête vers le serveur, sans enregistrer de données, de toute façon cryptées).

Enfin l'entreprise étant un lieu de travail, il peut être nécessaire de réguler l'accès à certains sites webs par le personnel. Il est très simple d'interdir l'accès à un site, un ensemble de sites, une plage d'adresse IP ou un TLD ("tous les sites en .com" par exemple).
Il est aussi possible pour le proxy de demander une identification (un couple login mot de passe), soit pour tout accès au web, soit pour l'accès à certains sites précis.

I-A. Définitions

On peut maintenant donner quelques définitions :
Un proxy est dit transparent s'il ne propose aucune authentification, et n'altère pas le contenu qui passe à travers lui.
Au contraire un proxy non transparent va effectuer un traitement quelconque altérant le message transmis.
Un proxy anonyme est un proxy garantissant l'anonymat des clients. Celui-ci n'envoie pas les en-têtes HTTP_VIA, HTTP_X_FORWARDED_FOR et HTTP_FORWARDED au serveur d'origine.
On peut aussi trouver des proxies d'interception, qui s'intercalent entre le client et le serveur de manière invisible, en général un routeur joue avec les couches transport et réseau du modèle OSI, pour dériver le paquet vers un proxy de manière invisible. Apache ne supporte pas ce mode.

I-B. Quelques produits

Vous aurez compris qu'un proxy forward peut représenter bien des avantages dans certains milieux, surtout concernant la question du cache des données. Il existe différents produits permettant le proxy HTTP.
Squid par exemple, est excellent : normal, il s'agit d'un logiciel dédié à cette unique tâche. Il est très léger, mais sa configuration peut rapidement devenir très longue et très technique.
Apache est un serveur web (que je ne présente pas), qui propose un module de proxy. Il est moins puissant qu'un produit dédié, mais très souvent largement suffisant, et il possède un autre avantage : il peut aussi servir de serveur web si on le souhaite ! Et oui, c'est tout de même sa raison d'être ;-).

II. Compilation et installation d'Apache

La compilation et l'installation sont simples. Notre exemple est basé sur une distribution Ubuntu Server, donc la procédure devrait être exactement la même sur toute distribution de type Debian, et très semblable sur une autre distribution Linux ou Unix.

Compilation et installation d'Apache
Sélectionnez

root:/apache-src> ./configure --enable-modules=proxy proxy-http proxy-connect cache disk-cache headers
root:/apache-src> make
root:/apache-src> make install

La version utilisée pour ce tutoriel est Apache 2.2.13

Apache sera installé par défaut dans /usr/local/apache2 ce qui nous convient très bien. Il pourra être nécessaire de créer un lien de démarrage depuis le script apachectl vers /etc/init.d puis l'ajouter aux scripts de démarrage du système

création du lien et démarrage automatique au démarrage du système
Sélectionnez

root:/> ln -s /usr/local/apache2/bin/apachectl /etc/init.d/apachectl
root:/> update-rc.d apachectl defaults

Apache propose une structure modulaire très éclatée, voici les modules dont nous avons besoin pour le sujet de cet article :

liste des modules nécessaires
  1. proxy : propose les fonctionnalités de base du proxy
  2. proxy-http : propose les fonctionnalités du proxy pour le protocole http
  3. proxy-connect : implémente le suivi de demande CONNECT pour les connexions SSL (HTTPS)
  4. cache : propose les fonctionnalités de base du cache des ressources
  5. disk-cache : propose les fonctionnalités de base du cache vers le système de fichiers
  6. headers : module permettant l'ajout d'en-têtes personnalisés à une réponse (facultatif)

La directive --enable-modules permet de compiler les modules statiquement dans le binaire final, et non en tant que librairies partagées (.so). Compiler en statique allourdit l'empreinte mémoire du binaire final, mais évite de passer par le système de chargement dynamique (mod_so) ce qui fait gagner très légèrement en performances et en vitesse de démarrage.

Notez que si vous ne compilez pas mod_proxy_connect, les clients de votre proxy ne pourront pas utiliser HTTPS. Plus d'infos sur la méthode CONNECT

Une fois Apache installé, la commande httpd -M permet de confirmer la présence des modules nécessaires :

 
Sélectionnez

root:/usr/local/apache2/bin> ./httpd -M

{ la liste des modules compilés s'affiche }

Nous voyons apparaître la liste des modules de base compilés via make, plus les modules que nous avons explicitement demandés.

La compilation possède évidemment des dépendances envers le système d'exploitation, à commencer par les outils nécessaires à la compilation elle-même. Sous Ubuntu Server, le package "build-essential" est un méta paquet possédant les dépendances vers les paquets nécessaires à la compilation (libc, make, g++ etc...)
Si une autre dépendance venait à manquer, le script "configure" vous le signalera lors de son lancement, vous n'aurez qu'à installer la dépendance manquante (apt-get ou aptitude).

III. Configuration en mode proxy forward

La configuration principale se situe dans conf/httpd.conf. Nous écrirons là-dedans tout ce qui est nécessaire au bon démarrage d'Apache.
Grâce à la directive Include, nous incluerons un script supplémentaire, placé dans conf/extra, que nous appellerons "httpd-proxy.conf". Nous écrirons là-dedans toutes les directives concernant le proxy.

C'est une bonne idée de séparer sa configuration en plusieurs fichiers. Ceci facilite grandement la maintenance de l'application. Regardez le contenu du dossier conf/extra, vous verrez que tout un tas de fichiers y sont présents. Chacun d'entre eux (si nécessaire), doit alors être inclus depuis le fichier de configuration principal httpd.conf

Nous n'allons pas détailler ici le listing complet de httpd.conf. Simplement, nous allons fermer Apache en temps que serveur web : il ne devra servir qu'à proxier des requêtes.
Nous allons commencer par le faire écouter sur le port 8080 (port généralement utilisé par les proxies), et ne pas le faire écouter sur le port 80, de cette manière là il n'agira pas comme serveur web par défaut.
Aussi, pour plus de sécurité, nous allons interdire l'accès à tout le monde à son DocumentRoot par défaut : personne n'a besoin d'interroger ce serveur de manière directe afin qu'il serve des pages, il ne doit servir que de proxy (bien sûr libre à vous de faire ce que vous voulez par la suite)

httpd.conf, non complet
Sélectionnez

Listen 192.168.0.1:8080
Servername JP_Dvp
DocumentRoot "/var/www"
<Directory />
    Order Deny,allow
    Deny from all
    AllowOverride none
</Directory>
Include conf/extra/httpd-proxy.conf

Vous aurez bien sûr deviné que dans notre exemple, le serveur écoute 192.168.0.1. Nous limitons exclusivement l'écoute à cette IP (et à ce port) pour plus de sécurité mais là encore, libre à vous de changer.

A quoi sert donc le DocumentRoot ? Premièrement Apache est un serveur web, il ne démarrera pas sans connaître un DocumentRoot. Deuxièmement, il reste possible d'interroger notre serveur de manière directe, via http://192.168.0.1:8080. La requête ainsi forgée est une requête directe qui arrive donc sur "/var/www" pour être servie, mais comme tout est interdit (deny from all), ceci mène à une réponse 403.
Une requête à proxier n'a pas la même forme qu'une requête à servir, car elle contient dans sa ligne de requête l'URL complète vers la ressource (GET http://server.com/here) et non juste le chemin relatif au serveur (GET /here). C'est comme ça qu'Apache sait si la requête lui est destinée directement, ou si elle doit être proxiée par mod_proxy.

Tout va donc se jouer dans conf/extra/httpd-proxy.conf (script à créer car il n'est pas présent par défaut dans l'installation d'Apache).
Remarquez que la documentation d'Apache est très bien faite, elle explique clairement chaque directive et chaque module, en laissant en général peu de flou autour des notions qu'elle décrit.
Il suffit de "suivre le guide" ;-)

conf/extra/httpd-proxy.conf
Sélectionnez

ProxyRequests On
<Proxy *>
    order deny,allow
    allow from 192.168.0.0/24
    deny from all
</Proxy>

Ces simples lignes activent le proxy pour toute machine dans la plage 192.168.0.0/24. Pour activer le proxy forward, ProxyRequests On est nécessaire.

Attention : il est indispensable de limiter l'accès à certaines machines (ici sur le réseau 192.168.0.0/24) et de bien régler son réseau (NAT, routeurs ...). Si la machine proxy est reliée à Internet (ce qui logiquement est le cas), alors il faut vérifier la table de routage. Si des connexions depuis internet peuvent entrer sur l'adresse écoutée via le routage (192.168.0.1:8080 ici), alors il faut les bloquer au risque d'ouvrir le serveur proxy au monde entier ce qui bien entendu est très dangeureux.
Souvent, le serveur proxy sert de routeur, et son interface d'écoute interne n'est pas la même que celle vers internet (eth0 et eth1 par exemple).

La directive <proxy> indique des règles à appliquer sur des URL. Nous avons choisi d'appliquer des règles de restriction d'accès, sur * ce qui signifie toute URL.
Nous aurions pu, grâce à mod_deflate, demander à compresser le contenu renvoyé pour les requêtes vers http://www.developpez.com par exemple :

 
Sélectionnez

<Proxy http://www.developpez.com>
    SetOutputFilter DEFLATE
</Proxy>

HTTP impose que tout proxy envoie un en-tête Via: autant sur la requête proxiée, que sur la réponse renvoyée au client. Ceci sert à tracer le chemin de la requête, à débogguer HTTP, et à faire des ajustements de protocole si nécessaire. La directive ProxyVia sert à cela, elle peut comporter plusieurs options : off, on, full ou block.

Signaler la présence du proxy dans les en-têtes HTTP
Sélectionnez

ProxyVia Full

Tous les serveurs contactés, comme les clients auxquels le proxy répond, recevront dès lors l'en-tête suivant : "Via: 1.1 JP_Dvp (Apache/2.2.13)".
La version de HTTP notamment est très importante dans le cas où des serveurs ou des proxies intermédiaires utiliseraient HTTP1.0 (rares).

Aussi, grâce à mod_headers que nous avons compilé, nous allons pouvoir rajouté des en-têtes, soit dans les réponses, soit dans les requêtes (soit les 2).
La directive header permet de manipuler les en-têtes envoyés en réponse. Nous allons rajouter un en-tête personnalisé :

Ajout d'un en-tête personnalisé dans les réponses du proxy
Sélectionnez

header always set JP_Dvp "JP_Dvp proxy server, %D"

%D indique le temps qu'a pris la requête, en microsecondes (une valeur de 275362 indique 275ms).

Autre point intéressant et indispensable : la journalisation. Que ce soit à des fins plus ou moins légales d'espionnage (un proxy par définition voit tout, car toute donnée passe physiquement par lui), ou tout simplement pour assurer la qualité du service et la sécurité des utilisateurs, un proxy doit logguer (journaliser) tout (ou parties, à méditer) ce qui passe par lui. Pas de problème sur ce point pour Apache, mod_log_config nous offre tout ce que l'on souhaite en la matière (et par défaut il fait parti des modules compilés).

 
Sélectionnez

LogFormat "From:%h at %t for \"%r\" \n\t %V responded: %s -%B bytes (%c)- %f" proxycommon
CustomLog logs/proxy.log proxycommon

Si vous lisez la doc officielle, vous verrez qu'on peut être beaucoup plus indiscret que ce qui est présenté ici, mais aussi beaucoup plus précis.
Il est bien sûr possible d'écrire autant de logs que l'on souhaite, voire même d'utiliser un pipe vers un programme type syslog ou encore PHP pourquoi pas ;-) On peut aussi journaliser uniquement les erreurs, certains codes HTTP, des variables d'environnement et j'en passe...

ProxyDomain peut être sympa si vous avez un DNS qui forward sur des machines. Exemple, un client tape "julien" puis valide. Apache va rajouter ".developpez" et si un enregistrement "julien.developpez" existe dans le DNS et renvoie vers le serveur de julien, alors le tour est joué.

III-A. Autorisations d'accès

Niveau contrôle d'accès, ProxyBlock permet de bloquer tout simplement l'accès à une URL (un domaine ou un tld aussi), une IP (ou plage) ou encore des mots :

 
Sélectionnez

ProxyBlock verybad 78.109 badserver.com .sex

Ici on bloque toute requête contenant "verybad" dans le host demandé, on bloque aussi tous les accès vers 78.109.0.0/16, on empêche l'accès à badserver.com (et donc tous ses sous-domaines) puis enfin à tout site utilisant le tld ".sex"
Notez qu'en bloquant un domaine on bloque son IP (une résolution DNS est effectuée au démarrage d'Apache sur la liste, attention ça peut rendre le processus long à démarrer), la liste peut tout de même vite devenir longue.
On trouve par contre des listes sur certains sites, qui classe par catégories autant les adresses IP que les domaines associés. En utilisant Include pour séparer la liste de la configuration principale du proxy et en jouant sur des mécanismes de mises à jour régulières de la liste, on peut trouver une solution plus ou moins satisfaisante... bien loin tout de même des possibilités de Squid et SquidGard.

Ensuite le proxy peut necéssiter une authentification avant d'être utilisé (RFC 2617). On pourra utiliser un code comme

Authentification proxy
Sélectionnez

<Proxy *>
    Order deny,allow
    Allow from 192.168.0.0/16
    Deny from all
    AuthType digest
    AuthName "julien_dvp"
    AuthDigestProvider file
    AuthUserFile auth/users
    Require valid-user
</Proxy>

Attention, des modules supplémentaires seront à compiler pour cette manipulation, qui n'est d'ailleurs qu'un exemple.
Exemple demandant une authentification proxy pour accéder. Notez que si on veut limiter cette authentification aux personnes ne matchant PAS la directive Allow (des "visiteurs"), il suffit de rajouter une directive "Satisfy Any". On peut aussi demander authentification seulement à certains navigateurs clients, ou leur interdire l'accès, mod_proxy hérite là de la souplesse générale d'Apache.

III-A-1. Cache

La mise en cache du contenu proxié est un vrai plus, pour cela, mod_cache va être nécessaire, heureusement nous l'avons compilé en début d'article.

Mise en place du cache pour le contenu proxié
Sélectionnez

CacheEnable disk /
CacheDefautExpire 86400
CacheRoot /var/apache/cache
CacheIgnoreHeaders Set-Cookie 
CacheDirLevels 2
CacheMaxFileSize 300000000

On ne va pas faire un tutoriel sur mod_cache, mais précisons quelques points. Apache respecte la RFC2616 (HTTP), et va respecter toutes ses directives. Il vous est possible de violer le protocole, en demandant au proxy de cacher des documents déclarés comme non cachables par le serveur d'origine, mais celà est hautement décommandé !
CacheDefaultExpire permet de donner un temps d'expiration aux réponses qui ne comportent pas cette information (ni en-tête "Expires", ni en-tête "Last-Modified"). Comme certains serveurs ou certaines applications omettent ces en-têtes, il peut être judicieux de cacher tout de même leur contenu, dans notre exemple pendant 86400 secondes soit un jour. Notez que la RFC décommande cela. Evidemment, la réponse n'est mise en cache que si elle ne spécifie pas elle-même le contraire (voyez le fonctionnement du cache HTTP)
CacheIgnoreHeaders permet de ne pas cacher certains en-têtes. Comme les cookies possèdent souvent des identifiants de session, nous ne souhaitons stocker cet en-tête en cache pour des raisons de sécurité.
CacheMaxFileSize indique le poids maximum de la ressource au delà duquel le cache ne doit pas agir, ici nous cachons des ressources allant jusqu'à 300Mo.
Les ressources sont cachées dans CacheRoot. Bien sûr l'utilisateur sous lequel tourne Apache doit avoir les droits en lecture/écriture dans ce dossier.
CacheDirLevels permet de contrôler CacheMaxExpire et CacheLastModifiedFactor permettent de calculer une valeur d'expiration pour les ressources n'en ayant pas spécifié (grâce à "Expires" ou "Cache-Control") mais ayant indiqué un "Last-Modified".
Apache calcule ainsi une date d'expiration selon la formule temps expiration = date actuelle + valeur minimale entre (date actuelle - valeur de Last-Modified * valeur CacheLastModifiedFactor) et (valeur de CacheMaxExpire) (tout ça oui).
Toutes ces informations sont disponibles sur le guide du cache d'Apache ou encore la documentation de mod_cache.

IV. Conclusions - Références

Voilà, le proxy forward d'Apache possède encore quelques options que la documentation officielle décrit. Certes Apache ne vaut pas un vrai proxy tel Squid, mais c'est une solution intéressante et facile à mettre en place en ce qui concerne le proxy forward. Il trouvera sa place dans des entreprises de petite à moyenne taille où il n'existe pas de hiérarchie de cache (chaînes de proxy). La mise en cache du contenu et le contrôle d'accès suffisent généralement dans la majorité des cas.
Attention ne mélangez pas le proxy forward et le reverse proxy. Apache peut faire les 2 (en même temps si besoin) et propose même dans le cas du reverse, mod_proxy_balance qui le tranforme en répartiteur de charge pratique pour créer un miroir avec de multiples destinations.
Configurer une passerelle sécurisée sous LinuxConfigurer une passerelle sécurisée sous Linux
Installation d'un proxy SquidInstallation d'un proxy SquidDocumentation officielle de mod_proxy