postfix : utilisateurs « virtuels » MySQL ; accès POP3[S], IMAP[S], SASL et TLS ; quota (bingo, j’ai tout mis dans le titre)

closeCet article a été publié il y a 9 ans 22 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Introduction

Hop,
Après ma doc d’initiation Debian, où un rapide chapitre est consacré au montage d’un serveur postfix, spamassassin, greylisting etc, dans une configuration simple,
Après cet article sur le montage complet cette fois, en incluant amavisd-new, anti-virus etc,
=> Voici le complément idéal, par exemple en PME (et même plus gros) :

  • Gestion d’utilisateurs virtuels, entendez par là « utilisateurs définis en base de données et non pas utilisateurs réels de l’OS ».
  • Mise en place de tout ce qu’il faut pour lire les mails (POP3, POP3S, IMAP, IMAPS) via les outils « courier-* »
  • Authentification via SASL
  • Mise en place d’authentification sécurisée plus forte pour l’envoi (TLS).

Pour le webmail, j’en ai parlé déjà quelques fois sur mon blog et dernièrement, la version 0.3 de roundcubemail a fait de gros progrès par rapport à la 0.1. Ca s’installe en 3 clics. Si j’ai le temps je ferai un article, mais c’est mal barré.

Situation de départ

Avoir un serveur postfix correctement monté, avec (ou pas) amavis, spamassassin, un anti-virus, du greylisting, un nom correct etc. Bref, un serveur qui fonctionne et qui accepte les mails correspondant à des Maildir d’utilisateur de l’OS. En gros, ce qui est expliqué là .
Je ne parlerai pas d’ouverture de ports réseaux etc, mais ça tombe sous le sens. Ni du fait qu’il vous faudra une base MySQL en état de marche.

Je n’ai pas trouvé de doc officielle sur le sujet. En gros, on a postfix-mysql qui permet de faire le lien entre le serveur de mails et la base, mais rien dans un gros fichier README qu’on a habituellement avec ce genre de paquet. Ou alors je l’ai loupé.
Le « postfix mysql howto » est juste résumé à ça : http://www.postfix.org/MYSQL_README.html et ça : http://www.postfix.org/mysql_table.5.html. Et pour le coup, ce n’est pas très sexy.

J’ai donc compilé tout un tas de différents articles le sujet, parfois incomplet, parfois des articles décrivant des méthodes à grand coup de compilation-c-est-pénible-et-stupide-pour-suivre-les-mises-à -jour etc. Parfois sans gestion de quota, parfois avec. Et en général, en expliquant rien du tout. Donc dans mon cas, je tâche d’expliquer ce qu’on fait.

Alors, comme à mon habitude, je laisserai trainer 2/3 bugs que j’ai pu rencontrer (de ma faute), ça peut permettre d’aider si vous faites les mêmes erreurs. Et on y apprend l’existence de quelques outils pour tester en ligne de commande.

Remarque pour la partie quota qui ne fonctionne pas nativement dans postfix, je la passe sous silence volontairement au début et y consacre un chapitre à la fin. En effet, c’est relativement dissocié de tout le reste et ça implique de recompiler postfix (ou d’utiliser un .deb fourni par je ne sais qui, mais vous êtes dépendant de lui pour les mises à jour). Comme certains se moqueront des quotas, je le traite à la fin. Beaucoup de guides de mise en place mélangent tout et prévoient des fonctions qu’ils n’exploitent pas. J’essaye pour ma part de faire des briques dissociables.

J’ai aussi tout installé sur un unique serveur. Ce n’est pas une nécessité, sur une grosse installation par exemple.

Autres documentations

A ce propos, j’ai lu à peu près tout ce qui suit (et plein d’autres) pour monter le serveur, notamment les 2 premiers lien. J’ai même en gros simplement repompé, traduit et expliqué :

Grandes étapes de la procédure

En gros, on va :

  • 1) Installer les applicatifs,
  • Créer la structure en base,
  • Décrire cette structure à postfix pour qu’il sache l’interroger
  • 2) Créer ce qu’il faut pour stocker les boites virtuelles
  • Faire quelques tests de livraison de mails pour valider la partie « utilisateurs définis en base »
  • 3) Paramétrer les outils pour récupérer les mails (courier-POP, IMAP et versions sécurisées par SSL)
  • Tester ces protocoles pour la récupération des mails
  • Voir l’analyse de quelques erreurs sur la partie « courier »
  • 4) Paramétrer les mécanismes d’authentification pour l’envoi (SASL, TLS)
  • Tester l’envoi, en TLS ou pas
  • Voir l’analyse de quelques erreurs sur la partie authentification pour l’envoi
  • 5) Optionnel : activer les quotas
  • Et enfin, voir ce que je n’ai pas traité

Installation des paquets

Pour une fois, tout ne descend pas par dépendance, car les différents modules d’authentification (identification via une base MySQL) ne sont pas obligatoires, donc pas de dépendance. Donc pour une fois il faut absolument tout copier-coller. Pour illustrer, installer courier-authlib n’amène pas forcément courier-authlib-mysql.

On a donc :

aptitude install postfix-mysql courier-pop courier-pop-ssl courier-imap courier-imap-ssl postfix-tls  libsasl2-2 libsasl2-modules libsasl2-modules-sql sasl2-bin libpam-mysql openssl courier-authdaemon courier-authlib-mysql

Vous pouvez à la limite zapper certains, comme le POP, si vous ne comptez pas l’utiliser. Notez que les webmails tapent en IMAP, si vous ne le saviez pas.

Connexion postfix <-> MySQL

Création de la base de données

