All links of one day
in a single page.
<Previous day - Next day>

rss_feedDaily RSS Feed
floral_left The Daily Shaarli floral_right
——————————— Monday 25, May 2026 ———————————

Migrer de Postfix vers OpenSMTPD

Sur mes infrastructures persos, j'essaye de diversifier les implémentations d'un même service. J'ai Apache httpd et nginx, BIND et nsd, etc. Objectifs : résilience et compétence.

Pour mes courriels, ce n'est pas vrai, j'utilise uniquement postfix (MTA), dovecot (IMAP et, sur un seul de mes domaines, MDA + Sieve), saslauthd (authentification SASL), et OpenDKIM (signature et vérification DKIM). Récemment, sur l'un de mes domaines, je suis passé de saslauthd à dovecot pour l'auth SASL.

J'utilisais policyd-spf-perl pour vérifier SPF, mais, en cas d'erreur dans la résolution de noms, ça rejette des expéditeurs légitimes dès la phase EHLO d'une transactions SMTP, et il n'est pas possible de débrayer cela. Il y a longtemps, sur l'un de mes domaines, j'utilisais procmail comme MDA et langage de filtre, mais la syntaxe est reloue et je n'ai pas besoin d'un filtrage poussé, donc je l'ai viré (postfix est MTA et MDA).

Sur l'un de mes domaines, j'ai décidé de remplacer Postfix par OpenSMTPD, sans d'autres motivations que les objectifs sus-mentionnés.

Pour ce faire, il y a le très complet tuto de Solène. Pour la signature DKIM, on peut utiliser dkimsign plutôt que rspamd. Pour la syntaxe des filtres, notamment pour FCrDNS, c'est ici. Comme pour les autres produits OpenBSD, les autres réponses sont dans le manuel.

Sur un système Debian, les paquets postfix et opensmtpd sont mutuellement exclusifs, mais la suppression de postfix ne purge pas sa configuration.



Pour inspiration, voici mon /etc/smtpd.conf :

# Taille max d'un courriel. Par défaut : 35M.
smtp max-message-size 15M

# Emplacement certifs et clé x509, et ré-activation de l'échange de clé DHE
pki "example.com" cert "/chemin/vers/chaîne/certificats.pem"
pki "example.com" key "/chemin/vers/clé/privée.pem"
pki "example.com" dhe auto

# Emplacement des alias
table aliases file:/etc/aliases

# Définition des filtres
filter "dkimsign"            proc-exec "filter-dkimsign -d example.com -s <SELECTEUR> -k /chemin/vers/clé/privée/dkim.pem" user _dkimsign group _dkimsign
filter "check_FCrDNS"        phase connect match !fcrdns disconnect "450 No FCrDNS"
filter "check_sender_isFQDN" phase mail-from match !mail-from regex ".+@.+" disconnect "500 FQDN required"
filter "check_sender_notMe"  phase mail-from match mail-from regex ".+@example.com$" disconnect "550 Wrong domain"

# Écoute
listen on eth0 port 25  tls         pki "example.com" filter { "check_FCrDNS", "check_sender_isFQDN", "check_sender_notMe" }
listen on eth0 port 587 tls-require pki "example.com" auth mask-src filter "dkimsign"
# listen on socket filter "dkimsign"

# actions possibles
action "local" maildir alias <aliases>
# action "local" mda "/usr/lib/dovecot/dovecot-lda -f %{sender} -a %{dest} -d %{user.username}" alias <aliases>
action "smtp" relay

# en entrée
match from any for domain "example.com" action "local"
match from local for local action "local"

# en sortie
match from mail-from spam@example.com auth tartempion for any action "smtp"
#match from local for any action "smtp"

