Aller au contenu | Aller au menu | Aller à la recherche

Another Home Page Blog

Linux et Logiciels libres

Fil des billets - Fil des commentaires

lundi, octobre 1 2012

Nombre d'occurrences dans un fichier - remix

Je détaillais dans un billet écrit il y a déjà un sacré bout de temps comment obtenir une sorte de top 10 des adresses IP effectuant le plus de requêtes dans un fichier de log Apache. J'ai décidé de revenir dessus, et de faire quelques déclinaisons de ce one-liner selon les recherches. Attention si vous voulez copier-coller ces exemples, ils ont été réalisés sous NetBSD, et la commande sort n'a pas les mêmes options. Grosso modo pour le moment, j'ai vu que là où on écrit sort -g sous GNU/Linux, il faut écrire sort -n sous NetBSD. J'ai aussi décidé de me limiter à un top 5 dans l'affichage, afin d'éviter un billet trop long.

Revenons donc d'abord sur le one-liner de base, les IP qui font le plus de requêtes, avec à gauche, l'adresse IP, et à droite le nombre de hits :

root@dev:/var/log/httpd# awk '{frequencies[$1]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -k 2,2 | head -5
81.X.Y.Z    6414
208.F.B.I 1578
178.K.G.B  1301
67.D.S.T  1179
77.C.I.A     1157

Ensuite, effectuons pareil mais sur les URLs visitées, toujours avec le nombre de hits à droite :

root@dev:/var/log/httpd# awk '{frequencies[$7]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -k 2,2 | head -5
/post/2008/05/17/installation-de-phpmyadmin-sur-CentOS-5        7787
/post/2008/05/24/Installation-de-mod_gnutls-sur-CentOS-5        4010
/post/2008/06/20/Utilisateurs-virtuels-sous-CentOS-5-avec-base-de-donnees-MySQL 1910
/post/2007/11/28/Installation-et-configuration-dun-serveur-dedie-OpenArena-071  1284
/post/2009/11/09/Utilisation-transparente-d-une-passerelle-SSH  1266

Comme il ne s'agit que de modifier le numéro du champ, on peut aussi voir les codes de retour HTTP les plus obtenus :

root@dev:/var/log/httpd# awk '{frequencies[$9]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -k 2,2 | head -5
200     57019
304     6156
404     1797
500     114
403     20

On peut ensuite aller chercher avec grep les pages causant des erreurs 500 ou 404.

Toujours avec la même facilité (un simple numéro de champ à modifier), on peut afficher les referers qui amènent le plus de hits :

root@dev:/var/log/httpd# awk '{frequencies[$11]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -k 2,2 | head -5
"-"     44306
"http://blog.anotherhomepage.org/post/2008/05/17/installation-de-phpmyadmin-sur-CentOS-5"       3443
"http://blog.anotherhomepage.org/post/2008/06/20/Utilisateurs-virtuels-sous-CentOS-5-avec-base-de-donnees-MySQL"        686
"http://blog.anotherhomepage.org/post/2009/11/09/Utilisation-transparente-d-une-passerelle-SSH" 552
"http://www.google.fr/search?q=phpmyadmin+centos&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:fr:official&client=firefox-a"   401

On remarque que beaucoup n'ont pas de referer, mais il est probable que ce soient des hits sur le flux RSS. On remarque aussi que j'ai beaucoup de referers de mon propre site, il me suffit de les filtrer si je ne veux pas les afficher. Afin de rendre le traitement plus rapide, je décide de mettre la commande grep en premier dans mon traitement :

root@dev:/var/log/httpd# grep -v "blog.anotherhomepage.org" access.log | awk '{frequencies[$11]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' | sort -nr -k 2,2 | head -5
"-"     44306
"http://www.google.fr/search?q=phpmyadmin+centos&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:fr:official&client=firefox-a"   401
"http://www.google.fr/search?q=centos+phpmyadmin&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:fr:official&client=firefox-a"   166
"http://forum.hardware.fr/hfr/OSAlternatifs/Installation/resolu-centos-phpmyadmin-sujet_70143_1.htm"    121
"http://www.google.fr/" 77

Reprenons notre affichage des URLs les plus visitées, mais cette fois prenons en compte les méthodes (GET, HEAD, POST) et la version du protocole HTTP :

root@dev:/var/log/httpd# awk -F "\"" '{frequencies[$2]++;} END {for (field in frequencies) printf "%s\t%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -k 4| head -5
GET /post/2008/05/17/installation-de-phpmyadmin-sur-CentOS-5 HTTP/1.1   4266
GET /post/2008/05/17/installation-de-phpmyadmin-sur-CentOS-5 HTTP/1.0   3521
GET /post/2008/05/24/Installation-de-mod_gnutls-sur-CentOS-5 HTTP/1.1   2181
GET /post/2008/05/24/Installation-de-mod_gnutls-sur-CentOS-5 HTTP/1.0   1829
GET /post/2008/06/20/Utilisateurs-virtuels-sous-CentOS-5-avec-base-de-donnees-MySQL HTTP/1.0    1193

