Ah oué, c’est tant si simple que ça en fait…

Michel, désespéré…

Coucou mes p’tits Rondoudous, ça faisait un moment.

Il m’est arrivé récemment une aventure magique avec des relais SMTP et je me suis dit que ce serait la bonne occasion de faire un petit résumé de ce qu’il faut faire pour gérer des cas un peu tordus.

L’idée est donc de configurer un relais SMTP d’abord simple avec un serveur SMTP que tu maîtrises à 100% et qui est dans le même réseau que toi, puis de s’attaquer à un relais SMTP un peu plus compliqué, passant par un TEM (Transactional E-Mail). En l’occurence, c’est celui de Scaleway mais tous ont un fonctionnement remarquablement similaire, donc ça devrait pouvoir s’appliquer à tous.

Et comme c’est un peu le standard, on va faire tout ça avec du Postfix, je suppose qu’on peut aussi avoir des configurations similaires avec d’autres serveur SMTP, mais c’est ce que j’ai sous la main (et c’est fort probable que toi aussi).

Commençons par le pourquoi

D’abord, tu pourrais te demander : mais pourquoi diable devrais-je me crever le cul à installer un relais SMTP qui envoient des messages tout propres depuis mes serveurs ?

Bah, c’est tout con : quand il y a des soucis, le serveur a tendance à le signaler par courriel. Et il y a encore beaucoup de services (cron est l’exemple le plus frappant) qui informe les utilisateurs par courriel de ce qui se passe.

Ça permet également dans le cas où un logiciel est installé sur le serveur de le faire s’appuyer sur le SMTP du relais plutôt que sur d’obscures configurations hétérogènes par toujours facile à manipuler.

Alors évidemment, si tu n’as ni cron, ni NUT, ni quoique ce soit qui serait susceptible d’envoyer des courriels depuis ton serveur, tu peux parfaitement t’en passer. Mais l’expérience montre que ça arrive bien plus souvent qu’on ne le souhaiterait.

Relais SMTP avec Submission

Un des trucs importants à comprendre dans Postfix, c’est qu’il y a une différence fondamentale entre SMTP et SMTPD : le premier est la partie « cliente » de SMTP, le second est la partie « serveur ». Oui, parce que dans certains cas, Postfix reçoit du courrier et s’arrange pour le traiter correctement (le transmettre à un Mail Delivery Agent par exemple) et dans d’autres cas, il doit aller le transmettre à un autre serveur (un autre Mail Transport Agent).

Pour faire tout ça, il y a des ports standards :

  • le port 25 est le port SMTP standard quand on ne chiffre pas la communication (ce qui n’empêche de la chiffrer a posteriori avec une commande STARTTLS) ;
  • le port 465 est le port SMTP standard mais avec chiffrement a priori (donc pas de STARTTLS, on est déjà chiffré quand on arrive) ;
  • le port 587 est le port de soumission pour demander gentiment à Postfix de livrer le message suggéré. Il est chiffré par défaut et demande généralement une authentification (pas forcément complexe mais voilà).

Donc, voilà, pour cette partie, je vais supposer que tu as déjà un serveur SMTP correctement configuré et que tu as accès au port 587 de ce dernier pour balancer tes mails.

En toute logique, c’est un cas assez standard et plutôt simple à mettre en œuvre.

Donc côté « client », on peut donc commencer par un main.cf de ce type :

# des trucs génériques dont on se fout un peu
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# !!! TRÈS IMPORTANT
append_dot_mydomain = no
myorigin = buttse.cx # il faut mettre ici le domaine d’origine du message,
# sinon, ça risque de poser pas mal de souci

# ça c’est les trucs génériques de Postfix
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
# la destination, c’est important : c’est un relais, donc il n’est pas
# supposé recevoir de messages autrement que pour les utilisateurs
# locaux
mydestination = $myhostname, localhost.$mydomain, localhost

# c’est là que la magie opère
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = yes
relayhost = [smtp.buttse.cx]:587

# notre serveur ne doit écouter qu’en local, jamais ailleurs
mynetworks = 127.0.0.0/8
inet_interfaces = loopback-only
smtpd_relay_restrictions = permit_mynetworks,defer_unauth_destination,reject

Et donc effectivement, toute la magie tient dans les variables smtp_sasl_* et notamment le fichier /etc/postfix/sasl_passwd, qui contient l’utilisateur/mot de passe pour le serveur SMTP :

[smtp.buttse.cx]:587 utilisateur:mot_de_passe

