Erste Produktivversion
This commit is contained in:
115
docs/mail-zustellbarkeit.md
Normal file
115
docs/mail-zustellbarkeit.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Zustellbarkeit ausgehender Mails
|
||||
|
||||
Die Anwendung setzt alle Kopfzeilen korrekt, aber ob eine Mail im Posteingang
|
||||
oder im Spam landet, entscheidet sich überwiegend im DNS und beim Relay – nicht
|
||||
im Anwendungscode. Diese Notiz trennt beides sauber.
|
||||
|
||||
## Was die Anwendung erledigt
|
||||
|
||||
| Kopfzeile | Warum |
|
||||
|---|---|
|
||||
| `Date` | Fehlt sie, werten praktisch alle Filter ab |
|
||||
| `Message-ID` | Mit der Domain aus `SMTP_ENVELOPE_FROM`, damit sie zur DKIM-Signatur passt |
|
||||
| `From` mit Anzeigename | RFC-konform kodiert über `email.headerregistry` |
|
||||
| `Reply-To` | Optional, wenn Antworten woanders hin sollen |
|
||||
| `Auto-Submitted: auto-generated` | RFC 3834 – unterdrückt Abwesenheitsschleifen |
|
||||
| `X-Auto-Response-Suppress: All` | Dasselbe für Exchange/Outlook |
|
||||
| EHLO-Hostname | Aus `SMTP_HELO_HOSTNAME` statt der Container-ID |
|
||||
| Envelope-From getrennt vom `From` | SPF wird gegen den Envelope geprüft |
|
||||
|
||||
Der Versand wiederholt sich bei temporären Fehlern mit wachsendem Abstand
|
||||
(2 s, 4 s, 8 s). Bei permanenter Ablehnung – Empfänger unbekannt, Absender
|
||||
abgelehnt, Authentifizierung fehlgeschlagen – bricht er sofort ab, statt das
|
||||
Relay weiter zu belasten.
|
||||
|
||||
## Was du im DNS einrichten musst
|
||||
|
||||
Ohne diese drei Einträge landen Mails auch bei perfektem Code im Spam. Alle
|
||||
beziehen sich auf die Domain aus `SMTP_ENVELOPE_FROM`.
|
||||
|
||||
**SPF** – erlaubt deinem Relay, für die Domain zu senden:
|
||||
|
||||
```
|
||||
example.de. IN TXT "v=spf1 mx a:mail.example.de -all"
|
||||
```
|
||||
|
||||
`-all` (hard fail) ist strenger als `~all` und wird von Empfängern besser
|
||||
bewertet. Setze es erst, wenn du sicher bist, dass alle legitimen Absender
|
||||
erfasst sind.
|
||||
|
||||
**DKIM** – signiert ausgehende Mails. Die Signatur erzeugt das Relay, nicht
|
||||
diese Anwendung. In Postfix über OpenDKIM oder rspamd, in mailcow ist es
|
||||
eingebaut. Der öffentliche Schlüssel gehört ins DNS:
|
||||
|
||||
```
|
||||
selector._domainkey.example.de. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBg..."
|
||||
```
|
||||
|
||||
Wichtig: Die signierende Domain (`d=` in der Signatur) muss zur Domain im
|
||||
`From`-Header passen, sonst schlägt die DMARC-Ausrichtung fehl.
|
||||
|
||||
**DMARC** – sagt Empfängern, was bei Fehlschlägen passieren soll:
|
||||
|
||||
```
|
||||
_dmarc.example.de. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.de; adkim=s; aspf=s"
|
||||
```
|
||||
|
||||
Fang mit `p=none` an und wertet die Reports ein paar Wochen aus, bevor du auf
|
||||
`quarantine` oder `reject` gehst.
|
||||
|
||||
**PTR (Reverse DNS)** – die IP deines Relays muss auf einen Namen auflösen, der
|
||||
vorwärts wieder auf dieselbe IP zeigt. Bei Hetzner setzt du das im
|
||||
Cloud-Console-Interface bzw. im Robot. Fehlt der PTR, weisen einige große
|
||||
Anbieter direkt beim `MAIL FROM` ab.
|
||||
|
||||
## Ausrichtung von From und Envelope-From
|
||||
|
||||
DMARC verlangt, dass mindestens eine der beiden Prüfungen *ausgerichtet* ist:
|
||||
|
||||
- **SPF-Ausrichtung:** Domain in `SMTP_ENVELOPE_FROM` = Domain in `SMTP_FROM`
|
||||
- **DKIM-Ausrichtung:** signierende Domain = Domain in `SMTP_FROM`
|
||||
|
||||
Mit `adkim=s; aspf=s` im DMARC-Record verlangst du exakte Übereinstimmung
|
||||
(nicht nur die Organisationsdomain). Das ist strenger und sicherer, bedeutet
|
||||
aber: `bounces@example.de` und `einkaufsapp@example.de` sind ausgerichtet,
|
||||
`bounces@bounce.example.de` wäre es nicht mehr.
|
||||
|
||||
## Testen
|
||||
|
||||
Vor dem ersten echten Versand:
|
||||
|
||||
```bash
|
||||
# Verbindung zum Relay prüfen, ohne etwas zu senden
|
||||
curl -s -b cookies.txt localhost:8000/api/admin/mail/check | jq
|
||||
|
||||
# Testnachricht auslösen
|
||||
CSRF=$(grep ea_csrf cookies.txt | awk '{print $7}')
|
||||
curl -s -b cookies.txt -H "X-CSRF-Token: $CSRF" \
|
||||
-X POST localhost:8000/api/admin/mail/test \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"to":"dein-postfach@example.de"}'
|
||||
|
||||
docker compose logs --tail=20 api
|
||||
```
|
||||
|
||||
Für eine unabhängige Bewertung eignet sich ein Dienst, der dir eine
|
||||
Wegwerf-Adresse gibt und die eingehende Mail auf SPF, DKIM, DMARC, PTR und
|
||||
Inhaltsmerkmale prüft – etwa `mail-tester.com`. Alles unterhalb von 8/10 lohnt
|
||||
das Nachbessern.
|
||||
|
||||
Achte in der Auswertung besonders auf:
|
||||
|
||||
- `SPF: pass` mit der Envelope-Domain
|
||||
- `DKIM: pass` mit `d=` gleich der From-Domain
|
||||
- `DMARC: pass`
|
||||
- kein Eintrag auf Spamhaus/Barracuda für deine Relay-IP
|
||||
|
||||
## Häufige Ursachen für Spam-Einstufung
|
||||
|
||||
| Symptom | Ursache |
|
||||
|---|---|
|
||||
| Landet bei Gmail im Spam, sonst nirgends | Fehlender oder falscher PTR-Eintrag |
|
||||
| DKIM `pass`, DMARC `fail` | Signierende Domain weicht von der From-Domain ab |
|
||||
| SPF `softfail` | Relay-IP nicht im SPF-Record der Envelope-Domain |
|
||||
| Alles `pass`, trotzdem Spam | Neue Domain ohne Sendereputation – das gibt sich nach einigen Wochen regelmäßigen Versands |
|
||||
| Outlook/Hotmail blockt | Microsoft verlangt oft eine Anmeldung beim SNDS-Programm |
|
||||
Reference in New Issue
Block a user