On note ici l'utilisation de l'option "-F" de awk pour changer le motif du séparateur de champ, ce qui me permet d'avoir des champs avec espace.

Enfin, dernier exemple, trions maintenant les User-Agents :

root@dev:/var/log/httpd# awk -F "\"" '{frequencies[$6]++;} END {for (field in frequencies) printf "%d\t%s\n" , frequencies[field], field;}' < ./access.log | sort -nr | head -5
10539   Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) VoilaBot BETA 1.2 (support.voilabot@orange-ftgroup.com)
6493    Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; SPV M700; OpVer 19.123.2.733) OrangeBot-Mobile 2008.0 (mobilesearch.support@orange-ftgroup.com)
4188    Mozilla/5.0 (compatible; Yahoo! Slurp/3.0; http://help.yahoo.com/help/us/ysearch/slurp)
3269    msnbot/2.0b (+http://search.msn.com/msnbot.htm)
3017    Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

J'ai décidé cette fois-ci d'afficher le nombre d'occurrences à gauche, car le nombre de champs (séparés par un espace) n'est plus fixe dans le cas des User-Agents. Mais au moment d'écrire cette phrase, j'ai de nouveau parcouru la page de manuel de sort et j'ai pu voir qu'il est possible de spécifier le séparateur de champ (option -t). J'ai utilisé le caractère $ pour séparer le nombre d'occurrences du libellé du User-Agent, suivi de 'tr' pour le remplacer par une tabulation :

awk -F "\"" '{frequencies[$6]++;} END {for (field in frequencies) printf "%s$%d\n" , field , frequencies[field];}' < ./access.log | sort -nr -t$ -k 2,2| tr $ "\t" | head -5
Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) VoilaBot BETA 1.2 (support.voilabot@orange-ftgroup.com)  10539
Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320; SPV M700; OpVer 19.123.2.733) OrangeBot-Mobile 2008.0 (mobilesearch.support@orange-ftgroup.com)        6493
Mozilla/5.0 (compatible; Yahoo! Slurp/3.0; http://help.yahoo.com/help/us/ysearch/slurp) 4188
msnbot/2.0b (+http://search.msn.com/msnbot.htm) 3269
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)        3017

Le choix du caractère de séparateur de champ est discutable, mais il ne change pas qu'après réflexion, l'affichage de la commande précédente me semble plus lisible. Et je pense qu'afficher le nombre d'occurences en permier sera plus lisible dans d'autres cas, comme le referer ou l'URL.

lundi, décembre 5 2011

Couleurs dans le terminal

Pour beaucoup de gens, la vue d'un terminal, en général en texte blanc sur fond noir (mais aussi en noir sur fond blanc ou beige sur certaines distributions), peut s'avérer très peu attrayante. En ce qui me concerne je me suis accommodé et j'ai fini par apprécier le terminal, grâce à quelques modifications cosmétiques apportant de la couleur. Je trouve ainsi mon environnement beaucoup plus lisible.

Le prompt

Dans bash (et probablement dans d'autres shells), il est possible de modifier l'apparence du prompt via la variable d'environnement PS1. Regardons quelle est la valeur de PS1 sur un système CentOS (les simples quotes visent à montrer qu'il y a un espace à la fin) :

[root@orgrimmar ~]# echo "PS1 vaut: '$PS1'"
PS1 vaut: '[\u@\h \W]\$ '

Il est possible d'en modifier l'apparence avec de nombreux paramètres, tels que la couleur, certaines informations. Par exemple, j'ai choisi d'appliquer la personnalisation suivante sur tous mes environnements bash :

nils@arreat:~$ echo "PS1 vaut: '$PS1'"
PS1 vaut: '\[\]\u\[\]@\[\]\h\[\]:\w\[\]\$\[\] '

Ce qui est gênant, c'est que si ma variable d'environnement possède des couleurs, leurs codes ne sont pas affichés mais directement interprétés. En réalité, ma variable PS1 vaut :

# récupartion depuis mon bashrc :
PS1=$'\[\E[01;32m\]\u\[\E[0m\]@\[\E[01;36m\]\h\[\E[0m\]:\w\[\E[01;32m\]\$\[\E[0m\] '

Le nom d'utilisateur et le signe "$" sont verts, tandis que le nom d'hôte est bleu. J'ai réalisé une variante pour l'utilisateur root où le vert est remplacé par du rouge.

Pour essayer, rien de plus simple : il suffit d'exporter la variable d'environnement PS1 avec une nouvelle valeur :

[root@orgrimmar ~]# echo "PS1 vaut: '$PS1'"
PS1 vaut: '[\u@\h \W]\$ '
[root@orgrimmar ~]# PS1=$'\[\E[01;32m\]\u\[\E[0m\]@\[\E[01;36m\]\h\[\E[0m\]:\w\[\E[01;32m\]\$\[\E[0m\] '
root@orgrimmar:~# echo "PS1 vaut: '$PS1'"
PS1 vaut: '\[\]\u\[\]@\[\]\h\[\]:\w\[\]\$\[\] '

Il est possible d'aller plus loin, comme de remplacer \h par \H pour obtenir le nom complet de la machine, d'insérer la date, d'afficher le prompt en gras... Vous trouverez chez Nixcraft les différents codes pour démarrer et stopper une couleur, ainsi que pour la mise en gras.

Si vos expérimentations amènent un résultat peu plaisant, deux possibilités : la première consiste à appliquer de nouveau l'ancienne valeur PS1, si vous avez copié son contenu ailleurs, ou d'aller le chercher par exemple dans /etc/bashrc ; la deuxième consiste tout simplement à fermer puis relancer votre terminal.

Une fois que votre nouveau prompt vous plaît, vous voulez rendre le changement définitif. Il est possible d'éditer son fichier .bashrc, .bash_profile ou .profile pour cela. Si vous souhaitez que ce changement soit effectif pour tous les utilisateurs, il est possible de modifier directement /etc/profile ou /etc/bashrc, mais je ne vous le recommande pas : il est possible de mal éditer le fichier et de supprimer accidentellement des commandes utiles, et donc de mettre en vrac son système.

Pour CentOS/RHEL/Fedora, j'ai pris l'habitude de créer un fichier nommé /etc/profile.d/prompt.sh : en effet, le fichier /etc/profile de ces distributions charge tous les .sh situés dans /etc/profile.d. Il devient donc aisé d'ajouter ou de retirer des personnalisations shell comme des alias, le prompt, et d'autres variables d'environnement qui affecteront tous les utilisateurs.

Pour NetBSD, j'ai choisi de créer un fichier /usr/pkg/etc/bashrc contenant ces personnalisations, et d'ajouter le contenu suivant dans /etc/profile (qui, par défaut, ne contient que des commentaires) :

if [ "${BASH_no}" != "no" ]; then
                 [ -r /usr/pkg/etc/bashrc ] && . /usr/pkg/etc/bashrc
fi

De la couleur dans ls

Selon votre système, cette option peut ne pas être disponible : cela fonctionne avec CentOS 4 et 5, mais pas avec NetBSD. Il s'agit tout simplement d'utiliser l'option --color, qui peut être complétée, par exemple --color=auto ou --color=tty. D'où viennent ces couleurs ? De la variable d'environnement LS_COLORS. On peut donc modifier cette variable pour afficher les couleurs différemment, et consulter la page de manuel de dircolors pour plus de détails.

Grep

La commande grep possède une option --color, parfois activée par défaut dans un alias sur certaines distributions. Elle colore en rouge la chaîne de caractères recherchée, que ce soit sous CentOS ou NetBSD.

Pages de manuel en couleur

most permet de visualiser un texte, comme more ou less. A la différence de ces deux derniers, most affiche les pages de manuel en couleur. Pour cela, vous pouvez utiliser la commande suivante :

PAGER=most man <votrecommande>

Pour que ce soit définitif, exportez la variable d'environnement PAGER=most. Attention toutefois, vérifiez que vous n'avez pas un PAGER=more qui traîne quelque part. Concernant la disponibilité du package, on peut le trouver dans pkgsrc ainsi que dans RPMForge.

Colorer ses fichiers de log

Un outil très pratique pour avoir des fichiers de log en couleurs est ccze. Il m'arrive de l'utiliser de la manière suivante :

tail -f /chemin/vers/mon/log/apache | ccze

Je peux aussi m'en servir sur un fichier qui n'est pas mis à jour en direct, en duo avec less :

ccze -A < monfichierdelog | less -R

Ce petit bijou connaît de nombreux formats de fichiers de log, et les rend du coup plus agréables à lire. C'est disponible dans pkgsrc et dans EPEL

Un top en couleur ?

Htop est une version améliorée de top qui, en plus d'afficher la couleur, affiche les taux d'occupation processeur et mémoire d'une manière un peu graphique. A noter cependant que cet outil est d'abord développé pour Linux, et qu'il faut, sous NetBSD, monter /proc avec l'option linux (celle-ci est cependant différente de la couche de compatibilité binaire linux). Htop est disponible dans pkgsrc et dans EPEL

Coloration syntaxique avec VIm

Vous trouvez vi trop morne et déprimant ? Installez VIm et activez la coloration syntaxique ! Souvent, seul vi est installé. Côté pkgsrc, le package se nomme vim et a pour dépendance vim-share. Côté Red Hat, on installera vim-enhanced (dispo dans les dépôts de base). Une fois ceci fait, ajoutez dans votre répertoire home un fichier .vimrc contenant au moins :

syn on
set nu

Ensuite, éditez un script shell, par exemple. Vous verrez la couleur et les numéros de ligne. Pour ceux qui comme moi on un fond noir ou sombre, on ajoutera la directive suivante à son .vimrc :

set bg=dark

La coloration syntaxique s'adaptera ainsi au fond de votre terminal.

Et voilà ! C'est Noël sur votre shell :-)

lundi, novembre 28 2011

Lancement de GNU Screen en arrière-plan

Les entrailles de GNU Screen (que j'abrègerai en screen par la suite) sont parfois difficiles à comprendre. L'histoire commence ainsi : je possède une machine NetBSD, un peu bruyante, que j'allume le matin au lever et que j'éteins le soir au coucher. J'utilise screen sur cette machine, et j'aimerais, par grosse fénéantise, que ce dernier se lance au démarrage de ma machine, en mode détaché. De la sorte, il ne me reste qu'à lancer un bon vieux screen -r lorsque que je m'y connecte et mon comportement ne change pas d'autres machines allumées 24h/24 : je me connecte, je screen -r et je suis prêt.

Jusque-là rien de bien particulier : un petit tour dans la page de manuel m'apprend que cela est déjà possible :

screen -d -m

Cette commande permet de faire en sorte qu'il démarre en mode détaché, et que c'est justement fait pour un éventuel script de démarrage. En bref, la paix dans le monde, mes amis :-)

Je me précipite donc sur ${EDITOR} et entame l'écriture épique d'un script shell qui va lancer screen en mode détaché sous l'identité de l'utilisateur que je suis, avec le fichier .screenrc qui convient. Le script fonctionne, le script fonctionne au démarrage de la machine (c'est mieux, hein ?), toujours la paix dans le monde, avec les oiseaux qui chantent, nous sommes dans un rêve :-)

Donc, plein d'illusions, je lance la commande screen -r . Et là, c'est le drame : le prompt de mon shell (bash) n'est pas coloré, et n'affiche pas le répertoire courant. Après avoir demandé conseil à mon moteur de recherche favori, je me rend compte que dans ce cas, screen a eu la bonne idée de remplacer la variable d'environnement PS1 (qui définit le prompt) par une valeur autre. D'où vient-elle ? Je ne le savais pas encore. J'ai essayé de redéfinir cette variable dans mon fichier de configuration .screenrc, sans succès. En désespoir de cause, je tente un unset PS1. Victoire ! J'ai mon prompt personnalisé ! je suis joie, bonheur, les oiseaux chantent, la paix dans le monde, tout ça tout ça...

Jusqu'à ce que j'édite un fichier texte. Et là, c'est le drame (à nouveau) : mon éditeur de texte, VIm, dispose d'une fonction de coloration syntaxique que j'active par défaut. C'est trèèèès pratique. J'active aussi la numérotation des lignes. Mais là, pas de couleur. Il s'agit pourtant d'un type de fichier connu. Je tente ma chance avec d'autres programmes disposant d'un affichage coloré, sans succès non plus. Après quelques bidouillages, je me rend compte qu'en changeant la variable d'environnement TERM de screen à xterm-color, j'obtiens à nouveau la couleur. En désespoir de cause j'ajoute export TERM=xterm-color au fichier /usr/pkg/etc/bashrc (ce qui m'évite de copier-coller un .bashrc dans le $HOME de mon utilisateur et de root), je relance le script et là : couleur :-)

