Intégrer la géolocalisation à Apache 2 sous Linux

| Jean-Yves Lacour | julp |

Connaître la situation géographique des clients possède plus régulièrement un but statistique. Or, il peut s'avérer utile de pouvoir prendre en compte cet élément au niveau de la configuration du serveur même, ainsi que par les langages exécutés au niveau du serveur.

- Titre : Intégrer la géolocalisation à Apache 2 sous Linux

- Auteurs : Jean-Yves Lacour, julp

- Parution : 10 février 2009

- Mise à jour : 25 avril 2020

- Licence : Copyright ® 2008 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Présentation

- II. Mise en place

- II-A. À partir des sources

- II-B. Exemple de Debian Etch pour une installation à partir des paquets binaires prévus

- III. Utilisation

- III-A. Par l'usage d'un langage de programmation côté serveur

- III-A-1. PHP

- III-A-2. Ruby

- III-A-3. Perl

- III-A-4. Autres langages

- III-B. Dans le cadre de la configuration du serveur seul

- III-B-1. Première application : interdire l'accès à certains utilisateurs étrangers

- III-B-2. Deuxième application : redirection suivant le lieu de connexion

- IV. Epilogue

- V. Remerciements

I - Présentation

Le module Apache GeoIP, développé et maintenu par la société MaxMind, vous permet non seulement de connaître la provenance de manière relativement fiable d'un visiteur, mais de la faire intervenir dans vos développements ou encore dans la configuration du serveur.

La société MaxMind ne fournit pas seulement des modules pour les différentes versions du serveur web Apache, mais aussi des bibliothèques pour divers langages (C - la base « commune », PHP, Perl, Java, etc.). Cependant, une intégration au niveau du serveur lui-même présente quelques avantages et prérequis.

L'unique condition préalable est de pouvoir accéder au serveur (donc de disposer des droits) pour pouvoir installer ce module et reconfigurer Apache.

Par contre, les enjeux sont multiples.

- Les informations relatives à la géolocalisation sont transmises sous forme de variable d'environnement au langage serveur employé. Leur récupération n'en est que plus aisée et ceci offre une certaine indépendance : il n'y a ainsi nul besoin d'installer une bibliothèque pour chaque langage que vous pourriez être amené à utiliser sur votre serveur.

- Apache devient alors capable d'interagir en fonction de la localisation du client. Il peut ainsi directement effectuer un filtrage des clients, les rediriger…

II - Mise en place

