SPF, DKIM und DMARC

E-Mails sind nach wie vor das am weitesten verbreitete Angriffsziel im Internet. Phishing und Spam nutzen gefälschte Absenderadressen, um an Zugangsdaten oder Geld zu kommen. Damit empfangende Mailserver überprüfen können, ob eine E-Mail wirklich von der Domain stammt, für die sie sich ausgibt, gibt es drei etablierte Standards: SPF, DKIM und DMARC.

Was ist SPF?

SPF (Sender Policy Framework) ist ein DNS-basierter Mechanismus, mit dem der Inhaber einer Domain festlegt, welche Server (IP-Adressen oder Hostnamen) berechtigt sind, E-Mails im Namen dieser Domain zu versenden.

Ein SPF-Record liegt als TXT-Record direkt in der DNS-Zone:

example.org.  IN  TXT  "v=spf1 mx a include:_spf.provider.net -all"
  • v=spf1 – Version des Records
  • mx / a – die Server hinter den MX- bzw. A-Records dürfen senden
  • include: – fremde Infrastruktur (z.B. ein Newsletter-Provider) einbinden
  • -all – alles andere wird abgelehnt (Hard Fail), alternativ ~all (Soft Fail)

Wichtig: Pro Domain gibt es nur einen SPF-Record, und dieser darf maximal 10 DNS-Lookups enthalten.

Wie SPF im Detail funktioniert

Der empfangende Mailserver vergleicht die IP des absendenden Servers mit der Policy im Record. Jeder Mechanismus (a, mx, ip4:, ip6:, include: …) liefert dabei ein Treffer/Nicht-Treffer, und das Präfix entscheidet, was daraus folgt:

  • + (Pass) – Absender ist legitimiert (Standard, wenn kein Präfix gesetzt ist)
  • - (Hard Fail) – Absender ist ausdrücklich nicht legitimiert → Ablehnung
  • ~ (Soft Fail) – verdächtig, aber toleriert → typischerweise Spam-Verdacht
  • ? (Neutral) – keine Aussage

In der Praxis hat sich ~all während der Einführungsphase bewährt; für produktive Domains mit sauber dokumentierter Versandinfrastruktur ist -all das Ziel.

Die 10-Lookup-Grenze ist die häufigste Stolperfalle: include:, a, mx, ptr, exists und redirect= zählen jeweils als DNS-Abfrage. Verschachtelte include:s des Providers zählen ebenfalls mit. Wird das Limit überschritten, liefert die evaluation ein permerror – viele Empfänger behandeln das wie einen Fail. Abhilfe schafft das Kürzen auf wirklich genutzte Dienste oder „Flattening“ (Auflösen der Includes in IP-Listen) – letzteres bedeutet aber manuellen Pflegeaufwand, sobald sich Provider-IPs ändern.

Häufige Fehler, die man in der Wildnis sieht:

  • zwei SPF-TXT-Records in einer Zone → sofort permerror
  • vergessene include:s nach einem Providerwechsel → legitime Mails failen
  • SPF schützt nur den Envelope-Absender (RFC 5321.MailFrom), nicht den sichtbaren From:-Header – genau diese Lücke schließt erst DMARC

Was ist DKIM?

DKIM (DomainKeys Identified Mail) signiert jede ausgehende E-Mail kryptographisch. Der sendende Server legt eine Signatur in den Mailheader ab; der Empfänger holt den passenden öffentlichen Schlüssel aus dem DNS und prüft, ob die Mail auf dem Weg unverändert blieb.

Der öffentliche Schlüssel liegt als TXT-Record unter einem sogenannten Selector:

selector._domainkey.example.org.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBg..."

Den privaten Schlüssel und den Selector generiert und konfiguriert man im eigenen Mailsystem (z.B. Postfix mit OpenDKIM, Rspamd oder Exchange).

Selektoren, Schlüssellängen und Key-Rotation

Der Selector ist ein frei wählbarer Name, über den der Empfänger den passenden Public Key findet (selector1._domainkey.example.org). Mehrere Selektoren können parallel existieren – so betreibt man z.B. getrennte Schlüssel für Newsletter und Transaktionsmails oder rotiert Keys, ohne den Mailbetrieb anzuhalten: neuen Selector publishen, umschalten, alten nach einer Übergangszeit löschen.