Avec le recul de l'écriture de ce billet, je me suis rendu compte que lorsque j'utilise screen -d -m, ce dernier charge mon fichier .profile (qui charge .shrc). Ces deux fichiers m'ont posé problème dans le passé : par exemple .profile contient deux exports qui entrent conflit avec mon bashrc, export EDITOR=vi et export PAGER=more (j'utilise vim et most à la place). J'ai aussi remarqué la ligne suivante dans le fichier .shrc :

export PS1="$(whoami)@$(hostname -s)$ "

Tiens, c'est marrant, c'est exactement le prompt que j'avais lors de mon premier problème... ;-)

Bref, ma solution n'est peut-être pas la plus élégante, mais au moins ça fonctionne. Mais comme on me l'a fait remarquer il y a presque deux mois, sur les systèmes Unix : There Is More Than One Way To Do It (Il y a plus d'une façon de le faire).

lundi, novembre 21 2011

Ajouter des robots dans Awstats

Aujourd'hui un nouvel épisode de mon outil de statistiques web du moment, Awstats. Souvenez-vous, nous avons déjà rencontré ce logiciel à trois reprises :

Aujourd’hui attardons-nous sur une autre possibilité d'Awstats : la détection des robots et moteurs de recherches. Si vous avez déjà des statistiques en place, vous aurez noté que vous disposez d'une rubrique Visiteurs Robots/Spiders dans votre page. Awstats ne peut pas connaître tous les robots sur le marché, de nouveaux sont créés tandis que d'autres disparaissent. Certains sont dédiés à des moteurs de recherche, d'autres sont des logiciels téléchargeables, pour effectuer des recherches ou créer un aggrégateur de flux RSS. Lorsqu'Awstats repère un robot qu'il ne connait pas, il peut l'afficher de deux manières : Unknown robot (identified by 'bot*') ou bien Unknown robot (identified by '*bot'). Vous comprenez donc qu'il cherche juste le mot bot dans le User-agent laissé par votre visiteur dans les logs de votre serveur web.

Si vous regardez souvent les logs de votre serveur web (activité qui peut semble à première vue excentrique, mais Ô combien intéressante en réalité), vous trouverez sans doute un robot qui n'est pas connu d'Awstats. Ce billet prend l'exemple avec cplanet, un aggrégateur RSS utilisé en particulier par un certain planet BSD francophone.

Awstats stocke les noms des robots qu'il connaît dans un fichier nommé robots.pm. Ce fichier, dans le cas d'une installation via pkgsrc sous NetBSD se trouve à l'endroit suivant : /usr/pkg/awstats/cgi-bin/lib/robots.pm. Effectuons-donc une copie de sauvegarde de ce fichier :

root@vhost:/usr/pkg/awstats/cgi-bin/lib# cp -vp robots.pm robots.pm.bak
robots.pm -> robots.pm.bak

Profitons-en pour copier la sauvegarde dans un autre fichier, que nous allons modifier :

root@vhost:/usr/pkg/awstats/cgi-bin/lib# cp -vp robots.pm.bak robots.pm.custom
robots.pm.bak -> robots.pm.custom

Avant de modifier le fichier, jetons un oeil aux logs (Apache dans mon cas) :

1.2.3.4 - - [04/May/2011:16:30:48 +0200] "GET /feed/atom HTTP/1.1" 200 105441 "-" "cplanet/0.6"

Le User-agent de cplanet est donc : "cplanet/0.6". Maintenant éditons notre robots.pm.custom. En lisant les commentaires on se rend compte que le fichier est organisé en plusieurs listes. Il faut donc ajouter notre nouveau robot dans deux d'entres elles, RobotsSearchIDOrder_list<X> (où <X> désigne un chiffre) et RobotsHashIDLib. J'ai choisi d'ajouter mon robot dans RobotsSearchIDOrder_list2, qui contient des robots peu connus. Je suis allé à la fin de cette liste mais je n'ai pas ajouté mon robot en toute fin de liste mais juste après un robot nommé zeus. Pourquoi ? Il s'avère que certains noms de robots sont des expressions régulières, et doivent être en fin ou en début de liste. Donc je ne souhaite pas les perturber.

Voici les lignes contenant zeus et cplanet (aux alentours de la ligne 965) :

'zeus',
'cplanet',

Passons à la deuxième liste, qui commence aux alentours de la ligne 1000. Vers la ligne 1320, on peut lire le commentaire suivant : Other robots reported by users. Je suis donc à nouveau descendu jusqu'à retrouver zeus et j'ai ajouté de cette manière cplanet, juste en-dessous :

'cplanet','<a href="http://git.etoilebsd.net/cplanet/" title="A rss feed agregator that generate static html pages" target="_blank">CPlanet RSS agregator</a>',

J'ai donc créé un identifiant pour mon robot, qui est en fait une chaîne de caractères basée sur le User-agent, et ai ajouté un lien vers l'URL du robot pour savoir d'où il vient, ainsi qu'un texte descriptif, en anglais. Notez bien le format de séparation, et que la virgule à la fin est obligatoire.

Maintenant que notre fichier personnalisé est prêt, reste à le mettre en production :

root@vhost:/usr/pkg/awstats/cgi-bin/lib# rm -vf robots.pm && ln -sv robots.pm.custom robots.pm
robots.pm
robots.pm -> robots.pm.custom

Si jamais Awstats doit être mis à jour, celui-ci écrasera le lien symbolique. Il faudra donc vérifier (avec la commande diff par exemple) si le projet Awstats a mis à jour de son côté le fichier, et reporter nos modifications dans une copie du nouveau. Pensez d'ailleurs à proposer vos nouveaux robots sur le bug tracker d'Awstats sur Sourceforge

lundi, octobre 17 2011

Installation de phpMyAdmin sur CentOS 6 - suite

Résumé de l'épisode précédent

Lors de mon précédent billet sur l'installation et la configuration de phpMyAdmin sur CentOS 6, nous avions obtenu une installation fonctionnelle, mais perfectible. Nous allons voir ensemble comment rendre l'installation plus confortable et tenter de la sécuriser un peu.

Authentification par cookie

Lors de la connexion à phpMyAdmin, c'est une authentification de type HTTP qui est envoyée. Sachant que nous n'avons pas encore activé HTTPS, les identifiants circulent en clair sur le réseau. De plus, à chaque fois qu'on ferme la fenêtre ou l'onglet du navigateur, il faut s'authentifier à nouveau. Le cookie devrait donc aider. Pour activer ce mécanisme, éditons le fichier de configuration de phpMyAdmin :

[root@crashtest ~]# vi /etc/phpMyAdmin/config.inc.php

A la ligne 41, on trouvera l'expression suivante :

$cfg['Servers'][$i]['auth_type']     = 'http';      // Authentication method (config, http or cookie based)?

Il suffit donc de remplacer 'http' par 'cookie' puis d'enregistrer le fichier. Le paramètre 'config' est à manipuler avec la plus grande précaution, et nécessite de renseigner les identifiants dans les champs suivants, ce qui n'est pas du tout sécurisé à mon sens. Une fois la modification effectuée, une (jolie ?) page d'identification devrait apparaître en lieu et place de l'horrible notification du navigateur demandant le login et le mot de passe. En prime, il est possible de choisir la langue :-)

