Postfix avec cyrus

Nous avons déjà pas mal parlé de ce MTA. Par rapport à la solution « Postfix plus Dovecot » déjà vue, nous avons quelques modifications à apporter :

  1. Expliquer à Postfix qu'il doit désormais remettre les messages locaux à cyrus ;
  2. lire certaines informations dans les tables de MySQL :
    • ses destinations ;
    • les alias ;
    • les redirections ;
  3. vérifier l'authentification des utilisateurs, quand c'est nécessaire, par SASL en utilisant les services de saslauthd ;

Comme il y a pas mal de nouveautés, nous allons reprendre progressivement, en partant de la configuration deBase.

Pour master.cf :

#
# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
#submission inet n       -       -       -       -       smtpd
#  -o smtpd_tls_security_level=encrypt
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
#  -o milter_macro_daemon_name=ORIGINATING
#smtps     inet  n       -       -       -       -       smtpd
#  -o smtpd_tls_wrappermode=yes
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
#  -o milter_macro_daemon_name=ORIGINATING
#628      inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
#qmgr     fifo  n       -       -       300     1       oqmgr
tlsmgr    unix  -       -       -       1000?   1       tlsmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       -       -       -       smtp
# When relaying mail as backup MX, disable fallback_relay to avoid MX loops
relay     unix  -       -       -       -       -       smtp
	-o smtp_fallback_relay=
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
retry     unix  -       -       -       -       -       error
discard   unix  -       -       -       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       -       -       -       lmtp
anvil     unix  -       -       -       -       1       anvil
scache    unix  -       -       -       -       1       scache
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent.  See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# See the Postfix UUCP_README file for configuration details.
#
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix	-	n	n	-	2	pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
  ${nexthop} ${user}

Pour main.cf :

# See /usr/share/postfix/main.cf.dist for a commented, more complete version


# Debian specific:  Specifying a file name will cause the first
# line of that file to be used as the name.  The Debian default
# is /etc/mailname.
#myorigin = /etc/mailname

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
#delay_warning_time = 4h

readme_directory = no

# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.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

# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.

myhostname = cyrus.nain-t.net
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = $myhostname
mydestination = nain-t.net, $myhostname, localhost.nain-t.net, localhost
relayhost = 
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

Config. minimale

Dans un premier temps, nous allons juste expliquer à Postfix qu'il doit transmettre les messages considérés comme locaux (mydestination) à cyrus, par l'intermédiaire du socket unix créé par cyrus. De plus, comme les utilisateurs sont des utilisateurs qui n'ont pas de compte unix, nous devons reconsidérer la façon dont Postfix évalue l'existence des utilisateurs locaux. En effet, par défaut, Postfix regarde le contenu du fichier passwd.

Nous ajoutons donc ces deux lignes à notre main.cf :

mailbox_transport = lmtp:unix:/cyrussock/lmtp
local_recipient_maps =
  1. la première ligne indique que le transport local doit se faire par le protocole lmtp via un socket unix qui se trouve dans le répertoire (mis en cage) /cyrussock/lmtp ;
  2. la seconde indique à Postfix qu'il n'a pas à se soucier de vérifier l'existence du compte local. Ce n'est pas gravissime pour l'instant, cyrus le fera de toutes manières. Dans l'avenir, si Postfix peut s'en assurer lui-même ce ne sera pas plus mal en termes de ressources utilisées.

Et nous assurons que le domaine nain-t.net est bien dans nos destinations :

mydestination = nain-t.net, cyrus.nain-t.net, cyrus.nain-t.net, localhost.nain-t.net, localhost
Après avoir forcé la reconfiguration de postfix (postfix reload), envoyons un message à test@nain-t.net :

# mail test@nain-t.net
Subject: test 0 
config mini
.
Cc: 

Et voyons dans les logs (/var/log/mail.info) :

