• 15 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 14/09/2023

Mettez en pratique : Administration de notre solution de messagerie

Dans ce chapitre, nous allons créer une interface web qui va communiquer avec notre système Linux pour configurer les services que nous avons installés.

Nous aurions pu utiliser des outils existants qui font la même chose, souvent mieux d’ailleurs, comme postfix.admin ou d’autres outils en ligne d’administration. Mais vous êtes ici pour apprendre et le fait de fabriquer nous-mêmes nos propres interfaces d’administration vous permettra à l’avenir trois choses importantes :

  • Comprendre comment fonctionnent des outils web que vous utilisez

  • Modifier les outils web que vous utilisez pour leur faire faire exactement ce que vous voulez

  • Créer vos propres outils web aux petits oignons ! 

Donc accrochez-vous, nous allons créer notre propre interface d’administration maison !

Comment fonctionne une interface d’administration ?

Ce n’est pas sorcier !

Il s’agit simplement de pages web qui contiennent des formulaires. Vous pouvez entrer des informations dans ces formulaires, comme une adresse mail à créer. Une fois le formulaire envoyé, la page PHP peut traiter l’information reçue et exécuter une action. Cette action peut être d’ajouter une information à la base de données, ou même d’exécuter un script shell au niveau du serveur pour réaliser une action au niveau du système d’exploitation.

Dans un premier temps, nous allons interagir avec la base de données pour ajouter des utilisateurs à notre solution de messagerie.

Ajout d’utilisateurs de messagerie à la base de données

Nous allons créer une page web qui contiendra un formulaire dans lequel nous indiquerons l’adresse à ajouter, ainsi que le mot de passe.

Nous allons commencer par spécifier l’endroit où sera situé notre site web. Pour l’instant, nous avons déjà un virtualhost pour RainLoop. Nous pourrions tout à fait en créer un nouveau. Cependant, notre machine n’hébergeant pas d’autres sites pour l’instant, nous pourrons utiliser le virtualhost par défaut et donc placer nos pages dans /var/www/html.

Notre première page sera index.php qui, pour l’instant, contiendra le strict minimum afin d’avoir une page HTML5 valide qui utilise Bootstrap. Pour cela, nous allons dans les pages exemples de Bootstrap et prenons le premier Starter Template qui correspond à une page simple avec un menu de navigation.

J’ai juste changé au passage les sources CSS de Bootstrap afin d’utiliser les CDN de Bootstrap plutôt que de les télécharger en local.

Voici le contenu de notre page index.php :

<!doctype html>
<html lang="en">
 <head>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 <meta name="description" content="Notre premiere interface d’administration">
 <meta name="author" content="Vous">

 <title>Starter Template for Bootstrap</title>

 <!-- Bootstrap core CSS -->
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
 <!-- Custom styles for this template -->
 <link href="starter-template.css" rel="stylesheet">
 </head>

 <body>

 <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
 <a class="navbar-brand" href="#">Navbar</a>
 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
 <span class="navbar-toggler-icon"></span>
 </button>

 <div class="collapse navbar-collapse" id="navbarsExampleDefault">
 <ul class="navbar-nav mr-auto">
 <li class="nav-item active">
 <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a>
 </li>
 <li class="nav-item">
 <a class="nav-link" href="#">Link</a>
 </li>
 <li class="nav-item">
 <a class="nav-link disabled" href="#">Disabled</a>
 </li>
 <li class="nav-item dropdown">
 <a class="nav-link dropdown-toggle" href="http://example.com" id="dropdown01" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Dropdown</a>
 <div class="dropdown-menu" aria-labelledby="dropdown01">
 <a class="dropdown-item" href="#">Action</a>
 <a class="dropdown-item" href="#">Another action</a>
 <a class="dropdown-item" href="#">Something else here</a>
 </div>
 </li>
 </ul>
 <form class="form-inline my-2 my-lg-0">
 <input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
 <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
 </form>
 </div>
 </nav>

 <main role="main" class="container">

 <div class="starter-template">
 <h1>Bootstrap starter template</h1>
 <p class="lead">Use this document as a way to quickly start any new project.<br> All you get is this text and a mostly barebones HTML document.</p>
 </div>

 </main><!-- /.container -->
 </body>
</html>