Maintenant, un message assez étrange risque d'apparaître lors de vos prochaines connexions, en bas de l'interface de phpMyAdmin : Vous devez ajouter dans le fichier de configuration une phrase de passe secrète (blowfish_secret). Allons donc éditer de nouveau le fichier de configuration, à la ligne 14 :

$cfg['blowfish_secret'] = ''; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */

Et entre les guillemets simple, on insère une phrase de passe. Quelques exemples :

  • je vois un gnou faire de la bicyclette
  • je ne sais pas programmer en python (ou perl, java, c, ruby, ce que vous voulez)
  • aieruhgpauOUGYVaerhg 07856qorieghg (oui, l'aléatoire fonctionne aussi)

Le but n'est pas de fournir une phrase intelligible ou facilement mémorisable, mais une suite de caractère assez longue pour chiffrer le mot de passe dans le cookie. Il ne sera pas nécessaire de réutiliser cette phrase de passe.

HTTPS

L'authentification par cookie apporte un mieux, mais celui-ci peut toujours être intercepté et rejoué par quelqu'un de malintentionné. De plus l'intercepteur pourra examiner le traffic et en retirer les commandes jouées, ou pourquoi pas le contenu des base de données. L'un des moyens d'empêcher cette interception est de chiffrer le trafic entre la machine cliente et le serveur hébergeant phpMyAdmin et MySQL. Pour cela nous allons activer mod_ssl dans Apache afin de naviguer en HTTPS dans phpMyAdmin.

Installons donc mod_ssl :

[root@crashtest ~]# yum install mod_ssl
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * epel: mirrors.ircam.fr
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package mod_ssl.x86_64 1:2.2.15-5.el6.centos set to be updated
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package        Arch          Version                         Repository   Size
================================================================================
Installing:
 mod_ssl        x86_64        1:2.2.15-5.el6.centos           base         85 k

Transaction Summary
================================================================================
Install       1 Package(s)
Upgrade       0 Package(s)

Total download size: 85 k
Installed size: 183 k
Is this ok [y/N]: y
Downloading Packages:

mod_ssl-2.2.15-5.el6.centos.x86_64.rpm                   |  85 kB     00:00

Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction

Installing     : 1:mod_ssl-2.2.15-5.el6.centos.x86_64                     1/1

Installed:
  mod_ssl.x86_64 1:2.2.15-5.el6.centos

Complete!

Relançons Apache :

[root@crashtest ~]# service httpd restart
Arrêt de httpd :                                           [  OK  ]
Démarrage de httpd :                                       [  OK  ]

Et rendons-nous sur phpMyAdmin, en HTTPS. Dans mon cas l'url est https://crashtest/phpmyadmin/ . Un message du navigateur signale alors que le certificat utilisé pour se connecter est auto-signé.

Il est courant d'accepter le certificat et de le mémoriser : à plus forte raison s'il s'agit d'une machine de tests ou de développement, il suffit de s'assurer que le certificat ne changera pas en le mémorisant dans le navigateur; si jamais ce message devait à nouveau s'afficher, soit vous avez réinstallé le serveur ou changé les certificats, soit un petit malin tente une attaque de type "homme du milieu" (man in the middle en anglais).

Il est aussi possible d'accepter le certificat sans pour autant le mémoriser, et (faire) créer les certificats adéquats, selon votre type d'organisation ; les grosses entreprises possèdent leur propre autorité de certification et la déploient sur leurs postes de travail. Si votre serveur est directement accessible depuis Internet, de nombreux prestataires proposent, gratuitement ou non, de générer un certificat qu'il vous faudra ensuite installer en lieu et place de ceux par défaut. Cela peut vous éviter de vérifier manuellement sur chaque nouvelle machine cliente qu'il s'agit du bon certificat.

La mise en œuvre détaillée d'un serveur HTTPS et d'une infrastructure de gestion de certificats SSL d'entreprise (appelée aussi PKI de l'anglais Public Key Infrastructure) ne fait pas partie des objectifs de ce billet, par conséquent elle est laissée en exercice au lecteur.