J’ai vu des définitions de base hyper complète, pour désactiver le POP (ou je ne sais quoi d’autre) par utilisateur. Sachez qu’il sera toujours temps d’ajouter une colonne à une table, de mettre des valeurs par défaut et d’ajouter une règle dans postfix pour prendre en compte une nouvelle fonctionnalité (c’est d’ailleurs ce qu’on fera pour les quotas de boîtes mails). Donc faisons simple au début.

On crée une base et un utilisateur dédié, avec des droits pas trop larges. Une fois connecté en root dans votre base (exemple : mysql -u root -p), on crée la base et donne des droits à cet utilisateur qu’on crée au passage :

mysql> create database postfix;
Query OK, 1 row affected (0.00 sec)

mysql> grant select,insert,update,delete on postfix.* TO 'mail_adm'@'localhost' identified by 'monpassadmin';
Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

ATTENTION : dans toute la suite, le nom de la base, le nom des tables, les identifiants etc sont à modifier pour refléter votre installation. Attention à ne pas copier-coller trop vite.

Ensuite, dans la base « postfix », on crée quelques tables :

use postfix;

CREATE TABLE domains (
        domain varchar(50) NOT NULL,
        PRIMARY KEY (domain)
);
CREATE TABLE forwardings (
        source varchar(80) NOT NULL,
        destination TEXT NOT NULL,
        PRIMARY KEY (source)
);
CREATE TABLE users (
        email varchar(80) NOT NULL,
        password varchar(20) NOT NULL,
        PRIMARY KEY (email)
);
CREATE TABLE transport (
        domain varchar(128) NOT NULL default '',
        transport varchar(128) NOT NULL default '',
        UNIQUE KEY domain (domain)
);

Ces 4 tables permettront, dans l’ordre, de :

  • définir la liste des domaines qu’on gère
  • créer des alias (renvoyer toto@domaine.fr vers tata@blabla.com + titi@blibli.com)
  • définir nos comptes et leurs propriétés
  • définir une table de transport, au besoin

Création des requêtes pour savoir comment interroger ces tables

Le principe est le suivant : on a créé des tables avec des noms de colonnes et de tables libres (on aurait pu tout changer), on doit donc indiquer à postfix comment vérifier un mot de passe, voir s’il y a un forward etc. Pour chaque action, on a une directive dans /etc/postfix/main.cf et un fichier de conf décrivant la base et la requête.
Le truc énorme, c’est qu’on pourrait ajouter n’importe quelle colonne (attribut) pour ajouter un test. Exemple stupide : le password est OK entre 8h et 20h, sinon impossible de relever ses mails.
On ajouterait une colonne et une clause supplémentaire dans le WHERE, tenant compte de l’heure et d’horaires autorisés pour tel utilisateur.

Voici les 4 fichiers de conf pour les 4 tables précédement créées :
Fichier /etc/postfix/mysql-virtual_domains.cf :

user = mail_adm
password = monpassadmin
dbname = postfix
query = SELECT domain AS virtual FROM domains WHERE domain='%s'
hosts = 127.0.0.1

Fichier /etc/postfix/mysql-virtual_forwardings.cf :

user = mail_adm
password = monpassadmin
dbname = postfix
query = SELECT destination FROM forwardings WHERE source='%s'
hosts = 127.0.0.1

Fichier /etc/postfix/mysql-virtual_mailboxes.cf :

user = mail_adm
password = monpassadmin
dbname = postfix
query = SELECT CONCAT(SUBSTRING_INDEX(email,'@',-1),'/',SUBSTRING_INDEX(email,'@',1),'/')
        FROM users WHERE email='%s'
hosts = 127.0.0.1

Fichier /etc/postfix/mysql-virtual_transports.cf :

user = mail_adm
password = monpassadmin
dbname = postfix
query = SELECT transport FROM transport WHERE domain='%s'
hosts = 127.0.0.1

Ensuite on sécurise ces fichiers qui contiennent des infos sensibles sur la base d’authentification de mails :

chown root:postfix /etc/postfix/mysql*cf
chmod 640 /etc/postfix/mysql*cf

Création de la structure de stockage des boîtes

Le principe est le suivant : on indique un répertoire racine, avec un utilisateur dédié sous lequel tout est stocké.
On aura une belle arborescence de ce type :

/ma/racine/des/maildirs/
        - /mon.domaine.fr/
                - user1
                        - cur
                        - new
                        - tmp
                        + .autre.sous.rep.de.maildir.genre.Sent
                + user2
        - /autre.domaine.a.moi.fr/
                + userX

Donc, disons que que le répertoire racine de stockage des boîtes mails sera /vmailboxes. On crée l’utilisateur avec un ID arbitraire :

groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /vmailboxes -m

Attention, vous allez aussi recopier le contenu de /etc/skel dans /vmailboxes, le home de « vmail ». Ce n’est pas forcément utile. Adaptez au besoin.

On indique tout ça à postfix

Dans un premier temps, je n’ajoute pas toute la conf dans /etc/postfix/main.cf. On va y aller progressivement vu ce qu’on vient d’installer. On ajoute donc :

virtual_alias_domains =
virtual_alias_maps = hash:/etc/postfix/virtual,proxy:mysql:/etc/postfix/mysql-virtual_forwardings.cf
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql-virtual_domains.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailboxes.cf
virtual_mailbox_base = /vmailboxes
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_create_maildirsize = yes
virtual_maildir_extended = yes
transport_maps = proxy:mysql:/etc/postfix/mysql-virtual_transports.cf
proxy_read_maps = $local_recipient_maps $mydestination $virtual_alias_maps \
             $virtual_alias_domains $virtual_mailbox_maps $virtual_mailbox_domains \
             $relay_recipient_maps $relay_domains $canonical_maps $sender_canonical_maps \
             $recipient_canonical_maps $relocated_maps $transport_maps $mynetworks