Par ailleurs, il y a un tout petit fichier CSS à ajouter afin que le rendu soit correct. Créez donc le fichier starter-template.css dans /var/ww/html et mettez le contenu suivant :

body {
 padding-top: 5rem;
 }
.starter-template {
 padding: 3rem 1.5rem;  text-align: center;
 }

Nous allons pouvoir regarder tout de suite ce que cela donne sur notre site web :

Page simple du template starter-template de bootstrap
Notre première page web

Bravo ! Notre première page existe.

Bon, pour l’instant, elle possède beaucoup de parties inutiles, mais nous ferons le ménage plus tard.

Nous allons créer notre formulaire afin de pouvoir ajouter des adresses mail.

Afin de faire cela, nous allons ajouter notre formulaire dans la partie main de notre page :

<main role="main" class="container">

 <div class="starter-template">
 <h1>Page d’ajout d’utilisateurs de messagerie</h1>
 <form>
 <div class="form-group">
 <input type="email" class="form-control" id="exampleInputEmail1" name="email" aria-describedby="emailHelp" placeholder="Entrez l’adresse mail a ajouter">
 </div>
 <div class="form-group">
 <input type="password" class="form-control" id="exampleInputPassword1" name="password" placeholder="Et le mot de passe">
 </div>
 <button type="submit" class="btn btn-primary">Ajouter</button>
 </form>
 </div>

 </main><!-- /.container -->

Et voici notre beau formulaire :

Page d'ajout d'un utilisateur avec formulaire simple
Notre premier formulaire

Bon, notre formulaire ne fait pas grand-chose vu que nous ne le renvoyons vers aucune page. Il va donc falloir le diriger vers une page PHP qui fera le traitement de l’adresse mail en l’ajoutant à la base de données. Pour cela, rien de plus simple, nous indiquons vers quelle page notre formulaire doit être renvoyé en ajoutant les informations dans la balise form :

<form method="post" action="ajout_utilisateur.php">

Il va ensuite falloir écrire notre page ajout_utilisateur.php qui va exécuter des commandes SQL pour ajouter notre utilisateur à notre base. Cependant, pour faire cela, il faut ajouter un package à notre système pour pouvoir utiliser des commandes SQL depuis PHP :

root@Debian02:/var/www/html# apt-get install php-mysql

Puis réaliser notre insertion à la base en PHP avec notre fichier ajout_utilisateur.php :

<?php
// Fonction de connexion à la base de données
// Renvoie un connecteur à la base
function db_connect()
{
 try
 {
 $bdd = new PDO('mysql:host=localhost;dbname=messagerie', 'messagerieUser', 'openclassrooms');
 }
 catch(Exception $e)
 {
 die('Erreur : '.$e->getMessage());
 }
 return($bdd);
}

$bdd=db_connect();
$debutEmail = explode('@', $_POST['email'])[0];
$req = $bdd->prepare('INSERT INTO virtual_users (domain_id, password , email, maildir) VALUES (1, ENCRYPT(:password, CONCAT("$6$", SUBSTRING(SHA(RAND()), -16))), :email, "itinet.fr/":debutEmail"/");');
$req->execute(array(
 'email' => $_POST['email'],
 'password' => $_POST['password'],
 'debutEmail' => $debutEmail
 ));
$req->closeCursor();

header('Location: http://mondomaine.com/result.php');
exit(); 
?>

Enfin, pour ne pas rester coincé sur notre page d’ajout, un renvoi est fait en fin de page vers result.php.

La page result.php est la même qu’index.php, seul le contenu du main change :

<main role="main" class="container">

 <div class="starter-template">
 <h1>Page d’ajout d’utilisateurs de messagerie</h1>
 <p>Bravo, l’adresse a bien ete ajoutee</p>
 <a class="btn btn-primary" href="index.php" role="button">Retour</a>
 </div>

 </main><!-- /.container -->

Nous pouvons d’ores et déjà tester si notre ajout d’utilisateur fonctionne. Allez sur la page d’index et entrez un nouvel utilisateur :

Page du formulaire rempli avec les informations de notre nouvel utilisateur
Notre nouvel utilisateur

Puis cliquez sur "Ajouter", si tout se passe bien vous devriez avoir la page de confirmation :

L'utilisateur a bien été ajouté
Page de résultat d’ajout d’utilisateur

Vous pouvez maintenant retourner sur la page d’ajout en cliquant sur le bouton retour.