Notre serveur accepte donc les connexions HTTP en clair et les connexions HTTPS chiffrées.

Pare-feu

En plus de chiffrer des connexions, il est possible de les filtrer. Dans le précédent billet, nous avons vu qu'Apache peut interdire ou accepter certains clients suivant leur adresse IP. Il est possible, avec un pare-feu (firewall en anglais), de filtrer les connexions Apache comme MySQL ou SSH et d'effectuer un contrôle plus fin sur les connexions.

Sur un système GNU/Linux, en particulier CentOS, le pare-feu de référence est Netfilter (qui fournit entre autres la commande iptables). La plupart des autres projets de pare-feu pour GNU/Linux sont généralement des surcouches à Netfilter.

Attention ! il est très facile, lorsqu'on manipule des règles de filtrage de connexions réseau, de scier la branche sur laquelle on est assis. Si bloquer accidentellement les connexions réseau lorsqu'on est devant la machine n'est pas bien grave, couper la connexion SSH qu'on utilise oblige à se déplacer, couper le pare-feu une fois devant la machine, puis repartir à son poste et se reconnecter.

Pour éviter ce genre de désagrément, il est possible de planifier une tâche qui coupe le firewall, par exemple toutes les 10 minutes. Ainsi, dès qu'on se rend compte que la machine ne répond plus à rien sur le réseau, il ne reste qu'à attendre 10 minutes tout au plus pour que la machine soit à nouveau accessible. L'inconvénient est qu'il faut réussir à faire ses modifications en moins de 10 minutes ! Nous allons donc éditer la crontab :