Commentaires :

  • Après « pki », il s'agit d'un label en libre choix. Par défaut, l'échange de clés DHE est désactivé, ne reste que ECDHE (variante sur courbes elliptiques de DHE). Si cette désactivation représente l'état de l'art, une configuration TLS trop exigeante fera que le courriel sera transmis en clair ;

  • Dans Debian, le programme filter-dkimsign est dans le paquet logiciel opensmtpd-filter-dkimsign ;

  • FCrDNS = forward-confirmed reverse dns. Nom DNS du SMTP distant = IP (A / AAAA) = nom DNS (PTR). Cohérence sur toute la chaîne. Au début, je le positionnais en phase « mail-from », car, en « connect », j'ai constaté des rejets illégitimes par manque de temps pour la résolution DNS (et que la phase intermédiaire existe en deux exemplaires, « ehlo » ou « helo », donc relou). Mais j'ai constaté cela aussi en phase « mail-from ». Je pense donc que le mieux est de renvoyer un code d'erreur temporaire (4xx). Je suis donc repassé en phase « connect » car, ainsi, ça permet de dégager très rapidement les robots qui ne font que des tentatives d'authentification. Je ne faisais pas cette vérification avec Postfix, il s'agit d'une nouveauté ;

  • Le filtre « check_sender_isFQDN » est un équivalent perso de « reject_non_fqdn_sender » de Postfix : il vérifie que le MAIL-FROM (dans la transaction SMTP, il ne s'agit pas du champ « From » dans l'entête d'un courriel) est un domaine complet. Note : pour vérifier si un domaine émetteur existe réellement, il faut un script externe ;

  • Le filtre « check_sender_notMe » est un équivalent maison de « check_sender_access » de Postfix (j'en parle ici). En résumé : dégager un client SMTP qui se présente avec notre domaine en MAIL-FROM durant la transaction SMTP ;

  • Sur le port 25, on propose STARTTLS, avec la paire certificats+clé dont le label est « example.com », et on applique les filtres précédemment définis aux emails entrants par ici ;

  • Sur le port 587 (submission), on exige STARTTLS et l'authentification (basée sur PAM), on retire l'adresse IP du client de l'entête « Received » (sur Postfix, j'utilisais un « header_check » pour ce faire et retirer aussi le User-Agent de mon logiciel de messagerie), et on appose une signature DKIM ;

  • « listen on socket filter "dkimsig" » permet de signer (DKIM) les courriels émis par les programmes locaux (commande « mail », programme PHP, etc.). Je commente car mes programmes n'envoient pas de courriels à l'extérieur, donc je n'ai pas besoin de cette conf ;

  • Dans les actions possibles, dont je rappelle que « local » et « smtp » sont des labels que j'ai choisis, j'ai défini opensmtpd comme MDA (première ligne), qui utilise la structure de stockage Maildir (mbox est également dispo), et la table d'alias définie plus haut. J'ai également défini, à la deuxième ligne et en commentaire, dovecot comme MDA, avec la gestion du délimiteur « + » (ex. : toto+dgfip@example.com). Je m'en expliquerai infra. Troisième ligne : opensmtpd est un client SMTP qui peut contacter directement l'extérieur ;

  • Les règles d'entrée se comprennent facilement : les courriels pour le domaine « example.com », peu importe leur provenance, seront traités par le composant MDA défini plus haut. Idem pour les courriels émis par les programmes locaux à destination des autres comptes locaux ;

  • La règle de sortie en commentaire permettrait aux programmes locaux (commande « mail », script PHP, etc.) d'envoyer des courriels à l'extérieur. Je n'en ai pas bessoin. La seule règle active dit que les courriels dont l'adresse d'expéditeur, dans la session SMTP (pas dans l'entête d'un email), est « spam@example.conf » et qui ont été remis par l'utilisateur authentifié « tartempion » peuvent être émis vers l'extérieur. Cette sur-déclaration, exhaustive, permet de retrouver le comportement de « smtpd_sender_login_maps » de Postfix : seul l'utilisateur tartempion peut utiliser l'adresse spam@example.com, pas les autres utilisateurs locaux ou authentifiés.



Voyons les différences avec Postfix.

Avec Postfix, je passe beaucoup de temps à définir la configuration TLS. OpenSMTPD se repose sur la libtls (libressl) qui prend en charge uniquement les versions 1.2 et 1.3 (source). De même, par défaut, les suites cryptographiques sont uniquement les suites AEAD de TLS 1.3 et 1.2 (sources : 1, 2, 3). Ça s'applique au serveur et au client SMTP. Depuis sa version 3 (empaquetée dans Debian 12), OpenSSL désactive aussi TLS < 1.2 au niveau du système, donc c'est cohérent, même si ça augmente la probabilité de courriels livrés en clair à cause d'exigences TLS trop élevées…

OpenSMTPD ne prend pas en charge le pipeling, donc pas de vulnérabilité SMTP smuggling à contrer. Idem pour l'absence de prise en charge de la commande VRFY et de l'exigence qu'un client se présente avec la commande EHLO (ou HELO) avant toute autre chose : c'est natif.

J'utilise OpenDKIM également pour vérifier la signature des courriels entrants. OpenSMTPD délègue aussi cela (mais, a priori, la syntaxe des échanges n'est pas la même, donc un programme pour Postfix ne fonctionne pas directement avec OpenSMTPD). Soit à rspamd, soit à un programme dédié. Pareil pour la vérification SPF. Au final, je ne regarde pas le résultat de ces validations, donc je décide de ne rien mettre en œuvre.

Avec Postfix, j'utilise « header_checks » et « body_checks » pour filtrer sommairement le spam. Cela me suffit. Il n'y a pas d'équivalent avec opensmtpd car il est conçu pour manipuler uniquement les enveloppes, pas les courriels eux-mêmes. C'est pour cela que j'ai déjà préparé dovecot comme MDA : pour faire rapidement du Sieve en cas de pic de spam.

Les autres restrictions de Postfix sur l'émetteur ou le destinataire sont natives avec OpenSMTPD : « smtpd_reject_unlisted_sender » est englobée dans ma vérification d'une auth SASL ou d'un compte local ; « reject_unknown_sender_domain » ne s'applique que si Postfix n'est pas la destination finale ; Etc.



Commandes utiles :

  • Vérifier la conf' : smtpd -n ;

  • Consulter la file d'attente (pour Postfix, c'est ici) : smtpctl show queue ;

  • Tenter de livrer les courriels en attente (idem) : smtpctl schedule all ;

  • Vider la file d'attente (idem) : smtpctl remove all ;

  • Après la création / suppression d'un alias, actualiser la table stockée en mémoire en partir du fichier : smtpctl update table aliases.
-