Les fichiers de conf MySQL en « mode proxy » (si on peut dire) permettent d’optimiser les accès en conservant en mémoire le contenu. Pratique pour éviter une tonne d’accès disque inutile.

N’oubliez pas de générer le fichier virtual.db, la « version base de données » associée au fichier « virtual ». Il vous sert à faire suivre par exemple certains comptes locaux vers d’autres adresses e-mails. Pour centraliser vos remontées de serveur autre part.

Rechargez postfix, ça devrait bien se passer. Allez voir dans /var/log/mail.info ou syslog sinon.
On va pouvoir tester.

Quelques tests de livraison de mails

Pour créer une boîte, il faudra au minimum :

  • Avoir déclaré le domaine qu’on gère
  • Avoir déclaré le compte
  • Avoir envoyé un mail de bienvenue, localement, pour forcer la création de boîte (arborescence
  • Eventuellement avoir ajouté des sous-répertoires pour que le webmail (si vous en mettez un) soit content : Sent, Trash, Drafts, Junk.

Ajout d’un domaine

Tout comme en postfix « normal », on indique dans « mydestination » la liste des noms de domaines gérés, il faut ici les indiquer dans la base. Et les supprimer de « mydestination » si vous les gériez localement jusqu’alors. On crée simplement un enregistrement en base :

INSERT INTO domains (domain) VALUES ('mxtest.mondomaine.fr');

(Je suppose que mxtest.mondomaine.fr existe et que l’enregistrement MX existe aussi et pointe vers notre serveur.

Création d’un utilisateur

Simplissime, dans la base aussi :

INSERT INTO users (email, password) VALUES ('jacques@mxtest.mondomaine.fr', ENCRYPT('mon_pass_en_clair'));

Initialisation de sa boîte mail

Connecté localement, par exemple, sur la machine, envoyez un mail en ligne de commande, avec mailx, mutt. Ce que vous voulez.
Là , ô magie, vous devriez voir l’arborescence /vmailboxes se peupler, par exemple avec /vmailboxes/mxtest.mondomaine.fr/jacques/
C’est une arborescence « Maildir » classique, avec cur, new, tmp.
Si le mail est arrivé, on peut pour l’instant le lire en ligne de commande en allant dans le répertoire, tout bêtement.

Création des autres sous-répertoires

Dans cette procédure de création d’un compte, je suggère d’ajouter, pour le webmail, la création des répertoires habituels d’un compte en IMAP. Si vous ne comptez livrer que des boîtes récupérables en POP, ne vous cassez pas la tête, c’est inutile.
Sinon :

su - vmail
cd /vmailboxes/mxtest.mondomaine.fr/jacques
for i in Sent Trash Drafts Junk
do
        maildirmake .$i #le . est important
done

Vous devriez voir les sous-répertoires .Sent, .Junk etc avec à chaque fois le cur/tmp/new et des permissions restreintes à l’utilisateur vmail.

Voilà , la boîte sait recevoir et postfix sait gérer nos comptes virtuels, pour livrer le courier dans les boîtes virtuelles.

Abonnement aux répertoires IMAP ; pour le webmail ou la consultation IMAP

Si besoin, pensez à inclure la création du fichier suivant dans votre procédure de création de boîte mail :

mxtest:/vmailboxes/mxtest.mondomaine.fr/jacques# cat courierimapsubscribed
INBOX
INBOX.Sent
INBOX.Drafts
INBOX.Trash
INBOX.Junk

Annexe : création d’un alias

Dans la table forwardings, à base de :

INSERT INTO forwardings VALUES ('mon_alias@bla.fr', 'dest1@toto.fr', 'dest2@tata.fr');

Actif immédiatement. Pas plus compliqué que ça :).

Partie lecture des mails, via « courier-* » utilisant MySQL pour l’authentification

Ici, on voit créer les services POP, IMAP et leurs versions sécurisées. Le tout sur fond d’authentification des utilisateurs définis en base MySQL.

Indiquer au « daemon » courier d’utiliser MySQL

Simplement, dans /etc/courier/authdaemonrc, changer la liste des modules utilisés pour l’authentification :

#authmodulelist="authpam"
authmodulelist="authmysql"

Dans /etc/courier/authmysqlrc, il faut positionner les variables suivantes :
MYSQL_SERVER localhost
MYSQL_USERNAME mail_adm
MYSQL_PASSWORD monpassadmin
MYSQL_PORT 0
MYSQL_DATABASE postfix
MYSQL_USER_TABLE users
MYSQL_CRYPT_PWFIELD password
MYSQL_UID_FIELD 5000
MYSQL_GID_FIELD 5000
MYSQL_LOGIN_FIELD email
MYSQL_HOME_FIELD « /vmailboxes »
MYSQL_MAILDIR_FIELD CONCAT(SUBSTRING_INDEX(email,'@',-1),’/’,SUBSTRING_INDEX(email,'@',1),’/’)
#MYSQL_NAME_FIELD name # NE PAS LAISSER CETTE VARIABLE, voir plus bas, cas d’erreur pour moi

Ici, bien évidemment, les valeurs indiquées reflètent les noms choisis en base.

Générer les certificats pour le POP3S et IMAPS

Cette partie est optionnelle, si vous ne voulez pas du POP3S et IMAPS. Dans ce cas vous pouvez aussi désinstaller courier-*-ssl.

On modifie les fichiers de conf de génération des certificats des daemon POP et IMAP sécurisés :

mxtest:/etc/courier# cat pop3d.cnf

RANDFILE = /usr/lib/courier/pop3d.rand

[ req ]
default_bits = 1024
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no

[ req_dn ]
C=FR
ST=France
L=PARIS
O=Courier Mail Server
OU=Automatically-generated POP3 SSL key
CN=mondomaine.fr
emailAddress=certif@mondomaine.fr


[ cert_type ]
nsCertType = server

Et pour, l’IMAP :


mxtest:/etc/courier# cat imapd.cnf

RANDFILE = /usr/lib/courier/imapd.rand

[ req ]
default_bits = 1024
encrypt_key = yes
distinguished_name = req_dn
x509_extensions = cert_type
prompt = no

[ req_dn ]
C=FR
ST=France
L=PARIS
O=Courier Mail Server
OU=Automatically-generated IMAP SSL key
CN=mondomaine.fr
emailAddress=certif@mondomaine.fr


[ cert_type ]
nsCertType = server

Et on génère les certificats à partir de ces modèles. Ca ne fait que connaître à l’avance certains paramètres :

mxtest:/etc/courier# mkimapdcert
cp: not writing through dangling symlink `/usr/lib/courier/imapd.pem'
chmod: cannot operate on dangling symlink `/usr/lib/courier/imapd.pem'
chown: ne peut effectuer une déférence sur `/usr/lib/courier/imapd.pem': Aucun fichier ou répertoire de ce type
Generating a 1024 bit RSA private key
................++++++
....++++++
writing new private key to '/usr/lib/courier/imapd.pem'
-----
1024 semi-random bytes loaded
Generating DH parameters, 512 bit long safe prime, generator 2
This is going to take a long time
..............+.....................................................................................+..+.....................
.........+.............................+....+...+........+...................................................................
.............+..........+...........+..+...+..............................+....................+......................+......
........................+.....+.+......+..........................................................+..........................
.........................................................+...........+.........+.......................+.....................
..+..........++*++*++*++*++*++*
subject= /C=FR/ST=France/L=PARIS/O=Courier Mail Server/OU=Automatically-generated IMAP SSL 

key/CN=mondomaine.fr/emailAddress=certif@mondomaine.fr
notBefore=Oct  7 15:17:54 2009 GMT
notAfter=Oct  7 15:17:54 2010 GMT
SHA1 Fingerprint=9D:62:E6:CF:B7:39:80:C8:4E:F9:09:9C:61:36:14:8F:0B:EF:3C:6E

Et :

mxtest:/etc/courier# mkpop3dcert
cp: not writing through dangling symlink `/usr/lib/courier/pop3d.pem'
chmod: cannot operate on dangling symlink `/usr/lib/courier/pop3d.pem'
chown: ne peut effectuer une déférence sur `/usr/lib/courier/pop3d.pem': Aucun fichier ou répertoire de ce type
Generating a 1024 bit RSA private key
..++++++
..................++++++
writing new private key to '/usr/lib/courier/pop3d.pem'
-----
1024 semi-random bytes loaded
Generating DH parameters, 512 bit long safe prime, generator 2
This is going to take a long time
...............+..........................++*++*++*++*++*++*
subject= /C=FR/ST=France/L=PARIS/O=Courier Mail Server/OU=Automatically-generated POP3 SSL 

key/CN=mondomaine.fr/emailAddress=certif@mondomaine.fr
notBefore=Oct  7 15:18:10 2009 GMT
notAfter=Oct  7 15:18:10 2010 GMT
SHA1 Fingerprint=DD:8D:7C:0D:87:9D:19:9D:62:BE:5B:9A:B2:D8:43:80:CF:47:E2:14

Redémarrer tout ce bazar

Ayé, tout est prêt, les certificats sont générés, on redémarre tout ce monde :

/etc/init.d/courier-authdaemon restart
/etc/init.d/courier-imap restart
/etc/init.d/courier-imap-ssl restart
/etc/init.d/courier-pop restart
/etc/init.d/courier-pop-ssl restart

Tests pour l’envoi, quelques erreurs analysées

Déjà , faites un telnet sur chaque port, 143, 993, 110, 995 pour voir si ça répond bien.
Ensuite, testez chaque protocole. Par exemple avec Thunderbird. Sur un poste Windows, pensez à la version « portable » qui n’interfèrera pas avec votre actuel Thunderbird. Sous Linux, vous pouvez jouer avec vos ~/.mozilla-thunderbird (j’ai plus le nom précis en tête) pour ne pas abîmer votre conf actuelle.

Erreurs rencontrées

Ces erreurs m’ont permis de découvrir la commande authtest pour faire des tests d’aut

Oubli de package

J’ai commencé par tester l’authentification IMAP en local (depuis un webmail, roundcube, installé sur la même machine). Ca ne passait pas. J’ai testé depuis Thunderbird, pareil (pas étonnant), mais cette fois ça a généré un message dans les logs, genre syslog ou mail.info je ne sais plus :

Oct  8 16:10:32 ns305192 authdaemond: libauthmysql.so: cannot open shared object file: No such file or directory

Oops, il manquait le package courier-authlib-mysql.
Heureusement, ce ne sera pas votre cas, c’est bien écrit dans le aptitude install du début.

Erreur sur MYSQL_NAME_FIELD

Ensuite, j’ai eu le droit à une vraie erreur :

Oct  8 16:53:56 serveur imapd: authentication error: Input/output error

C’est l’occasion de mentionner la commande authtest (faites man authtest. Ca peut aider :

authtest ducon@lajoie.fr monpasswd
Authentication FAILED: Input/output error

Activez le debug dans /etc/courier/authdaemonrc avec DEBUG_LOGIN=1, redémarrez le service et allez voir les logs. Ca m’a permis de voir les erreurs SQL :

Oct  8 18:39:21 ns305192 authdaemond: mysql_query failed, reconnecting: Unknown column 'name' in 'field list'

En cherchant un peu, j’ai vu qu’il restait le champ MYSQL_NAME_FIELD dans /etc/courier/authdaemonrc. Une fois modifié, ça passe :

mxtest:/etc/courier# !auth
authtest bla@bla.fr monpassword
Authentication succeeded.

     Authenticated: bla@bla.fr (uid 5000, gid 5000)
    Home Directory: /vmailboxes
           Maildir: mondomaine.fr/jacques/
             Quota: (none)
Encrypted Password: OXXXXgfT0lXjI
Cleartext Password: monpassword
           Options: (none)

A ce moment là, tout marche pour la récupération, webmail aussi (puisque IMAP aussi).

Partie authentification des utilisateurs, pour l’envoi

Pour bien comprendre, on va activer 2 modes : SASL et TLS – encore que le mode TLS reste optionnel. Vous pourriez dans ce cas désinstaller les paquets concernés.
Le premier (SASL) pour avoir un mécanisme d’authentification pour le SMTP « normal », port 25.
Le deuxième (TLS) pour avoir un mécanisme plus sécurisé, avec certificat etc, en TLS (anciennement SMTPS, ou SMTP avec SSL). C’est aussi sur le port 25 (pas 465), et, de ce que j’en ai compris, ça tend à remplacer le SMTPS, sur port 465. C’est expliqué là et j’ai peut-être un peu abrégé l’explication violemment.

Paramétrage de toute la chaîne postfix/sasl/pam/mysql

Pour ne pas se perdre, il faut comprendre que postfix va déléguer l’authentification à une couche d’abstraction, SASL. On va dire à SASL de se baser sur le mécanisme PAM (Pluggable Authentication Module) et on va dire à PAM de contrôler les logins/passwords dans une base MySQL, en lui décrivant cette base. Pigé ?

Dire à SASL d’utiliser PAM pour l’authentification

SASL est chrooté (je ne me souviens plus bien, mais c’est le boxon, j’ai dû en parler dans des posts précédents)
On crée son environnement de « chroot » :

mkdir --parents /var/spool/postfix/var/run/saslauthd

Ensuite, on paramètre le daemon SASL correctement :
Positionner START=yes dans /etc/default/saslauthd
et modifier la fin du fichier comme suite :

#OPTIONS="-c -m /var/run/saslauthd"
OPTIONS="-c -m /var/spool/postfix/var/run/saslauthd -r"

Le reste du fichier par défaut doit être bon (DESC= »SASL Authentication Daemon », NAME= »saslauthd », MECHANISMS= »pam », MECH_OPTIONS= » », THREADS=5).

Vous pourrez voir au passage qu’on indique (via le paramètre MECHANISMS) à SASL d’utiliser PAM.
Maintenant, on va dire à PAM d’utiliser MySQL.

Demander à PAM d’utiliser MySQL en décrivant notre structure

Dans /etc/pam.d/smtp (à créer je crois), on indique qu’on va utiliser le module MySQL pour PAM :

mxtest:/etc/pam.d# cat smtp
auth    required   pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=email passwdcolumn=password crypt=1
account sufficient pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=email passwdcolumn=password crypt=1

Ensuite, dire à postfix de faire confiance à SASL pour l’authentification

Maintenant que SASL sait faire le boulot en base de données, on configure postfix pour authentifier les utilisateurs via SASL/PAM/MYSQL.
Dans /etc/postfix/sasl/, s’assurer du contenu de smtpd.conf :

pwcheck_method: saslauthd
mech_list: plain login
allow_plaintext: true
auxprop_plugin: mysql
sql_hostnames: 127.0.0.1
sql_user: mail_adm
sql_passwd: monpassadmin
sql_database: postfix
sql_select: select password from users where email = '%u'

Ajoutez l’utilisateur postfix au groupe sasl :

adduser postfix sasl

Enfin, expliquer à postfix de bien vouloir relayer les envois d’utilisateurs authentifiés via SASL. Ce qui donne par exemple dans /etc/postfix/main.cf :

smtpd_sender_restrictions = permit_sasl_authenticated
        reject_unauth_pipelining
        permit_mynetworks
        reject_unauth_destination
        reject_unknown_sender_domain
        reject_non_fqdn_sender
        check_policy_service inet:127.0.0.1:60000

C’est surtout le « permit_sasl_authenticated » qui nous intéresse là.
Voilà, postfix sait maintenant contrôler les expéditeurs avec l’aide de SASL. Ils doivent se signer et exister dans la base.
Maintenant on met en place le TLS, si vous voulez utiliser du vrai cryptage SSL pour l’authentification.

TLS : certificat et configuration

Dans /etc/postfix/,on crée les clefs de cryptage :

mxtest:/etc/postfix# openssl req -new -outform PEM -out smtpd.cert -newkey rsa:2048 -nodes -keyout smtpd.key -keyform PEM -days 3650 -x509
Generating a 2048 bit RSA private key
................................................+++
................................................+++
writing new private key to 'smtpd.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:FR
State or Province Name (full name) [Some-State]:France
Locality Name (eg, city) []:PARIS
Organization Name (eg, company) [Internet Widgits Pty Ltd]:MASOCIETE
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:mondomaine.fr
Email Address []:certif@mondomaine.fr

IMPORTANT : le « Common Name » doit correspondonre à votre /etc/mailname
Protégez la clef privée : chmod 640 smtpd.key
J’ai mis 10 ans, on aurait pu mettre plus ou moins, c’est le paramètre 3650 dans la ligne de commande.

On ajoute pour finir la conf suivante pour activer le TLS et indiquer le certificat à utiliser. Dans /etc/postfix/main.cf toujours :

smtpd_tls_cert_file=/etc/postfix/smtpd.cert
smtpd_tls_key_file=/etc/postfix/smtpd.key
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes # à  voir, ça peut interdire certains outlook buggés, je crois, de tête.
smtpd_sasl_authenticated_header = yes

C’est fini, on relance tout ça

Redémarrez postfix et saslauthd via les /etc/init.d/saslauthd restart et /etc/init.d/postfix restart qui vont bien.

Tests pour l’envoi, quelques erreurs analysées

Après un rapide telnet sur le port 25 (quand même), testez avec un « ehlo mailhost » puor voir si le TLS est activé :

250-STARTTLS

Ensuite, comme pour la réception des mails, testez avec un client mail correct, en mode SMTP normal et avec TLS.

Erreurs rencontrées

Fausse manip dans /etc/pam.d/smtp

Tous les tests SMTP, avec et sans TLS étaient KO. Dans les logs, simplement ça :

Oct 14 13:52:39 ns305192 postfix/smtpd[6759]: connect from bla.fr[81.Y.67.X]
Oct 14 13:52:41 ns305192 postfix/smtpd[6759]: warning: SASL authentication failure: Password verification failed
Oct 14 13:52:41 ns305192 postfix/smtpd[6759]: warning: bla.fr[81.Y.67.X]: SASL PLAIN authentication failed: authentication failure
Oct 14 13:52:41 ns305192 postfix/smtpd[6759]: warning: bla.fr[81.Y.67.X]: SASL LOGIN authentication failed: authentication failure
Oct 14 13:52:43 ns305192 postfix/smtpd[6759]: disconnect from bla.fr[81.Y.67.X]

Je me souviens avoir utilisé saslfinger pour tester et debugger. Essayez-le.
Au final, je m’étais simplement trompé dans le mot de passe MySQL du fichier /etc/pam.d/smtp

Quotas

Sujet totalement optionnel. J’ai besoin dans mon cas de pouvoir affecter des quotas par utilisateur.
Postfix ne gère pas les quotas nativement, il faut appliquer un patch. Donc recompiler. Ou trouver une source, mais au niveau mise à jour de sécurité, vous dépendez alors du type qui maintient le dépôt.
J’ai préféré appliquer moi-même le patch.
Le patch est donné par ce projet : http://vda.sourceforge.net. Il est disponible pour toutes les versions postfix, notamment la 2.5.5, actuellement celle de Debian/Lenny. 32 ou 64 bits.

Ajout du dépôt de sources

D’abord, dans mon cas, j’ajoute un dépôt de sources, via la ligne suivante dans /etc/apt/sources.list :

deb-src ftp://ftp2.fr.debian.org/debian/ lenny main

Après un aptitude update, on va télécharger les sources et tout le bazar pour compiler.

Récupération des sources

serv:/etc/postfix# apt-get build-dep postfix
Lecture des listes de paquets... Fait
Construction de l'arbre des dépendances
Lecture des informations d'état... Fait
Les NOUVEAUX paquets suivants seront installés :
  build-essential comerr-dev debhelper dpkg-dev g++ g++-4.3 gettext hardening-wrapper html2text intltool-debian libcdb-dev libcdb1 libdb-dev libdb4.6-dev libfile-remove-perl
  libkadm55 libkrb5-dev libldap2-dev libmail-box-perl libmail-sendmail-perl libmime-types-perl libmysqlclient15-dev libobject-realize-later-perl libpcre3-dev libpcrecpp0
  libpq-dev libsasl2-dev libssl-dev libstdc++6-4.3-dev libuser-identity-perl lsb-release po-debconf tinycdb zlib1g-dev
0 mis à jour, 34 nouvellement installés, 0 à enlever et 0 non mis à jour.
Il est nécessaire de prendre 22,7Mo dans les archives.
Après cette opération, 69,1Mo d'espace disque supplémentaires seront utilisés.
Souhaitez-vous continuer [O/n] ?

Ensuite, on récupère les sources de postfix :

serv:/etc/postfix# cd /usr/src
serv:/usr/src# apt-get source postfix

Ca download et décompresse (et gueule car je n’ai pas mis la clef GPG blablabla pour vérifier la signature du paquet, tout ça).

Récupération du patch, compilation

On récupère le patch qui va bien pour notre version postfix et on génère les .deb

cd /usr/src
wget http://vda.sourceforge.net/VDA/postfix-2.5.5-vda-ng.patch.gz
gunzip postfix-2.5.5-vda-ng.patch.gz
cd postfix-2.5.5
patch -p1 < ../postfix-2.5.5-vda-ng.patch
dpkg-buildpackage

Remplacement du postfix officiel

Une fois placé dans /usr/src, un simple dpkg -i postfix_2.5.5-1.1_i386.deb postfix-mysql_2.5.5-1.1_i386.deb suffit à remplacer postfix et son extension mysql. Ca ne flingue même pas la conf actuelle. Bref, nickel.

On ajoute le champ quota, on indique à postfix et courierauth

Dans la base de données, on ajoute la colonne. J'ai choisi 20 Mo par défaut.

mysql> alter table users add quota bigint (20) default '20971520' after password;
Query OK, 1 row affected (0.10 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> desc users;
+----------+-------------+------+-----+----------+-------+
| Field    | Type        | Null | Key | Default  | Extra |
+----------+-------------+------+-----+----------+-------+
| email    | varchar(80) | NO   | PRI | NULL     |       |
| password | varchar(20) | NO   |     | NULL     |       |
| quota    | bigint(20)  | YES  |     | 20971520 |       |
+----------+-------------+------+-----+----------+-------+
3 rows in set (0.00 sec)

Le alter s'est occupé de mettre la valeur par défaut sur les comptes existants

On crée alors la requête qui vérifie les quotas :

mxtest:~# cat /etc/postfix/mysql-virtual_mailbox_limit_maps.cf
user = mail_adm
password = monpassadmin
dbname = postfix
query = SELECT quota FROM users WHERE email='%s'
hosts = 127.0.0.1

Pensez à priver l'accès à ce fichier.

Et on ajoute cette notion de limite dans notre postfix-qui-sait-gérer-les-quotas, dans /etc/postfix/main.fr :

virtual_mailbox_limit_maps = proxy:mysql:/etc/postfix/mysql-virtual_mailbox_limit_maps.cf
virtual_mailbox_limit_override = yes
virtual_maildir_limit_message = "The user you are trying to reach is over quota - L'utilisateur que vous cherchez a contacter a depasse son quota de mails"
virtual_overquota_bounce = yes

Sans oublier d'ajouter "$virtual_mailbox_limit_maps" dans la variable "proxy_read_maps".

Indication des quotas à "courier"

On précise à courier qu'il y a des quotas en créant ou décommentant la variable suivante :

mxtest:~# grep quota /etc/courier/authmysqlrc
MYSQL_QUOTA_FIELD       quota

On recharge le tout, on teste, on se congratule. Youpi, c'est fini.

Ce que je n'ai pas traité

j'ai pas joué avec les transports

Pas besoin dans mon cas.

Avoir un procmailrc par utilisateur ?

Aucune idée, je verrai plus tard.
En effet, où doit-on stocker le .procmailrc ??? ou alors faire un /etc/procmailrc général qui éventuellement trie par destinataire... lourd.

Désactivation de protocole par utilisateur

Exemple, besoin de désactiver le webmail ou le POP pour un utilisateur ? A priori, avec les "critères supplémentaires" que l'on peut passer dans les fichiers .cf de description de requêtes, ça doit être jouable. Je n'ai pas cherché car pas encore eu besoin.
Evidemment, il faudra ajouter une/des colonne dans la table, sûrement un booléen indiquant si le protocole est accepté pour l'utilisateur X.

Alerte sur les dépassements de quota

Pas eu encore le temps, mais il faudra y penser pour détecter les boulets qui ont des boîtes pleines et ne comprennent pas pourquoi ils ne reçoivent rien.
Je n'ai pas encore regardé : chapitre 11 dans cette page.
Voilà, j'espère que ça roulera pour votre installation et que vous aurez ainsi une belle plateforme de mails.

24 réflexions au sujet de « postfix : utilisateurs « virtuels » MySQL ; accès POP3[S], IMAP[S], SASL et TLS ; quota (bingo, j’ai tout mis dans le titre) »

  1. Julien

    gneeeh ???
    Ldap, Active Directory OK mais Mysql ???
    La gestion des utilisateurs LDAP avec postfix se fait très bien, pourquoi ajouter mysql ?
    De plus c’est à priori un des rôles de LDAP que de fournir les infos utilisateurs.

    Répondre
    1. michauko Auteur de l’article

      Je te force pas
      mais je vais te faire un dessin : imagine un serveur completement extérieur a ma société, serveur qui me sert a fournir un service de mail a des gens, avec différents domaines qui n’ont rien a voir. Pourquoi mettrais-je un ldap et ses 50 champs par utilisateur ? Encore moins dans le ldap de ma boite si c’est celui auquel tu pensais

      néanmoins si tu veux décliner cet article en version postfix-ldap, ce sera utile dans bien d’autres cas
      n’hésite pas

      Répondre
  2. macsim

    J’utilise une autre table utilisateur que toi dans laquel j’ai (en autre)
    disablepop3,disableimap,stmpaccess,access en ENUM(‘Y’,’N’)
    Pour controler les utilisateurs. En effet il faut agrémenter tes requete avec des disablepop3 = N AND etc…

    Quelques piste pour améliorer ton post, ajouter le suppot d’amavis + postgrey + un autorespondeur pour le mode vaccance (yaa).

    Répondre
  3. michauko Auteur de l’article

    Oui, SVP, donne les exemples de fichiers « .cf » améliorés, ça servira, pour désactiver le POP ou autre protocole

    Pour améliorer mon post avec amavis etc, il suffit de lire les premières lignes : amavis, postgrey etc sont déjà décrits dans un autre article. Mais je découpe un peu chaque sujet. Ca fait déjà des guides assez longs

    Pour le répondeur, je l’aborde en plus complexe là : http://michauko.org/blog/?s=vacation

    Répondre
  4. macsim

    c’est dans le /etc/courier/authmysqlrc qu’il faut définir ça pas dans la partie postfix, tu peux utiliser MYSQL_AUXOPTIONS_FIELD et MYSQL_WHERE_CLAUSE

    ex: MYSQL_WHERE_CLAUSE access=’y’

    amuse toi bien 😉

    Répondre
  5. JB

    je ma suis lancer dans le tuto, mais j’ai un soucis :

    Oct 25 22:11:11 robert postfix/cleanup[4212]: fatal: open database /etc/postfix/virtual.db: No such file or directory
    Oct 25 22:11:12 robert postfix/master[1986]: warning: process /usr/lib/postfix/cleanup pid 4212 exit status 1
    Oct 25 22:11:12 robert postfix/master[1986]: warning: /usr/lib/postfix/cleanup: bad command startup — throttling

    quand j’enlève la conf virtual, pas d’erreur (mais pas d’user sql ;))

    une idée ?

    Merci

    Répondre
    1. michauko Auteur de l’article

      Quel est ton paramètre dans /etc/postfix/main.cf qui appelle un fichier « virtual » ?
      Soit c’est un des fichiers que je mentionne, qui définit une requête SQL, mais dans ce cas il manque « mysql: » devant.
      Soit c’est un autre morceau de conf virtuelle (alias et domaines virtuels) et dans ce cas, si ça doit rester, je suggère simplement un « postmap /etc/postfix/virtual » pour générer le .db

      Répondre
  6. JB

    ha ben il manquais juste postfix-mysql dans les paquets à installer 😉

    Sinon le fichier mysql-virtual_email2email.cf n’est utilisé nul part dans la doc, c’est normal ?

    Répondre
  7. michauko Auteur de l’article

    En effet, quelle buse…
    J’ajoute postfix-mysql & supprime l’autre, pas besoin, c’est un résidu de mix de différentes conf pour tenter d’arriver à un truc propre

    merci

    Répondre
  8. Greg

    Hello, Juste un petit commentaire, pour te féliciter de tout le boulot que tu fait !

    De véritable mise en situation d’entreprise, c’est très rarement le cas sur « d’autre » blog

    Petit coup de coeur pour tes links « about chevreuse 🙂  » Habitant à 20km de ce lieu!

    Greg.

    Répondre
  9. Serial Blogueur

    Ouch, c’est serieusement complet ce que tu nous a fait là…YaPuKa !!! Te reste plus qu’a nous faire le détail sur ce que tu n’as pas traité 🙂

    Répondre
  10. michauko

    Merci. Disons que quand je mets l’outil en place, connaissant la manip dans les grandes lignes au moins, je pense à tracer tout ce que je fais. Du coup, la doc est complète, sauf problème lorsque je réécris en bon français et que j’insère des fautes, ou que les conditions de départ étaient plus complètes que prévu.
    Bon bref, ça me fait plaisir si ça ressert, encore plus si c’est tel quel.

    Pour ce que je n’ai pas traité, je verrai un peu plus tard ; au moins la gestion des quotas. ‘Pas eu le temps là

    Répondre
  11. bouxroute

    merci pour ce tuto! 2 choses néanmoins:

    – comme dit précédemment, il faudrait
    ° soit retirer de la conf postfix l’appel « virtual_alias_maps = hash:/etc/postfix/virtual »
    ° soit indiquer qu’il faut générer ce fichier « virtual » avec la commande postmap

    – Gare à la conf que tu donnes de /etc/courier/authdaemonrc : en faisant un simple copier/collé de ce qui est visible, certains caractères exotiques sont convertis en point (.) par mysql. Pour exemple

    « SUBSTRING_INDEX(email,’@’,1)

    donne

    SUBSTRING_INDEX(email,.@’,1)

    Répondre
    1. michauko Auteur de l’article

      Merci
      – ok j’ai parlé du /etc/postfix/virtual
      – j’ai forcé des & apos ; à  la place. WordPress doit les interpréter. Pas le temps/l’envie de chercher où/pourquoi

      Répondre
  12. bouxroute

    A noter aussi: dans ta conf postfix, au niveau de « virtual_alias_maps », il y a 2 fois:

    proxy:mysql:/etc/postfix/mysql-virtual_forwardings.cf

    Répondre
  13. Ping : utilisateurs postfix virtuels : ajouter un répondeur “vacation” - Le blog de Michauko

  14. Ping : Changer son mot de passe dans roundcubemail+postfix+utilisateurs virtuels - Le blog de Michauko

  15. bouxroute

    Petite erreur sur la création de l’utilisateur vmail:

    au lieu de:

    useradd -g vmail -u 5000 vmail -d /vmailboxes -m

    il faut mettre:

    useradd -g vmail -u 5000 vmail -d /var/spool/vmailboxes -m

    merci 😉

    Répondre
  16. bouxroute

    arf ! sauf si le but de la manoeuvre est réellement de mettre ce répertoire directement en début de racine…. si c’est le cas, je gagne la palme du commentaire inutile 😛

    Répondre
  17. bouxroute

    En suivant tout le tuto j’ai un problème lorsque je tente d’envoyer un email en authentifiant via SASL. Mail.log affiche:

    postfix/smtpd[8696]: connect from XXX.XXX.XXX.XXX
    postfix/smtpd[8696]: warning: SASL authentication failure: Password verification failed
    postfix/smtpd[8696]: warning: XXX.XXX.XXX.XXX: SASL PLAIN authentication failed: authentication failure

    En activant les logs de mysql, on comprend que la requête effectuée est ( en reprenant ton exemple dans le tuto ):

    SELECT password FROM users WHERE email = ‘jacques’

    Or, tu remplis la colonne ’email’ avec l’adresse complète ( « INSERT INTO users (email, password) VALUES (‘jacques@mxtest.mondomaine.fr’, ENCRYPT(‘mon_pass_en_clair’)); » )

    Pour compenser ce souci, j’ai modifié le fichier /etc/pam.d/smtp :

    auth required pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=SUBSTRING_INDEX(email,’@’,1) passwdcolumn=password crypt=1
    account sufficient pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=SUBSTRING_INDEX(email,’@’,1) passwdcolumn=password crypt=1

    au lieu de :

    auth required pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=email passwdcolumn=password crypt=1
    account sufficient pam_mysql.so user=mail_adm passwd=monpassadmin host=127.0.0.1 db=postfix table=users usercolumn=email passwdcolumn=password crypt=1

    Bye bye

    Répondre
  18. Ping : Upgrade Debian Squeeze => Wheezy : retour d’expérience - Le blog de Michauko

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.