Zur Schlüsselwahl:

  • RSA 2048 Bit ist heute der sinnvolle Standard; 1024 Bit gilt als veraltet
  • Ed25519 ist schlanker und schneller, wird aber nicht von jedem Empfänger unterstützt – im Zweifel RSA
  • Der TXT-Record darf inklusive Anführungszeichen keine 255 Zeichen pro String überschreiten; lange RSA-Keys werden deshalb in mehreren Strings hinterlegt (die meisten DNS-Oberflächen erledigen das automatisch)

Ein praktischer Nebeneffekt von DKIM: Die Signatur bleibt beim Weiterleiten (Forwarding) erhalten, während SPF am Weiterleitungsserver scheitert. Deshalb sollte DMARC so konfiguriert werden, dass DKIM-Alignment allein genügt, um eine Mail als legitim zu werten.

Was ist DMARC?

DMARC (Domain-based Message Authentication, Reporting and Conformance) verknüpft SPF und DKIM miteinander und fügt zwei Dinge hinzu:

  • eine Policy, was mit nicht authentifizierten Mails passieren soll
  • einen Report-Kanal, über den empfangende Mailserver dem Domain-Inhaber Berichte zustellen

Ein typischer DMARC-Record:

_dmarc.example.org.  IN  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@example.org; pct=100; adkim=r; aspf=r"

Die wichtigsten Felder:

  • p=none – nur überwachen (quarantine = Spam-Ordner, reject = ablehnen)
  • rua= – Mailbox, in der die aggregierten Reports (XML) landen
  • adkim / aspf – Alignment-Modus (relaxed oder strict)
  • pct – Prozentsatz der Mails, auf den die Policy angewendet wird

Alignment – das Herzstück von DMARC

Ein einzelnes SPF- oder DKIM-Pass reicht für DMARC nicht aus: Entscheidend ist das Alignment, also ob die authentifizierte Domain zum sichtbaren From:-Header passt. Beim relaxed Modus (adkim=r / aspf=r, der Default) genügt die Übereinstimmung der organisatorischen Domain – eine Mail mit From: newsletter@example.org, versendet über bounce.provider.net mit SPF auf provider.net, aligned nicht. Korrekt Beispiel: DKIM-Signatur von example.org und From: @example.org → Alignment vorhanden. Im strict Modus (s) muss die Domain exakt übereinstimmen, was bei Nutzung externer Dienstleister oft nicht erreichbar ist.

Zusätzlich relevant:

  • sp= – eigene Policy für Subdomains, falls diese strenger oder lockerer sein soll als die Hauptdomain
  • rua= liefert Aggregate Reports (Zusammenfassungen), ruf= die detaillierten Failure Reports – letztere unterstützen viele große Provider aus Datenschutzgründen gar nicht oder nur eingeschränkt
  • Zeigt rua= auf eine fremde Domain, verlangt der Empfänger dort einen Verifizierungs-Record (example.org._report._dmarc.…), um Report-Missbrauch zu verhindern

Die Reports werden als XML-Anhänge per E-Mail zugestellt – bei mehreren großen Providern kommen da schnell täglich Dutzende Mails zusammen. Genau hier setzt ein DMARC-Analyzer an.

Warum überhaupt selbst hosten?

Kommerzielle DMARC-Plattformen abrechnen meist pro Domain und Nachrichtenvolumen – teuer, wenn man viele Domains betreut. Mit DmarcAnalyzer (GitHub) gibt es eine freie Alternative (Apache-2.0):

DmarcAnalyzer Logo

  • Self-hosted, unbegrenzt viele Domains, keine Kosten pro Domain
  • Automatischer Abruf der Report-Mailbox, Parsen und Auswertung der XML-Berichte
  • Erkennung von Spoofing und nicht authentifizierten Absendern
  • Geführter Weg von p=none bis p=reject
  • Multi-Tenant-fähig (Clients/Mandanten), OIDC Single Sign-on möglich
DmarcAnalyzer Dashboard mit DMARC-Report-Übersicht über alle Domains

Installation mit Docker Compose

Voraussetzungen

Für die nächsten Schritte setzen wir ein Linux mit einem aktuell installierten Docker voraus – genau wie in unserem Vaultwarden-Artikel mit Traefik als Reverse Proxy, der automatisch Let’s Encrypt Zertifikate verwaltet.

