diff --git a/README.md b/README.md index d16fdd4..746d446 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ OVH, sinon il décrit un état qui n'existe plus. | Document | Sujet | |---|---| | [`docs/courrier-sortant.md`](docs/courrier-sortant.md) | relais, SPF/DKIM/DMARC, plafonds, boîtes du service | +| [`docs/courrier-entrant.md`](docs/courrier-entrant.md) | usurpation du domaine, liste blanche postmaster d'ISPConfig, tri dans Junk | | [`docs/certificats.md`](docs/certificats.md) | les deux mécanismes TLS, leurs pièges, les réparations | | [`docs/sauvegardes.md`](docs/sauvegardes.md) | sauvegarder les bases, et surtout les restaurer | @@ -206,6 +207,17 @@ les pièges qui vont avec : --- +## Courrier entrant + +Un message qui se fait passer pour `@alpinux.org` doit être refusé ou rangé +dans `Junk`. Le 28/09/2026, un phishing usurpant `postmaster@` est passé sans +aucun contrôle, à cause d'une liste blanche posée par ISPConfig. Les trois +protections mises en place, et comment les vérifier : + +→ **[`docs/courrier-entrant.md`](docs/courrier-entrant.md)** + +--- + ## Certificats TLS Deux mécanismes coexistent — un certificat certbot multi-domaines pour huit diff --git a/docs/courrier-entrant.md b/docs/courrier-entrant.md new file mode 100644 index 0000000..388909e --- /dev/null +++ b/docs/courrier-entrant.md @@ -0,0 +1,191 @@ +# Courrier entrant d'owni : l'usurpation du domaine + +Comment owni refuse un message qui se fait passer pour `@alpinux.org`, et +pourquoi `postmaster@` était la seule adresse qu'on pouvait usurper sans aucun +contrôle. Le pendant sortant est +[`courrier-sortant.md`](courrier-sortant.md). + +État au 29/09/2026. + +## L'incident du 28/09/2026 + +Un phishing (« Modification d'un e-mail envoyé », lien vers +`*.web.core.windows.net`) est arrivé dans la boîte `postmaster@`, avec +`From:` et `To:` tous deux à `postmaster@alpinux.org`. Il ne portait **aucun** +en-tête `X-Spam-*` ni `Authentication-Results`. + +``` +Received: from [171.249.196.126] (unknown [171.249.196.126]) + by owni.alpinux.org (Postfix) with ESMTP id 79115207A0 +``` + +L'expéditeur était une ADSL Viettel, qui s'est connectée directement au port +25, sans authentification. Pourtant, le SPF `-all` et le DMARC +`p=quarantine` du domaine auraient suffi à l'arrêter. Aucun des deux n'a été +consulté. + +## Pourquoi il est passé + +**Postfix l'a accepté.** Le destinataire est local, et l'expéditeur +`postmaster@` existe, donc `reject_unlisted_sender` le laisse passer. Il n'y +avait pas de `reject_sender_login_mismatch`. Et même activé, celui-ci n'aurait +rien vu : la boîte `postmaster@` a le SMTP désactivé +(`mail_user.disablesmtp = 'y'`), elle est donc **absente** de +`smtpd_sender_login_maps`. + +**rspamd ne l'a pas analysé.** Dans `/var/log/rspamd/rspamd.log` : + +``` +apply static settings whitelist; rcpt matched; priority high +task is whitelisted +(default: S (no action): [0.00/15.00] []) ... settings_id: whitelist +``` + +L'installeur d'ISPConfig écrit dans `/etc/rspamd/local.d/users.conf` : + +``` +whitelist { + priority = 5; + rcpt = "postmaster"; rcpt = "hostmaster"; rcpt = "abuse"; + want_spam = yes; +} +``` + +L'intention est bonne : la RFC 2142 veut que ces adresses reçoivent toujours +les plaintes. Mais `want_spam = yes` coupe **toutes** les vérifications, SPF +et DMARC compris, pour tout ce qui leur est adressé. + +## Les trois protections en place + +### 1. rspamd : liste noire sur l'enveloppe (interface ISPConfig) + +Dans **Email → Spamfilter → Blacklist**, deux entrées : `postmaster@alpinux.org` +et `abuse@alpinux.org` (table `spamfilter_wblist`). + +ISPConfig génère `/etc/rspamd/local.d/users/spamfilter_wblist_N.conf` : si +l'**expéditeur d'enveloppe** est cette adresse et le destinataire +`@alpinux.org`, alors `reject = 0.2`. La priorité (50) l'emporte sur la liste +blanche (5). + +À ne pas confondre avec **Email → Global Filters → Blacklist**, qui +alimente la table Postfix `mail_access`. Celle-ci est lue **avant** +`permit_sasl_authenticated` : un expéditeur ajouté là serait refusé même +authentifié. + +### 2. Postfix : expéditeur local ⇒ authentification (interface ISPConfig) + +Dans **System → Server Config → owni → Mail**, l'option « Reject sender and +recipient login mismatch » est cochée. ISPConfig réécrit alors : + +``` +smtpd_sender_restrictions = reject_authenticated_sender_login_mismatch, + permit_mynetworks, + check_sender_access proxy:mysql:/etc/postfix/mysql-virtual_sender.cf, + reject_sender_login_mismatch, permit_sasl_authenticated, + reject_non_fqdn_sender, reject_unlisted_sender +``` + +- Un client **non authentifié** qui envoie avec une boîte existante (SMTP + activé) est refusé. +- Un client **authentifié** ne peut envoyer qu'avec sa propre adresse, ou + avec un alias marqué « Allow send as ». + +Avant d'activer l'option, j'ai vérifié sur 3 jours de logs que tous les envois +SASL utilisaient leur propre adresse (`git_app.noreply`, `alpid.noreply`). +`localhost` envoie avec `alpinux@` et `secretaire@`, mais passe par +`permit_mynetworks`. + +### 3. rspamd : analyser sans jamais rejeter (fichier, hors interface) + +`want_spam = yes` est remplacé dans `/etc/rspamd/local.d/users.conf` : + +```diff + rcpt = "abuse"; +- want_spam = yes; ++ # Plus de want_spam : on analyse (SPF/DMARC), on ne rejette jamais (RFC 2142) ++ apply { ++ actions { ++ reject = null; ++ greylist = null; ++ } ++ } +``` + +L'ancien fichier est sauvegardé dans `users.conf.bak-20260929`. Pour que la +correction survive à une mise à jour d'ISPConfig, une copie est placée dans +`/usr/local/ispconfig/server/conf-custom/install/rspamd_users.conf.master`. + +## Pourquoi pas « destination locale ⇒ authentification » + +Ce serait refuser toute la réception : le port 25 est le MX, et aucun serveur +extérieur (Gmail, Orange, les listes) ne s'y authentifie. La règle porte sur +l'**expéditeur** local (protection 2). + +Celle-ci ne suffit pas seule : elle lit l'enveloppe (`MAIL FROM`), pas +l'en-tête `From:`. Un `MAIL FROM:` accompagné de +`From: postmaster@alpinux.org` passe Postfix. C'est DMARC, dans rspamd, qui +l'arrête, et il ne le peut que si la liste blanche ne l'en empêche pas +(protection 3). + +## Où finit le spam de postmaster@ + +Dans `Junk`. La chaîne : + +1. rspamd, via `milter_headers`, pose `X-Spam: Yes` et + `X-Spam-Status: Yes, score=…`. +2. Dovecot exécute `sieve_before = /var/vmail/%d/%n/.ispconfig-before.sieve`, + généré par ISPConfig parce que `mail_user.move_junk = 'y'` : + `if header :matches "X-Spam-Status" "Yes, *" { fileinto :create "Junk"; stop; }` +3. En secours, `sieve_before2 = /var/lib/dovecot/sieve/spam-to-junk.sieve` + fait la même chose. + +La relève de la messagerie lit `INBOX,Junk` (`IMAP_PM_DOSSIER`) : un rapport +DMARC classé à tort reste donc traité. + +## Vérifier + +```sh +# Rejouer une usurpation dans rspamd, depuis l'IP de l'attaquant +rspamc -h localhost:11333 -i 171.249.196.126 \ + -F -r < test.eml + +# Le tri sieve, à sec, sur une copie du script et un maildir jetable +# ($W doit être lisible par vmail ; -u postmaster@… échoue sur l'auth) +rspamc -h localhost:11333 --mime -i 171.249.196.126 -F bidon@example.ru \ + -r postmaster@alpinux.org < test.eml > $W/out.eml +sudo sieve-test -o mail_uid=vmail -o mail_gid=vmail \ + -o mail_location=maildir:$W/md $W/before.sieve $W/out.eml + +# Un utilisateur qui envoie avec une adresse qui n'est pas la sienne +sudo grep "not owned by user" /var/log/mail.log +``` + +Résultats relevés le 29/09/2026 : + +| Scénario | Avant | Après | +|---|---|---| +| Enveloppe et `From:` = postmaster@, vers postmaster@ | accepté, 0.00 | **rejeté** | +| Enveloppe bidon, `From:` = postmaster@, vers postmaster@ | accepté, 0.00 | **Junk**, 17.80 (SPF, DMARC) | +| Même chose vers president@ | rejeté | rejeté | +| Rapport externe légitime vers postmaster@ | accepté | accepté, -0.40 | + +## Pièges + +- **Un refus `not owned by user`** : quelqu'un envoie avec un alias sans + « Allow send as ». Cocher la case sur l'alias dans ISPConfig ; ne pas + décocher l'option globale. +- **Après une mise à jour d'ISPConfig**, vérifier que `users.conf` n'a pas + retrouvé `want_spam` : la copie dans `conf-custom/install/` doit l'éviter. +- **Les boîtes au SMTP désactivé et les redirections sans « Allow send as »** + (`abuse@`, `kernel@`, `cedric.a5l@`) ne sont pas couvertes par la + protection 2. Seuls DMARC et, pour `abuse@`, la liste noire les protègent. + +## Retour arrière + +```sh +sudo cp -a /etc/rspamd/local.d/users.conf.bak-20260929 /etc/rspamd/local.d/users.conf +sudo rm /usr/local/ispconfig/server/conf-custom/install/rspamd_users.conf.master +sudo systemctl reload rspamd +``` + +Les protections 1 et 2 s'annulent depuis l'interface ISPConfig.