Jun 16 10:15:06 cyrus postfix/pickup[8271]: 7CC2E4800D3C: uid=0 from=<root>
Jun 16 10:15:06 cyrus postfix/cleanup[8277]: 7CC2E4800D3C: message-id=<20080616081506.7CC2E4800D3C@cyrus.nain-t.net>
Jun 16 10:15:06 cyrus postfix/qmgr[8272]: 7CC2E4800D3C: from=<root@cyrus.nain-t.net>, size=323, nrcpt=1 (queue active)
Jun 16 10:15:06 cyrus cyrus/lmtpunix[8281]: Delivered: <20080616081506.7CC2E4800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.test
Jun 16 10:15:06 cyrus postfix/lmtp[8280]: 7CC2E4800D3C: to=<test@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.3, delays=0.06/0.01/0.03/0.2, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 10:15:06 cyrus postfix/qmgr[8272]: 7CC2E4800D3C: removed
Jun 16 10:22:32 cyrus cyrus/ctl_cyrusdb[8292]: checkpointing cyrus databases
Jun 16 10:22:32 cyrus cyrus/ctl_cyrusdb[8292]: done checkpointing cyrus databases

C'est bon signe. Mais comment cyrus a-t-il géré la chose ?

Tout démarre dans /var/spool/cyrus/mail :

:/var/spool/cyrus/mail# ls
a  b  c  d  domain  e  f  g  h	i  j  k  l  m  o  q  r	s  stage.  t  v  w  x  y  z

Le répertoire domain nous attire :

:/var/spool/cyrus/mail# cd domain/
:/var/spool/cyrus/mail/domain# ls
n

Un seul répertoire, qui s'appelle n (comme nain-t ?) :

:/var/spool/cyrus/mail/domain# cd n/
:/var/spool/cyrus/mail/domain/n# ls
nain-t.net

Ah oui, c'est bien rengé n'est-ce pas ?

:/var/spool/cyrus/mail/domain/n# cd nain-t.net/
:/var/spool/cyrus/mail/domain/n/nain-t.net# ls
t

Un seul répertoire t (comme test ?) :

:/var/spool/cyrus/mail/domain/n/nain-t.net# cd t
:/var/spool/cyrus/mail/domain/n/nain-t.net/t# ls
user
:/var/spool/cyrus/mail/domain/n/nain-t.net/t# cd user/
:/var/spool/cyrus/mail/domain/n/nain-t.net/t/user# ls
test

Oui :-D

:/var/spool/cyrus/mail/domain/n/nain-t.net/t/user# cd test/
:/var/spool/cyrus/mail/domain/n/nain-t.net/t/user/test# ls
1.  cyrus.cache  cyrus.header  cyrus.index

Le fichier 1. a l'air intéressant :

:/var/spool/cyrus/mail/domain/n/nain-t.net/t/user/test# cat 1. 
Return-Path: <root@cyrus.nain-t.net>
Received: from cyrus.nain-t.net ([unix socket])
	 by cyrus (Cyrus v2.2.13-Debian-2.2.13-14+b1) with LMTPA;
	 Mon, 16 Jun 2008 10:15:06 +0200
X-Sieve: CMU Sieve 2.2
Received: by cyrus.nain-t.net (Postfix, from userid 0)
	id 7CC2E4800D3C; Mon, 16 Jun 2008 10:15:06 +0200 (CEST)
To: test@nain-t.net
Subject: test 0
Message-Id: <20080616081506.7CC2E4800D3C@cyrus.nain-t.net>
Date: Mon, 16 Jun 2008 10:15:06 +0200 (CEST)
From: root@cyrus.nain-t.net (root)


config mini

C'est lui. Voyons avec telnet si nous pouvons y accéder en imap :

# telnet localhost 143
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
* OK cyrus Cyrus IMAP4 v2.2.13-Debian-2.2.13-14+b1 server ready
01 login test@nain-t.net epikoi
01 OK User logged in
02 list "*" "*"
* LIST (\HasNoChildren) "/" "INBOX"
02 OK Completed (0.000 secs 2 calls)
03 select INBOX
* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)]  
* 1 EXISTS
* 1 RECENT
* OK [UNSEEN 1]  
* OK [UIDVALIDITY 1213600558]  
* OK [UIDNEXT 2]  
03 OK [READ-WRITE] Completed
04 fetch 1 BODY[HEADER]
* 1 FETCH (FLAGS (\Recent \Seen) BODY[HEADER] {530}
Return-Path: 
Received: from cyrus.nain-t.net ([unix socket])
	 by cyrus (Cyrus v2.2.13-Debian-2.2.13-14+b1) with LMTPA;
	 Mon, 16 Jun 2008 10:15:06 +0200
X-Sieve: CMU Sieve 2.2
Received: by cyrus.nain-t.net (Postfix, from userid 0)
	id 7CC2E4800D3C; Mon, 16 Jun 2008 10:15:06 +0200 (CEST)
To: test@nain-t.net
Subject: test 0
Message-Id: <20080616081506.7CC2E4800D3C@cyrus.nain-t.net>
Date: Mon, 16 Jun 2008 10:15:06 +0200 (CEST)
From: root@cyrus.nain-t.net (root)

)
04 OK Completed (0.000 sec)
05 fetch 1 BODY[TEXT]
* 1 FETCH (BODY[TEXT] {13}
config mini
)
05 OK Completed (0.000 sec)
06 logout
* BYE LOGOUT received
06 OK Completed
Connection closed by foreign host.