DmarcAnalyzer besteht aus einem App-Container (ASP.NET Core + React, Konsole und Report-Abruf in einem Prozess) und einer PostgreSQL-Datenbank.

DNS-Vorbereitung

Zunächst braucht die Domain einen DMARC-Record, dessen rua= auf eine Mailbox zeigt, die der Analyzer später abrufen kann:

_dmarc.example.org.  IN  TXT  "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.org"

Außerdem muss der gewünschte Hostname (im Beispiel dmarc.example.org) per A/AAAA-Record auf den Server zeigen, damit Traefik das Zertifikat holen kann.

Docker compose für DmarcAnalyzer mit traefik für https

In diesem Beispiel betreiben wir DmarcAnalyzer hinter einem Reverse Proxy namens Traefik, der sich automatisch letsencrypt Zertifikate verwalten kann. Der Aufbau entspricht unserem Vaultwarden-Setup.

docker-compose.yaml:

services:
  traefik:
    container_name: traefik
    image: traefik:latest
    ports:
      - 80:8080
      - 443:8443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik-data:/letsencrypt
    networks:
      - default
    command:
      - --entrypoints.web.address=:8080
      - --entrypoints.websecure.address=:8443
      - --providers.docker
      - --log.level=ERROR
      - --certificatesresolvers.leresolver.acme.httpchallenge=true
      - --certificatesresolvers.leresolver.acme.email=info@example.org
      - --certificatesresolvers.leresolver.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web
    labels:
      - "traefik.http.routers.http-catchall.rule=hostregexp(`.+`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    restart: always
  dmarc-analyzer:
    container_name: dmarc-analyzer
    image: ghcr.io/dmarc-analyzer-net/dmarc-analyzer:latest
    restart: unless-stopped
    networks:
      - default
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      # Konsole und Report-Abruf in einem Prozess
      APP_MODE=all
      ConnectionStrings__Default=Host=postgres;Port=5432;Database=dmarc_analyzer;Username=dmarc;Password=${POSTGRES_PASSWORD}
      Database__MigrateOnStartup=true
      # Verschluesselt die Zugangsdaten der Report-Mailbox at rest,
      # siehe .env Datei weiter unten
      Security__CredentialEncryptionKey=${DMARC_ENCRYPTION_KEY}
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8080/api/v1/auth/setup > /dev/null"]
      interval: 10s
      timeout: 5s
      retries: 20
      start_period: 150s
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dmarc.rule=Host(`dmarc.example.org`)"
      - "traefik.http.routers.dmarc.entrypoints=websecure"
      - "traefik.http.routers.dmarc.tls=true"
      - "traefik.http.routers.dmarc.tls.certresolver=leresolver"
      - "traefik.http.routers.dmarc.service=dmarc"
      - "traefik.http.services.dmarc.loadbalancer.server.port=8080"
  postgres:
    container_name: dmarc-postgres
    image: postgres:18-alpine
    restart: unless-stopped
    networks:
      - default
    environment:
      POSTGRES_DB=dmarc_analyzer
      POSTGRES_USER=dmarc
      POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      PGDATA=/var/lib/postgresql/data
    volumes:
      - ./dmarc-pgdata:/var/lib/postgresql/data
networks:
  default:
    internal: false
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: fd00:1::/64
          gateway: fd00:1::1

Hinweise zum Setup:

  • Die Ports 8080/5432 werden bewusst nicht mehr nach außen veröffentlicht – der Zugriff läuft ausschließlich über Traefik.
  • Der interne Port der Konsole ist 8080, deshalb zeigt der Loadbalancer-Label darauf.
  • Der Healthcheck hat ein großzügiges start_period, weil beim ersten Start die Datenbankmigrationen durchlaufen werden.

Verschlüsselungskey und Passwörter

Neben der docker-compose.yaml wird eine .env Datei benötigt. Der Key verschlüsselt die Mailbox-Zugangsdaten in der Datenbank (AES-256-GCM). Er sollte sicher verwahrt werden – geht er verloren, müssen alle hinterlegten Mailbox-Zugänge neu eingegeben werden:

user@bar:~$ echo "DMARC_ENCRYPTION_KEY=$(openssl rand -base64 32)" >> .env
user@bar:~$ echo "POSTGRES_PASSWORD=$(openssl rand -base64 24)" >> .env

DmarcAnalyzer starten

