Rechercher des fichiers dans une arborescence bureautique avec mlocate et php

Objectif

Disposer d’un moteur de recherche dans l’arborescence bureautique, qui permette de retrouver un fichier ou un dossier, à partir de son nom.

Dans un second temps, possibilité d’ouvrir le fichier en consultation (mode web).

L’outil utilise deux logiciels : mlocate pour effectuer la recherche, et PHP pour afficher le résultat.

Configuration de Mlocate

Mlocate est un outil de recherche disponible dans Linux, qui fonctionne avec deux commandes :updatedb, qui permet de mettre à jour la base de données, et locate, qui permet de retrouver un fichier à partir d’un morceau de son nom.

La commande locate sera utilisée dans le script PHP.

Script de mise à jour de la base de données

/root/updatedbDonnees.sh
#!/bin/bash
CHEMIN="/opt/donnees"
DATABASE="/var/lib/mlocate/donnees.db"
EXCLUSION="/opt/donnees/dossier_exclu /opt/donnees/.recycle"
updatedb -U $CHEMIN -e "$EXCLUSION" -l 0 -o $DATABASE
chgrp www-data /var/lib/mlocate/donnees.db

Le programme permet d’indiquer des dossiers qui ne vont pas être indexés. La base de données créée est spécifique à la recherche envisagée.

Pour permettre au serveur Apache de consulter le fichier, les droits d’accès lui sont donnés.

Programmation du script

L’exécution ne dure que quelques dizaines de secondes, il peut être programmé relativement souvent. Actuellement, il est exécuté à 10:00, 13:00, 16:00 et 19:00, par l’intermédiaire de la crontab :

crontab -l
00 10,13,16,19 * * * /root/updatedbDonnees.sh

Interrogation de la base de données

Script PHP – index.php

<?php
/**
* adapter le cas echeant le script)
* Realisation : Eric Quinton
* Date : 01/12/2011
*/
/*
* Donnees specifiques a l'implementation
*/
$database = "/var/lib/mlocate/donnees.db";
$locate = "locate -i -d ".$database;
$titre = "Recherche de fichiers dans l'arborescence bureautique ";
$racine = "/opt/donnees/";
$racineWeb = "https://donnees.mondomaine.com/";
$imageLien = "modifier.png";
$CSS = "blue.css";
/*
* Preparation de la page web
*/
echo '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
echo '<html>';
echo '<head>';
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">';
echo "<title>".$titre."</title>";
echo '<link rel="stylesheet" href="'.$CSS.'" type="text/css">';
echo '</head>';
echo '<body>';
echo "<h1>".$titre."</h1>";
echo '<form name="recherche" method="GET" action="'.$_SERVER["SCRIPT_NAME"].'">';
echo '<table>';
echo '<tr>';
echo '<td class="dataModif">Partie du nom du fichier à rechercher : ' ;
echo '<input name="nom" size="45" value="'.$_REQUEST["nom"].'">';
echo '<input type="submit" value="Rechercher...">';
echo '<br><div class="message">(mots de 3 lettres au moins)</div>';
echo '</td>';
echo '</tr>';
echo '</table>';
echo '</form>';
/*
* Gestion du retour du formulaire
*/
if (strlen($_REQUEST["nom"]) > 3) {
/*
* Preparation de la requete
*/
$requete = $locate;
/*
* Ajout de chaque mot dans la requete d'extraction
*/
$listeMot = explode(" ",trim($_REQUEST["nom"]));
$i = 0;
foreach($listeMot as $key=>$value) {
if (strlen($value)>2) {
if ($i == 0) {
$requete.=' '.$value;
}else{
$requete.='|grep -i '.$value;
}
$i++;
}
}
exec($requete,$result);
/*
* Preparation du tableau et ecriture
*/
if (count($result)>0) {
/*
* Ecriture de l'aide
*/
echo "<div class='message'>Les fichiers ne sont accessibles (en lecture) que si vous disposez des droits adéquats
et que si le dossier considéré peut être consulté en mode web</div>";
$pos = strlen($racine);
/*
* Traitement de chaque ligne
*/
foreach ($result as $key=>$value) {
$ligne = substr($value,$pos);
$lien = $racineWeb.$ligne;
echo '<a href="'.$lien.'">'.
'<img src="'.$imageLien.'" alt="consulter le fichier" title="consulter le fichier" height="20">'.
'</a> '.$ligne.'<br>';
}
}else{
echo "Aucun résultat retourné...";
}
}
echo '</body>';
echo '</html>';
?>

La recherche s’effectue d’abord en exécutant une commande locate à partir du premier mot saisi, puis des grep successifs pour chaque mot supplémentaire. Les commandes sont non sensibles à la casse. Chaque mot doit contenir au moins 3 lettres pour être pris en compte.