[root@crashtest ~]# crontab -e

Il est fort probable qu'elle soit vide, puisqu'il s'agit de la crontab de root et que la machine est fraîchement installée. Ajoutons la ligne suivante :

*/10    *       *       *       *       /etc/init.d/iptables stop > /dev/null 2>&1

Et voilà ! Toutes les 10 minutes, le pare-feu sera désactivé. Le temps d'effectuer une modification, et de la valider. Attention cependant, une fois que les changements seront validés, penser à effacer cette ligne, ou à la commenter. Pour plus d'information : la page de manuel. Une fois le garde-fou mis en place, passons aux choses sérieuses : définir les règles de filtrage à mettre en place, puis les mettre en place.

Afin de rester dans les clous de la distribution, nous n'allons pas créer un script de pare-feu personnalisé, mais utiliser le fichier déjà en place pour sauvegarder les règles. Ce fichier est /etc/sysconfig/iptables, mais comme indiqué en anglais en tête de ce fichier, il n'est pas recommandé de l'éditer manuellement. Nous allons donc lancer le pare-feu, ajouter des règles avec la commande iptables, vérifier leur bon fonctionnement, les sauvegarder, et vérifier la sauvegarde.

Lancement du pare-feu :

[root@crashtest ~]# service iptables start
iptables : Application des règles du pare-feu :            [  OK  ]