Tout va pour le mieux. Mais pour ces premiers essais, nous avons dû indiquer que nain-t.net était dans mydestination « en dur » , dans main.cf. Ce n'est évidemment pas la bonne solution. Il nous faut trouver le moyen de dire à Postfix qu'il doit chercher ces informations dans la base de données MySQL.

Postfix et MySQL

Postfix sait dialoguer avec MySQL, à la condition d'installer le paquet postfix-mysql (Il existe également postfix-pgsql pour PostgreSQL et postfix-ldap pour les annuaires LDAP).

aptitude install postfix-mysql

Pour « mydestination »

Dans notre base mail, nous avons une table domain. Voyons ce qu'il y a dedans :

# mysql mail -u mail -p
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 73
Server version: 5.0.51a-6 (Debian)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> select * from domain;
+-------------+------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
| domain_name | prefix     | maxaccounts | quota   | domainquota | transport | freenames | freeaddress | folders |
+-------------+------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
| nain-t.net  | nain-t.net |       10000 | 2000000 |           0 | cyrus     | NO        | YES         |         | 
+-------------+------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
1 row in set (0.00 sec)

Donc, une requête du genre :

mysql> select domain_name from domain;

devrait faire l'affaire :

+-------------+
| domain_name |
+-------------+
| nain-t.net  | 
+-------------+
1 row in set (0.00 sec)

Il suffit de trouver le moyen de dire à Postfix qu'il doit lire ses destinations en faisant cette requête dans la base de données.

Nous allons créer un fichier /etc/postfix/db/mydestination.cf qui aura cette forme (ancienne syntaxe) :

hosts = 127.0.0.1
user = mail
password = epikoi
dbname = mail
table = domain
select_field = domain_name
where_field = domain_name

C'est assez clair pour ne pas trop s'étendre sur le sujet. Le seul point à approfondir un peu est la directive hosts = 127.0.0.1. En effet, si nous mettons à la place hosts = localhost, Postfix va en déduire que puisque la base est locale, il faut y accéder par un socket Unix (que MySQL place par défaut dans /var/run/mysqld/mysqld.sock) et ça ne fonctionnera tout simplement pas. Pourquoi ? Parce que Postfix est en cage dans /var/spool/postfix !!

  • Soit nous déplaçons ce socket dans la cage, comme nous l'avons fait pour cyrus et saslauthd au risque de perturber d'autres applications qui utiliseraient MySQL ;
  • soit il nous faut passer par un socket réseau, c'est ce que comprend Postfix si nous indiquons une adresse IP dans le paramètre hosts ;
  • soit, enfin, nous adoptons la même stratégie que dans le chapitre précédent, à savoir utiliser le service proxy de Postfix qui est là pour résoudre ces problèmes.

A vous de voir en fonction de votre contexte. Ici le choix du socket réseau est juste fait pour montrer que ça fonctionne aussi.

Les versions « modernes » (2.2 et plus) de Postfix comprennent une syntaxe plus compacte et plus proche de SQL :

hosts = 127.0.0.1
user = mail
password = epikoi
dbname = mail
query = SELECT domain_name FROM domain WHERE domain_name = '%s'

Puis il faut modifier dans /etc/postfix/main.cf la ligne mydestination comme suit :

mydestination = mysql:/etc/postfix/db/mydestination.cf

Un postfix reload et un essai :

# mail test@nain-t.net
Subject: test 1
mydestination dans MySQL
.
Cc: 

