+++
title = "Configurer un relais SMTP (proprement)"
date = 2023-04-18
[taxonomies]
tags = [ "système", "smtp", "tem" ]
+++

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

*Michel, désespéré…*

<!-- more -->

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 (_**T**ransactional **E**-**M**ail_). 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 _**M**ail **D**elivery **A**gent_ par exemple) et dans d’autres cas, il doit aller le transmettre à un autre serveur (un autre _**M**ail **T**ransport **A**gent_).

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 :

```postfix
# 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 :

```postfix
[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` :

```stunnel
[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 :

```postfix
relayhost = [localhost]:10465
```

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

```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` :

```postfix
recipient_canonical_maps = regexp:/etc/postfix/recipient_canonical
```

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

```postfix
/.+/ 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` :

```postfix
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) :

```postfix
/.+/ 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.