Il nous reste quand même à vérifier que tout cela n’est pas de la poudre aux yeux et que notre nouvel utilisateur a bien été ajouté et peut utiliser sa messagerie !

Pour cela, allons déjà regarder la base de données :

root@Debian02:/var/www/html# mysql -u messagerieUser -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 98
Server version: 10.1.26-MariaDB-0+deb9u1 Debian 9.1

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use messagerie;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [messagerie]> SELECT * FROM virtual_users;
+----+-----------+------------------------------------------------------------------------------------------------------------+-----------------+------------------+
| id | domain_id | password | email | maildir |
+----+-----------+------------------------------------------------------------------------------------------------------------+-----------------+------------------+
| 1 | 1 | $6$3067b02c754e84d4$p1VFCHfYEMWqMww3s3HegmZIZULsO9E3qAU3bUhKmsA0GI9BylVYYetibIhaGrEmoaUQZb.6wHP.CIzaBQiup0 | toto@itinet.fr | itinet.fr/toto/ |
| 2 | 1 | $6$a28f1e61c36f780c$iqEi8L4EgmwsgHkAg/WC9FJibOiRRHU/IB49ye.QNNV8FewLzEck2C1A6BK36G/0VVQmchLac6sArfpIOT.L// | eric@itinet.fr | itinet.fr/eric/ |
+----+-----------+------------------------------------------------------------------------------------------------------------+-----------------+------------------+
10 rows in set (0.00 sec)

Youhou ! Notre utilisateur a bien été ajouté, nous devrions pouvoir aller voir sa messagerie.

Nous nous connectons avec les identifiants créés :

Connexion à rainloop avec eric@itinet.fr
Connexion à RainLoop avec eric@mondomaine.local

Et hop ! Nous sommes connectés !

Bon, nous n’avons pas encore beaucoup de mails, mais vous pouvez faire des tests d’envoi entre eric@itinet.fr et toto@mondomaine.local ; ça devrait marcher !

Eh oui, normalement, RainLoop aurait dû nous dire qu’il n’était pas possible de nous connecter, car les répertoires contenant la boîte mail de l’utilisateur n’existaient pas. Et pourtant, nous n’avons eu aucun message d’alerte, et nous pouvons même recevoir des mails !

Quelle est cette sorcellerie ? 

Déjà, nous pouvons aller voir si les répertoires sont bien présents :

root@Debian2:/var/www/html# ls /var/mail/vhosts/mondomaine.local/
eric toto

A priori, les répertoires ont bien été créés ! Et tous les répertoires nécessaires aussi :

root@Debian2:/var/www/html# ls /var/mail/vhosts/mondomaine.local/eric
cur dovecot.mailbox.log dovecot-uidvalidity.5ba0c961 tmp
dovecot.index.cache dovecot-uidlist new
dovecot.index.log dovecot-uidvalidity subscriptions

C’est donc que ces répertoires ont été créés lors de notre première connexion à RainLoop.

Effectivement, RainLoop offre la possibilité de créer automatiquement les répertoires nécessaires aux utilisateurs si ceux-ci n’ont pas été créés.

Attention, cependant, ce n’est pas le cas de tous les webmails.

Que faire si votre webmail ne crée pas automatiquement les répertoires de la bonne façon ?

Je vous propose trois solutions possibles :

  1. La première est simple, il suffit d’envoyer un mail à l’utilisateur lors de la création de son adresse, et Postfix se chargera de créer les répertoires.

  2. La seconde est plus complexe et consiste à créer vous-même les répertoires et les droits associés dans un script qui sera lancé à chaque création d’un utilisateur.

  3. Enfin, la troisième s’appuie sur la seconde, mais au lieu de créer les répertoires à la main, vous pouvez utiliser la commande maildirmake, qui crée automatiquement les répertoires.

Vous avez donc réussi à créer une page web qui nous permet de créer automatiquement nos utilisateurs, bravo !

En utilisant ce principe, vous pourrez vous coder vous-même vos propres outils pour administrer vos serveurs.

Mais si nous avons vu comment interagir avec une base de données, il nous reste encore à voir comment interagir avec le système. Pour cela, nous allons créer une nouvelle page qui nous permettra de relancer notre serveur de messagerie.

Une page pour relancer nos services

Nous allons créer une nouvelle page web ; il va donc falloir commencer à organiser un peu notre site.