Le script nécessite deux fichiers complémentaires : un icône (modifier.png), et une feuille de style (blue.css), qui sont tous les deux présents dans le même dossier.

Le script étant placé dans la partie privée de l’intranet, seuls les agents de la structure peuvent y accéder.

Les liens html renvoient vers l’arborescence, protégée par la configuration spécifique d’Apache.

Création d’un alias DNS

Nous avons créé un alias DNS pour permettre la recherche à partir de l’entréehttps://donnees.mondomaine.com

Création d’un certificat spécifique pour le nouvel alias

L’accès à l’arborescence s’effectuant en mode https, un nouveau certificat a été généré (cf. documentation correspondante). Le certificat est utilisé dans la configuration spécifique du site « alias » (cf. ci-dessous).

Configuration spécifique d’Apache

L’accès à l’arborescence bureautique, depuis Apache, impose deux mécanismes :

d’une part, le compte d’exécution d’Apache (www-data) doit avoir un accès en lecture aux dossiers accessibles ;

d’autre part, les dossiers doivent être protégés, en limitant les accès uniquement aux agents qui disposent d’un compte dans l’annuaire, et qui font partie des groupes autorisés pour chaque dossier (directive directory du ou des fichiers de configuration).

Voici le contenu du fichier décrivant la configuration « racine » de l’arborescence considérée, avec contrôle des groupes autorisés à partir de l’annuaire LDAP :

sites-available/90_donnees.conf 
<VirtualHost *:80>
ServerAdmin adresse@mondomaine.com
ServerName donnees.mondomaine.com
ServerPath /donnees.mondomaine.com
RewriteEngine on
RewriteRule ^ https://donnees.mondomaine.com%{REQUEST_URI} [R]
</virtualHost>
<directory /opt/donnees>
options Indexes
order allow,deny
allow from all
allowOverride all
AuthType basic
AuthName mondomaine
AuthBasicProvider ldap
AuthLDAPURL "ldaps://ldap.mondomaine.com/ou=people,ou=service,o=societe,c=fr?uid?sub"
AuthLDAPGroupAttributeIsDN off
AuthLDAPGroupAttribute memberUid
require ldap-group cn=groupe_autorise,ou=Group,ou=service,o=societe,c=fr
</directory>
<VirtualHost *:80>
ServerName donnees
ServerPath /donnees
RewriteEngine on
RewriteRule ^ https://donnees.mondomaine.com%{REQUEST_URI} [R]
</VirtualHost>
<VirtualHost *:443>
ServerName donnees.mondomaine.com
ServerPath /donnees.mondomaine.com
DocumentRoot /opt/donnees
SSLEngine on
SSLCertificateFile /etc/ssl/certs/donnees_mondomaine.pem
SSLCertificateKeyFile /etc/ssl/private/serveur_mondomaine.pem
SSLCACertificateFile /etc/ssl/certs/certificat_racine_autorite_certification.crt
</VirtualHost>

Parlons sécurité...

Si vous souhaitez pouvoir ouvrir les fichiers en mode web, vous devez impérativement donner les droits de lecture (voire d’écriture, si vous activez le webdav) au compte www-data dans votre arborescence bureautique.

Dans ce cas là, les droits d’accès sont définis, pour Apache, dans les fichiers de configuration (directivedirectory), ou par l’intermédiaire de fichiers .htaccess (déconseillé dans ce cas de figure, les utilisateurs pouvant les supprimer ou les modifier).

Si vous activez PHP dans Apache dans ce serveur, vous créez alors une magnifique brèche dans la sécurité... Je m’explique...

N’importe quel utilisateur peut déposer, dans son arborescence, un fichier php, qui contiendrait, par exemple, le code suivant :

exec("cat /opt/donnees/dossier_prive/fichier_confidentiel.txt", $result);
print_r($result);

affichera le contenu du fichier, tout simplement parce que la commande exec fonctionne avec les droits de l’utilisateur www-data, et non de celui qui est derrière le clavier !

La seule solution simple pour colmater cette faille, dite d’attaque par un homme dans la place (man in the middle en anglais), est d’inactiver le mode PHP dans le serveur Apache de votre serveur bureautique (a2dismod php).

Pour en revenir à notre moteur de recherche, vous devrez donc déployer l’interface de consultation dans un autre serveur. Quant à l’accès au fichier d’index /var/lib/mlocate/donnees.db, deux possibilités s’offrent à vous :

soit vous réalisez un montage nfs ;

soit vous réalisez une copie de l’index une fois qu’il a été généré, vers le serveur web, par la commande scp. Cette solution a l’avantage d’être plus rapide à l’exécution, il n’y a pas besoin d’accès réseau pour interroger l’index.

Mais surtout, n’activez pas le mode php dans votre serveur bureautique !