P9 Système d'hébergement domestique

De Wiki de Projets IMA


Vidéo HD


Cahier des charges

Présentation générale du projet

Contexte

Les gens font confiance à des organismes comme google pour gérer leurs courriels, voire pour les protéger. Il n'est pas évident que leur confiance soit bien placée. Ce projet doit permettre à tout utilisateur de créer quelques comptes de messagerie sur une système embarqué de type raspberry et permettre de conserver les données à la maison.

Le système doit être constitué à base de standards (base LDAP, serveur de messagerie connu et maintenu, client de messagerie idem). L'interface d'administration doit être elle aussi très simple d'utilisation.

Un effort particulier doit être porté sur l'alimentation du système embarqué. L'idéal serait un mode de veille lorsqu'aucun paquet TCP/IP n'est adressé à la machine. Il est aussi demandé de mettre au point une alimentation à base d'énergie renouvelable (e.g. panneau solaire) permettant d'alimenter totalement ou partiellement le système. Enfin pour permettre de se passer de la box grande consommatrice d'énergie, le système embarqué doit pouvoir en reprendre les fonctionnalités principale (connexion avec le DSLAM, redirection des ports UDP/TCP, ...).

Objectif du projet

L'objectif est de réaliser un système embarqué avec une alimentation autonome pour héberger une messagerie électronique domestique. Par domestique, il faut comprendre pour une dizaine de boites aux lettres. En outre, le système devra pouvoir être alimenté par un panneau solaire ou, le cas échéant, prendre relai sur le secteur lorsque l'alimentation fournie par le soleil n'est plus suffisante.

Description du projet

Choix techniques : matériel et logiciel

Matériel obtenu à ce jour :

  • 1 Raspberry Pi 2 [obtenue le 07/10/2015]
  • 1 Carte µSD 8 GB [obtenue le 07/10/2015]
  • 1 Cable USB/µUSB [obtenu le 07/10/2015]
  • 1 Câble RJ45 [obtenu le 07/10/2015]

Suivi de l'avancement du Projet

Semaine 1 (21/09/2015)

Notre but étant la réalisation d'une messagerie électronique domestique capable de gérer une dizaine de boites aux lettres, nous nous orientons dans un premier temps sur les points suivants que nous allons éclaircir afin d'en tirer un cahier des charges.

Nous allons étudier et approfondir les points suivants :

  • Installer une base LDAP, ou avoir plutôt avoir plusieurs comptes UNIX
  • Installer un serveur de messagerie SMTP, et IMAP/POP
  • Investiguer du côté de POSTFIX
  • Étudier la taille d'un mail vide, et en moyenne, et voir combien ça fait par rapport au système
  • Enquêter sur la forme des fichiers utilisateurs (généraliser les données)

Ce qui doit pouvoir être fait :

  • Distinction administrateur / utilisateur
  • Un administrateur doit pouvoir gérer les comptes de messagerie (addition/suppression/etc)
  • Gestion des quotas/espace disque réservé par ex
  • Choix du quota (par qui ? options ?)
  • SECURISER : apache, mod_security, étudier l'utilisation d'un pare-feu logiciel ?

Prévision du Matériel :

  • Raspberry Pi 2
  • Alimentation RPi
  • Carte SD (8Go)

Le module énergétique sera équipé des éléments suivants :

  • 1 MPPT
  • Convertisseur Numérique Analogique : MAX5250
  • Potentiomètre : MCP4261
  • Relai : R561D.56 NTE
  • Résistances : Deux de 100Ω et deux de 10kΩ
  • une LED
  • 40 pins broches mâle/mâle (Digikey Parts : A26509-40-ND), découpé par la suite en 8-8-6-4
  • Circuit d'alimentation autonome : cellule photovoltaique
  • Circuit d'alimentation autonome : batterie (avec port micro-USB et port USB)

Semaine 2 (28/09/2015)

Dans un premier temps l'idée est de développer le système principal avec les fonctionnalités. Autrement dit les différents packages et fonctionnalités installées sur la raspberry ainsi que l'interface utilisateur sur le site web. Dans cette partie nous listerons les différentes fonctionnalités que nous aimerions implémenter.


☐ Serveur SMTP (communications entre serveurs mails SMPT) et serveur IMAP/POP3 (postier)

  • Postfix pour SMTP
  • Dovecot, Courier ou Cyrus pour IMAP/POP3
  • Implémentation de protocoles plus complexes et offrant notamment des fonctionnalités de chiffrement (SMTPS / ESMTP / SSL / HTTPS / Certificat)
  • LMTP

☐ Gestion particulière des gros mails et notamment de leurs pièces jointes

  • Bigfile pour un stockage en ligne
  • Décodage base 64 des pièces jointes ? (difficulté++)

☐ Gestion des utilisateurs

  • Plusieurs comptes, en utilisant LDAP
  • La possibilité de s'identifier en tant qu'administrateur

