+++

title = "Z-Push, IMAP, sexe et NextCloud"
date = 2016-12-26
aliases = [ "post/2016/12/26/Z-Push,-IMAP,-sexe-et-NextCloud"]
[taxonomies]
tags = [ "mail", "système", "nextcloud" ]
+++

Peut-être pas de sexe finalement, vous n'avez pas été très sage cette année…

Plus sérieusement, je n'ai vu que très peu de ressources en français sur ce sujet, alors que je pense que c'est plutôt utile et rigolo à faire.

<!-- more -->

Tu auto-héberges tes messages chez toi. C'est bien. Tu auto-héberges aussi ton calendrier et ton carnet d'adresses via NextCloud. C'est bien. Et puis, c'est drôlement pratique, tu ne divulgues ainsi pas ta liste d'amis aux grands méchants de l'Internet. Tout cela est très zoli, très pratique et tout sauf que dès que tu veux configurer un ordiphone pour aller avec tout ça, et bien, ça devient nettement plus compliqué :
* sur un PommePhone, il faut entrer tes informations IMAP/SMTP puis ton login/pass ; ensuite, il faut entrer l'adresse du calendrier, puis ton login/pass ; enfin, il faut entrer l'adresse de ton carnet d'adresses puis ton login/pass.
* sur un RobotChelouPhone, il faut entrer tes informations IMAP/SMTP puis ton login/pass ; ensuite, il faut télécharger DavDroid, puis entrer l'URL de ton instance ownCloud/NextCloud puis entrer ton login/pass.

Alors, soyons bien d'accord : une fois que c'est fait, ça marche bien (même très bien) et c'est extrêmement pratique. Je n'ai personnellement jamais eu besoin d'y retoucher.