Et dans les logs :

Jun 16 11:51:52 cyrus postfix/pickup[8408]: 4418E4800D3C: uid=0 from=<root>
Jun 16 11:51:52 cyrus postfix/cleanup[8414]: 4418E4800D3C: message-id=<20080616095152.4418E4800D3C@cyrus.nain-t.net>
Jun 16 11:51:52 cyrus postfix/qmgr[8409]: 4418E4800D3C: from=<root@cyrus.nain-t.net>, size=336, nrcpt=1 (queue active)
Jun 16 11:51:52 cyrus cyrus/lmtpunix[8418]: Delivered: <20080616095152.4418E4800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.test
Jun 16 11:51:52 cyrus postfix/lmtp[8417]: 4418E4800D3C: to=<test@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.28, delays=0.06/0.01/0.04/0.18, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 11:51:52 cyrus postfix/qmgr[8409]: 4418E4800D3C: removed
Jun 16 11:52:32 cyrus cyrus/ctl_cyrusdb[8419]: checkpointing cyrus databases
Jun 16 11:52:32 cyrus cyrus/ctl_cyrusdb[8419]: done checkpointing cyrus databases

Bon. Mais c'est bien un truc qui marche de façon intelligente ?

# mail test@machin.net
Subject: test 2
domaine inconnu dans notre base
.
Cc: 

Et dans les logs ?

Jun 16 11:56:31 cyrus postfix/pickup[8408]: 3A5254800D3C: uid=0 from=<root>
Jun 16 11:56:31 cyrus postfix/cleanup[8431]: 3A5254800D3C: message-id=<20080616095631.3A5254800D3C@cyrus.nain-t.net>
Jun 16 11:56:31 cyrus postfix/qmgr[8409]: 3A5254800D3C: from=<root@cyrus.nain-t.net>, size=327, nrcpt=1 (queue active)
Jun 16 11:56:38 cyrus postfix/smtp[8433]: 3A5254800D3C: host mx00.1and1.co.uk[212.227.15.150] refused to talk to me: 550 RBL rejection: local dynamic IP address 82.243.80.13
Jun 16 11:56:38 cyrus postfix/smtp[8433]: 3A5254800D3C: host mx01.1and1.co.uk[212.227.15.134] refused to talk to me: 550 RBL rejection: local dynamic IP address 82.243.80.13
Jun 16 11:56:38 cyrus postfix/smtp[8433]: 3A5254800D3C: host mx00.1and1.co.uk[212.227.15.134] refused to talk to me: 550 RBL rejection: local dynamic IP address 82.243.80.13
Jun 16 11:56:38 cyrus postfix/smtp[8433]: 3A5254800D3C: host mx00.1and1.co.uk[212.227.15.169] refused to talk to me: 550 RBL rejection: local dynamic IP address 82.243.80.13
Jun 16 11:56:38 cyrus postfix/smtp[8433]: 3A5254800D3C: to=<test@machin.net>, relay=mx01.1and1.co.uk[212.227.15.150]:25, delay=7.5, delays=0.06/0.01/7.5/0, dsn=4.0.0, status=deferred (host mx01.1and1.co.uk[212.227.15.150] refused to talk to me: 550 RBL rejection: local dynamic IP address 82.243.80.13)

Ben oui, le domaine machin.net existe… Postfix a donc essayé de contacter les MX correspondants, preuve que Postfix a bien compris que machin.net n'était pas dans ses destinations.

Au passage, observons que les MX de 1and1.co.uk ne prennent pas de gants et blacklistent toutes les adresses IP dites dynamiques c'est-à-dire celles qui sont attribuées aux divers fournisseurs d'accès. Ceci veut dire que même avec un Postfix complètement configuré et référencé comme tel sur l'internet, nous ne passerons pas dans certains domaines. Merci aux administrateurs totalitaires :-/

Mais revenons à nos moutons, car nous n'avons pas encore fini…

Pour les alias

Rappelons-le, un alias est une vraie fausse adresse. Autrement dit, une adresse qui ne correspond pas à une vraie boîte, mais qui pointe sur une autre adresse qui, elle, en a une.

Avec notre web-cyradm, créons dans le domaine nain-t.net un alias : aliastest@nain-t.net et faisons le pointer sur l'adresse test@nain-t.net

