MAJ du 30/07/2016 : Ce tutoriel se fonde sur ejabberd 16.01 et une Ubuntu 16.04 LTS. Il a été testé sur la version 8.7 de Zimbra (sortie le 15/07/2016).
Depuis sa version 7.x, Zimbra n’a plus de module de chat natif. Cette fonctionnalité devrait revenir en deux temps :
1- intégration d’un client XMPP dans le client web de Zimbra 8.7 -> nécessitera un serveur externe
2- ré-internalisation d’un serveur XMPP dans Zimbra pour la version 8.8
Je vous propose ici d’intégrer un système de chat externe (client web et serveur) dans Zimbra 8.6. Le serveur pourra notamment être réutilisé lors de la migration à Zimbra 8.7.
Choix du client Javascript
Je comptais au départ intégrer le client XMPP javascript JSXC car outre le chat textuel, il propose de la visio one2one directement via son navigateur sans plugin (protocole WebRTC). Cependant j’ai choisi de poursuivre le travail d’intégration de Converse.js de Barry de Graaff et d’intégrer l’authentification Zimbra.
Contrairement à JSXC, Converse.js ne gère pas la visio. Cependant rien ne laisse entendre que le client officiel qui sera intégré dans de la future version 8.7 offrira ce service. Ce n’est peut-être pas plus mal de ne pas habituer les utilisateurs à ce service loin d’être indispensable. La migration en sera d’autant plus facile.
Comment ça fonctionne ?
- Lors de la connexion au client web Zimbra, le zimlet com_zimbra_converse se lance automatiquement.
- Le zimlet récupère l’adresse email de l’utilisateur connecté ainsi que le token d’identification. L’email et le token (préfixé avec « zimbra_auth_token++ ») sont passés au client javascript Converse.js.
- Ce client envoie alors ces données au serveur ejabberd via le proxy HTTP Zimbra. Notez qu’il est primordial que l’interface HTTP du serveur d’ejabberd soit chiffrée (HTTPS/TLS) si le serveur ejabberd n’est pas sur la même machine que le serveur Zimbra afin que le token d’authentification Zimbra ne puisse pas être compromis par une attaque man-in-the-middle.
- Le serveur ejabberd passe l’email et l’auth token préfixé à un script d’authentification spécifique.
- Ce script teste la présence de la chaine « zimbra_auth_token++ » en début de token.
- Si elle est présente : le script vérifie via l’API REST de Zimbra que le token est valide et appartient à un utilisateur qui a le droit d’accéder à la ressource demandée (à l’inbox du compte dans notre cas).
- Si elle est absente (c’est donc un client XMPP distinct du client web Zimbra qui souhaite se connecter) : le script vérifie que le couple email/password est valide auprès du serveur Zimbra
- Le script renvoie True ou False au serveur ejabberd selon que le serveur Zimbra ait validé ou non les paramètres transmis
Cette architecture est donc totalement transparente pour l’administrateur système. Il n’est pas nécessaire de créer les comptes sur le serveur ejabberd.
Le Zimlet et le script d’authentification ejabberd sont disponibles sous licence libre sur mon compte Github.
Installer un serveur ejabberd et paramétrer l’authentifieur Zimbra
Installer le serveur :
zimbra@zimbra01 ~ sudo apt-get install ejabberd
Créer le compte admin dans le fichier /etc/ejabberd/ejabberd.yml :
admin:
user:
– « admin »: « localhost »
– « admin »: « fontaine-consultants.fr »
hosts:
– « localhost »
– « fontaine-consultants.fr »
Créer le mot de passe associé :
zimbra@zimbra01 ~ sudo ejabberdctl register admin localhost mon_mot_de_passe
Vérifier que le module de binding HTTP est bien activé dans le fichier /etc/ejabberd/ejabberd.yml :
mod_http_bind: {}
Activer le chiffrement sur le port XMPP 5022 et sur le binding HTTPS
Télécharger le script d’authentification Zimbra et donner les droits associés :
cd /etc/ejabberd/
zimbra@zimbra01 ~ sudo wget https://raw.githubusercontent.com/yuntux/com_zimbra_converse/master/zimbra_auth.py
zimbra@zimbra01 ~ sudo chown ejabberd:ejabberd zimbra_auth.py
Ajouter le script d’authentification Zimbra dans le fichier /etc/ejabberd/ejabberd.yml :
##
## Authentication using external script
## Make sure the script is executable by ejabberd.
##
auth_method: external
extauth_program: « /etc/ejabberd/zimbra_auth.py »
extauth_cache: 0
extauth_instances: 1
Redémarrer le serveur :
zimbra@zimbra01 ~ sudo service ejabberd restart
Intégrer le Zimlet et paramétrer Zimbra
L’accès au ZM_AUTH_TOKEN n’est pas accessible par la fonction JS document.coockie() dans le Zimlet. Il est nécessaire de passer par une JSP pour récupérer le token d’identification (bizarrement document.cookie ne permet pas d’y accéder coté client).
Dans Zimbra 8.6, les JSP ne sont pas activées par défaut. Il est nécessaire de les activer :
root@zimbra01:~# su – zimbra
zimbra@zimbra01:~$ zmprov gs monserveur.net | grep zimbraZimletJspEnabled
(Nothing showed)
zimbra@zimbra8-dev:~$ zmprov ms monserveur.net zimbraZimletJspEnabled TRUE
Pour des raisons de sécurité, le navigateur bloque l’accès à un autre domaine/port depuis le javascript du client web Zimbra. Il est donc nécessaire de passer par un proxy ancré dans Zimbra pour atteindre le serveur ejabberd depuis le client web Zimbra :
zimbra@zimbra01 ~# nano /opt/zimbra/conf/nginx/templates/nginx.conf.web.https.default.template
* avant la dernière accolade, ajouter les lignes suivantes :
location /http-bind {
proxy_pass http://monserveur.net:5280/http-bind/;
}
Télécharger et paramétrer le Zimlet :
zimbra@zimbra01 ~ cd /opt/zimbra/zimlets-deployed
zimbra@zimbra01 ~ mkdir _dev
zimbra@zimbra01 ~ cd dev
Télécharger le dossier (et les fichiers qu’il contient) https://github.com/yuntux/com_zimbra_converse/blob/master/com_zimbra_converse
Remplacer si nécessaire l’URL du binding http vers le serveur ejabberd aux lignes 51 et 57 de converse_zimlet.js
Redémarrer Zimbra :
zimbra@zimbra01:~$ zmcontrol restart
Recharger le client web : le client devrait se connecter au serveur ejabberd automatiquement.
Point sur le chiffrement des communication
Par défaut, ejabberd est configuré pour écouter sur le port 5222 en startTLS. Cela permet de faire un handshake « en claire » puis de passe en mode chiffré si le client et le serveur en sont capables. Le startTLS permet ainsi de gérer des communications chiffrées et non chiffrées sur un même port.
Pour forcer l’usage du chiffrement sur ce port, il est nécessaire de dé-commenter la ligne starttls_required: true correspondant au port 5222. Pour renforcer la sécurité, il est aussi possible de désactiver les version obsolètes de chiffrement via les lignes ( protocol_options: – « no_sslv3 » – « no_tlsv1 »).
Si certains clients souhaitent se connecter directement en mode chiffré, sans passer par le hanshake startTLS, vous devez ouvrir un autre port (par exemple le 5223, qui est le port chiffré standard pour XMMPP) en indiquant tsl = true au lieu de starttls=true. Ce port ne sera utilisé que si la case « utiliser SSL est coché sur Apple Message » ou si l’option « Utiliser le SSL ancien style » est sélectionnée sous Pidgin.
Archiver les messages
L’archivage des messages échangés en XMPP peut basiquement être fait par le client. Cependant, si vos utilisateurs sont amenés à se connecter depuis différents clients, ceux-ci ne disposeront que des messages qui leurs auront été destinés (que lorsqu’ils étaient connectés).
Deux normes XMPP existent pour gérer l’archivage : la XEP-0136 et la XEP-0313. La XEP-0136 est plus ancienne est plus complète… mais aussi plus difficile à implémenter. De fait, peu de clients sont compatibles avec cette dernière. La XEP-0313 a été créé pour palier à cette écueil de normalisation. Elle est moins complète mais implémentée par plusieurs clients, dont Converse.JS.
Pour mettre en oeuvre cette fonctionnalité, il est nécessaire d’installer une base de données robuste, a base de donnée livrée avec ejabberd (Mnesia) n’étant pas conçue pour supporter efficacement de gros volumes. Voici comment installer la base Mysql :
- installer les paquets erlang-p1-mysql et mysql-server
- créer l’utilisateur Mysql ejabbed : echo « GRANT ALL ON ejabberd.* TO ‘ejabberd’@’localhost’ IDENTIFIED BY ‘password’; » | mysql -h localhost -u root
- créer la base de données ejabberd : echo « CREATE DATABASE ejabberd; » | mysql -h localhost -u ejabberd -p
- importer le schéma de base de données :
- gzip -d /usr/share/doc/ejabberd/examples/mysql.sql.gz
- mysql -h localhost -D ejabberd -u ejabberd -p < mysql.sql
- changer les valeurs du fichier /etc/ejabberd/ejabberd.yml
- sql_type: mysql
- sql_server: « localhost »
- sql_database: « ejabberd »
- sql_username: « ejabberd »
- sql_password: « password »
On peut dès lors activer le module d’archivage conforme à la XEP-0313. Ajouter les lignes suivante à la section modules :
mod_mam:
db_type: odbc
default: always
Redémarrer ejabberd : service ejabberd restart
Le module d’archivage doit alors être fonctionnel. Les messages sont stockés dans la table « archive » de la base de données.
Prochaines étapes
Actuellement le script d’authentification ejabberd/zimbra appelle l’API de consultation de la boite du calendrier Zimbra avec une amplitude de 0 jours (donc aucun évènement) : https://localhost/home/user@monserveur.net/calendar.atom?start=0days&end=0day
Cela constitue un détournement relativement lourd : on télécharge le calendrier (vide par construction) à chaque chargement du client web Zimbra. Apparemment, il serait possible d’appeler un service GetInfoRequest pour vérifier la validité et le propriétaire du token. De plus dans le cas tordu où Alice aurait volontairement partagé son inbox avec Eve (qui dispose d’un autre compte sur le serveur), Eve pourrait se faire passer pour Alice sur le chat (en utilisant son token avec l’adresse d’Alice). Le pauvre Bob se ferait avoir, une fois de plus. Notons cependant que si Alice a suffisamment confiance en Eve pour partager sa boite de réception email, il est probable qu’elle accepte également que Eve accède à son compte XMPP !
En ce qui concerne le login classique (par client XMPP externe), il est peut-être possible d’appeler le service de login de Zimbra pour déterminer la validité d’un couple user/password sans demander à voir l’inbox (ce qui n’est pas très optimisé).
Remerciements
Je remercie Barry de Graaff pour la version originale de son module com_zimbra_converse. Je remercie également StarXpert qui m’a permis de comprendre qu’il faut passer par une JSP pour obtenir l’auth-token en lisant le code de son module Starxpert Jappix Chat.