ais dès que l'on change d'appareil (ce qui arrive plus souvent qu'on ne le voudrait), il faut retaper toute la procédure ; en matière de *Single Sign-On*, on a vu mieux et enfin, Androïd n'est pas compatible nativement avec CardDav/CalDav, ce qui force à passer par un outil tiers (concernant DavDroid, il marche très très bien, je suis d'ailleurs content d'avoir fait un don au développeur).

On va donc essayer de réunir tout ça derrière un seul protocole : ActiveSync. Alors oui, ActiveSync est propriétaire, c'est moche toussa, mais au moins il marche très bien, il est compatible nativement avec pratiquement tous les ordiphones (Pomme, RobotChelou, je ne suis pas sûr pour RenardDeFeu et GentillesseHumaine) et si tu héberges quelqu'un d'autre chez toi (au hasard, ta femme), ce sera plus simple pour lui/elle de configurer un compte qui redescend ensuite toutes ses informations.

Sans compter le fait qu'on va avoir droit à deux ou trois bonus en prime.

#  Authentification de NextCloud et IMAP sur la même base

Pour que tout cela puisse fonctionner efficacement, il va falloir avoir le même login/pass pour l'ensemble des applications concernées. C'est ce que l'on appelle du CSO, *Common Sign-On*.

Le plus simple dans la plupart des cas est de calquer l'authentification de l'ensemble des applis sur l'authentification IMAP. Il y a de très nombreux connecteurs permettant de faire cela sur un nombre considérable d'appli ([TinyTiny-RSS|https://tt-rss.org/|en|TT-RSS] par exemple) et au pire, il n'est pas très compliqué d'écrire son propre module d'authentification.

Personnellement, mon serveur de messagerie authentifie directement mes utilisateurs système, ownCloud, remplacé par NextCloud, s'authentifie directement sur le système aussi via [user_pwauth](https://apps.owncloud.com/content/show.php?content=148406).

J'ai maintenant le même login/pass sur NextCloud/ownCloud et sur mes serveurs IMAP et SMTP.

#  Présentation et installation de Z-Push

[Z-Push](http://z-push.org/) est une application écrite intégralement en PHP permettant de faire un serveur ActiveSync à partir d'autres composants (*backends*). Pour l'installation, il suffit de suivre les instructions disponibles sur [cette page](https://wiki.z-hub.io/display/ZP/Installation). En gros, installer le nouveau dépôt, valider la clé GPG et c'est parti mon kiki !

Je vous recommande d'installer les paquets suivants :
* `z-push-backend-caldav`, `z-push-backend-carddav` et `z-push-backend-imap` : parce que sinon, ça va moyennement marcher ;
* `z-push-autodiscover` : permet de faire de l'auto-découverte de paramètres (et donc évite au toto-user de mal rentrer lui-même les paramètres) ;
* `z-push-state-sql`: permet de stocker les états ActiveSync dans une base de données plutôt que dans des fichiers plats ;
* `z-push-ipc-sharedmemory` : pour les grosses instances (quelques dizaines d'utilisateurs ?), cela va accélérer l'exécution du code.

Une fois que tout ce bazar, et ses dépendances, sont installés, il est nécessaire de changer les autorisations pour deux répertoires : `/var/lib/z-push` et `/var/log/z-push`. Il faut qu'ils appartiennent à l'utilisateur et au groupe faisant tourner le serveur Web (`www-data` pour Debian, `httpd` pour CentOS).

#  Configuration
## NginX

Je ne vais pas tout expliquer en détail, mais voilà en gros, la configuration à appliquer pour que le truc fonctionne. C'est un peu la toutouille de cuisine après avoir subi un nombre conséquent d'essai/erreur.

```
server {
        listen *:443 ssl;
        listen [::]:443 ssl;
        server_name z.mab.it autodiscover.mab.it; # le premier est l'URL de Z-Push, le second sert pour la découverte automatique

        ssl_certificate /etc/letsencrypt/live/z.mab.it/fullchain.pem; ## vous savez vous servir de LetsEncrypt ? Et bien servez-vous en !
        ssl_certificate_key /etc/letsencrypt/live/z.mab.it/privkey.pem;

        root /usr/share/z-push;

        index index.php;

        client_max_body_size 4M;
        client_body_buffer_size 128k;

        location / { ## un grand classique en NginX pour tout rediriger vers index.php
                try_files $uri $uri/ index.php;
        }

        location /Microsoft-Server-ActiveSync { ## c'est l'URL la plus important : le mobile va taper dessus pour toutes les opérations
                rewrite ^(.*)$ /index.php last;
        }

        location ~* ^/autodiscover/autodiscover\.xml$ { ## cela permet de rediriger l'autodiscover (quelque soit la casse) vers le script PHP qui va bien)
                rewrite ^(.*)$ /autodiscover/autodiscover.php last;
        }

        location ~* \.php$ { ## tout fichier PHP est ensuite interprété\
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php5-fpm.sock;
        }
}
```

Normalement, avec ça, NginX devrait être capable de prendre en considération toutes les étapes du processus : auto-découverte, demande d'informations, etc… Si tu as la (mal)chance d'avoir de l'Apache, tu dois pouvoir arriver à faire un truc à peu près similaire sans trop de problème (mais je te laisse te démerder, je suis allergique à Apache).

## PHP-FPM

Ça, c'est la petite surprise du chef, qui fait plaisir aux papilles et qui réjouit les estomacs. Voilà une configuration qu'on ne trouve nul part et qui pourtant est indispensable : il faut ajouter la librairie `libawl-php`, installée dans les dépendances plus haut, dans le PATH d'exécution de PHP-FPM. Le PATH par défaut est `.:/usr/share/php`, il faut donc y ajouter `/usr/share/awl/inc` sous Debian (à adapter à votre distrib) dans `/etc/php5/fpm/php.ini` :

```
include_path = ".:/usr/share/php:/usr/share/awl/inc"
```

Au lieu de cela, tu pourrais créer un pool spécifique dans PHP-FPM contenant le bon PATH. Je n'en avais pas vraiment l'utilité, donc j'ai préféré procéder comme ça, mais le choix reste le tien.

## Z-Push

Normalement, l'installation a dû créer une chiée de fichiers de configuration dans `/etc/z-push`. Chaque fichier permet de configurer un morceau du logiciel.

# MySQL/MariaDB

Cet étape est facultative : tu peux très bien laisser le comportement par défaut qui consiste à créer les fichiers d'état de Z-Push dans `/var/lib/z-push`. Sinon, tu peux éditer le fichier `/etc/z-push/state-sql.conf.php` pour y entrer tes paramètres SQL : nom du serveur, utilisateur, mot de passe, base qu'il faudra créer au préalable. Il faut ensuite éditer le fichier `/etc/z-push/z-push.conf.php` pour l'informer d'où stocker les états :

```
/**********************************************************************************
 * StateMachine setting
 *
 * These StateMachines can be used:
 *   FILE  - FileStateMachine (default). Needs STATE_DIR set as well.
 *   SQL   - SqlStateMachine has own configuration file. STATE_DIR is ignored.
 *           State migration script is available, more informations: https://wiki.z-hub.io/x/xIAa
 */
    define('STATE_MACHINE', 'SQL');
    define('STATE_DIR', '/var/lib/z-push/');
```

# Auto-découverte

On va modifier le fichier `/etc/z-push/autodiscover.conf.php` pour lui indiquer les paramètres suivants :
```
[…]
define('SERVERURL', 'https://z.mab.it/Microsoft-Server-ActiveSync'); ## permet de spécifier l'URL de push au terminal
[…]
define('USE_FULLEMAIL_FOR_LOGIN', false); ## dans mon cas, je n'utilise que la partie de gauche de mon courriel pour m'authentifier. Cela peut être différent si ton serveur IMAP ne fonctionne pas comme ça
[…]
define('BACKEND_PROVIDER', 'BackendCombined'); ## logiquement, ça devrait être détecté automatiquement, mais on est jamais trop prudent
```

# IMAP

Dans le fichier `/etc/z-push/imap.conf.php`, nous allons indiquer les paramètres IMAP. Cela va essentiellement consister à renseigner les paramètres de la boîte et notamment les dossiers d'envoi, de brouillons, etc…

```
## Donne les paramètres IMAP de base
define('IMAP_SERVER', 'localhost'); ## ou autre chose si c'est sur un autre serveur
define('IMAP_PORT', 143);
## Ce paramètre est super important : il donne la chaîne d'ouverture de la boîte par la librairie PHP-IMAP
## En gros, il faut obligatoirement du TLS, sans vérifier le certificat (inutile pour ce cas)
define('IMAP_OPTIONS', '/tls/novalidate-cert/norsh');

## Si tu ne mets pas ce paramètre à True, rien ne marche :)
define('IMAP_FOLDER_CONFIGURED', true);
```

Pour le reste, j'ai juste changé le répertoire des pourriels en `JUNK` et le répertoire des archives en `ARCHIVES`. Ce sont les paramètres par défaut que j'ai pour Dovecot, cela permet simplement de les calquer. Adapte à tes propres besoins (au pire, pour tester, ça va fonctionner correctement).

*Si tu te poses la question, à juste titre, de la configuration SMTP, il n'y en a pas. Z-Push utilise par défaut les fonctions d'envoi de message de PHP. Bien évidemment, c'est configurable :).*

# CalDav

Là, on arrive sur la partie un peu *touchy*. Il s'agit de configurer le fournisseur CalDav (NextCloud dans mon cas, ce peut être ownCloud évidemment ou tout autre serveur CalDav). L'idée est de fournir dans un premier temps le chemin vers la collection de calendriers, puis vers un calendrier par défaut. Sur tous les appareils où j'ai essayé, tous les calendriers étaient toujours détectés correctement, mais il se peut que tu ne voies que le calendrier par défaut.

Dans le fichier `/etc/z-push/caldav.conf.php` donc :
```
## Indique le nom, le protocole et le port pour le serveur CalDav
define('CALDAV_PROTOCOL', 'https');
define('CALDAV_SERVER', 'nextcloud.mab.it');
define('CALDAV_PORT', '443');
## L'URL de la collection. Le %u remplace le nom d'utilisateur. Pour ownCloud/NextCloud :
define('CALDAV_PATH', '/remote.php/dav/calendars/%u/');
## Le calendrier par défaut
define('CALDAV_PERSONAL', 'default');
## Apparemment, ownCloud/NextCloud ne supporte pas cette option (synchronisation de masse)
define('CALDAV_SUPPORTS_SYNC', false);
```

*Si tu as un NextCloud installé récemment, ton calendrier principal ne s'appellera `default` mais `calendar`. C'est l'héritage de ownCloud 6 que je trimballe…*

# CardDav

ême principe que pour CalDav : une collection, un carnet par défaut. Comme pour CalDav, il faut indiquer les bonnes URL aux bons endroits, avec néanmoins quelques variantes. Démonstration sur `/etc/z-push/carddav.conf.php` :

```
## Même principe que plus haut
define('CARDDAV_PROTOCOL', 'https');
define('CARDDAV_SERVER', 'nextcloud.mab.it');
define('CARDDAV_PORT', '443');
## le chemin vers la collection de carnets d'adresses. Attention / à la fin obligatoire !
define('CARDDAV_PATH', '/remote.php/dav/addressbooks/users/%u/');
## le carnet par défaut, / à la fin toujours obligatoire !
define('CARDDAV_DEFAULT_PATH', '/remote.php/dav/addressbooks/users/%u/default/');
## il faut commenter la GAL (Global Address List) : c'est une option Outlook qui n'existe évidemment pas avec CardDav
// define('CARDDAV_GAL_PATH', '/caldav.php/%d/GAL/');
## Pareil, non supporté
define('CARDDAV_SUPPORTS_SYNC', false);
## Si tu ne veux pas que ça merde, laisse cette option vide !! Ça force la recherche des fichiers VCF de la collection de carnet d'adresses en .vcf. Ce n'est pas le cas avec NextCloud
define('CARDDAV_URL_VCARD_EXTENSION', *);
```

Tu peux laisser les autres options par défaut.

*Si tu as un NextCloud installé récemment, ton carnet d'adresses principal ne s'appellera `default` mais `contacts`. C'est toujours l'héritage de ownCloud 6 que je trimballe…*

# Dernières configurations et test de l'ensemble

C'est là qu'on bascule du très scientifique au mystique. On va configurer le `BackendCombined` pour faire correspondre des fonctions ActiveSync à des backends (IMAP, CardDav ou CalDav donc). Ça se passe dans le fichier `/etc/z-push/combined.conf.php` :

```
class BackendCombinedConfig {
    public static function GetBackendCombinedConfig() {
        return array(
        ## Le nom des différents Backend et une lettre leur est associée
            'backends' => array(
                'i' => array(
                    'name' => 'BackendIMAP',
                ),
                'd' => array(
                    'name' => 'BackendCardDAV',
                ),
                'c' => array(
                    'name' => 'BackendCalDAV',
                ),
            ),
            'delimiter' => '/',
            ## Et voilà les différentes fonctions ActiveSync
            ## pour chaque fonction on précise si ça doit aller dans IMAP (i), dans CardDav (c) ou dans CalDav (d)
            ## ci-dessous, un truc qui marche
            'folderbackend' => array(
                SYNC_FOLDER_TYPE_INBOX => 'i',
                SYNC_FOLDER_TYPE_DRAFTS => 'i',
                SYNC_FOLDER_TYPE_WASTEBASKET => 'i',
                SYNC_FOLDER_TYPE_SENTMAIL => 'i',
                SYNC_FOLDER_TYPE_OUTBOX => 'i',
                SYNC_FOLDER_TYPE_TASK => 'c',
                SYNC_FOLDER_TYPE_APPOINTMENT => 'c',
                SYNC_FOLDER_TYPE_CONTACT => 'd',
                SYNC_FOLDER_TYPE_NOTE => 'c',
                SYNC_FOLDER_TYPE_JOURNAL => 'c',
                SYNC_FOLDER_TYPE_OTHER => 'i',
                SYNC_FOLDER_TYPE_USER_MAIL => 'i',
                SYNC_FOLDER_TYPE_USER_APPOINTMENT => 'c',
                SYNC_FOLDER_TYPE_USER_CONTACT => 'd',
                SYNC_FOLDER_TYPE_USER_TASK => 'c',
                SYNC_FOLDER_TYPE_USER_JOURNAL => 'c',
                SYNC_FOLDER_TYPE_USER_NOTE => 'c',
                SYNC_FOLDER_TYPE_UNKNOWN => 'i',
            ),
            'rootcreatefolderbackend' => 'i',
        );
    }
}
```

La partie purement mystique est terminée. Si tu fais pointer ton butineur vers l'adresse Web de l'application Z-Push (`https://z.mab.it` dans mon cas), cela devrait te demander une authentification. Si tu arrives à t'authentifier avec un utilisateur IMAP, ça devrait te cracher une belle erreur comme quoi tu ne peux pas faire de GET sur cette page. C'est parfaitement normal, tu n'es pas un périphérique ActiveSync (on m'aurait menti ?).

En gros, maintenant, t'as plus qu'à tester avec un périphérique Androïd ou Apple : dans l'application Mail, il suffit de rentrer adresse électronique et mot de passe, l'auto-découverte devrait se charger du reste et commencer à synchroniser carnet(s) d'adresses, calendrier(s) et courriels. Petite astuce pour RobotChelouPhone : tu peux créer un second profil pour faire le test. Ça évitera de tout casser dans le tien et ça coûte pas grand-chose.

Tu peux vérifier les différents appareils connectés avec la commande `z-push-admin` :

```
# z-push-admin -a list                                                                             

All synchronized devices

Device id                           Synchronized users
-----------------------------------------------------
androidcaaaaavava                   gibbon
```

On peut d'ailleurs récupérer tout un tas d'information sur le terminal, ce qui peut être plus ou moins pratique (Big Brother quelqu'un ?) :

```
# z-push-admin -a list -u gibbon

Synchronized devices of user: gibbon
-----------------------------------------------------
DeviceId:               androidcaaaaavava
Device type:            bqAquarisX
UserAgent:              bqAquarisX5/6.0.1-EAS-2.0
Device Model:           Aquaris X5
Device IMEI:            XXXXXXXXXXXXXXX
Device friendly name:   Aquaris X5
Device OS:              Android 6.0.1
Device Operator:        PourriOp (3333)
ActiveSync version:     14.0
First sync:             2016-12-27 09:04
Last sync:              2016-12-27 09:07
Total folders:          14
Short folder Ids:       No
Synchronized folders:   6 (1 in progress)
Synchronized data:      Emails(2) Calendars(3) Contacts
Synchronization progress:
        Folder: Inbox                Sync: Synchronizing Status: 22% (8/36)
Status:                 OK
WipeRequest on:         not set
WipeRequest by:         not set
Wiped on:               not set
Policy name:            default
Attention needed:       No errors known
```

# BONUS!!§!
## Placer des paramètres de sécurité par défaut

Le truc pratique avec ActiveSync, c'est que ça permet de régler quelques paramètres par défaut pour le téléphone : on peut forcer le chiffrement du stockage, forcer un mot de passe de déverrouillage plus ou moins complexe, rendre impossible l'installation d'un second compte de messagerie, interdire l'utilisation de l'appareil photo, d'une carte SD, etc… Ça offre pas mal de possibilité et ça permet de s'assurer que l'appareil est un minimum sécurisé en tout temps (et sans rien rajouter d'autres).

Tout cela se passe dans le fichier `/etc/z-push/policies.ini`. La politique par défaut est assez laxiste, mais on peut assez facilement la renforcer un peu. Voici les quelques options que je vous conseille :
```
; mot de passe requis
devpwenabled = 1
; pas obligatoirement alphanumérique
alphanumpwreq = 0
; longueur minimum du mot de passe
mindevpwlenngth = 4
; chiffrement du terminal requis
reqdevenc = 1
```

Tu peux évidemment jouer avec les autres options : temps minimal avant que le terminal ne se verrouille, nombre d'essais de mot de passe avant de briquer le terminal, etc… Ce que je te propose ici est ce que je considère comme le minimum.

## Effacer un terminal à distance

On peut complètement effacer un terminal à distance grâce à ActiveSync. Attention, c'est hyper sérieux, le truc rigole pas du tout : __ÇA EFFACE VRAIMENT TOUT !!§!__. En plus de pouvoir faire une bonne blague pour le 1er avril, ça peut toujours être utile si le terminal est perdu ou volé : on s'assure que rien dessus ne sera retrouvé.

```
# z-push-admin -a wipe -d androidcaaaaavava

Are you sure you want to REMOTE WIPE device 'androidcaaaaavava' [y/N]:
```

On peut également effacer tous les terminaux liés à un utilisateur en particulier. Bref, c'est un joli petit bonus, je trouve, bien sympathique à l'utilisation.

#  Conclusion

ActiveSync, c'est mal parce que c'est propriétaire. Malheureusement, pour le moment, c'est la seule solution fiable pour contrôler 99% des terminaux mobiles disponibles sur le marché. Alors, oui, c'est moche, mais en attendant une alternative viable, autant essayer d'en tirer le meilleur.

Et comme disent tous les connards : à l'année prochaine !

Addendum :

Si tu as une configuration de messagerie un peu chelou (genre ton `$myorigin` dans Postfix ne correspond pas à ton domaine de messagerie), n'oublie pas de mettre ce paramètre dans la configuration :

```
define('IMAP_DEFAULTFROM', '@mondomainedemessagerie.com');
```

Ça permet de s'assurer que le `Return-Path` et le `From` de l'email envoyé est correct.