Als Erstes legen wir einen Benutzer und passende Verzeichnisse an:

root@bar:~# adduser -g docker dmarc
root@bar:~# su - dmarc
dmarc@bar:~$ mkdir traefik-data
dmarc@bar:~$ mkdir dmarc-pgdata
dmarc@bar:~$

Jetzt werden die obige docker-compose.yaml unter /home/dmarc/docker-compose.yaml sowie die .env Datei im selben Verzeichnis abgelegt. Beide Dateien sollten entsprechend der lokalen Gegebenheiten angepasst werden (Hostnamen, ACME-E-Mail, Passwörter). Nun kann man die Container herunterladen und die Anwendung starten:

dmarc@bar:~$ docker compose pull 
dmarc@bar:~$ docker compose up -d
dmarc@bar:~$

Nachdem Traefik das Zertifikat geholt hat, steht die Konsole unter der konfigurierten URL zur Verfügung. Beim ersten Aufruf legt man das Admin-Konto an – danach ist die Registrierung gesperrt.

DmarcAnalyzer: Erster Aufruf, Anlegen des Admin-Kontos

Analyzer konfigurieren

Anschließend richtet man in der Weboberfläche ein:

  1. einen Client (Mandanten) anlegen
  2. die zu überwachende Domain hinzufügen
  3. als Report Source die Mailbox verbinden, in der die rua=-Reports ankommen (z.B. dmarc-reports@example.org)
DmarcAnalyzer: Client und zu überwachende Domain anlegen
DmarcAnalyzer: Report-Quelle einrichten, Mailbox für rua-Reports verbinden

Ab dann ruft ein Hintergrundprozess die Mailbox regelmäßig ab, entpackt die XML-Berichte und wertet SPF/DKIM-Alignment pro absendender Quelle aus.

Wenn die ersten Reports eingegangen sind, sieht man pro Domain, welche Quellen legitim senden und wo es hakt. Erst wenn alle legitimen Quellen sauber authentifizieren, sollte die Policy schrittweise verschärft werden:

DmarcAnalyzer Auswertung einer DMARC-Report-Quelle mit SPF- und DKIM-Alignment pro Absender
p=none           p=quarantine      p=reject

Mit pct= lässt sich die Verschärfung zusätzlich stufenlos einführen.

Optional: Anmeldung per OIDC (Single Sign-on)

DmarcAnalyzer kann Benutzer über einen beliebigen OpenID-Connect-Provider authentifizieren – die Projekt-Doku führt Guides für Zitadel, Keycloak, Authentik, Google und Microsoft Entra ID. Wichtig zu wissen: OIDC ersetzt nur das Login, nicht die Rechteverwaltung. Rollen und Client-Zuordnungen bleiben weiterhin in der Anwendung selbst; eine SSO-Sitzung ist intern identisch mit einer Passwort-Sitzung.

Eingeschaltet wird das Feature per Umgebungsvariablen, der Callback läuft unter /api/v1/auth/oidc/callback:

environment:
  Auth__Oidc__Enabled=true
  Auth__Oidc__Authority=https://id.example.org
  Auth__Oidc__ClientId=dmarc-analyzer
  Auth__Oidc__DisplayName=Example ID
  Auth__Oidc__AutoProvision=false

Sobald Auth__Oidc__Enabled aktiv ist, zeigt die Login-Seite zusätzlich einen „Sign in with …“-Button. Mit AutoProvision=false (empfohlenes Produktions-Setting) legt ein Admin Benutzer vorher an, ohne Passwort; beim ersten SSO-Login wird das Konto dann über die verifizierte E-Mail-Adresse verknüpft.

Fazit

SPF, DKIM und DMARC sind heute Pflichtprogramm für jede Domain, die E-Mail versendet. Mit DmarcAnalyzer bekommt man ein freies, selbst gehostetes Monitoring dazu, das ohne Lizenzkosten pro Domain auskommt und die Daten nicht an einen Cloud-Dienst übergibt. Kombiniert mit Docker und Traefik ist der Betrieb so einfach wie bei unseren anderen Self-Hosting-Stacks.

Sollten Sie Fragen zu SPF, DKIM und DMARC in Ihrer IT Umgebung haben oder Unterstützung benötigen, nehmen Sie bitte Kontakt zu uns auf.

Zuletzt geändert: 28. August 2026