C'est dans la table virtual que ça se passe :

mysql> select * from virtual;
+----------------------+------------------+------------------+--------+
| alias                | dest             | username         | status |
+----------------------+------------------+------------------+--------+
| test@nain-t.net      | test@nain-t.net  | test@nain-t.net  |      1 | 
| aliastest@nain-t.net | test@nain-t.net  | nain-t.net       |      1 | 
+----------------------+------------------+------------------+--------+
2 rows in set (0.00 sec)

Il est à peu près clair que si la destination de test@nain-t.net est bien test@nain-t.net, pour aliastest@nain-t.net, la destination n'est pas aliastest@nain-t.net mais toujours test@nain-t.net.

D'ailleurs, cyrus n'est pas du tout au courant de tout ça :

# cyradm --user cyrus --server localhost
Password: 
localhost> lm
user/test@nain-t.net (\HasNoChildren)         
localhost> 

La seule BAL existante est bien test@nain-t.net.

Moralité, nous avons bien les informations dans la base, mais comme nous n'avons pas expliqué à Postfix qu'il fallait en tenir compte, il n'effectuera pas la réécriture d'adresse de destination et le message sera refusé par cyrus. Je vous laisse faire la manip si vous n'êtes pas convaincu, personnellement, j'ai mieux à faire.

Il faut agir sur la paramètre virtual_alias_maps, par le biais d'une requête SQL bien sentie :

mysql> SELECT dest FROM virtual WHERE alias = 'aliastest@nain-t.net' AND status ='1';
+-----------------+
| dest            |
+-----------------+
| test@nain-t.net | 
+-----------------+
1 row in set (0.00 sec)

(Si status est différent de 1, c'est que l'alias est désactivé, il ne faut donc pas en tenir compte).

Il ne reste plus qu'à mettre ça dans un fichier /etc/postfix/db/virtual-alias.cf (directement dans la nouvelle syntaxe) :

hosts = 127.0.0.1
user = mail
password = epikoi
dbname = mail
query = SELECT dest FROM virtual WHERE alias = '%s' AND status ='1'

et ajouter à main.cf la ligne :

virtual_alias_maps = mysql:/etc/postfix/db/virtual-alias.cf

(Forcer postfix reload)

Essayons :

# mail aliastest@nain-t.net
Subject: test alias
redirection OK
.
Cc: 

Et voir les logs :

Jun 16 14:15:14 cyrus postfix/pickup[8683]: BF7E04800D3C: uid=0 from=<root>
Jun 16 14:15:14 cyrus postfix/cleanup[8689]: BF7E04800D3C: message-id=<20080616121514.BF7E04800D3C@cyrus.nain-t.net>
Jun 16 14:15:14 cyrus postfix/qmgr[8684]: BF7E04800D3C: from=<root@cyrus.nain-t.net>, size=335, nrcpt=1 (queue active)
Jun 16 14:15:15 cyrus cyrus/lmtpunix[8695]: Delivered: <20080616121514.BF7E04800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.test
Jun 16 14:15:15 cyrus postfix/lmtp[8694]: BF7E04800D3C: to=<test@nain-t.net>, orig_to=<aliastest@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.3, delays=0.06/0.01/0.04/0.18, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 14:15:15 cyrus postfix/qmgr[8684]: BF7E04800D3C: removed

C'est explicite. Détail intéressant : nous récupérons l'en-tête du message reçu et stocké par cyrus (nous savons maintenant où le trouver) :

Return-Path: <root@cyrus.nain-t.net>
Received: from cyrus.nain-t.net ([unix socket])
	 by cyrus (Cyrus v2.2.13-Debian-2.2.13-14+b1) with LMTPA;
	 Mon, 16 Jun 2008 14:15:14 +0200
X-Sieve: CMU Sieve 2.2
Received: by cyrus.nain-t.net (Postfix, from userid 0)
	id BF7E04800D3C; Mon, 16 Jun 2008 14:15:14 +0200 (CEST)
To: aliastest@nain-t.net
Subject: test alias
Message-Id: <20080616121514.BF7E04800D3C@cyrus.nain-t.net>
Date: Mon, 16 Jun 2008 14:15:14 +0200 (CEST)
From: root@cyrus.nain-t.net (root)

