Cet article n’est pas un copy pasta et ça marche, il faut prendre son temps pour comprendre comment chaque outil fonctionne :)
Le mail, c’est un enfer sur plein de points et beaucoup préfèrent en laisser la gestion à de gros organismes afin de ne pas s’embêter avec…
Problème, ces grands organismes, vous saurez jamais vraiment ce qu’ils font avec vos mails… Suffit qu’il y’ait du contenu sensible ou encore plus drôle, du contenu mal interprété et on voit vite le bordel (coucou GMail…).
Du coup quelles solutions pour éviter de confier ses mails à un tiers de confiance ? Les héberger sois même sur son propre serveur !
Mais bon comme dit c’est un sacré bazar, mais c’est aussi pour ça que je rédige ce post.
Pourquoi OpenSMTPD au lieu de PostFix ?
Je sais pas vous, mais vous avez essayé de regarder la documentation de PostFix ? Elle est juste immonde et désolé mais bosser avec des outils avec une doc en carton, sans façon.
OpenSMTPD est un serveur SMTP conçu pour être fiable, simple, rapide, et surtout sécurisé (les grands commandements des projets OpenBSD entre autres).
Pour l’instant la team OpenBSD stipule que ce n’est pas encore vraiment prêt à tenir la charge d’énorme domaines niveau trafic, cependant pour des domaines de petites et de moyennes taille, c’est juste parfait !
Ok mais c’est quoi tout le reste ?
Un serveur SMTP suffit pour envoyer et recevoir des mails, cependant il y a pleins d’autres aspects qu’il faut prendre en compte.
rspamd : Servira à signer avec DKIM vos mails et aussi filtrer ce qui rentre avec de l’antispam etc…Dovecot : Pouvoir lire ses mails c’est pratique et il propose IMAP comme protocole.PostgreSQL : PAM c’est rigolo 2 secondes mais pour de l’usage multi-domaines, c’est pas super génial, donc stocker tout ce qui est identifiants, etc… une BDD c’est le mieux. (OpenSMTPD et Dovecot supportent également MySQL et SQLite 3)
Pré-requis
Pour commencer, ça paraît évident, mais il vous faut un domaine, prenez ce qui vous conviens chez Gandi ou OVH (ou n’importe quel autre registar).
Verifiez que votre registar / l’organisme qui héberge votre zone dns supporte DNSSEC, c’est très important pour éviter le dns-hijack.
Ensuite le mail étant un sacré bordel, je vous recommande très fortement de dédier une machine virtuelle ou alors une machine complète pour votre serveur mail.
Il est important que celle-ci puisse avoir sa propre IPv4/v6 publique et que vous puissiez changer les reverses dns.
Vous pouvez le faire de manière sûre et certaine chez :
- OVH / OVH Télécom
- Scaleway (dédiés)
- Digital Ocean (Blocage du port 25)
- Vultr (après demande au support pour déblocage du port 25)
- Hetzner
Pour toutes les étapes qui suivront, on part du principe que vous respectez tous les pré-requis ci-dessus et que vous avez une machine/vm/whatever sous OpenBSD fonctionnelle (nous sommes actuellement à la version 6.9 du système).
PostgreSQL
Pour installer PostgreSQL sur OpenBSD c’est assez simple vu que c’est disponible directement dans les dépots :
pkg_add postgresql-server
Sur OpenBSD, il faut initier le serveur pour pouvoir le démarrer la première fois, ça se fait de cette façon :
su - _postgresql
cd /var/postgresql/
initdb --locale=en_US.UTF-8 -D /var/postgresql/data/ -U postgres --auth=scram-sha-256 --pwprompt --encoding=UTF-8
Ensuite on peut démarrer le service et commencer à créer des utilisateurs et des schémas.
rcctl enable postgresql
rcctl start postgresql
psql -U postgres -W
CREATE USER _smtpd;
CREATE USER _dovecot;
CREATE USER vmail;
CREATE DATABASE vmail;
ALTER DATABASE vmail OWNER TO vmail;
\c vmail
CREATE TABLE public.credentials (
id SERIAL NOT NULL,
email character varying(255) DEFAULT ''::character varying NOT NULL,
password character varying(255) DEFAULT ''::character varying NOT NULL
);
ALTER TABLE public.credentials OWNER TO vmail;
CREATE TABLE public.domains (
id SERIAL NOT NULL,
domain character varying(255) DEFAULT ''::character varying NOT NULL
);
ALTER TABLE public.domains OWNER TO vmail;
CREATE TABLE public.virtuals (
id SERIAL NOT NULL,
email character varying(255) DEFAULT ''::character varying NOT NULL,
destination character varying(255) DEFAULT ''::character varying NOT NULL
);
ALTER TABLE public.virtuals OWNER TO vmail;
GRANT CONNECT ON DATABASE vmail TO _smtpd;
GRANT CONNECT ON DATABASE vmail TO _dovecot;
GRANT USAGE ON SCHEMA public TO _smtpd;
GRANT USAGE ON SCHEMA public TO _dovecot;
GRANT SELECT ON TABLE public.credentials TO _smtpd;
GRANT SELECT ON TABLE public.credentials TO _dovecot;
GRANT SELECT ON TABLE public.domains TO _smtpd;
GRANT SELECT ON TABLE public.domains TO _dovecot;
GRANT SELECT ON TABLE public.virtuals TO _smtpd;
GRANT SELECT ON TABLE public.virtuals TO _dovecot;
\q
Ensuite il faut qu’on autorise les utilisateurs à se connecter à la base en mode peer, ça se fait dans /var/postgresql/data/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
local all postgres scram-sha-256
local all all peer
# IPv4 local connections:
host all all 127.0.0.1/32 scram-sha-256
# IPv6 local connections:
host all all ::1/128 scram-sha-256
# Allow replication connections from localhost, by a user with the
# replication privilege.
local replication all scram-sha-256
host replication all 127.0.0.1/32 scram-sha-256
host replication all ::1/128 scram-sha-256
Pour finir on redémarre juste le service PostgreSQL :
rcctl restart postgresql
OpenSMTPD
OpenSMTPD est préinstallé sur OpenBSD, il faut donc installer uniquement les extensions qui nous intéresse, en l’occurrence celles pour rspamd et PostgreSQL.
Du coup on installe les paquets opensmtpd-filter-rspamd et opensmtpd-extras-pgsql.
pkg_add opensmtpd-filter-rspamd opensmtpd-extras-pgsql
Maintenant on va toucher au fichier de configuration.
/etc/mail/smtpd.conf
Dégagez tout et mettez cette configuration :
# This is the smtpd server system-wide configuration file. # See smtpd.conf(5) for more information.
srs key CléARemplacer
srs key backup CléARemplacer
## Certs
pki domain.tld cert ""
pki domain.tld key ""
table domains postgres:/etc/mail/pgsql.conf
table virtuals postgres:/etc/mail/pgsql.conf
table passwd postgres:/etc/mail/pgsql.conf
table sndrs postgres:/etc/mail/sndrs-pgsql.conf
filter "rdns" phase connect match !rdns disconnect "550 DNS error"
filter "fcrdns" phase connect match !fcrdns disconnect "550 DNS error"
filter "rspamd" proc-exec "filter-rspamd"
## Ports to listen on, and how to listen on them
listen on egress port 25 tls pki domain.tld hostname server.domain.tld filter { "rdns", "fcrdns", "rspamd" }
listen on egress port 465 smtps pki domain.tld hostname server.domain.tld mask-src received-auth senders <sndrs> auth <passwd> filter "rspamd"
listen on egress port submission tls-require pki domain.tld hostname server.domain.tld mask-src received-auth senders <sndrs> auth <passwd> filter "rspamd"
action "dovecotFinal" lmtp "/var/dovecot/lmtp" rcpt-to virtual <virtuals>
action "send" relay srs
match from any for domain <domains> action "dovecotFinal"
match from auth for any action "send"
match from local for any action "send"
Remplacez server.domain.tld
par le FQDN de votre serveur et mettez votre certificat TLS public et la clé privée.Remplacez aussi les domain.tld
par votre domaine si vous utilisez un certificat TLS wildcard, si votre certificat est standalone, vous pouvez remplacer par votre FQDN. (Les clés publiques/privées se collent dans les lignes pki)Remplacez également les clés SRS, celles-ci sont de préférence à changer tous les ans, elles permettent à ce que les mails transférées sous certaines conditions ne soient pas considérés comme du spam.(elles se génèrent comme suit : head -c 30 /dev/urandom | base64
)
Le dernier match de la configuration autorise tout envoi de mail depuis localhost, si vous n’êtes pas à l’aise avec cette idée, vous pouvez supprimer la ligne.
Bien sûr il faut dire à OpenSMTPD quelle base de données utiliser et aussi avec quels identifiants… Et bien ça se passe dans /etc/mail/pgsql.conf
!
# PostgreSQL
conninfo host='/tmp'
dbname='vmail'
user='_smtpd'
query_alias SELECT destination FROM virtuals WHERE email=$1;
query_credentials SELECT email, password FROM credentials WHERE email=$1;
query_domain SELECT domain FROM domains WHERE domain=$1;
EDIT 09/03/2023 : Grâce à Tobias Fiebig, j’ai maintenant une façon fiable d’empêcher le spoofing des adresses du champ “From” !
J’ai mis à jour le smtpd.conf plus haut mais en plus de ça il faut rajouter un fichier /etc/mail/sndrs-pgsql.conf
.
# PostgreSQL
conninfo host='/tmp' dbname='vmail' user='_smtpd'
query_mailaddrmap with t as (SELECT REGEXP_REPLACE( $1,'[+]([^@])+\@', '\@' ) as addr) select virtuals.email as mail from virtuals join t on REGEXP_REPLACE(virtuals.destination, '[+]([^@])+\@', '\@' ) = t.addr JOIN domains AS vd ON virtuals.email LIKE CONCAT('%',vd.domain,'%') UNION select email from credentials join t on credentials.email = t.addr;
Cette commande SQL c’est de la pure magie noire, en gros elle permet de donner une liste de tous les alias lié à une adresse mail et va donc nous permettre de définir la liste des alias autorisés à être utilisé par un tel user logué.
Et bien sûr c’est compatible RFC 5233, que du bonheur.
Hop là, pour OpenSMTPD c’est bon !
rspamd
Maintenant on attaque rspamd, pareil la version sur les dépôts
pkg_add rspamd
Dans /etc/rspamd/local.d/redis.conf
on configure comment accéder au service Redis :
servers = "127.0.0.1";
password = monsupermdp
Et dans /etc/rspamd/override.d/dkim_signing.conf
on configure les clés qui vont servir à signer les mails pour DKIM.Mettez votre domaine et le path que vous voulez (idem pour le selecteur), on génère les clés juste après.
allow_username_mismatch = true;
selector = "main";
use_esld = false;
domain {
domain.tld {
path = "path to key";
selector = "selector";
}
domain.tld {
path = "path to key";
selector = "selector";
}
domain.tld {
path = "path to key";
selector = "selector";
}
}
On peut aussi setup les reports DMARC (ici /etc/rspamd/local.d/dmarc.conf
) dans le cas où on a gardé la dernière ligne de configuration d’OpenSMTPD
reporting {
# Required attributes
enabled = true; # Enable reports in general
email = 'email@domain.tld'; # Source of DMARC reports
domain = 'domain.tld'; # Domain to serve
org_name = 'Organisation Name'; # Organisation
# Optional parameters
#bcc_addrs = ["postmaster@example.com"]; # additional addresses to copy on reports
report_local_controller = false; # Store reports for local/controller scans (for testing only)
helo = 'rspamd.localhost'; # Helo used in SMTP dialog
smtp = '127.0.0.1'; # SMTP server IP
smtp_port = 25; # SMTP server port
from_name = 'Rspamd'; # SMTP FROM
msgid_from = 'rspamd'; # Msgid format
max_entries = 1k; # Maxiumum amount of entries per domain
keys_expire = 2d; # Expire date for Redis keys
#only_domains = '/path/to/map'; # Store reports merely from those domains
}
Vous pouvez ensuite ajouter cette ligne à votre crontab :
30 1 * * * /usr/local/bin/rspamadm dmarc_report
Redis
Pensez à configurer redis.
rcctl enable redis
rcctl start redis
redis-cli
CONFIG SET requirepass "mypassword"
DKIM
La pour le coup c’est super simple :
openssl genrsa -out /etc/mail/dkim-domain.tld-private.key 2048
openssl rsa -in /etc/mail/dkim-domain.tld-private.key -pubout -out /etc/mail/dkim-domain.tld-public.key
Et hop là toutes mes félicitations vous êtes en possession d’une super clé DKIM !
La deuxième commande génère la clé publique que vous pouvez utiliser pour faire votre record DKIM sur votre zone DNS.
Attention, n’essayez pas de générer des clés au delà de 2048 bits, quasiment aucun serveur mail ne sera en mesure de les lire.Si votre provider DNS ne supporte pas les clés 2048 bits, vous pouvez essayer de passer en 1024.
Dovecot
Car lire ses mails c’est quand même pratique :')
pkg_add dovecot-pigeonhole dovecot-postgresql dovecot
Avant de commencer à configurer il faut autoriser dovecot à bypass la limite de fichiers ouverts dans OpenBSD via /etc/login.conf
dovecot:\
:openfiles-cur=1024:\
:openfiles-max=2048:\
:tc=daemon:
Et ensuite on édite une nouvelle configuration ici : /etc/dovecot/dovecot.conf
ssl = required
ssl_cert = </path/to/certificate
ssl_key = </path/to/private/key
ssl_min_protocol = TLSv1.2
ssl_prefer_server_ciphers = yes
disable_plaintext_auth = yes
protocols = lmtp imap
service lmtp {
unix_listener lmtp {
user = vmail
group = vmail
}
}
service imap-login {
inet_listener imaps {
port = 993
}
}
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/home/vmail/%u
}
mail_location = maildir:/home/vmail/%u/MailDir
protocol imap {
mail_plugins = $mail_plugins imap_sieve
}
protocol lmtp {
mail_plugins = $mail_plugins sieve
}
plugin {
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.environment
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY APPEND
imapsieve_mailbox1_before = file:/usr/local/lib/dovecot/sieve/report-spam.sieve
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve
imapsieve_mailbox3_name = Inbox
imapsieve_mailbox3_causes = APPEND
imapsieve_mailbox3_before = file:/usr/local/lib/dovecot/sieve/report-ham.sieve
sieve_pipe_bin_dir = /usr/local/lib/dovecot/sieve
}
Vous remarquerez un user vmail
dans la config… Il existe pas ? Normal ! On va remédier à ça.
mkdir /home/vmail
useradd -m -c "Virtual Mail" -d /home/vmail -s /sbin/nologin vmail
chown -R vmail: /home/vmail
Fichier de configuration suivant pour la connexion à PostgreSQL : /etc/dovecot/dovecot-sql.conf.ext
# Database driver: mysql, pgsql, sqlite
driver = pgsql
connect = host=/tmp dbname=vmail user=_dovecot
default_pass_scheme = BLF-CRYPT
password_query = \
SELECT email, password \
FROM credentials WHERE email = '%u'
On pensera également à rajouter les mécanismes d’apprentissage pour rspamd :
cd /usr/local/lib/dovecot/sieve
cat <<EOF > report-ham.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.mailbox" "*" {
set "mailbox" "${1}";
}
if string "${mailbox}" "Trash" {
stop;
}
if environment :matches "imap.user" "*" {
set "username" "${1}";
}
pipe :copy "sa-learn-ham.sh" [ "${username}" ];
EOF
cat <<EOF > report-spam.sieve
require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"];
if environment :matches "imap.user" "*" {
set "username" "${1}";
}
pipe :copy "sa-learn-spam.sh" [ "${username}" ];
EOF
cat <<EOF > sa-learn-ham.sh
#!/bin/sh
exec /usr/local/bin/rspamc -d "${1}" learn_ham
EOF
cat <<EOF > sa-learn-spam.sh
#!/bin/sh
exec /usr/local/bin/rspamc -d "${1}" learn_spam
EOF
rcctl restart dovecot
sievec report-ham.sieve
sievec report-spam.sieve
chmod 755 sa-learn-ham.sh
chmod 755 sa-learn-spam.sh
Presque fini !
On peut commencer à créer des utilisateurs, des domaines etc…Pour ça je vous conseille d’avoir 2 terminaux ouvert :
- Un sur le shell postgres
psql -U postgres
- Un autre ouvert de côté
Côté postgres vous pouvez vous connecter à la base vmail
avec la commande \c vmail
.
De l’autre côté, vous pouvez générer le hash de mot de passe de votre premier compte avec cette commande : doveadm pw -s BLF-CRYPT -r 9
Celà devrait vous donner un résultat du type :{BLF-CRYPT}$2y$09$Kpm01vaz0bGzXKRZ5D2PoumTRE.ihmKktlcJiToVTfWpvmlhT6XMu
Pour que ET OpenSMTPD ET Dovecot puissent comprendre ce résultat, on va le modifier de la façon suivante :
- Retirer l’entête
{BLF-CRYPT}
- Changer l’entête du hash de
$2y$09$
en$2b$09$
Pour la deuxième opération, elle est à faire car OpenSMTPD ne reconnait pas l’en-tête $2y$
alors que $2b$
si, et vu que $2b$
et $2y$
sont de même type, on peut les interchanger sans problème.
Notre hash ressemblera donc à ça :$2b$09$Kpm01vaz0bGzXKRZ5D2PoumTRE.ihmKktlcJiToVTfWpvmlhT6XMu
On peut repasser sur le shell postgres :
INSERT INTO credentials (email, password) VALUES ('email@domain.tld', 'monSuperHash');``INSERT INTO virtuals (email, destination) VALUES ('email@domain.tld', 'vmail');``INSERT INTO domains (domain) VALUES ('domain.tld');
\q
Maintenant on redémarre tous les services :
rcctl enable smtpd dovecot rspamd
rcctl restart smtpd dovecot rspamd
Et on vérifie que la commande hostname
ressorte bien un FQDN complet, autrement pensez à le corriger :)Hop là et en théorie votre serveur mail est normalement fonctionnel !
DNS
Quelques petits records DNS à ajouter sur la zone de votre domaine.
Premièrement un A et un AAAA pointant sur votre serveur, ça parait con mais je préfère le mentionner. Ensuite un MX pointant sur le FQDN de votre serveur.
Maintenant commence la partie fun, DKIM, SPF et DMARC
# DKIM
main._domainkey.domain.tld. TXT "v=DKIM1; k=rsa; t=s; s=email; h=sha256; p=<public-dkim-key>"
# SPF
domain.tld. TXT "v=spf1 mx -all"
# DMARC
_dmarc.domain.tld. TXT "v=DMARC1; p=none"
Hop là normalement ça devrait être bon.
Fini !
Voilà, vous voici avec votre propre serveur mail !
Vous pouvez tester son fonctionnement via un service comme mail-tester et ensuite profiter :)Merci à leonekmi, luclu7 et ramle pour la relecture ! :)