Il faut un fichier de hash, donc un petit coup de postmap sur le fichier en question et hop !, tout fonctionne ! Dès que le serveur veut envoyer un message, il passera par le SMTP local, il aura la bonne adresse source et devrait s’appuyer sur le relais qui a été donné.

Ça c’était le cas le plus simple.

Submission failed!

Mais alors que se passe-t-il si ton fournisseur ou ton hébergeur ne te laisse pas accéder au port 587 ? Généralement, la plupart des TEM ont une config alternatives pour ce cas-là : le port 2525 pour certains, ou comme Scaleway le port 2465 (mais le problème est le même pour le port 465). L’idée, c’est donc d’utiliser le port SMTPS avec Postfix.

Sauf que l’option relayhost de Postfix ne supporte pas les connexions SMTPS. Parce que bien sûr que non.

Du coup, il va falloir ruser pour convaincre Postfix de faire comme d’habitude. Et cette ruse, elle n’est pas si compliquée que ça, elle s’appelle stunnel.

Sous Debian/Ubuntu, tu peux donc installer le paquet stunnel4 et faire une petite configuration de ce type, dans /etc/stunnel/smtp-wrapper.conf :

[smtp-tls-wrapper]
accept = 10465
client = yes
connect = smtp.tem.buttse.cx:465

Et du coup, lorsque l’on démarre le service stunnel4, on voit effectivement qu’il écoute sur le port 10465. Il suffit alors de reconfigurer notre petit Postfix comme suit pour s’adapter :

relayhost = [localhost]:10465

Et évidemment, changer également le login/pass/host dans /etc/postfix/sasl_passwd :

[localhost]:10465 login:pass

Et boum !, ça fait des chocapics !

Bonus 1 : configurer Postfix pour réécrire l’adresse cible

Quand il s’agit de cron, le service a tendance à envoyer les messages à l’utilisateur local concerné. Donc si le cron www-data, qui gère les services Web, se vautre complètement, il enverra un mail à www-data, l’utilisateur local. C’est super intéressant, mais en l’occurence, on préfèrerait largement qu’il l’envoie à un autre destinataire, j’ai nommé l’administrateur du système.

Pour cela, on peut forcer Postfix à faire une réécriture de tous les messages de destination pour les envoyer ailleurs. Ce n’est pas bien compliqué, il suffit d’ajouter la ligne suivante dans le main.cf :

recipient_canonical_maps = regexp:/etc/postfix/recipient_canonical

Et le fichier /etc/postfix/recipient_canonical se présentera comme suit :

/.+/ admin@buttse.cx

Évidemment, petit malin, rien n’empêche d’aller mettre d’autres choses dans ce fichier. Là, tout sera réécrit vers admin@buttse.cx, ce n’est pas forcément le comportement souhaité in fine.

Bonus 2 : configurer Postfix pour réécrire les adresses sources

Normalement™, le simple fait d’avoir un myorigin dans le main.cf devrait suffire à containdre les adresses locales à utiliser un nom de domaine bien précis.

Malheureusement pour certaines configurations, ce n’est pas toujours le cas. Et notamment le From et le Envelope-From SMTP ne sont pas toujours identiques. Pis, il peut arriver que le TEM ou le relais SMTP refuse le message s’il n’y a pas correspondance entre ces deux entêtes dans le courriel. C’est en réalité parfaitement logique, l’objectif étant d’éviter que le message ne se retrouve perdu pour non-respect de SPF/DKIM par exemple.

Mais du coup, ça fait un peu chier…

Heureusement, encore une fois Postfix à la rescousse ! On peut aussi forcer ce dernier a réécrire les adresses (tous les champs entêtes) pour y mettre une adresse donc on est certain de la conformité.

Pour cela, il va falloir ajouter quelques lignes dans notre main.cf :

sender_canonical_classes = envelope_sender, header_sender
sender_canonical_maps =  regexp:/etc/postfix/sender_canonical_maps
smtp_header_checks = regexp:/etc/postfix/header_check

Et on produit donc les deux maps permettant de changer les deux entêtes à la fois dans /etc/postfix/sender_canonical_maps et /etc/postfix/header_check (c’est exactement le même fichier) :

/.+/ youplaboom@mondomainesour.ce

Et voilà, tous les courriels vont maintenant venir de youplaboom@mondomainesour.ce !

Conclusation

Bah voilà, maintenant, il n’y a plus d’excuses pour ne pas mettre un relais SMTP tout propre sur tes serveurs histoire d’avoir une idée de ce qui se passe sur le système.