Pour cela, nous allons imaginer que pour l’instant, il n’aura que trois pages :

  1. Une page d’accueil

  2. Une page d’ajout d’utilisateurs

  3. Une page pour relancer le système

Nous allons donc renommer notre page index.php pour l’appeler vue_ajout_utilisateur.php, et nous allons créer une nouvelle page d’index qui sera notre page d’accueil.

root@Debian2:/var/www/html# cp index.php vue_ajout_utilisateur.php

Et nous allons donc modifier notre page index.php pour en faire la page d’accueil. Comme d’habitude, nous allons modifier la partie main :

<main role="main" class="container">

 <div class="jumbotron">
 <h1 class="display-4">Bonjour et bienvenue sur notre interface d’administration !</h1>
 <p class="lead">Ce site permet d’administrer notre solution de messagerie, de A a B (et bientot Z).</p>
 </div>

 </main><!-- /.container -->

Mais maintenant, nous n’avons plus de lien vers notre page d’ajout d’utilisateur. Pour cela, il va falloir changer notre menu et ne laisser que nos trois pages. Pour cela, nous allons modifier la partie nav :

<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="index.php">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="vue_ajout_utilisateur.php">Ajout d’utilisateur</a>
</li>
<li class="nav-item">
<a class="nav-link" href="restart.php">Redemarrage</a>
</li>
</ul>
<form class="form-inline my-2 my-lg-0">
<input class="form-control mr-sm-2" type="text" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form>
</div>
</nav>

Mais attention, nous devons aussi modifier la partie nav de notre page vue_ajout_utilisateur.php pour rester cohérents. Et voici maintenant notre beau site !

Page d'accueil avec nouveau menu
Page d’accueil avec nouveau menu

Il nous reste maintenant à réaliser la page de redémarrage, que nous avons appelée restart.php.

Cette page va simplement exécuter une commande shell depuis la page PHP grâce à la fonction exec de PHP. Pour cette page, nous allons copier index.php et modifier le main. Pour le fun, je vous propose d’utiliser un modal qui est une fenêtre pop-up qui va demander à l’utilisateur de valider son choix. Voici la page restart.php, du moins la partie main :

<main role="main" class="container">
 <div class="center">
 <!-- Button trigger modal -->
 <button type="button" class="btn btn-success btn-lg btn-block" data-toggle="modal" data-target="#exampleModal">
 Redemarrer le serveur de messagerie
 </button>

<!-- Modal -->
 <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
 <div class="modal-dialog" role="document">
 <div class="modal-content">
 <div class="modal-header">
 <h5 class="modal-title" id="exampleModalLabel">Redemarrage du serveur</h5>
 <button type="button" class="close" data-dismiss="modal" aria-label="Close">
 <span aria-hidden="true">×</span>
 </button>
 </div>
 <div class="modal-body">
 Etes-vous bien sur de vouloir redemarrer le serveur ?  </div>
 <div class="modal-footer">
 <button type="button" class="btn btn-secondary" data-dismiss="modal">Heu, non en fait</button>
 <form method="post" action="res.php">
 <button type="submit" class="btn btn-danger">Oui, fonce !</button>
 </form>
 </div>
 </div>
 </div>
 </div>
 </div>

</main><!-- /.container -->

Attention, cependant, les pop-up que nous utilisons sont en JavaScript. Il nous faut donc utiliser les balises suivantes à la fin de votre fichier restart.php, avant la dernière balise </html>.

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>

Vous pouvez voir que notre page est créée :

Page de redémarrage du serveur
Page de redémarrage du serveur

Et quand on clique dessus, une validation nous est demandée :

Pop-up de validation du redémarrage
Pop-up de validation du redémarrage

Cependant, pour l’instant, si l’on clique sur non, on revient à la page, mais si l’on clique sur oui, rien ne va se passer. En effet, la page res.php va être appelée, mais nous ne l’avons pas encore créée. Ce sera cette page qui va relancer le service en appelant le script shell script_relance_messagerie.sh :

<?php
exec('sudo /home/toto/script_relance_messagerie.sh'); 
header('Location: http://mondomaine.com/index.php');
exit(); 
?>

Il nous faut encore créer le script script_relance_messagerie.sh :

#!/bin/bash
systemctl restart postfix
systemctl restart dovecot