Vérification des règles actuellement activées :

[root@crashtest ~]# service iptables status
Table : filter
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
2    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0
3    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
4    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22
5    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:25
6    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination
1    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

Et si on tente de se connecter à phpMyAdmin, cela ne fonctionne plus. Il faut donc accepter les connexions vers le port 80 (HTTP) et 443 (HTTPS). Nous allons insérer dans la chaine INPUT avant la règle numéro 5 (celle qui accepte le port 25 tcp) une règle acceptant le port 80 :

[root@crashtest ~]# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT

Si on se connecte à phpMyAdmin, cela fonctionne en HTTP, mais pas en HTTPS. Continuons, cette fois insérons notre règle avant la numéro 6 (décalage oblige du fait de notre insertion précédente) :

[root@crashtest ~]# iptables -I INPUT 6 -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT

Voilà, maintenant nous accédons à phpMyAdmin en HTTPS. Vérifions les règles en mémoire pour comparaison avec la situation précédente :

[root@crashtest ~]# service iptables status
Table : filter
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
2    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0
3    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
4    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22
5    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:80
6    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:443
7    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:25
8    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination
1    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

A noter que la commande iptables -L -n donne le même résultat, et pourrait servir sur d'autres distributions Linux. A présent, sauvegardons notre configuration :

[root@crashtest ~]# service iptables save
iptables : Sauvegarde des règles du pare-feu dans /etc/sysconfig/iptables : [  OK  ]

Vérifions la sauvegarde :

[root@crashtest ~]# cat /etc/sysconfig/iptables
# Generated by iptables-save v1.4.7 on Thu Sep 22 20:34:19 2011
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [1118:858094]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 25 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT
# Completed on Thu Sep 22 20:34:19 2011