Ici ne sera présentée que l'installation du module sous forme dynamique afin de pouvoir l'activer ou désactiver à volonté et pour une mise en place rapide (nous évitant une recompilation complète d'Apache).

II-A - À partir des sources

Il est nécessaire de télécharger trois choses avant de commencer :

- la bibliothèque C, dépendance requise au module que nous compilerons dans un deuxième temps :

cd ~
wget http://www.maxmind.com/download/geoip/api/c/GeoIP-1.4.5.tar.gz

Vérifier éventuellement qu'il n'existe pas de versions plus récentes que ce soit maintenant pour la bibliothèque C comme pour les sources du module que nous abordons ci-dessous.

- les sources du module propres aux versions de la branche 2 d'Apache :

wget http://www.maxmind.com/download/geoip/api/mod_geoip2/mod_geoip2_1.2.5.tar.gz

- enfin, télécharger une base de données mise à disposition gratuitement (donc plus limitée). Il en existe deux : celle de base dont vous ne pourrez tirer que le pays du client et une seconde fournissant des informations plus précises puisque capable de vous donner la ville et la région de l'internaute.

# La base "pays" uniquement
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz

# La base "allégée des villes"
wget http://www.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz

La fiabilité des bases de données mises gratuitement à votre disposition est estimée à 98 % pour celle des pays et à 69 % en ce qui concerne celle des villes. Avec une perte supplémentaire d'environ 1,5 % tous les mois si vous ne téléchargez pas sa nouvelle version paraissant de manière mensuelle.

Procédons à leur installation :

1. Comme mentionné plus haut, un certain ordre doit être respecté : il faut commencer par l'API C de GeoIP puisque le module repose sur celle-ci.
Décompressez l'archive :

tar xzf GeoIP-1.4.5.tar.gz -C /usr/local/src

Puis procédez à la compilation et installation (requérant les droits administrateur) :

cd /usr/local/src/GeoIP-1.4.5/
./configure --prefix=/usr/local/geoip
make
make install

Pour compiler cette bibliothèque, vous devrez posséder sur votre système les fichiers d'entête de la bibliothèque zlib. Si ce n'est pas le cas, cherchez un paquet éponyme suffixé de dev ou devel puis installez-le.

2. Vient ensuite le tour du module Apache. Détarez les sources de celui-ci…

tar xzf ~/mod_geoip2_1.2.5.tar.gz -C /usr/local/src

… puis passez à sa compilation.

cd /usr/local/src/mod_geoip2_1.2.5/
apxs2 -i -a -L/usr/local/geoip/lib -I/usr/local/geoip/include -lGeoIP -c mod_geoip.c

Pour cette étape, il vous sera nécessaire de posséder les fichiers d'entête d'Apache. Ceux qui ont donc installé leur serveur sous forme de paquet binaire auront sans doute à chercher un paquet nommé sur le modèle apache2(-threaded|mpm)-dev(el) ou encore httpd-dev(el).

Suivant votre système, la commande apxs2 pourra être plus simplement appelée apxs.

La commande apxs2, telle qu'elle a été utilisée ci-dessus suppose que le répertoire où elle est située figure comme valeur de la variable d'environnement PATH. À défaut, il est possible d'en préciser son chemin.

3. Décompressez la base que vous avez récupérée plus tôt à l'aide de gunzip :

gunzip GeoIP.dat.gz
# ou suivant la base choisie
gunzip GeoLiteCity.dat.gz

Pour la déplacer vers un répertoire plus « approprié » :

cp ~/GeoIP.dat /usr/local/geoip/share/GeoIP/
# ou suivant la base choisie
cp ~/GeoLiteCity.dat /usr/local/geoip/share/GeoIP/

Pour la suite, assurez-vous que cette base est accessible en lecture de tous (surtout de l'utilisateur sous lequel fonctionne Apache).

Nous pouvons maintenant passer à la configuration d'Apache. Vérifiez tout d'abord la présence d'une ligne LoadModule ressemblant à celle ci-dessous (chemin à adapter) qui chargera notre nouveau module :

LoadModule geoip_module /usr/lib/apache2/modules/mod_geoip.so

Rajoutez-la à la suite des autres si elle se fait porter absente. Gardez le fichier ouvert en vue de placer une ligne LoadFile avant la précédente, nécessaire au bon fonctionnement du module, afin qu'Apache puisse auparavant charger la librairie geoip de laquelle dépend le module :

LoadFile /usr/local/geoip/lib/libGeoIP.so

Puis activez et configurez à présent notre module par la mention des lignes suivantes :

<IfModule mod_geoip.c>
GeoIPEnable On
GeoIPDBFile /usr/local/geoip/share/GeoIP/GeoIP.dat # ou /usr/local/geoip/share/GeoIP/GeoLiteCity.dat
</IfModule>

À ce stade vous devriez relancer le serveur et vérifier le bon fonctionnement du tout en poursuivant directement à la partie intitulée.

II-B - Exemple de Debian Etch pour une installation à partir des paquets binaires prévus

Dans la mesure où votre système prévoit des paquets pour le module GeoIP, vous avez tout intérêt à les privilégier : son installation n'en sera que plus rapide et plus facile.

Dans le cas de Debian, cela peut se résumer à la commande suivante :

aptitude install libapache2-mod-geoip

Debian vous installera uniquement la base de données « pays ». Si vous prévoyez d'en utiliser une autre ou une version plus à jour, il vous faudra éventuellement décommenter la directive GeoIPDBFile du fichier /etc/apache2/mods-available/geoip.conf en retirant le caractère dièse (#) apparaissant en début de ligne et en modifier la valeur pour pointer sur la vôtre.

Normalement le module devrait être actif par défaut à l'issue de son installation. Assurez-vous-en, en contrôlant la présence des liens symboliques geoip.conf et geoip.load dans le répertoire /etc/apache2/mods-enabled ou exécutez la commande a2enmod, qui le fera pour vous dans le cas contraire :

a2enmod geoip

Enfin, (re)démarrer le serveur web :

/etc/init.d/apache2 force-reload

III - Utilisation

Les variables d'environnement pour la base de données de type « pays » sont :

- GEOIP_COUNTRY_CODE : code international du pays (suivant la norme ISO 3166) ;

- GEOIP_COUNTRY_NAME : nom complet du pays (en anglais).

Si GeoIP est incapable de déterminer la provenance de l'utilisateur, les variables ne seront pas définies.

En ce qui concerne la base de données « allégée des villes », viennent s'ajouter à la liste précédente :

- GEOIP_REGION : un code indiquant la région (voir les valeurs plus bas ainsi que leur correspondance pour la France) ;

- GEOIP_CITY : la ville ;

- GEOIP_DMA_CODE : code DMA (Designated Market Area) attribué à une zone où les fréquences (télévision, radio, etc.) sont identiques (concerne les États-Unis, valeur par défaut : 0) ;

- GEOIP_AREA_CODE : indice téléphonique représentant une zone précise (concerne les États Unis, valeur par défaut : 0) ;

- GEOIP_LATITUDE : la latitude ;

- GEOIP_LONGITUDE : la longitude ;

- GEOIP_POSTAL_CODE : le code postal (concerne uniquement les États-Unis, ne sera pas définie pour tout autre pays).

Les valeurs de la variable GEOIP_REGION pour les différentes régions françaises avec leur correspondance sont :

Région

Code

Alsace

C1

Aquitaine

97

Auvergne

98

Basse-Normandie

99

Bourgogne

A1

Bretagne

A2

Centre

A3

Champagne-Ardenne

A4

Corse

A5

Franche-Comté

A6

Haute-Normandie

A7

Île-de-France

A8

Languedoc-Roussillon

A9

Limousin

B1

Lorraine

B2

Midi-Pyrénées

B3

Nord-Pas-de-Calais

B4

Pays de la Loire

B5

Picardie

B6

Poitou-Charentes

B7

Provence-Alpes-Côte d'Azur

B8

Rhône-Alpes

B9

III-A - Par l'usage d'un langage de programmation côté serveur

La récupération des différentes « variables » créées par le module de géolocalisation dépend de la manière dont ce dernier a été configuré. En effet, suivant la valeur de sa directive GeoIPOutput, elles ne seront pas accessibles de la même manière.

- Lorsque GeoIPOutput a pour valeur Notes, les variables relatives à la géolocalisation du client ne sont créées que dans la table interne d'Apache appelée Notes. Ce n'est pas, de ce fait, la méthode la plus facile pour ensuite les atteindre avec un langage serveur.

- En revanche, avec la valeur Env, toutes ces variables sont exportées à la manière des variables d'environnement système. Cette forme facilite fortement leur usage, quel que soit le langage employé côté serveur, comme nous le verrons ci-dessous.

- Enfin, la valeur All (par défaut) est à recommander puisque toutes ces variables seront définies deux fois, selon les méthodes des deux points précédents. Ainsi il n'y a plus lieu de se demander par quel moyen y avoir recours.

III-A-1 - PHP

Étant donné qu'il est plus courant d'utiliser PHP en tant que module Apache, vous disposez alors sans doute de l'extension apache qui fournit, entre autres, la fonction apache_note. Ainsi que GeoIPOutput ait pour valeur Notes ou All, vous serez en mesure d'exploiter toutes les variables GEOIP.

Cependant, beaucoup trouveront certainement plus naturel de trouver ces informations dans le tableau superglobal $_SERVER. Chose possible dès lors que GeoIPOutput sera positionné à All ou Env.

Ci-dessous, un code PHP listant l'ensemble des variables GEOIP, dans l'hypothèse où elles vous sont mises à disposition comme des variables d'environnement :

foreach ($_SERVER as $k => $v) {
if (stripos($k, 'GEOIP') !== FALSE) {
printf('%s : %s<br />', $k, $v);
}
}

III-A-2 - Ruby

Par contre dans le cas de Ruby, il sera sans doute plus facile de trouver ces variables dans le hash ENV, recensant, comme son nom l'indique, toutes les variables d'environnement (ce qui implique que GeoIPOutput a pour valeur Env ou bien All) du fait de son utilisation CGI. Le code équivalent au script PHP présenté ci-dessus pourrait s'écrire de la sorte :

ENV.each do |k,v|
puts "#{k} => #{v}" if k ~= /GEOIP/i
end

III-A-3 - Perl

Pour des raisons autant de facilité que de portabilité, seule la méthode visant à exploiter l'exportation de ces variables comme variable d'environnement sera présentée (impliquant de nouveau GeoIPOutput à Env ou All). Vous les trouverez donc dans le hachage %ENV :

foreach (keys(%ENV)) {
print "$_ => $ENV{$_}$/" if $_ =~ /GEOIP/i;
}

III-A-4 - Autres langages

Nous vous invitons, pour ce qui est des autres langages, à consulter leurs documentations respectives ou à vous orienter sur les forums.

III-B - Dans le cadre de la configuration du serveur seul

III-B-1 - Première application : interdire l'accès à certains utilisateurs étrangers

Une première application, allant à l'encontre des principes d'accessibilité et de liberté d'Internet, pourrait être de bloquer des utilisateurs faisant partie de tel ou tel pays voire, pourquoi pas, de n'autoriser que ceux de pays prédéterminés.

En guise d'exemple, nous souhaitons bloquer les internautes russes et chinois. Se présentent deux possibilités :

- En introduisant une variable d'environnement :

SetEnvIf GEOIP_COUNTRY_CODE ^CN|RU$ FORBIDDEN=1

Deny from env=FORBIDDEN

Notre variable d'environnement FORBIDDEN ne sera créée que si l'on sait que le client est russe (code pays « RU ») ou chinois (de code « CN ») d'après le module GeoIP. Le fait que cette variable soit dans ces conditions définies lui vaudra un blocage (le serveur retournera la page d'erreur 403 au lieu de la page attendue).

- À l'aide du module de réécriture :

RewriteEngine on
RewriteCond %{ENV:GEOIP_COUNTRY_CODE} ^CN|RU$
RewriteRule .* - [F]

Strictement la même chose, écrite avec des règles de réécriture. En réalité, le module a été quelque peu détourné de son but premier : le tiret fourni en deuxième paramètre de la directive RewriteRule indique qu'il ne faut pas effectuer de réécriture et c'est l'option F (pour forbidden, située entre les crochets, qui va retourner l'erreur 403 pour nos clients identifiés comme russes et chinois.

III-B-2 - Deuxième application : redirection suivant le lieu de connexion

Pour ce second exemple d'application, l'objectif consiste à distinguer la provenance de l'utilisateur afin d'éventuellement rediriger celui-ci sur la page qui lui serait potentiellement la plus intéressante. Ici, nous supposerons qu'il s'agit d'un site dédié à la région Corse (code associé : A5 - voir le tableau plus haut ; de domaine corse.fr).

Si l'utilisateur est reconnu comme étant local, nous le redirigerons sur la page vie-pratique.html fournissant diverses informations utiles et actualisées lorsqu'il demandera pour la première fois le document d'index (index.html). Ce premier passage sera marqué par un cookie, expirant après une heure, dans le but de ne pas entraver sa navigation.

On pourrait schématiser cette opération par l'algorithme suivant :

si GEOIP_REGION = "A5" et page_courante = "/index.html" et non cookie_defini("local")
alors
créer_cookie("local", "1", ".corse.fr", 60, "/")
/**
* Paramètres :
* - nom du cookie
* - sa valeur
* - le nom de domaine auquel il est associé
* - durée en minutes avant expiration
* - le chemin sur lequel il s'applique
**/
redirection("/vie-pratique.html")
fsi

Qui se traduirait à l'aide d'une règle de réécriture :

RewriteEngine on

RewriteCond %{ENV:GEOIP_REGION} =A5                                            # Règle n°1
RewriteCond %{HTTP_COOKIE} !\blocal\b                                          # Règle n°2
RewriteRule ^(:?index.html)?$ /vie-pratique.html [L,CO=local:1:.corse.fr:60:/] # Règle n°3

La première règle permet d'indiquer que seules les connexions établies depuis la région Corse, c'est-à-dire lorsque GEOIP_REGION est égal à A5, sont concernées.

La deuxième vérifie qu'un cookie nommé local n'a pas été créé. Si tel était le cas, on n'abandonnerait là la lecture de la règle, l'internaute ne serait pas redirigé.

Enfin l'opération de réécriture ou redirection, qui sera appliquée si les conditions précédentes ont été remplies et que le document demandé est la page d'index. À ce moment on en profite pour marquer la redirection en créant le cookie (option CO).

IV - Epilogue

Bien qu'il existe de multiples solutions pour l'usage de l'« API » GeoIP, prenons pour preuve PHP qui se décline en une extension, en une bibliothèque écrite en PHP pur ou un paquet PEAR. Nous espérons, au travers de cet article, avoir montré tout l'intérêt de la déporter au sein du serveur web. D'autant plus, qu'un module est disponible pour chacune des branches d'Apache (l'installation du module pour une version 1.3 étant sensiblement identique).

Liens Developpez en relation :

- Connaître le pays d'un visiteur en fonction de son adresse IP en PHP

Liens externes :

- Le site de la société MaxMind à l'origine de ce module (entre autres)

V - Remerciements

Nous tenons à remercier RideKick pour sa relecture attentive.