redirection OK

Constatez que dans l'en-tête du message reçu, le champ To: contient toujours aliastest@nain-t.net et non test@nain-t.net.

Pour les redirections

D'abord, quelle différence entre une redirection (forward) et un alias ?

Le but du jeu dans le cas d'une redirection, est de renvoyer un message destiné à une vraie adresse (qui dispose d'une BAL) vers une autre adresse, pas nécessairement dans le même domaine d'ailleurs. Il y a donc ici la possibilité de conserver une copie du message redirigé dans la boîte initiale.

Autrement, il n'y a aucune différence, comme nous allons le voir.

Avec notre web-cyradm, nous créons un second domaine : autredomaine.net et dedans, nous créons un compte test@autredomaine.fr. Voyons la base :

mysql> select * from domain;
+------------------+------------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
| domain_name      | prefix           | maxaccounts | quota   | domainquota | transport | freenames | freeaddress | folders |
+------------------+------------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
| nain-t.net       | nain-t.net       |       10000 | 2000000 |           0 | cyrus     | NO        | YES         |         | 
| autredomaine.net | autredomaine.net |          10 | 2000000 |           0 | cyrus     | NO        | YES         |         | 
+------------------+------------------+-------------+---------+-------------+-----------+-----------+-------------+---------+
2 rows in set (0.00 sec)


mysql> select * from accountuser;
+-----------------------+---------------+------------------+------------------+------+-----+-------+----------+
| username              | password      | prefix           | domain_name      | imap | pop | sieve | smtpauth |
+-----------------------+---------------+------------------+------------------+------+-----+-------+----------+
| cyrus                 | ep5LE34pgfO7Q |                  |                  |    1 |   1 |     1 |        1 | 
| test@nain-t.net       | ep5LE34pgfO7Q | nain-t.net       | nain-t.net       |    1 |   1 |     1 |        1 | 
| test@autredomaine.net | ep5LE34pgfO7Q | autredomaine.net | autredomaine.net |    1 |   1 |     1 |        1 | 
+-----------------------+---------------+------------------+------------------+------+-----+-------+----------+
3 rows in set (0.00 sec)

Maintenant, en accédant au compte de test@nain-t.net par web-cyradm, nous créons une redirection vers test@autredomaine.net, en conservant une cpie du message : Redirection

La preuve que c'est pareil qu'un alias, nous retrouvons la modification dans la table virtual :

mysql> select * from virtual;
+-----------------------+---------------------------------------+-----------------------+--------+
| alias                 | dest                                  | username              | status |
+-----------------------+---------------------------------------+-----------------------+--------+
| test@nain-t.net       | test@nain-t.net                       | test@nain-t.net       |      1 |
| aliastest@nain-t.net  | test@nain-t.net                       | nain-t.net            |      1 | 
| test@nain-t.net       | test@autredomaine.net,test@nain-t.net |                       |      1 | 
| test@autredomaine.net | test@autredomaine.net                 | test@autredomaine.net |      1 | 
+-----------------------+---------------------------------------+-----------------------+--------+
4 rows in set (0.00 sec)
Donc, à priori, nous n'avons rien à modifier dans la configuration de Postfix, les redirections devraient être opérationnelles. Vérifions :

# mail test@nain-t.net
Subject: premier essai forward
ça roule
.
Cc: 

Les logs :

Jun 16 14:54:30 cyrus postfix/pickup[8683]: 961684800D3C: uid=0 from=<root>
Jun 16 14:54:30 cyrus postfix/cleanup[8733]: 961684800D3C: message-id=<20080616125430.961684800D3C@cyrus.nain-t.net>
Jun 16 14:54:30 cyrus postfix/qmgr[8684]: 961684800D3C: from=<root@cyrus.nain-t.net>, size=336, nrcpt=2 (queue active)
Jun 16 14:54:30 cyrus cyrus/lmtpunix[8741]: Delivered: <20080616125430.961684800D3C@cyrus.nain-t.net> to mailbox: autredomaine.net!user.test
Jun 16 14:54:30 cyrus postfix/lmtp[8739]: 961684800D3C: to=<test@autredomaine.net>, orig_to=<test@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.38, delays=0.07/0.02/0.05/0.24, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 14:54:31 cyrus cyrus/lmtpunix[8742]: Delivered: <20080616125430.961684800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.test
Jun 16 14:54:31 cyrus postfix/lmtp[8740]: 961684800D3C: to=<test@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.46, delays=0.07/0.03/0.11/0.24, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 14:54:31 cyrus postfix/qmgr[8684]: 961684800D3C: removed