On peut donc voir que les règles acceptant les ports 80 sont bien sauvegardées. La règle autorisant le port 25 n'est pas utile, elle fut ajoutée en exemple lors du billet sur une installation minimaliste de CentOS 6. Le retrait de cette règle est laissé en exercice au lecteur ;-)

Une fois les règles en place donnant satisfaction, il faut penser à retirer le garde-fou en éditant la crontab : on peut alors supprimer la ligne désactivant iptables, ou la mettre en commentaire en place le caractère "#" devant. Après le retrait du garde-fou, on peut activer le pare-feu au démarrage :

[root@crashtest ~]# chkconfig --list iptables
iptables        0:arrêt 1:arrêt 2:arrêt 3:arrêt 4:arrêt 5:arrêt 6:arrêt
[root@crashtest ~]# chkconfig iptables on
[root@crashtest ~]# chkconfig --list iptables
iptables        0:arrêt 1:arrêt 2:marche        3:marche        4:marche        5:marche        6:arrêt

Base de données phpMyAdmin

phpMyAdmin est maintenant un outil complet avec de nombreux paramètres. Certains peuvent être utilisés via le fichier de configuration, mais pour d'autres, une base de données est nécessaire. D'ailleurs, selon le paquet phpMyAdmin installé (une version à jour est arrivée pendant l'écriture des deux billets), vous pouvez avoir le message suivant en bas de l'interface : Le stockage de configurations phpMyAdmin n'est pas complètement configuré, certaines fonctionnalités ont été désactivée. Pour en connaître la raison, cliquez ici. Dans la version plus récente, cet avertissement a été retiré.

Utilisons phpMyAdmin pour créer un nouvel utilisateur dit de contrôle (via l'onglet Privilèges), et appelons-le tout simplement phpmyadmin. Le paramètre client est Local, et on génèrera le mot de passe aléatoirement. Pensez à copier ce mot de passe ailleurs, on va en avoir besoin un peu plus tard. Toujours dans l'interface de création de l'utilisateur, cochons l'option Créer une base portant son nom et donner à cet utilisateur tous les privilèges sur cette base. Enfin, cliquons sur le bouton du bas : Créer un compte d'utilisateur. Une autre manipulation est nécessaire car l'utilisateur de contrôle a besoin d'un peu plus de droits. Pour aller plus vite, rechargeons les privilèges puis cliquons sur l'onglet SQL et entrons le texte suivant dans le champ (j'espère que vous avez bien copié le mot de passe généré de tout à l'heure ;-)):

GRANT USAGE ON mysql.* TO 'phpmyadmin'@'localhost' IDENTIFIED BY 'motdepassealeatoire';
GRANT SELECT (
    Host, User, Select_priv, Insert_priv, Update_priv, Delete_priv,
    Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv,
    File_priv, Grant_priv, References_priv, Index_priv, Alter_priv,
    Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv,
    Execute_priv, Repl_slave_priv, Repl_client_priv
    ) ON mysql.user TO 'phpmyadmin'@'localhost';
GRANT SELECT ON mysql.db TO 'phpmyadmin'@'localhost';
GRANT SELECT ON mysql.host TO 'phpmyadmin'@'localhost';
GRANT SELECT (Host, Db, User, Table_name, Table_priv, Column_priv)
    ON mysql.tables_priv TO 'phpmyadmin'@'localhost';

Cliquons sur Exécuter et on nous signale que MySQL a retourné des résultat vides. Pensons à recharger les privilèges (dans l'onglet Privilèges Encore une chose. Il nous faut peupler la base de données créée pour phpMyAdmin. Pour cela, revenons dans le shell de notre serveur et utilisons le fichier SQL fourni par phpMyAdmin :

[root@crashtest ~]# mysql -u root -p < /usr/share/phpMyAdmin/scripts/create_tables.sql

Maintenant éditons à nouveau le fichier de configuration de phpMyAdmin :

[root@crashtest ~]# vi /etc/phpMyAdmin/config.inc.php

Et renseignons aux lignes 34 et 36 l'utilisateur de contrôle et son mot de passe :

$cfg['Servers'][$i]['controluser']   = 'phpmyadmin';          // MySQL control user settings
                                                    // (this user must have read-only
$cfg['Servers'][$i]['controlpass']   = 'motdepassealeatoire';          // access to the "mysql/user"
                                                    // and "mysql/db" tables).
                                                    // The controluser is also
                                                    // used for all relational
                                                    // features (pmadb)

Une fois le fichier enregistré et déconnecté puis reconnecté à phpMyAdmin, nous pouvons utiliser toutes les possibilités de cet outil !

SELinux

J'avoue ne pas être familier avec SELinux. Je me suis contenté d'éditer /etc/sysconfig/selinux et de passer le paramètre SELINUX à enforcing. Un reboot plus tard, SELinux est activé, httpd, mysqld sont lancés, et phpMyAdmin est accessible !

- page 1 de 14