☐ Listes de diffusion

☐ Comptes mail temporaires (10 minutes mail like)

☐ Antivirus & Spam

  • ClamAV ou SpamAssassin ou autres : lourd.
  • iptables dans un premier temps.
  • Antispam maison, gestion du contenu, marquage de spam par l'utilisateur, "boites intelligentes" de spams.

☐ Les noms de domaines sont peut être nécessaires dès le départ pour tester le fonctionnement des fonctionnalités (telnet qui dit nope ?)

  • Record A
  • Record MX

☐ Sauvegarde automatique périodique de la Raspberry Pi (pendant le développement, pour éviter de perdre les données)

Il faudra aussi prêter attention aux fonctionnalités disponibles sur l'interface et ne pas laisser des fonctionnalités fantômes issues d'un template générique que l'on a pu trouver sur l'Internet.

Semaine 3 (05/10/2015)

Nous avons précisé les éléments constituants l'interface web :

  • Gestion des comptes (création, modification, suppression d'utilisateurs...)
  • Gestion des listes de diffusions
  • Gestion des quotas
  • Affichage du courrier
  • Gestion de l'antivirus

Semaine 4 (12/10/2015)

Nous avons mis en place un github pour suivre le développement : GitHub.

Nous commençons à visualiser la structure de notre serveur mail. Dans un premier temps nous constituons notre base de données. Finalement nous utiliserons bien LDAP, qui semble relativement simple à utiliser contrairement à notre première impression.

Nous avons utilisé un tutoriel de Gandi pour mettre en place la base de notre annuaire. Nous utiliserons les schemas disponibles dans le paquet courier-ldap. Nous avons écrit un script en perl permettant d'installer ce schema facilement, nous évitant d'avoir à réinstaller le paquet complet pour récupérer un unique fichier.


Il est important maintenant de changer les règles d'accès à la base de données. En effet la base de données est disponible pour n'importe qui, on peut y entrer avec la commande :

ldapsearch -c -h localhost -b dc=domain,dc=tld -x

Nous avons ici aussi écrit un script permettant de le faire automatiquement. A ce stade il est nécessaire de s'identifier en tant qu'administrateur pour voir l'arbre :

ldapsearch -c -h localhost -b dc=domain,dc=tld -D "cn=admin, dc=domain,dc=tld" -W


On peut dorénavant développer la structure de notre directory. Nous avons choisi de reprendre la base du tutoriel de Gandi et d'ajouter une entité mail qui regroupera tout les utilisateurs du serveur mail. Nous avons donc écrit un fichier .ldif décrivant cette structure et l'avons ajoutée avec la commande :

ldapadd -D "cn=admin,dc=domain,dc=tld" -W -h localhost -f ldif/mail_tree.ldif

On peut vérifier que la structure a bien été ajoutée à notre arbre avec la commande :

ldapsearch -D "cn=admin,dc=domain,dc=tld" -W -h localhost -b "dc=domain,dc=tld"


Maintenant, on peut ajouter nos utilisateurs à la base de données. Ici il est d'autant plus intéressant d'automatiser cette manipulation. Le principe est d'écrire un fichier .ldif et de l'ajouter de la même manière que précedemment avec ldapadd. Nous nous sommes inspirés ici encore du tutoriel de Gandi tout en l'adaptant. Le script perl add_user.pl simplifie grandement l'ajout d'un utilisateur. On pourra par la suite le modifier pour l'utiliser directement avec du code php. On peut vérifier que l'utilisateur a bien été ajouté avec la commande habituelle ldapsearch et même tester l'identification avec :

ldapwhoami -vvv -h localhost -D "cn=username,dc=mail,dc=domain,dc=tld" -x -W

Si la commande renvoie Result: Success (0) alors l'identification a fonctionné.


La commande ldapsearch permet aussi de filtrer les recherches dans la base. Par exemple, avec notre configuration on peut afficher les utilisateurs avec la commande :

ldapsearch -D "cn=admin,dc=domain,dc=tld" -W -b "dc=mail,dc=domain,dc=tld" "(&(objectClass=CourierMailAccount))"

On peut aussi indiquer quels champs à afficher en les indiquant à la fin de cette commande.

Semaine 5 (19/10/2015)

DNS et SSL

Nous avons obtenu un nom de domaine auprès de Gandi pour notre projet. Le site est accessible à l'adresse : intimail.pw

nous avons configuré un serveur DNS et la certification SSL sur notre raspberry pi. Pour la démarche je vous renvoie à notre wiki de TP de Protocoles Réseaux Avancés. La méthode est identique.

De la même manière que dans le TP de PRA, nous voulions implémenter DNSSEC. Cependant arrivé à la dernière étape, nous nous sommes rendu compte que Gandi ne propose pas cette fonctionnalité pour les .pw. Dans l'éventualité où les DNSSEC seraient implémentés au cours de notre projet, nous pourrons activer cette fonctionnalité rapidement.

La différence réside dans le fait que pour un serveur mail il faut un enregistrement de type MX. Dans le fichier de configuration des enregistrements de bind, nous avons ajouté les lignes :

@       IN      MX      10 mail.intimail.pw.
mail    IN      A       193.48.57.171

De plus, un serveur mail doit pouvoir faire les résolutions inverses. En effet certains serveurs mail sont configurés pour rejeter ou retarder la livraison de mails provenant de serveurs dont la résolution inverse n'est pas effectuée. L'enregistrement PTR devient alors :

171     IN      PTR     mail.intimail.pw.

A ce stade, le serveur DNS devrait être correctement configuré et le SSL fonctionnel.

LDAP

Pour notre annuaire LDAP, nous avons utilisé les schémas proposés dans le package courier-ldap. Ces fichiers particuliers permettent à ldap de structurer sa base de données. Après différents essais et discussion, nous avons retenu la structure suivante :

LDAP directory structure

Nous avons regroupé la totalité de nos entrées sous une entité commune mail, celle ci se divisant ensuite en deux groupes people et groups. Le dc people regroupe les informations des utilisateurs, que l'on détaillera plus tard. Le dc groups permettra quant à lui regroupera les différentes listes de diffusion. Il contient aussi le groupe particulier des administrateurs qui permettra de donner des droits privilégiés à certains utilisateurs.

Pour ce qui est des informations des utilisateurs, voici un exemple de fichier de configuration pour l'utilisateur Jean Valjean :

dn: cn=jvaljean,dc=people,dc=mail,dc=intimail,dc=pw
uid: jvaljean
mail: jvaljean@intimail.pw
sn: Valjean
givenName: Jean
displayName: Jean Valjean
mailbox: intimail.pw/jvaljean/
homeDirectory: /home/vmail/
objectClass: top
objectClass: inetOrgPerson
objectClass: CourierMailAccount
userPassword: {SSHA}gCbVPaSGUaqjrq0mTQY77sOH4Xcq59Fg

Ces différentes informations ne sont pas définitives : certains champs peuvent peut être être retirés (comme le champ homeDirectory qui pour l'instant est commun et identique à tout les utilisateurs).

Web

Nous avons cherché un template léger, simple, mais néanmoins fonctionnel et agréable pour notre interface web. L'idée est, plutôt que de prendre un webmail classique dont nous n'utiliserions que 25 à 50% des capacités, de réaliser un webmail qui répond à toutes les exigences du cahier des charges et implémentant uniquement le nécessaire. Il s'est avéré après recherches que l'une des meilleures solutions était de partir sur une interface codée en PHP (ce dernier implémente une API dédiée à LDAP), agrémentée de Bootstrap pour le JS/CSS. Notre choix s'est finalement porté sur le template Lumino, que l'on peut essayer par ici.

Après divers test, il s'avère qu'une page de cette interface met en moyenne pour charger totalement :

  • 2,10s sur une connexion mobile 3G moyenne (4MB/s)
  • 1,90s sur une connexion 4G moyenne (15MB/s)
  • 2,80s sur une connexion DSL moyenne (2MB/s)
  • 1,90s sur une connexion Wifi (30MB/s)
  • 1,70s sur une connexion THD (100MB/s)

Résultats qui semble apporter bon compris poids/qualité de l'interface.

Nous avons alors déployé cette interface, dans un dossier /var/www/webmail/, puis ajouté le VirtualHost correspondant à notre site dans la configuration apache

Config à venir, quelqu'un a éteint tutur06, notre raspberry est donc off :(

Semaine 6 (26/10/2015)

Firewall

Etant donné que nous avons subi plusieurs tentatives de connexion SSH frauduleuses sur notre serveur, nous avons décidé de mettre en place un semblant de sécurité sur ce dernier. Nous avons donc écrit un petit script iptables simples pour filtrer les entrées. Celui ci se contente de bloquer tout les paquets entrant et de laisser passer les paquets par les ports ou nous avions vraiment besoin (ici SSH, HTTP, HTTPS, DNS, NTP, SMTP, POP3 et IMAP). Après discussion, nous avons resserré davantage nos règles. Désormais le SSH n'est autorisé que pour des adresses ip spécifiées dans un fichier de liste blanche.

Nous avons aussi repéré dans le fichier /var/loh/auth.log plusieurs tentatives de connexions. Dans un premier temps nous avions cru à un problème de configuration de notre structure. Après avoir vérifié dans le log /var/log/syslog nous nous sommes aperçu qu'il s'agissait de connexions depuis des ip extérieures qui nous sont inconnues. Etant donné le nombre d'essais nous pensons que c'était une tentative d'intrusion sur le serveur SMTP par bruteforce. Comme première approche de contre-mesure nous avons pris le parti de constituer une liste noire. En effet ici nous n'avons pas le choix de laisser le port 25 (SMTP) ouvert. Pour cela nous avons utilisé les commandes :

cat /vat/log/syslog | grep SASL\ LOGIN\ authentication\ failed                           # Pour lister les messages de connexion échouées
sed -ne 's/.*\[\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)\].*/\1/p'      # Pour n'afficher que les ip
*file* | sort | uniq                                                                     # Pour classer et éliminer les doublons

On peut ensuite vérifier que les ip listées sont bien inconnues. Nous avons ainsi constitué une liste noire de 23 adresses ip.

Postfix

Nous avons installé l'agent de transfert de mail Postfix (apt-get install postfix postfix-ldap) sur la raspberry et configuré celui ci en accord avec notre base de données LDAP. Après un test d'envoi via telnet :

telnet ex 25
Trying xxx.xxx.xxx.xxx...
Connected to intimail.pw.
Escape character is '^]'.
220 mail.intimail.pw
ehlo intimail.pw
250-mail.intimail.pw
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM:toto(arobase)titi.com
250 2.1.0 Ok
RCPT TO:jvaljean(arobase)intimail.pw
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: Premier Test
Here is my test
.
250 2.0.0 Ok: queued as 0EDFC21428
quit
221 2.0.0 Bye
Connection closed by foreign host.

Il est maintenant intéressant de tester notre DNS avec un outil en ligne. Nous avons ici utilisé l'excellent dnsstuff pour cela. C'est grâce à ce dernier que nous avons pu corriger les erreurs de notre serveur mail, à savoir :

  • SMTP Greeting : Il permet de faire en sorte que le serveur mail est reconnu par les autres serveurs. Cela évite que certains serveurs ne refusent les mails provenant d'intimail.pw.
  • Acceptance of abuse/postmaster : Les serveurs mails doivent avoir deux adresses abuse@... et postmaster@... . Nous avons donc créé ces deux boîtes mail pour répondre à ces conditions

Cyrus SASL

Nous avons implémenté l'authentification sur le serveur SMTP en SASL avec Cyrus. Après configuration, on peut maintenant envoyer des mail depuis notre serveur sur un serveur extérieur tel que gmail.

LDAP

Nous avons créé quelques utilisateurs LDAP pour peupler notre répertoire, contenant diverses informations utiles, afin de pouvoir commencer à implémenter une API d'authentification et réaliser divers essais sur l'interface web. Nous avons aussi ajouté un champ quota dans les informations des utilisateurs. A première vue ce champ pourra être utilisé avec le serveur IMAP et POP3. Cependant, afin d'éviter d'installer des services supplémentaires, nous choisissons de nous orienter dans une autre direction. Postfix nous permet à l'heure actuelle d'envoyer et recevoir des mails avec nos utilisateurs virtuels, et stocke chaque mail dans un fichier sur le système. L'interface web peut donc, sans Dovecot ou serveurs IMAP/POP3, parser par elle-même les mails reçus et préparer un envoi. Il nous faut, en prenant cette voie, réfléchir à l'implémentation des quotas, non gérés par PostFix. L'idée serait de lancer un script quand Postfix reçoit un mail, avant de le stocker sur le système, et de vérifier que la taille de ce mail + de ceux déjà reçu/envoyés par avant ne dépasse pas le quota attribué à un utilisateur, et rejeter le mail à l'expéditeur si on va dépasser alors le quota.

Web

Nous avons modifié le template de site web en accord avec nos objectifs. L'utilisateur doit d'abord se connecter avec ses identifiants LDAP (uid et mot de passe correspondant à son unique Distinguished Name (DN: cn=jvaljean,dc=people,dc=mail,dc=intimail,dc=pw). Une fois connecté, il tombe sur une interface que l'on pourrait appeler une "vue globale", réunissant les informations principales : le nombre de nouveaux mails, l'utilisation actuelle de son quota sur le disque, les quelques derniers mails reçus, et un calendrier afin de se repérer simplement niveau date. Sur le côté, il est possible d'aller voir plus en détail l'inbox où se trouvent tous les mails reçus, ou encore aller voir les messages envoyés, et supprimer les courriels dans la poubelle. Si l'utilisateur est administrateur, il a aussi accès à un onglet "Manage" d'où il pourra prendre fonction de ses pleins pouvoirs. Cette dernière fonctionnalité n'étant pas encore implémentée, tout utilisateur voit pour l'instant cet onglet.

Toute la journée du mardi le code web a été réaménagé et modulé. Nous réfléchissons désormais au stockage et à la récupération de l'état instantané du quota mail (stocker cette valeur dans un fichier, ou en fait un attribut supplémentaire d'un utilisateur LDAP, où est d'ailleurs déjà stocké son quota max). L'idée est de ne pas trop solliciter le système et d'incrémenter ou décrémenter une variable à chaque réception/envoi de mail plutôt que recalculer entièrement la taille du dossier où sont les mails. On pourra en outre envisager de mettre en place une tâche périodique (via CRON) qui vérifiera en heures creuses que la taille du dossier mail est bien égale au quota en variable dans le système.

Par la suite il a été convenu de supprimer Apache et de le remplacer par le serveur web Lighttpd, plus léger et plus performant. Pour la configuration, on rajoute simplement un hôte au fichier /etc/lighttpd/lighttpd.conf auquel on fournit notamment le chemin d'accès aux fichiers du site, ou encore les clés pour SSL. Nous avons alors réinstallé PHP et nous l'avons reconfiguré et activé pour Lighttpd. Enfin, l'activation du mod_rewrite et la mise en place de règles différentes de celles d'Apache (qui font la même chose mais à la sauce Lighttpd) nous permettent de conclure cette migration en environ 25-30min (!). Le mod_rewrite, un peu gadget nous l'avouons, permet chez nous de résoudre une URL de type www.domaine.com/page.php en écrivant simplement www.domaine.com/page. On atteint donc la page de connexion de notre webmail par https://www.intimail.pw/login. Nous avons en outre pu préciser la suite de notre projet. L'idée est d'avoir 2 ports sur lequel Postfix écoute (disons *:25 et localhost:10025). A réception d'un email sur *:25, postfix est configuré pour lancer un script que nous réalisons. Ce script vérifie le quota de l'utilisateur, si le mail reçu ne le dépasse pas il transfère alors le mail à localhost:10025 qui l'accepte et écrit le mail sur le disque de manière normale. Sinon, il renvoie un code d'erreur et le mail n'est pas accepté. De plus, le script mettra à jour un index dans un fichier propre à chaque utilisateur que le webmail lira. En somme, l'utilisation de fichiers quota et index évite à l'interface web de vérifier entièrement les dossiers et sous-dossiers mail à chaque actualisation de la page, ce qui créerait une charge énorme pour la Pi. On s'abstient en plus d'installer Dotecove/Un serveur IMAP/Un serveur POP grâce à notre webmail et nos scripts qui gèrent tout et uniquement ce dont nous avons besoin. Notre architecture s'oriente donc sur les schémas suivants :

A classical mail server infrastructure
Our light mail server infrastructure

Scripting

Nous avons écrit un script perl pour envoyer des mails de manière automatique. Précisons que nous avons dû installer libswitch-perl pour pouvoir l'utiliser. Ce package devra être ajouté dans les dépendances lors de la constitution du fichier d'installation de notre outil.

Semaine 7 (02/11/2015) - Architecture du système de fichiers

File system architecture

Notre système s'articule autour de 2 chemins distincts. A droite en rouge, l'architecture classique par défaut de postfix. Les mails reçus sont enregistrés par défaut dans un dossier par utilisateur et par domaine, dans le dossier new. A gauche notre architecture greffée, dans un sous dossier différent, par utilisateur et par domaine. Dans ces sous-dossiers, on distingue alors 2 dossiers et 1 fichier.

  • Le fichier contient la valeur actuelle du quota de l'utilisateur. On préfère cette méthode plutôt que de faire calculer l'espace disque disponible dans un dossier par le système à chaque fois. On fera tourner un script, de nuit, périodiquement, pour vérifier que le quota correspond bel et bien à la valeur dans le fichier, pour éviter d'incrémenter/décrémenter une erreur.
  • Un dossier mail, dans lequel sont stockés les mails envoyés
  • Un dossier json, dans lequel sont stockés les conteneurs json, qui contiennent les en-têtes des mails envoyés, reçus, spams et poubelles. On accède donc à toutes les infos utiles au webmail en lisant ces json, plutôt que de lire à chaque fois tous les mails, ce qui serait extrêmement lourd et lent.

Semaine 8 (09/11/2015) - Postfix et filtrage

Nous avons établi une première version de notre script de traitement des mails arrivant sur le serveur. Nous nous sommes inspirés pour cela de la configuration proposée dans le manuel de Postfix. La configuration se fait via le fichier master.cf. Ce fichier permet de configurer tout les processus utilisés par Postfix pour fonctionner. Dans notre cas, il nous permet de déclarer un service de filtrage et d'indiquer au démon qui récupère les mails entrants (smtpd) de les transférer à ce service via un programme pipe. La configuration se fait de la manière suivante :

# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp       inet  n       -       -       -       -       smtpd
  -o content_filter=filter
filter     unix  -       n       n       -       5       pipe
  flags=Rq user=vmail null_sender=
  argv=/path/to/script $queue_id $size $sender $recipient


Nous avons décidé de suivre la même structure que dans le manuel de Postfix. C'est à dire que postfix fait appel à un script de filtrage principal, qui va lui même faire appel à d'autres scripts répondant à des besoins plus spécifiques. Pour l'instant, le script va parser certaines informations des mails entrant dans des fichier .json, qui servent de base de données. En voici un exemple :

[
 {
   "from": "michel.drucker@gmail.com",
   "subject": "Vivement dimanche",
   "timestamp": "2015-11-8 19:59:53",
   "unixtimestamp": "1447012793",
   "queueid": "48FE03FA87",
   "filepath": "???",
   "size": "1676",
   "status": "0",
   "pj": "0",
   "id": "0",
   "to": "jbond@intimail.pw"
 }
]

Il serait également intéressant d'ajouter un champ permettant d'indiquer le chemin du fichier contenant le mail. Cependant au moment de l'exécution, même si nous avons accès au contenu du fichier, il est à priori impossible de connaître le nom définitif du fichier (il y a une part d'aléatoire). Ils serviront à l'affichage des mails dans les boîtes de réception dans le webmail. On pourra par la suite ajouter d'autres scripts permettant par exemple de vérifier la nature du message ou encore son contenu (Antivirus / Antispam / Pièces jointes).

Il est intéressant également de pointer ici que le fichier de configuration permet de préciser l'utilisateur qui va exécuter le script de filtrage. Ici nous avons mis user=vmail. Le script de filtrage s'exécutera donc en tant que vmail. Or pour l'interface, il faut qu'apache puisse accéder aux fichiers écrits par des processus de vmail. Pour les fichiers .json, écrits par le script de filtrage décrit plus haut, la solution du relativement simple. Il a suffit d'ajouter quelques commandes chmod pour autoriser la lecture et éventuellement l'écriture au groupe. Nous avons ensuite ajouté www-data au groupe vmail.

Quant aux fichiers contenant les mails, ceux ci sont écrits par postfix, et nous n'avons pour l'instant pas trouvé le moyen d'autoriser l'accès à ces fichiers par www-data. Ce problème, ainsi que la difficulté à récupérer le nom du fichier (décrit plus haut), nous pousse à nous demander si le délivrement des mails ne devrait pas être géré par un programme de notre confection, plutôt que le programme sendmail de postfix. En théorie, cette méthode répond parfaitement aux deux problèmes mais sendmail propose certainement une gestion plus aboutie que nous ne saurons reproduire dans un temps limité.

Semaine 9 (16/11/2015)

Semaine 10 (23/11/2015)

Semaine 11 (30/11/2015)

Nous avons travaillé sur le parser de mails et tentons d'implémenter notre architecture de fichiers.

D'autre part, nous avons travaillé l'interface web. Le contenu des tables affichant les en-tête des mails étant généré dynamiquement en fonction du fichier .json, il est impossible d'ajouter des balises <a href> pour rendre ces en-têtes cliquables pour afficher le contenu d'un mail.

La solution retenue et implémentée est d'utiliser un événement JavaScript. Pour une table avec un id table, on récupère l'en-tête cliquée à l'aide de la fonction JavaScript suivante :

$('#table').on('click-row.bs.table', function (e, row, $element) {
    console.log(row, $element);
    alert("L'utilisateur d'intimail a cliqué sur "+row);
});

On peut donc récupérer l'en-tête du fichier, plus spécifiquement le champ contenant le chemin vers le fichier mail brut. On pourra alors envoyer ce chemin dans une variable à l'aide d'une requête POST lancée dans la fonction ci-dessus avec AJAX, et être emmené vers une page qui traitera et affichera à l'utilisateur ce fichier mail brut.

Semaine 12 (07/12/2015)

Parseur pour les mails entrants

Comme nous l'avons dit à la semaine , nous avions rencontré un problème lors de la réception des mails. Pour resituer le problème : lors de la réception d'un mail, nous avons créé un hook pour postfix, qui s'exécute lors de la réception d'un email. La difficulté étant qu'au moment de cette exécution, les fichiers contenant les mails ne sont pas écrits dans le système de fichier, rendant impossible la récupération des noms de fichier. De plus, ces fichiers comportent les permissions 600 pour l'utilisateur et le groupe vmail:vmail.

La solution que nous proposons est de détacher le processus de parsing au moment de l'appel du script. De cette manière, le mail peut être délivré correctement et passe quand même dans le script de parsing, nous permettant ainsi de récupérer les mails et les informations critiques. On peut même en profiter pour appliquer les droits dont nous avons besoin. A priori, il suffirait juste d'autoriser le groupe vmail en lecture et d'ajouter www-data à ce groupe pour pouvoir afficher le contenu des mails dans l'interface Web. Résumons schématiquement le processus :

Un mail arrive
  Le hook de postfix est exécuté
    Le mail est envoyé via Sendmail --------------------------------------------------------------> Le mail est en attente de délivrement
    Le script de parsing est lancé                                                                   |
      Le script vérifie dans les dossiers la présence du nouveau mail                                |
      et réessaye un certain nombre de fois tant que tout les destinataires ne sont pas traités      |
       |                                                                                             v
       |                                                                                            Le mail est délivré et existe dans le système de fichier
       v                                                                                             |
      Le script a trouvé le fichier et le traite <----------------------------------------------------
Fin

Nous avons aussi un peu réduit le contenu des éléments json en retirant le champ filepath. Les fichiers mails sont déplacés dans un dossier et renommés suivant le modèle $unixtimestamp.$queueid. Si nous rencontrons des collisions de noms (ce qui devrait théoriquement ne pas arriver) nous garderons les noms de fichier standard de postfix et restaurerons le champ filepath.

Interface Web

L'interface web progresse, et la page d'accueil est désormais totalement terminée et dynamique. Comme on peut le voir sur l'image ci-dessous, la nombre de mails dans la boite de réception, dans la poubelle, et le quota sont retrouvés et calculés dynamiquement, et ce en chargeant simplement les fichiers .json d'en-tête que nous avons crée, ce qui évite au système des calculs lourds. En outre le bouton "Write an email", présent dans la barre de navigation en haut mais aussi en gros sur la page d'accueil permet d'ouvrir une fenêtre pour écrire un email. L'envoi fonctionne parfaitement sur le réseau interne de l'école (nous devons encore passer la Raspberry sur un modem en dehors de Polytech pour éviter les foudres du CRI.), et ne devrait poser aucun problème une fois cette histoire de filtrage bypassée. Nous savons aussi envoyer des pièces-jointes, pour l'instant en dur pour la Proof of Concept, nous implémenterons bientot la possiblité à un utilisateur de téléverser son propre fichier (à taille raisonnable). Nous réflechissons au stockage de la PJ : serveur distant ?


Webmail Home Page

Semaine 13 (14/12/2015)

  • Écriture du rapport intermédiaire
  • Réalisation du support de soutenance

Semaine 14 (04/01/2016)

  • Écriture d'un guide d'une bonne dizaine de pages détaillant l'installation du système intimail point par point
  • Début de réécriture des scripts shell en perl pour des raisons d'optimisation

Semaine 15 (11/01/2016)

Poursuite de la longue réécriture du code shell en perl.

Semaine 16 (18/01/2016)

Côté web : Début du travail sur les pièces jointes, manque par contre l'affichage des PJ quand on ouvre un mail depuis l'interface. Manque la mise à 1 de l'attribut "pj" quand une pièce jointe est présente.

Côté serveur : La complexité du script permettant l'extraction des informations des mails devenant trop importante, nous avons pris le parti de réécrire le code dans un langage un peu plus simple : le Perl. Il permettra d'éviter l'utilisation de (nombreuses) variables d'environnement, tout en proposant des performances accrues.

Semaine 17 (25/01/2016)

Côté web :

  • Poursuite du travail sur les pièces jointes : envoi OK, types supportés GIF, JPG, PNG, PDF, ZIP, vérification par extension MIME, taille max d'une PJ variabilisé.
  • Travail sur l'en-tête des mails envoyés : résolu un bug qui disait qu'une pièce jointe était présente même quand on envoyait que du texte (type text/html quand pas de PJ au lieu de type data/multipart tout le temps dans le header mail)
  • Gros travail sur les fichiers d'en-tête JSON : fonctions PHP de suppression d'un mail (fichier sur le disque et suppression dans l'en-tête JSON), fonctions de mise à jour du quota à l'envoi (réception fonctionnel aussi), fonctions d'ajout d'un mail (fichier sur le disque et en-tête JSON)
  • Interface : gestion des différentes boites : les mails écrits se retrouvent dans "Envoyés", les mails reçus dans "Boite de réception", manque que le flag comme spam pour les mettre dans la boite à spam, mais la boite est déjà là en tout cas

Côté serveur : Implémentation de la gestion des quotas. Quand un mail arrive, chaque destinataire voit son quota contrôlé pour déterminer si oui ou non le mail peut être délivré. Comme nous l'avions dit précédemment, postfix gère le rebond des mails grâce au code de terminaison du script. Or nous à priori il va falloir proposer une solution maison pour pouvoir le faire. En effet, si un mail est refusé pour cause de quota plein, le mail reçu est tout bonnement supprimé sans notifier ni le destinataire, ni l'envoyeur.

Semaine 18 (01/02/2016)

Côté web :

  • Identification d'un administrateur ou simple utilisateur
  • Début de création de la page de gestion des comptes : une page administrateur permettant de régler quelques variables (par ex: taille max des PJ, nom de domaine, création d'un compte...), et de sélectionner un utilisateur existant redirigeant vers...
  • ... une page de gestion d'un compte particulier sélectionné au préalable, permettant de changer l'UID, le nom de la personne, le quota, le mot de passe, ou de supprimer le compte
  • Configuration générale du webmail (variables nom de domaine, identification de l'administrateur LDAP, port LDAP...) via un fichier config.php à part contenant ces valeurs.
  • Création d'une fonction permettant de modifier un paramètre de la configuration par lecture/modification/réécriture du fichier de config.
  • Après une journée de prise de tête : configuration via un conteneur JSON, plus simple à manipuler pour en modifier les paramètres depuis l'interface. Le fichier se nomme désormais config.inc. Un soin est apporté à l'interdiction d'accès aux fichiers ".inc" : dans la barre d'adresse en accès au fichier le client reçoit une erreur 403, cela évite que n'importe qui puisse voir le contenu du fichier et les précieux identifiants du serveur LDAP... tout en laissant à l'utilisateur web www-data la lecture du fichier pour son usage.

Côté serveur : Mise en place d'un système de récupération des pièces jointes. Pour cela nous utiliserons la commande munpack du paquet mpack. Cette commande permet de décomposer un fichier mail pour notamment en récupérer les pièces jointes. De plus, après test et concertation, nous nous sommes rendu compte qu'il serait intéressant d'utiliser cette même commande pour extraire les messages des mails, évitant ainsi un chargement assez long sur l'interface graphique.

Semaine 19 (08/02/2016)

Côté Web :

  • Implémentation du back-end des fonctions de gestion d'un compte : changement UID, mot de passe, nom+prénom, quota, tout fonctionne.
  • Suppression d'un compte : fonctionnelle
  • Création d'un compte : fonctionnelle
  • Création d'une fonction permettant d'ajouter un utilisateur à une liste/un groupe donné(e) : OK
  • Création d'une fonction permettant de supprimer un utilisateur d'une liste/un groupe donné(e) : OK
  • Création d'une fonction permettant de lister les groupes existants : OK
  • Création d'une fonction permettant de créer/supprimer un groupe : OK

Au 10/02, on peut créer des comptes, les dossiers sont crées sur le système sans problème à réception d'un mail de bienvenue, on peut envoyer et recevoir des messages, donner les droits d'administrations, gérer la configuration du webmail depuis l'interface, gérer les utilisateurs et leurs attributs depuis l'interface, tout semble prêt relativement au cahier des charges initial. Nous allons migrer notre installation sur une autre IP et autre domaine.

Côté serveur : Croyant que les fonctionnalités demandées étaient implémentées, nous nous sommes rendu compte après tests que les mails envoyés d'un compte Intimail à un autre compte Intimail n'apparaissaient pas. Ceci était du au fait que le script utilisé n'était activé que lors de la réception d'un mail par le réseau (serveur smtp). Nous avons donc modifié la manière donc les mails sont passés au script, en ajoutant une règle dans master.cf :

mailrte    unix  -       n       n       -       5       pipe
  flags=Rq user=vmail null_sender=
  argv=/home/moth/Documents/pfe/postfix/json_parser.pl $queue_id $size $sender $recipient

Pas de grande nouveauté ici. Dans le fichier main.cf :

virtual_transport = mailrte

Ces commandes passent TOUT les mails au script défini via l'entrée standard. C'est notre script qui doit alors se débrouiller pour délivrer les mails (précédemment, postfix se chargeait toujours d'écrire les fichiers). Encore une fois, nous avons donc dû modifier notre programme en conséquence.

Semaine 20 (15/02/2016)

Nous avons migré la raspberry d'une IP interne au réseau Polytech vers une IP externe. La migration s'est passée sans aucun problème, le plus long étant d'attendre que les zones DNS se propagent (cela a bien pris 1 heure !). Nous sommes donc passés de 193.48.57.171 vers 5.23.44.85. Nous émettons enfin des mails vers les serveurs extérieurs (en particulier Google Mail, Microsoft Live...). La théorie de départ que nos échecs d'envoi étaient dû aux restrictions du CRI s'est donc bien vérifiée : plus aucun problème juste en migrant.


Finalisation de la gestion des PJs : en cas de mail contenant une pièce jointe, un bandeau permet d'ouvrir chacune d'entre-elles dans un nouvel onglet. La pièce jointe en base 64 dans le fichier mail brut est reconstruite dans un dossier accessible au serveur web puis supprimée pour un gain de place (le fichier d'origine ou reconstruit est plus petit en taille que sa version encodée en base 64, on optimise donc ainsi le quota et l'espace disque).

Corrections de bogues d'encodage des mails entre ISO et UTF8.

Travail sur la création d'un paquet Debian.

Semaine 21 (22/02/2016)

  • Tournage de la vidéo du projet avec Laurent Engels du service Communication
  • Fin de la rédaction du rapport final de PFE
  • Rédaction du diaporama de présentation orale du PFE

Fichiers Rendus

Interface web : intiMail.pw


Rapport de Mi-Projet : ICI

Rapport de Projet : ICI

Vidéo de Projet : [http:// Soon™] (Tournée le 22/02 10h avec Laurent Engels)


Code de l'interface + README expliquant le contenu : intiMail.zip

Code Perl du script pour Postfix et scripts de configuration : ICI

Fichier de configuration (Postfix/Lighttpd) fonctionnels : Config Postfix+Lighttpd

Package Debian d'installation de l'interface : intiMail.deb

Guide d'installation (version seule, déjà inclus dans le rapport final) : Guide d'installation