C'est bon. Et est-ce que c'est récursif ? Autrement dit, si j'envoie un message à aliastest@nain-t.net, comment ça va se passer ?

Jun 16 15:02:08 cyrus postfix/pickup[8683]: 3807A4800D3C: uid=0 from=<root>
Jun 16 15:02:08 cyrus postfix/cleanup[8746]: 3807A4800D3C: message-id=<20080616130208.3807A4800D3C@cyrus.nain-t.net>
Jun 16 15:02:08 cyrus postfix/qmgr[8684]: 3807A4800D3C: from=<root@cyrus.nain-t.net>, size=343, nrcpt=2 (queue active)
Jun 16 15:02:08 cyrus cyrus/lmtpunix[8752]: Delivered: <20080616130208.3807A4800D3C@cyrus.nain-t.net> to mailbox: autredomaine.net!user.test
Jun 16 15:02:08 cyrus postfix/lmtp[8750]: 3807A4800D3C: to=<test@autredomaine.net>, orig_to=<aliastest@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.45, delays=0.06/0.01/0.06/0.32, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 15:02:08 cyrus cyrus/lmtpunix[8753]: Delivered: <20080616130208.3807A4800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.test
Jun 16 15:02:08 cyrus postfix/lmtp[8751]: 3807A4800D3C: to=<test@nain-t.net>, orig_to=<aliastest@nain-t.net>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.47, delays=0.06/0.03/0.13/0.26, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 15:02:08 cyrus postfix/qmgr[8684]: 3807A4800D3C: removed

Ça fonctionne de manière récursive. Il a oublié d'être bête ce Postfix.

Techniquement, nous sommes en place à part un petit détail. Quid des messages administratifs, envoyés à root, postmaster, MAILER_DAEMON et autres noms de services ?

Envoyons un message à root pour voir :

# mail root
Subject: vla autre chose
ça va coincer...
.
Cc: 

Les logs…

Jun 16 15:09:47 cyrus postfix/pickup[8683]: 6AB974800D3C: uid=0 from=<root>
Jun 16 15:09:47 cyrus postfix/cleanup[8776]: 6AB974800D3C: message-id=<20080616130947.6AB974800D3C@cyrus.nain-t.net>
Jun 16 15:09:47 cyrus postfix/qmgr[8684]: 6AB974800D3C: from=<root@cyrus.nain-t.net>, size=342, nrcpt=1 (queue active)
Jun 16 15:09:47 cyrus postfix/smtp[8778]: 6AB974800D3C: to=<root@cyrus.nain-t.net>, orig_to=<root>, relay=none, delay=0.08, delays=0.06/0.02/0/0, dsn=5.4.4, status=bounced (Host or domain name not found. Name service error for name=cyrus.nain-t.net type=AAAA: Host not found)
Jun 16 15:09:47 cyrus postfix/cleanup[8776]: 7ECA94800D3F: message-id=<20080616130947.7ECA94800D3F@cyrus.nain-t.net>
Jun 16 15:09:47 cyrus postfix/qmgr[8684]: 7ECA94800D3F: from=<>, size=2340, nrcpt=1 (queue active)
Jun 16 15:09:47 cyrus postfix/bounce[8779]: 6AB974800D3C: sender non-delivery notification: 7ECA94800D3F
Jun 16 15:09:47 cyrus postfix/qmgr[8684]: 6AB974800D3C: removed
Jun 16 15:09:47 cyrus postfix/smtp[8778]: 7ECA94800D3F: to=<root@cyrus.nain-t.net>, relay=none, delay=0.03, delays=0.02/0/0/0, dsn=5.4.4, status=bounced (Host or domain name not found. Name service error for name=cyrus.nain-t.net type=AAAA: Host not found)
Jun 16 15:09:47 cyrus postfix/qmgr[8684]: 7ECA94800D3F: removed