Ici, nous faisons vraiment un script le plus simple possible. Si vous le souhaitez, vous pouvez y indiquer des conditions afin de vérifier si le service est lancé ou non.

Nous sommes prêts à relancer notre serveur depuis notre page web... ou presque, car si vous testez maintenant, cela ne va pas fonctionner.

En effet, vous pouvez voir que, dans ma page PHP, le script est appelé avec sudo avant. Cela est nécessaire, car notre page PHP va lancer le script avec les droits d’utilisateur www-data, qui est l’utilisateur qui a lancé nginx. Cependant, cet utilisateur n’aura pas le droit de relancer des services comme Postfix ou Dovecot ; nous devons donc lui donner le droit de le faire temporairement grâce à sudo.

Il faut donc configurer sudo pour autoriser www-data à lancer la commande  /home/toto/script_relance_messagerie.sh  avec les droits root.

Pour cela, commençons par installer sudo :

root@Debian2:/var/www/html# apt install sudo

Puis ajoutons une ligne pour l’utilisateur www-data dans le fichier /etc/sudoers grâce à la commande visudo.

Voici la ligne à ajouter :

www-data ALL = NOPASSWD: /home/toto/script_relance_messagerie.sh

Nous sommes maintenant vraiment prêts à relancer notre serveur depuis le site :

Page index.php après relance du serveur
Page index.php après relance du serveur

Mais nous ne savons pas vraiment si cela a fonctionné ou non, car nous n’avons aucun message de retour.

Je vous propose pour tester de stopper notre service Postfix à la main et de voir s’il peut être relancé depuis notre page web. On commence donc par arrêter Postfix :

root@Debian2:/var/www/html# systemctl stop postfix

Et l’on vérifie qu’il est bien stoppé :

root@Debian2:/var/www/html# ss -antp | grep 25
root@Debian2:/var/www/html#

Pas de service sur le port 25 en vue, Postfix est bien arrêté.

Nous allons maintenant relancer nos services depuis la page web, puis regarder sur le serveur si Postfix est bien lancé :

root@Debian2:/var/www/html# ss -antp | grep 25
LISTEN 0 100 *:25 *:* users:(("master",pid=3678,fd=13))
LISTEN 0 100 :::25 :::* users:(("master",pid=3678,fd=14))

Parfait, le service Postfix a été relancé, donc notre script fonctionne parfaitement bien !

Nous avons donc réussi à créer un site web à partir duquel nous pouvons administrer notre serveur de messagerie, du moins nous avons commencé. Vous pourrez donc ajouter des fonctionnalités, comme effacer des utilisateurs de messagerie, les lister, etc.

La limite n’est que votre imagination !

Quelques éléments complémentaires pour finir

Il est nécessaire quand même de vous mettre en garde sur quelques points :

  • Dès lors que vous publiez un formulaire, il faut vérifier ce qui est entré par l’utilisateur pour ne pas introduire de faille de sécurité.

  • Ceci est encore plus vrai dès que vous interagissez avec le système d’exploitation et utilisez des scripts. Si l’un de vos scripts est lancé avec sudo et a les droits root, et que son contenu est modifiable, un vilain pirate pourra faire tout ce qu’il veut sur votre serveur... 

  • Notre serveur est en écoute en HTTP et non HTTPS, donc quiconque arrivera à écouter les communications qui en proviennent verra passer les mots de passe des utilisateurs que nous ajoutons en clair sur le réseau !

  • Nous avons fait notre site rapidement, les pages ne sont pas organisées, ni commentées ni normalisées. Je vous conseille fortement, si vous souhaitez faire un site plus évolué, de prendre de bonnes habitudes d’organisation dès le départ. En effet, cela deviendra de plus en plus difficile à réaliser au fur et à mesure que vous avancerez dans sa réalisation.

  • Nous avons mis en place des scripts shell très simplistes, mais je vous conseille à chaque fois que vous le pourrez de mettre des conditions pour vérifier ce qui est fait. Et surtout, n’oubliez pas de toujours mettre des chemins absolus pour vos commandes, car vous ne savez pas toujours dans quel cadre exactement est exécuté votre script et quel est le PATH associé.

Cependant, vous pouvez être fier de vous ! Vous avez mis en place une solution (presque) complète de messagerie et avez créé une interface web pour l’administrer.

Exemple de certificat de réussite
Exemple de certificat de réussite