Ici, Postfix ne s'en sort pas parce que root n'est pas une adresse. Il en conclut (à juste titre) que c'est le root local et donc reconstruit une adresse présentable en ajoutant @{$myhostname}, ici cyrus.nain-t.net. Une fois cette adresse construite, il ne sait plus trop comment la router, car il ne sait pas que c'est une destination locale (mydestination) et il ne sait pas non plus trouver un MX qui fasse l'affaire.

Notez au passage le ping-pong en perspective : root@cyrus.nain-t.net envoie un message à root@cyrus.nain-t.net, qui n'est pas livrable, et la notification part donc dans la BAL de l'expéditeur, soit root@cyrus.nain-t.net… Mais Postfix s'en tire très bien et n'insiste (heureusement) pas sur les double-bounce.

Une solution possible pour régler cette question est de créer une BAL sysop@nain-t.net par exemple, et d'utiliser le fichier /etc/aliases pour rediriger tous les messages administratifs (locaux) vers cette boîte :

# cat /etc/aliases
mailer-daemon: postmaster
postmaster: root
nobody: root
hostmaster: root
usenet: root
news: root
webmaster: root
www: root
www-data: root
ftp: root
abuse: root
noc: root
security: root
clamav: root
amavis: root
cyrus: root

root: sysop@nain-t.net
La technique est simple, tous les noms d'oiseaux susceptibles de recevoir des messages locaux sont redirigés sur root qui, en fin de compte, est redirigé sur sysop@nain-t.net.

Notez que cette table existait déjà mais ne contenait rien, ou rien d'intéressant, mais était déjà référencée dans le main.cf :

...
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
...

Reste à la passer à la moulinette du postalias :

postalias /etc/aliases

Ce qui va créer un nouveau fichier /etc/alias.db

Puis nous vérifions que tous les noms d'hôte local figurent dans mydestination en plus des destinations tirées de MySQL :

mydestination = cyrus.nain-t.net, localhost.nain-t.net, localhost,  mysql:/etc/postfix/db/mydestination.cf

Et de forcer Postfix à revoir sa configuration :

postfix reload

(N'oublions pas de créer la boîte sysop@nain-t.net avant d'essayer) :

# mail security
Subject: test redirections locales
ça marche.
.
Cc: 

Jun 16 16:32:22 cyrus postfix/pickup[8937]: 657C64800D3C: uid=0 from=<root>
Jun 16 16:32:22 cyrus postfix/cleanup[8953]: 657C64800D3C: message-id=<20080616143222.657C64800D3C@cyrus.nain-t.net>
Jun 16 16:32:22 cyrus postfix/qmgr[8938]: 657C64800D3C: from=<root@cyrus.nain-t.net>, size=350, nrcpt=1 (queue active)
Jun 16 16:32:22 cyrus cyrus/lmtpunix[8957]: Delivered: <20080616143222.657C64800D3C@cyrus.nain-t.net> to mailbox: nain-t.net!user.sysop
Jun 16 16:32:22 cyrus postfix/lmtp[8956]: 657C64800D3C: to=<sysop@nain-t.net>, orig_to=<security>, relay=cyrus.nain-t.net[/cyrussock/lmtp], delay=0.32, delays=0.08/0.01/0.04/0.19, dsn=2.1.5, status=sent (250 2.1.5 Ok)
Jun 16 16:32:22 cyrus postfix/qmgr[8938]: 657C64800D3C: removed

Remarques

  1. Comme Postfix dispose maintenant du moyen de savoir si une adresse virtuelle existe ou non, par le biais de virtual_alias_maps, nous pouvons retirer la ligne local_recipient_maps = du main.cf.
  2. La configuration décrite ici ne fonctionnera totalement qu'à la condition que nous ayons choisi de faire gérer les domaines virtuels par Cyrus.
  3. Nous n'avons pour l'instant pas utilisé les services de SASL dans Postfix. Ils ne sont en effet nécessaires que si Postfix doit authentifier ses clients. Pour l'instant, seul l'hôte local peut être client de Postfix : mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128. Si les notations IPv6 vous gênent ([::ffff:127.0.0.0]/104 [::1]/128), ignorez-les ou mieux, reportez-vous au chapitre idoine.
  4. Se poser quelques questions sur la sécurité de l'ensemble ne sera pas forcément une mauvaise idée.