Hands-on: de implementatie van SPF, DKIM en DMARC voor Postfix

Bewerkelijk, maar veilig en schaalbaar

Concept van e-mailberichten in een virtuele ruimte

In dit artikel bespreken we de installatie en configuratie van de internetbeveiligingsstandaarden SPF, DKIM en DMARC op de Postfix-mailserver. Deze 3 standaarden gaan phishing, spam, virussen en andere malware tegen door de afzender (een mailadres/domein), de verzender (een mailsysteem) en de authenticiteit (de inhoud) van een mailbericht te beveiligen. Voor onze setup gaan we uit van een CentOS 8.1 Linux-server met daarop een goed werkende standaard-installatie van Postfix, en een recht-toe-recht-aan zonefile voor het met DNSSEC beveiligde domein example.nl.

Deze set-up breiden we vervolgens uit met: • de publicatie van SPF-recordsde validatie van SPFde ondertekening met DKIMde publicatie van DKIM-recordsde validatie van DKIMde publicatie van DMARC-recordsde validatie van DMARC Waar relevant geven we verschillende mogelijkheden (waarbij we ons wel beperken tot de pakketten die kant-en-klaar in de repositories voor CentOS beschikbaar zijn) en bespreken we de afwegingen om tot een keuze te komen. Bovendien zijn de configuraties ingebed in uitleg over de werking van de standaarden. Zoals je al aan de lengte van dit artikel kunt zien, is deze set-up nogal bewerkelijk. Wat je ervoor terug krijgt is een goed dichtgetimmerd mailsysteem dat veilig en schaalbaar is, en voldoet aan de laatste internetstandaarden. Bovendien is mailbeveiliging op domeinniveau een praktische voorwaarde bij de transitie van IPv4 naar IPv6, waar je niet langer kunt filteren op IP-adres.

Inhoudsopgave

Hands-on: de implementatie van SPF, DKIM en DMARC in Exim Hands-on: de implementatie van DANE voor Postfix

Deel I – Inleiding

1.1 SPF, DKIM en DMARC

SPF, DKIM, en DMARC gaan phishing, spam, virussen en andere malware tegen door de afzender (een mailadres/domein), de verzender (een mailsysteem) en de authenticiteit (de inhoud) van een mailbericht te beveiligen. Daarvoor moet de verwerking van berichten door de server worden aangepast en moeten diverse nieuwe records in de DNS-informatie worden opgenomen. Hoewel ondertekening met DNSSEC voor deze toepassing niet strikt noodzakelijk is – dat wil zeggen verplicht volgens de standaarden – is dat wel een belangrijke toevoeging. Daarmee is het drietal SPF/DKIM/DMARC een van de eerste concrete toepassingen van de cryptografisch beveiligde infrastructuur die over het afgelopen decennium met DNSSEC is aangelegd. Hoewel deze 3 standaarden meestal gezamenlijk worden ingezet, hoeft dat niet perse. Je kunt ook alleen DKIM of SPF inzetten, of DMARC weglaten. Dat is ook de reden dat we deze standaarden hieronder onafhankelijk van elkaar bespreken en implementeren. De resulterende configuraties leveren ook steeds een zelfstandige oplossing op die prima op zichzelf kan staan.

1.2 Hoe en waar SPF, DKIM en DMARC ingrijpen

Basiskennis van het SMTP- en mailprotocol, alsook het UNIX operating system, is wel vereist. Naast het IP-adres waarmee hij verbinding maakt met de ontvangende mailserver, is dit de informatie die de aanbiedende mail server via de SMTP handshake overdraagt:

  • bij het 'EHLO'/'HELO'-commando: de hostname van de aanbiedende mailserver

  • bij het 'MAIL FROM'-commando: het afzender/return-adres van het bericht

  • bij het 'RCPT TO'-commando: de geadresseerde(n) voor wie het bericht bestemd is

  • in de 'From' header: de afzendernaam en -adres (de display name) zoals uiteindelijk getoond aan de ontvangende gebruiker

En dit is hoe onze 3 beveiligingsstandaarden deze informatie bevestigen (valideren):

  • SPF: de validatie van de 'EHLO'/'HELO' en 'MAIL FROM'-informatie (de mail envelope)

    • (optioneel) check de 'EHLO'/'HELO' hostname om te zien of het IP-adres daarvan inderdaad resolvet naar het IP-adres van de client, waarmee is bewezen dat de client (de aanbiedende mailserver) inderdaad is wie hij zegt te zijn

    • (verplicht) check het afzenderdomein van het 'MAIL FROM'-adres om te zien of het IP-adres van de client inderdaad geautoriseerd is om mail van dit domein aan te bieden, waarmee is bewezen dat de client (de aanbiedende mailserver) gerechtigd is om namens de afzender berichten door/uit te sturen

    • is er geen 'MAIL FROM'-informatie beschikbaar (typisch voor bounce-berichten), dan valt SPF terug van het afzenderdomein naar de 'EHLO'/'HELO' hostname

    • het (positieve) resultaat is een gevalideerde mail envelope

    • validatie doe je zo vroeg mogelijk in de mailpijplijn (op de MX-gateway), zodat de software bij een harde faal nog foutmeldingen terug kan geven via de lopende SMTP-verbinding

    • als de validatie van een bericht/envelope faalt, dan kan een verbinding/bericht gelijk geblokkeerd worden, of het bericht wordt verder doorgegeven met het negatieve resultaat in een 'Received-SPF' en/of 'Authentication-Results' header

  • DKIM: de ondertekening van uitgaande mailberichten en hun headers, en de validatie van de handtekening onder binnenkomende berichten

    • check of de digitale handtekening meegestuurd met het bericht (in de 'DKIM-Signature' header) klopt; gebruik daarvoor de publieke sleutel gepubliceerd in het signing domain dat ook in deze header staat aangegeven;

    • omdat het signing domain niet persé overeen hoeft te komen met het afzenderdomein in de 'MAIL FROM' of de 'From' header, doet DKIM in eerste instantie niet meer dan de verantwoordelijkheid van het signing domain bevestigen voor het in het verkeer brengen van een bericht

    • de ondertekening van uitgaande berichten doe je zo laat mogelijk in de verzendende mailpijplijn (op de laatste hop van jouw infrastructuur), zodat zo veel mogelijk headers onder de handtekening vallen; de validatie van de handtekening onder binnenkomende berichten doe je zo vroeg mogelijk in de ontvangende pijplijn (op de eerste hop van jouw infrastructuur), zodat nieuw toegevoegde of aangepaste headers het validatieproces zo min mogelijk verstoren

    • DKIM blokkeert zelf geen berichten; de uitkomst van de validatie wordt aan het bericht toegevoegd in een 'Authentication-Results' header en bijvoorbeeld meegenomen in de score van een spamfilter of in de DMARC-evaluatie

  • DMARC: de verwerking van de uitkomsten van de SPF- en/of DKIM-validatie op basis van een policy gepubliceerd door het afzenderdomein in de 'From' header

    • check of het afzenderdomein in de 'From' header ten eerste overeenkomt met het afzenderdomein in de envelope (voor SPF) en ten tweede met het signing domain (voor DKIM); met deze alignment-check wordt een koppeling gemaakt tussen de SPF/DKIM-gevalideerde afzenderdomeinen enerzijds en de informatie in de 'From' header (de display name) anderzijds

    • gebruik de policy zoals gepubliceerd in het afzenderdomein van de 'From' header om te beslissen wat te doen met binnengekomen mail-berichten waarvan zowel de SPF- als de DKIM-validatie (inclusief de alignment) negatief is: doorlaten, blokkeren of in quarantaine plaatsen

    • stuur desgevraagd (in de policy) geaggregeerde en failure-rapportages naar de beheerder van het afzenderdomein; deze berichten zijn gestandaardiseerd zodat ze automatisch kunnen worden verwerkt

1.3 Moderne internetstandaarden

Hieronder bespreken we achtereenvolgens de implementatie van SPF, DKIM en DMARC. In dit artikel doen we dat voor Postfix, na Exim de meest gebruikte mailserver. De configuratie van de Exim-server doorlopen we op vergelijkbare wijze in een ander artikel. Met de bespreking van deze 2 mailservers dekken we meer dan 90 procent van de huidige mailservermarkt. In dit artikel laten we zien hoe je DANE, een ander protocol dat voortbouwt op de DNSSEC-infrastructuur, gebruikt om de TLS-certificaten van je mail-gateway te borgen. Omdat SPF, DKIM en DMARC enerzijds en DANE voor mail anderzijds volledig onafhankelijk zijn van elkaar, maakt het niet uit welke van deze 2 je eerst implementeert. Wel is het gebruik van DNSSEC verplicht voor DANE, terwijl dit voor SPF, DKIM en DMARC alleen aanbevolen is. Op onze DNSSEC-pagina vind je hands-on beschrijvingen voor de implementatie van DNSSEC-ondertekening en -validatie voor Unbound, Infoblox, PowerDNS en BIND. Heb je deze 5 standaarden geïmplementeerd en draaien je systemen ook op IPv6, dan is dit alles bij elkaar genoeg voor een 100 procent score op Internet.nl. Met de inzet van deze moderne internet(beveiligings)standaarden zijn je mailsystemen helemaal up-to-date en voldoen ze aan de laatste eisen en inzichten zoals die onder andere door Forum Standaardisatie [1, 2] en het Nationaal Cyber Security Centrum (NCSC) [1, 2] opgesteld zijn. De Internet.nl-portal is sowieso een goede tool om te zien waar je je nu bevindt en om tijdens de implementatie van nieuwe internetstandaarden te checken of alles inderdaad werkt zoals bedoeld.

1.4 Gebruikte software

We zijn voor dit artikel uitgegaan van de CentOS Linux-distributie versie 8.1, de community-editie van Red Hat Enterprise Linux (RHEL). Daarop zijn we aan de slag gegaan met deze softwarepakketten:

Op moment van schrijven (juni 2020) zijn dit de laatste versies van de betreffende pakketten zoals die beschikbaar zijn in de standaard repositories van CentOS versie 8.1. De Amavis-new-, ClamAV-, OpenDKIM- en OpenDMARC-pakketten zijn afkomstig uit de EPEL repository. Deze Extra Packages for Enterprise Linux worden gecompileerd uit de Fedora-software, de development-distributie voor RHEL. Dat betekent dat je deze set-up op identieke wijze na kunt bouwen op RHEL of Fedora. Voor het enablen en installeren van de vereiste PowerTools en EPEL repositories, gebruik je achtereenvolgens deze commando's:

  dnf config-manager --set-enabled PowerTools
  dnf install epel-release
  dnf config-manager --set-enabled epel

Waar je in grotere productie-omgevingen ClamAV en SpamAssassin als zelfstandige daemons zou laten draaien, maken wij hier gebruik van de features (libraries) ingebakken in Amavis-new. Sommige van de zelfstandige daemons zou je in een kleinere omgeving ook lokaal op een UNIX domain socket kunnen draaien.

1.5 Autoritatief DNS

Uitgangspunt voor onze configuratie is om te beginnen een goed werkende autoritatieve DNS-server voor het domein example.nl. Daarvoor gebruiken we deze deze zonefile:

$TTL 1d  ; ttl is 1 day
@              IN    SOA      dns1.example.nl. dns.example.nl. (
                              2020050800  ; serial (date & version)
                              8h          ; refresh every 8 hours
                              20m         ; retry after 20 minutes
                              4w          ; expire after 4 weeks
                              20m         ; negative caching ttl is 20 minutes
                              )

; DNS name servers
               IN    NS       dns1.example.nl.  ; primary name server
               IN    NS       dns2.example.nl.  ; secondary name server

; SMTP mail gateways
               IN    MX       10 mx.example.nl.            ; MX gateway
               IN    MX       100 fallback-mx.example.nl.  ; fallback MX gateway

; hosts
               IN    A        192.0.2.162            ; server
               IN    AAAA     2001:db8:1192::2:162   ; server (IPv6)
www            IN    CNAME    example.nl.            ; WWW server
ftp            IN    CNAME    example.nl.            ; FTP server
mx             IN    A        192.0.2.106            ; mail gateway
mx             IN    AAAA     2001:db8:1192::2:106   ; mail gateway (IPv6)
mail           IN    A        192.0.2.107            ; mail server
mail           IN    AAAA     2001:db8:1192::2:107   ; mail server (IPv6)
imap           IN    A        192.0.2.108            ; IMAP server
imap           IN    AAAA     2001:db8:1192::2:108   ; IMAP server (IPv6)
smtp           IN    A        192.0.2.109            ; SMTP gateway
smtp           IN    AAAA     2001:db8:1192::2:109   ; SMTP gateway (IPv6)

; exterior hosts
dns1           IN    A        192.0.2.5              ; primary name server
dns1           IN    AAAA     2001:db8:1192::2:5     ; primary name server (IPv6)
dns2           IN    A        198.51.100.6           ; secondary name server
dns2           IN    AAAA     2001:db8:2198::16:6    ; secondary name server (IPv6)
fallback-mx    IN    A        198.51.100.106         ; fallback mail gateway
fallback-mx    IN    AAAA     2001:db8:2198::32:106  ; fallback mail gateway (IPv6)

1.6 Ondertekend DNS

Bovendien is deze zone ondertekend met DNSSEC, wat betekent dat elke Resource Record Set (RRset) voor dit domein van een digitale handtekening is voorzien. Daarmee kan de ontvanger van een DNS-antwoord de authenticiteit van die informatie valideren. De records met deze handtekeningen en de andere records voor DNSSEC staan in ons geval niet in de zonefile. BIND named genereert in een moderne configuratie namelijk zelf de ondertekende zone, waarvan de informatie vervolgens via de autoritatieve DNS-servers op Internet wordt gepubliceerd. In dit hands-on artikel beschrijven we de hele configuratie voor de geautomatiseerde ondertekening van je domeinen op BIND. En op onze DNSSEC-pagina vind je vergelijkbare hands-on artikelen voor de ondertekening en validatie op diverse andere DNS-servers en -resolvers. Zoals je in het vervolg zult zien, bestaat de configuratie van SPF en DMARC uit de toevoeging van extra records in de DNS-configuratie, en beperkte aanpassingen in de configuratie van je mailserver. Omdat DKIM zijn eigen digitale handtekeningen met uitgaande berichten meestuurt en deze valideert op binnenkomende berichten, zijn daarvoor wel ingrijpende veranderingen in je mailserver configuratie nodig.

Deel II – De configuratie van Postfix en Amavis

Voordat we in het diepe springen, geven we hier eerst nog meer achtergrondinformatie over de set-up van ons Postfix/Amavis-combo. Dat maakt het straks makkelijker om te begrijpen waar SPF, DKIM en DMARC precies aangrijpen. Bovendien zijn deze aanpassingen sowieso nodig om deze beveiligingsstandaarden daadwerkelijk te kunnen implementeren. Je zult zien dat de configuratie van SPF, DKIM en DMARC straks relatief eenvoudig is als dit alles eenmaal op zijn plaats ligt. Hieronder bespreken we de opbouw van onze configuratie in 2 stappen. Bij de bespreking van OpenDKIM bouwen we deze Postfix/Amavis-workflow verder uit.

2.1 De Postfix-configuratie

Om te beginnen geven we hieronder de opties die we hebben ingesteld/veranderd in de basisconfiguratie van Postfix (in de file '/etc/postfix/main.cf'). Voor de precieze betekenis verwijzen we je naar de bijbehorende man page.

  myhostname = mail.example.nl
  mydomain = example.nl
  inet_interfaces = all
  inet_protocols = all
  mydestination = $myhostname, localhost.$mydomain, localhost
  mynetworks_style = host
  alias_maps = hash:/etc/aliases
  alias_database = hash:/etc/aliases
  receive_override_options = no_address_mappings
  recipient_delimiter = +
  home_mailbox = Maildir/
  smtpd_banner = $myhostname ESMTP $mail_name
  local_destination_concurrency_limit = 2
  default_destination_concurrency_limit = 5
  tls_ssl_options = NO_COMPRESSION, NO_RENEGOTIATION

De optie 'NO_RENEGOTIATION' wordt pas ondersteund vanaf Postfix versie 3.4. Gebruik voor oudere versies de OpenSSL optie-waarde '0x40000000'.

2.2 De basis-workflow

In het diagram hieronder geven we de workflow zoals die is na deze basisconfiguratie van Postfix. Binnenkomende mailberichten worden aangenomen op de publieke (well-known) poort 25 (smtp), en na een check door SpamAssassin intern doorgegeven voor aflevering. Poort 587 (submission) is na authenticatie (via SASL) beschikbaar als SMTP-gateway voor het versturen van berichten door de eigen gebruikers.

2.3 Betere bescherming mail-gateways

Zoals je in onderstaande configuratie (in de file '/etc/postfix/master.cf') ziet, hebben we ook al een aantal opties aan de SMTP- en submission-services toegevoegd om de ingangen van onze mail-gateways beter te beschermen. Voor de publieke ingang op poort 25 gaat het om verschillende checks op de geldigheid van de informatie die tijdens de SMTP handshake door de verzendende server in de 'HELO'/'EHLO'-, 'MAIL FROM'- en 'RCPT TO'-commando's aan onze gateway wordt gegeven.

  smtp    inet    n    -    n    -    -    smtpd
      -o smtpd_recipient_restrictions=reject_invalid_helo_hostname,
         reject_non_fqdn_sender,reject_unknown_sender_domain,
         reject_non_fqdn_recipient,reject_unknown_recipient_domain,
         reject_unauth_destination,reject_rbl_client,zen.spamhaus.org,
         permit
      -o smtpd_helo_required=yes
      -o disable_vrfy_command=yes
  submission    inet    n    -    n    -    -    smtpd
      -o syslog_name=postfix/submission
      -o smtpd_tls_security_level=encrypt
      -o smtpd_sasl_auth_enable=yes
      -o smtpd_tls_auth_only=yes
      -o smtpd_reject_unlisted_recipient=no
      -o smtpd_client_restrictions=$mua_client_restrictions
      -o smtpd_helo_restrictions=$mua_helo_restrictions
      -o smtpd_sender_restrictions=$mua_sender_restrictions
      -o smtpd_recipient_restrictions=
      -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
      -o milter_macro_daemon_name=ORIGINATING
      -o disable_vrfy_command=yes

Daarmee zijn we gelijk bij de kern van de problemen met het basis (E)SMTP-protocol gekomen: de aanbiedende server kan bij de commando's 'EHLO'/'HELO' en 'MAIL FROM' (die samen met de informatie uit het 'RCPT TO'-commando de envelope van het mailbericht vormen) opgeven wat hij maar wil. Dat maakt dat het mailadres van de verzender (daarna in de 'Return-Path' header) en de hostname van de aanbieder (daarna in de 'Received' header) zo makkelijk te spoofen zijn.

2.4 Check IP-adres van aanbiedende mailserver

De enige harde informatie die we in dit stadium wel kunnen controleren is het IP-adres waarmee het aanbiedende systeem (de client) contact zoekt met onze server. Hier gebruiken we die informatie dan ook om te checken of deze aanbieder op de zwarte lijst van de DNSBL-dienst van het Spamhaus Project staat. Hieronder zul je zien dat we straks op datzelfde punt (gatekeeping) hetzelfde IP-adres gebruiken om te controleren of het aanbiedende systeem inderdaad (via de SPF-policy) geautoriseerd is om berichten te versturen vanaf het afzenderdomein zoals gebruikt in het 'MAIL FROM'-commando. Hetzelfde geldt voor de validatie van de DKIM-handtekeningen onder binnenkomende berichten: dat wil je zo vroeg mogelijk doen (op de eerste hop van jouw infrastructuur), voordat door je eigen systemen weer allerlei nieuwe headers aan een binnengekomen bericht zijn toegevoegd.

2.5 SMTP-gateway voor eigen gebruikers

Belangrijkste in de configuratie voor de submission-service op poort 587 is dat deze alleen toegankelijk is voor geauthentiseerde gebruikers en dat de toepassing van TLS-versleuteling op deze verbindingen verplicht is. Voor deze poort gebeurt dat overigens altijd via het STARTTLS-commando. Er was door IANA ooit wel een speciale TCP-poort (nummer 465) gereserveerd voor SMTP-over-SSL (SMTPS), maar die is nooit gestandaardiseerd en inmiddels ook weer toegewezen aan een heel andere service. Mailadressen bevatten immers geen aanduiding voor een al-dan-niet-beveiligd transport, en mailsystemen hebben geen mogelijkheid om direct met de afzender te interacteren, anders dan via een bounce. Vandaar dat we via nu DANE/STARTTLS het transport 'zo veel mogelijk' proberen te beveiligen. De configuratie van DANE voor mail bespreken we in dit artikel. Daarin behandelen we ook de mogelijkheden om de veiligheid van Postfix te verbeteren via de cryptografische instellingen voor TLS (hardening).

2.6 Amavis-new

Hieronder zie je hoe onze workflow voor mailberichten er uit ziet na de installatie en configuratie van Amavis-new. Dit mailfilter vormt de lijm (middleware) tussen de Postfix MTA (Message Transfer Agent) en hulpmiddelen als virusscanners (hier ClamAV), spamfilters (hier SpamAssassin) en andere filters.

Hoewel de meeste MTA's, waaronder ook Postfix, ingebouwde (native) ondersteuning hebben voor ClamAV en SpamAssassin, is het gebruik van een extern mailfilter sowieso aan te raden vanwege de flexibiliteit en schaalbaarheid. Amavis-new is bovendien perfect geschikt voor wat wij hier willen bewerkstelligen. We kunnen dit filter direct gebruiken voor de validatie van SPF, DKIM en DMARC, en voor de DKIM-ondertekening van uitgaande mailberichten.

2.7 De configuratie van Amavis

Hoewel CentOS ook een HowTo-document voor Amavis-new online heeft staan, is deze sterk verouderd. Hieronder zie je de opties zoals we die zelf in het configuratiebestand '/etc/amavisd/amavisd.conf' hebben ingesteld:

  $mydomain = 'example.nl';
  $enable_dkim_verification = 1;
  $enable_dkim_signing = 1;
  $sa_tag_level_deflt  = -999;
  virus_admin_maps => ["postmaster\@$mydomain"],
  spam_admin_maps  => ["postmaster\@$mydomain"],
  $virus_admin               = "postmaster\@$mydomain";
  $mailfrom_notify_admin     = "postmaster\@$mydomain";
  $mailfrom_notify_recip     = "postmaster\@$mydomain";
  $mailfrom_notify_spamadmin = "postmaster\@$mydomain";
  $recipient_delimiter = '+';
  $myhostname = 'mail.example.nl';
  $notify_method  = 'smtp:[127.0.0.1]:10025';
  $forward_method = 'smtp:[127.0.0.1]:10025';
  $final_spam_destiny        = D_DISCARD;
  $spam_quarantine_to = undef; # Do nothing with Spam

Merk op dat de DKIM-opties '$enable_dkim_verification' en '$enable_dkim_signing' standaard al aan staan. Zolang de bijbehorende cryptografische sleutels niet ook geconfigureerd zijn, zal Amavis echter geen DKIM-ondertekening (kunnen) doen. En zolang er geen mailberichten door Postfix naar de Amavis-service worden doorgestuurd, gebeurt er sowieso niets. Sommige van deze opties behandelen we verderop nog.

2.8 De koppeling van Postfix en Amavis

Om te zorgen dat binnengekomen mailberichten (op poort 25) na de initiële checks door Postfix inderdaad naar Amavis doorgestuurd worden, voegen we in het configuratiebestand '/etc/postfix/master.cf' deze regel toe aan de smtp/25-service:

    -o content_filter=smtp-amavis:[127.0.0.1]:10024

Omdat spamfiltering nu door Amavis wordt verzorgd, commentariëren we bovendien de 'spamassassin'-optie uit:

    # -o content_filter=spamassassin

In de configuratie van Amavis (in de file '/etc/amavisd/amavisd.conf') kun je zien dat deze standaard al geconfigureerd is om te luisteren naar poort 10024:

  $inet_socket_port = 10024;  # listen on this local TCP port(s)

In de configuratie hierboven hadden we al ingesteld dat berichten na verwerking weer teruggestuurd worden naar Postfix op poort 10025 (net als de notificatieberichten afkomstig van Amavis zelf):

  $notify_method  = 'smtp:[127.0.0.1]:10025';
  $forward_method = 'smtp:[127.0.0.1]:10025';

2.9 Van Amavis weer terug naar Postfix

Tenslotte maken we de cirkel rond door voor Postfix een (lokale) nieuwe service te definiëren voor poort 10025. Van daaruit worden binnenkomende berichten (daar afgeleverd door Amavis) dan zonder verdere checks door Postfix afgeleverd bij de ontvanger. Daarvoor voegen we de volgende servicedefinitie toe aan de file '/etc/postfix/master.cf'":

  # Amavis
  smtp-amavis        unix    -    -    n    -    2    smtp
      -o smtp_data_done_timeout=1200
      -o smtp_send_xforward_command=yes
      -o disable_dns_lookups=yes
      -o max_use=20
  127.0.0.1:10025    inet    n    -    n    -    -    smtpd
      -o content_filter=
      -o local_recipient_maps=
      -o relay_recipient_maps=
      -o smtpd_restriction_classes=
      -o smtpd_delay_reject=no
      -o smtpd_client_restrictions=permit_mynetworks,reject
      -o smtpd_helo_restrictions=
      -o smtpd_sender_restrictions=
      -o smtpd_recipient_restrictions=permit_mynetworks,reject
      -o smtpd_data_restrictions=reject_unauth_pipelining
      -o smtpd_end_of_data_restrictions=
      -o mynetworks=127.0.0.0/8
      -o smtpd_error_sleep_time=0
      -o smtpd_soft_error_limit=1001
      -o smtpd_hard_error_limit=1000
      -o smtpd_client_connection_count_limit=0
      -o smtpd_client_connection_rate_limit=0
     -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks,no_milters
      -o local_header_rewrite_clients=
      -o smtpd_milters=

Opstarten van Amavis doen we als volgt:

  systemctl start amavisd.service
  systemctl status amavisd.service
  systemctl enable amavisd.service

Met deze uitgebreide set-up als uitgangspunt zijn we nu klaar voor de daadwerkelijke implementatie van SPF, DKIM en DMARC.

Deel III – SPF

SPF, kort voor Sender Policy Framework, voorkomt dat MX-gateways mailberichten accepteren van ongeautoriseerde systemen. Daartoe wordt door de eigenaar van een maildomein een lijst van geldige adressen gepubliceerd via DNS. Dat zijn typisch de SMTP-gateways die eindgebruikers moeten instellen voor hun uitgaande berichten (via poort 587), maar bijvoorbeeld ook de adressen van een externe dienstverlener die namens een organisatie marketing-mail verstuurd. Ontvangende gateways kunnen deze lijst van adressen gebruiken om de verzender te controleren (valideren) voordat zij een bericht aannemen. Voor de zonefile voor example.nl zoals we die hierboven hebben gespecificeerd betekent dat dus dat we daar (ergens onder de origin '@') het volgende record toevoegen:

    IN    TXT    "v=spf1 mx a ip4:192.0.2.109 ip6:2001:db8:1192::/64"
                 " a:senders.example.nl a:senders.bizbooster.com -all"

Naast dit TXT-record kun je ook nog een specifiek SPF-record tegenkomen (met precies dezelfde inhoud). Dit is een erfenis uit de begintijd van SPF. Volgens de laatste standaard (RFC 7208) moet het SPF-record type echter helemaal niet meer gebruikt worden. Is er geen 'MAIL FROM'-informatie beschikbaar (typisch voor bounce-berichten), dan valt SPF terug van het afzenderdomein naar de 'EHLO'/'HELO' hostname. Die geef je daarom een eigen SPF-record als volgt:

  mail    IN    TXT    "v=spf1 a -all" ; SPF record for fallback to EHLO hostname

3.1 SPF-records

Kijken we naar de inhoud van het record zelf, dan zien we na de versie-aanduiding 'v=spf1' een typische SPF-policy: eerst een lijstje van systemen die wel geautoriseerd zijn om mail namens dit domein te versturen, gevolgd door een afsluitende '-all', wat betekent dat alle andere systemen dat niet mogen. Alternatief hier is een '~all' (een zogenaamde softfail), wat aangeeft dat berichten van niet-validerende systemen niet geblokkeerd moeten worden maar doorgestuurd voorzien van een vlaggetje (tag). We komen deze straks bij de bespreking van DMARC nog tegen. Waar alleen een 'a' of een 'mx' aangeeft dat de systemen zoals gespecificeerd in de betreffende records van hetzelfde domein geautoriseerd zijn, geeft een policy als 'a:senders.bizbooster.com' je de gelegenheid om de verdere invulling van de specifieke servers naar anderen te delegeren. Op die manier kun je bijvoorbeeld een marketing-dienstverlener autoriseren om namens jou (dat wil zeggen: ogenschijnlijk afkomstig vanaf jouw mail-domein) in bulk mailberichten naar je klanten te sturen. Nog een stap verder gaat de 'include:bizbooster.com' policy, waarbij de policy voor jouw domein uitgebreid wordt met de SPF-policy zoals die bij bizbooster.com gespecificeerd is. Een dergelijke set-up voorkomt dat een dienstverlener elke keer contact op moet nemen voor aanpassingen in je DNS-configuratie als hij de adressen van zijn mailinfrastructuur aanpast. Een overzicht van de beschikbare policy-elementen vind je hier, of bij de bron in RFC 7208. In die RFC kun je overigens ook lezen dat 'a' niet alleen duidt op de IPv4-adressen in de A-records maar ook op de IPv6-adressen in de AAAA-records.

3.2 SPF-validatie

De validatie van SPF moet zo vroeg mogelijk in de workflow plaatsvinden, dat wil zeggen direct op de SMTP-service (op poort 25) van Postfix (op de MX-gateway). Op die manier kunnen binnenkomende verbindingen van ongeautoriseerde systemen direct na de overdracht van de mail envelope worden gedropt, en kan daarbij nog een foutmelding worden teruggegeven in de lopende SMTP-sessie. In het diagram hieronder zie je waar we de SPF check aan de workflow zullen toevoegen.

3.3 De SPF Policy Server for Postfix

We maken daarvoor gebruik van de SPF Policy Server for Postfix (beschikbaar in de EPEL repository). Voor de installatie van deze software is alleen dit commando nodig:

  dnf install pypolicyd-spf

Het meegeleverde configuratiebestand '/etc/python-policyd-spf/policyd-spf.conf' is nogal basic, maar voor nu voldoende:

  debugLevel = 1
  TestOnly = 1

  HELO_reject = Fail
  Mail_From_reject = Fail

  PermError_reject = False
  TempError_Defer = False

  skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1

In de file '/usr/share/doc/pypolicyd-spf/policyd-spf.conf.commented' vind je een uitgebreide configuratie-template met commentaar. Maar als je meer wilt loont het wellicht ook de moeite om naar de SPF Engine te kijken, waarin de ontwikkeling van de SPF Policy Server door dezelfde auteur is voortgezet.

3.4 De SPF Policy Server toevoegen aan Postfix

Om binnenkomende berichten nu daadwerkelijk te laten valideren door de SPF Policy Server, moeten we deze eerst als nieuwe service toevoegen aan onze Postfix-configuratie (in '/etc/postfix/master.cf'):

  # SPF Policy Server for Postfix
  policyd-spf    unix    -    n    n    -    0
      spawn user=policyd-spf argv=/usr/libexec/postfix/policyd-spf

Omdat deze service niet onder de 'root'- of 'postfix'-gebruiker mag draaien, maken we hierbij ook een nieuwe gebruiker 'policyd-spf' aan:

  useradd -c "SPF Policy Server for Postfix" -d /etc/python-policyd-spf
      -s "/sbin/nologin" policyd-spf

Vervolgens voegen we het gebruik van deze nieuwe service toe aan de checks die bij de SMTP handshake op poort 25 worden uitgevoerd. Daartoe voegen we eerst deze optie toe aan de file '/etc/postfix/main.cf':

  # policyd-spf service
  policyd-spf_time_limit = 3600

En daarna deze optie aan de smtp/25-service in de file '/etc/postfix/master.cf':

    -o smtpd_relay_restrictions=check_policy_service,unix:private/policyd-spf,permit

3.5 De Postfix-configuratie

Let op: in de meeste handleidingen die je online kunt vinden voor de validatie van SPF op Postfix wordt aangeraden om deze opties aan de file '/etc/postfix/main.cf' toe te voegen:

  # policyd-spf service
  policyd-spf_time_limit = 3600

  smtpd_recipient_restrictions =
    reject_unauth_destination,
    check_policy_service unix:private/policyd-spf

Dit werkt echter niet voor onze set-up waarin ook Amavis in de workflow verwerkt is: we willen de optie 'smtpd_recipient_restrictions' niet op alle smtpd-gebaseerde services (waaronder ook submission/587 en 127.0.0.1:10025) toepassen, alleen op smtp/25. Los daarvan wordt deze optie langzaamaan vervangen door de optie 'smtpd_relay_restrictions'. Omdat die nieuwe optie pas in een later stadium van de SMTP handshake inhaakt, heeft smtpd op dat punt meer informatie over de mail envelope ter beschikking (om naar de log te kunnen schrijven).

Tip!

Gebruik het commando 'postconf (-d)' om de volledige (default) configuratie van Postfix op te vragen.

3.6 SPF-headers

Na de restart van Postfix kunnen we controleren of SPF inderdaad gevalideerd wordt door de headers van een binnengekomen mailbericht langs te lopen. En zie daar inderdaad de 'Received-SPF' header gelijk na het aanpakken van dit testbericht aangeboden door het systeem sender.afzenderdomein.nl. Daaruit blijkt dat dat systeem inderdaad geautoriseerd is (Pass) voor afzenderdomein.nl.

  systemctl restart postfix.service
  Return-Path: <tester@afzenderdomein.nl>
  X-Original-To: ontvanger@example.nl
  Delivered-To: ontvanger@mail.example.nl
  Received: from localhost (localhost [127.0.0.1])
          by mail.example.nl (Postfix) with ESMTP id A1A58280526E
          for <ontvanger@example.nl>; Tue, 12 May 2020 15:30:10 +0200 (CEST)
  X-Virus-Scanned: amavisd-new at example.nl
  Received: from mail.example.nl ([127.0.0.1])
          by localhost (mail.example.nl [127.0.0.1]) (amavisd-new, port 10024)
          with ESMTP id WRX9GtYYGLmm for <ontvanger@example.nl>;
          Tue, 12 May 2020 15:30:08 +0200 (CEST)
  Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=203.0.113.109;
          helo=sender.afzenderdomein.nl; envelope-from=tester@afzenderdomein.nl;
          receiver=<UNKNOWN> 
  Received: from sender.afzenderdomein.nl (sender.afzenderdomein.nl [203.0.113.109])
          by mail.example.nl (Postfix) with ESMTP id E02892800240
          for <ontvanger@example.nl>; Tue, 12 May 2020 15:30:07 +0200 (CEST)
  Date: 12 May 2020 15:30:06 +0200
  To: ontvanger@example.nl
  Subject: Test SPF validatie
  Message-ID: <bcfae90dce9c64d9c651570120470b02@sender.afzenderdomein.nl>
  Errors-To: tester@afzenderdomein.nl
  From: Tester <tester@afzenderdomein.nl>
  Reply-To: tester@afzenderdomein.nl

Om de signalering van niet-validerende mailberichten te testen kun je deze online tool voor het versturen van valse berichten gebruiken.

Let op: In februari 2024 werden wij geattendeerd op een probleem met de SPF-records van een grote Nederlandse access provider. Via het 'include'-statement wordt een lange lijst IP-adressen binnengehaald. De libspf2 library die door Exim gebruikt wordt, blijkt het lange TXT-record niet in zijn geheel te kunnen verwerken en de lijst IP-adressen/blokken na 64 stuks af te breken. De adressen/blokken die daarna volgen worden niet meer gebruikt bij de validatie. Dat betekent dat mail-berichten afkomstig van systemen in deze adresreeksen ten onrechte niet meer geaccepteerd worden. Het zou kunnen dat dit probleem ook speelt bij Postfix (gecombineerd met libspf2), maar dat hebben we niet uitgezocht.

Omdat dit soort fouten veel uitzoekwerk kost, willen we dit probleem hier in ieder geval noemen, zodat mail-beheerders niet opnieuw voor een raadsel komen te staan.

Voor DNS-beheerders betekent dit dat zij lange SPF-records het beste kunnen opsplitsen in afzonderlijke TXT-records met maximaal 64 IP-adressen/blokken. De betreffende provider is ook op de hoogte gesteld van de problemen die hun SPF-records opleveren.

Deel IV – DKIM

DKIM, kort voor DomainKeys Identified Mail, voorziet de body en header van elk uitgaand mailbericht van een digitale handtekening. De publieke sleutel wordt via DNS gepubliceerd, zodat een ontvangende MX-gateway de digitale handtekening van binnenkomende berichten kan verifiëren (valideren). Met het zetten van een DKIM-handtekening neemt de verzender (signing domain) verantwoordelijkheid voor het in het mail-verkeer brengen van een bericht. Heeft de verzender een goede reputatie, dan vergroot hij hiermee de zogenaamde 'deliverability' van het bericht. Oftewel: hij verkleint de kans dat een bericht als spam wordt aangemerkt. DKIM blokkeert zelf meestal dan ook geen berichten. De uitkomst van de validatie wordt meegenomen in de score van een spamfilter als SpamAssassin. DKIM is ook nadrukkelijk niet bedoeld om de authenticiteit, integriteit en vertrouwelijkheid van berichten te waarborgen. Dat moet op gebruikersniveau gebeuren met end-to-end encryptie. DKIM is gestandaardiseerd in RFC 6376, 8301 en 8463.

4.1 Inhaken van DKIM in de workflow

Voor onze set-up zijn er nu 2 manieren om DKIM in de workflow op te nemen. De eerste is door gebruik te maken van de DKIM-engine die is ingebakken in Amavis-new. En voor een beter schaalbare (modulaire) oplossing is er OpenDKIM, een milter dat ook beschikbaar is in de EPEL repository. We werken beide mogelijkheden hieronder uit. Voor welke optie je ook kiest, voor de DKIM-ondertekening van de uitgaande mailberichten van onze eigen gebruikers moeten we sowieso de workflow voor de submission/587-service uitbreiden. Onderstaand diagram laat zien hoe we op vergelijkbare wijze als we dat eerder voor de smtp/25-service deden een extra Amavis-gebaseerde service inlassen (nu op interne poort 10026). Die nieuwe service draagt dan zorg voor het scannen en filteren van uitgaande berichten, en daarna dus ook voor de DKIM-ondertekening daarvan.

4.2 Uitbreiding van de workflow

Voor deze herschikking van de workflow moeten we om te beginnen zorgen dat Amavis ook naar poort 10026 luistert. Dat doen we door in het configuratiebestand '/etc/amavisd/amavisd.conf' het volgende statement te enablen:

  #$inet_socket_port = 10024;  # listen on this local TCP port(s)
  $inet_socket_port = [10024,10026];  # listen on multiple TCP ports

Gelijk daaronder zie je dat voor poort 10026 de policy bank 'ORIGINATING' voor de verwerking van mailberichten afkomstig van eigen gebruikers al voorgedefinieerd is:

  # it is up to MTA to re-route mail from authenticated roaming users or
  # from internal hosts to a dedicated TCP port (such as 10026) for filtering
  $interface_policy{'10026'} = 'ORIGINATING';

  $policy_bank{'ORIGINATING'} = {  # mail supposedly originating from our users
    originating => 1,  # declare that mail was submitted by our smtp client
    allow_disclaimers => 1,  # enables disclaimer insertion if available
    # notify administrator of locally originating malware
    virus_admin_maps => ["virusalert\@$mydomain"],
    spam_admin_maps  => ["virusalert\@$mydomain"],
    warnbadhsender   => 1,
    # forward to a smtpd service providing DKIM signing service
    forward_method => 'smtp:[127.0.0.1]:10027',
    # force MTA conversion to 7-bit (e.g. before DKIM signing)
    smtpd_discard_ehlo_keywords => ['8BITMIME'],
    bypass_banned_checks_maps => [1],  # allow sending any file names and types
    terminate_dsn_on_notify_success => 0,  # don't remove NOTIFY=SUCCESS option
  };

We passen deze policy bank aan als volgt:

  # notify administrator of locally originating malware
  #virus_admin_maps => ["virusalert\@$mydomain"],
  #spam_admin_maps  => ["virusalert\@$mydomain"],
  virus_admin_maps => ["postmaster\@$mydomain"],
  spam_admin_maps  => ["postmaster\@$mydomain"],

  #forward_method => 'smtp:[127.0.0.1]:10027',
  forward_method => 'smtp:[127.0.0.1]:10025',

Het gaat dan vooral om die laatste wijziging. Omdat we voor de ondertekening in eerste instantie gebruik maken van de ingebouwde DKIM-engine, laten we Amavis de berichten van onze interne gebruikers na scannen, filteren en ondertekenen gelijk doorsturen naar de trusted Postfix-service op poort 10025. Om deze workflow voor interne berichten compleet te maken, moeten we nu zorgen dat berichten aangeleverd bij de submission/587-service van Postfix worden doorgestuurd naar de Amavis ORIGINATING-service op poort 10026. Dat doen we door in de file '/etc/postfix/master.cf' deze optie aan de submission/587-service toe te voegen:

    -o content_filter=smtp-amavis:[127.0.0.1]:10026

4.3 DKIM-ondertekening

Eerder zagen we al dat deze 2 opties voor respectievelijk de validatie en de ondertekening van DKIM standaard al aan staan in de configuratie van Amavis (in de file '/etc/amavisd/amavisd.conf'):

  $enable_dkim_verification = 1;
  $enable_dkim_signing = 1;

Er kan echter niet ondertekend worden zolang er geen cryptografische sleutels beschikbaar zijn om die handtekeningen ook daadwerkelijk te zetten. Dat die sleutels er in de uitgangssituatie nog niet zijn kunnen we eenvoudig checken met behulp van het volgende commando:

  amavisd -c /etc/amavisd/amavisd.conf showkeys

4.4 Genereren DKIM-sleutelpaar

We beginnen onze DKIM-configuratie dan ook met het genereren van een cryptografisch sleutelpaar als volgt:

  [root@system ~]# amavisd -c /etc/amavisd/amavisd.conf genrsa
       /etc/amavisd/example.nl.dkim20200512.pem 2048
  Private RSA key successfully written to file
       "/etc/amavisd/example.nl.dkim20200512.pem" (2048 bits, PEM format)

In de Amavis configuratie-directory '/etc/amavisd/' treffen we nu het sleutelbestand 'example.nl.dkim20200512.pem'. Deze bevat een sleutelpaar bestaande uit een private en een publieke sleutel (in PEM-formaat). De specifieke inhoud daarvan is hier minder van belang, maar zorg wel dat dit sleutelbestand de juiste (veilige) rechten krijgt:

  chmod 640 example.nl.dkim20200512.pem
  chown root:amavis example.nl.dkim20200512.pem

Hoewel je hier ook voor een grotere sleutellengte zou kunnen kiezen (4096 in plaats van 2048 bits), hebben DKIM-handtekeningen maar een beperkte geldigheidsduur (hieronder: 10 dagen). Ze worden immers alleen maar gebruikt voor de aflevering van berichten, waarvoor in het slechtste geval een paar dagen nodig is. Met een sleutellengte van 2048 bits kan het DNS-verkeer over het efficiënte UDP-protocol blijven lopen, in plaats dat opgeschakeld moet worden naar het zwaardere TCP-protocol. Zorg in ieder geval dat je het onveilige SHA-1 algoritme niet meer gebruikt (zoals uitgelegd in RFC 8301). RFC 8463 definieert het gebruik van ECDSA voor DKIM. De voordelen van dit het moderne cryptografische algoritme hebben we hier uitgelegd voor DNSSEC. We denken echter dat het op dit moment (juni 2020) nog te vroeg is om ECDSA voor DKIM te gebruiken. Onderteken je je mail met een nieuwe standaard die nog niet door alle DKIM-validerende MX-gateways herkend wordt, dan loop je het risico dat je berichten niet afgeleverd worden.

4.5 Toevoegen DKIM-sleutelpaar aan Amavis

We voegen dit nieuwe sleutelpaar nu toe aan Amavis door de volgende configuratie in de file '/etc/amavisd/amavisd.conf' op te nemen:

  dkim_key(
    'example.nl',
    'dkim20200512',
    '/etc/amavisd/example.nl.dkim20200512.pem'
    );
  @dkim_signature_options_bysender_maps = ( {
    "example.nl" => {
      d   => 'example.nl',
      a   => 'rsa-sha256',
      c   => 'relaxed/simple',
      ttl => 10*24*3600
      }
    } );

In het eerste gedeelte definiëren we het sleutelpaar zelf. In het tweede gedeelte specificeren we de details voor de digitale handtekeningen. Zoals je ziet gebruiken we hier het rsa-sha256 algoritme en hebben de handtekeningen een geldigheidsduur van 10 dagen (10*24*3600 seconden), ruim voldoende voor de aflevering van een mailbericht. In de 'DKIM-Signature' header zie je straks de begin- en eindtijd terug in respectievelijk de 't=' en 'x=' tags. Daarnaast is er nog de 'n=' tag om notities aan de handtekening toe te voegen. Het gebruik van de 'l=' tag (body length count) wordt afgeraden. Door de handtekening te beperken tot het eerste gedeelte van een bericht, blijft het resterende gedeelte kwetsbaar voor manipulatie.

4.6 Canonicalization

De optie "c => 'relaxed/simple'" specificeert de nauwkeurigheid waarmee respectievelijk de header- en body-secties van een bericht worden behandeld bij het genereren en controleren van de digitale handtekening. Omdat met name de spatiëring/afbreking in berichten onderweg wel eens veranderd wordt, kan dat problemen opleveren bij een strikte controle. Een belangrijke eigenschap van cryptografische hash-functies, essentieel onderdeel bij de ondertekening, is immers dat een enkel gewijzigd bit in het bericht leidt tot een compleet andere hash (een digitale samenvatting), en dus tot een ongeldige handtekening. Je kunt een ondertekend bericht robuuster maken tegen veranderingen tijdens het transport door de handtekening onafhankelijk te maken van de precieze spatiëring/afbreking. Dat doe je door de headers en de body eerst te converteren naar een standaard berichtenformaat (een normaalvorm), en dan pas de digitale handtekening te zetten. Als de ontvanger aan zijn kant precies hetzelfde doet, dan komt daar ook precies dezelfde hash uit. Ter vergelijking: als een programmeur wil checken of 2 woorden 'hetzelfde' zijn, dan converteert hij beide ook eerst naar kleine letters. Vanzelfsprekend kost deze omzetting (canonicalization) wel extra rekenkracht. Vandaar dat er in RFC 6376 2 verschillende conversieniveaus gedefinieerd zijn: 'relaxed' kost meer werk dan 'simple', maar levert vaker een goedgekeurde handtekening op. Vandaar dat we deze gebruiken voor de headers, en de minder arbeidsintensieve 'simple'-conversie voor de body van onze berichten. Het moge duidelijk zijn dat deze instelling met name relevant is voor organisaties die grote hoeveelheden mail verwerken. Om vergelijkbare redenen definieerden we eerder in de ORIGINATING policy bank van Amavis onderstaande optie. Door 8-bits karakters uit te filteren (en ons te beperken tot ASCII) verkleinen we de kans dat berichten onderweg naar de ontvangende mailserver verminkt worden.

  smtpd_discard_ehlo_keywords => ['8BITMIME'],

4.7 DKIM-ondertekening voor meerdere afzenderdomeinen

Wil je hetzelfde sleutelpaar gebruiken voor meer afzenderdomeinen die door deze DKIM-pijplijn gaan, dan gebruik je een configuratie als volgt:

  dkim_key(
    'example.nl',
    'dkim20200512',
    '/etc/amavisd/example.nl.dkim20200512.pem'
    );

@dkim_signature_options_bysender_maps = ( {
  "example.nl" => { d => 'example.nl', a => 'rsa-sha256', ttl => 10*24*3600 },
  "anotherexample.nl" => { d => 'example.nl', a => 'rsa-sha256', ttl => 10*24*3600 },
  } );

Of een catch-all zoals deze:

  @dkim_signature_options_bysender_maps = ( {
    "." => {
      d   => 'example.nl',
      a   => 'rsa-sha256',
      c   => 'relaxed/simple',
      ttl => 10*24*3600
      }
    } );

Wees je ervan bewust dat je hier één enkel signing domain gebruikt voor meerdere (verschillende) afzenderdomeinen. Omdat de validatie steeds plaatsvindt op basis van jouw signing domain ('d=example.nl;'), hoef je het bijbehorende DKIM-record ook alleen aan dat domein toe te voegen.

4.8 Elk afzenderdomein zijn eigen sleutelpaar

Wil je elk afzenderdomein zijn eigen sleutelpaar op zijn eigen signing domain geven, dan moet je ook elk domein van zijn eigen DKIM -ecord voorzien. De Amavis-configuratie ziet er dan als volgt uit:

  dkim_key(
    'example.nl',
    'dkim20200512',
    '/etc/amavisd/example.nl.dkim20200512.pem'
    );
  @dkim_signature_options_bysender_maps = ( {
    "example.nl" => {
      d   => 'example.nl',
      a   => 'rsa-sha256',
      c   => 'relaxed/simple',
      ttl => 10*24*3600
      }
    } );

  dkim_key(
    'anotherexample.nl',
    'dkim20200516',
    '/etc/amavisd/anotherexample.nl.dkim20200516.pem'
    );
  @dkim_signature_options_bysender_maps = ( {
    "anotherexample.nl" => {
      d   => 'anotherexample.nl',
      a   => 'rsa-sha256',
      c   => 'relaxed/simple',
      ttl => 10*24*3600
      }
    } );

Of een dergelijke configuratie ook direct een betere deliverability oplevert, durven we zo niet te zeggen. Uit onze bespreking van de oude DKIM-records hieronder blijkt wel dat men meerdere pogingen heeft gedaan om een sterkere koppeling te maken tussen afzenderdomein en signing domain. Wil je straks met DMARC ook het 'From'-afzenderdomein beveiligen, dan moet elk afzenderdomein sowieso ook zichzelf als signing domain ingesteld hebben.

4.9 Het DKIM-record

Na de restart van Amavis kunnen we de beschikbaarheid van ons nieuwe sleutelpaar verifiëren met de 'showkeys' opdracht:

  systemctl restart amavisd.service
  [root@system ~]# amavisd -c /etc/amavisd/amavisd.conf showkeys
  ; key#1 2048 bits, s=dkim20200512, d=example.nl,
      /etc/amavisd/example.nl.dkim20200512.pem
  dkim20200512._domainkey.example.nl.    3600 TXT (
    "v=DKIM1; p="
    "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt0RLit2O7Jp2QvDHqMZJ"
    "3ZS08pzLj2PUY3QS1LTeVW6dTJ6pKTF/FbW+bikNPnCCPPNsep1lBcwqBNBvin/m"
    "RqMlSThQ9l/EutGodrxmZWNzOfcYEeV8B9e2yby8yJUDiLSV5w36WFo/ad/CeLeZ"
    "Pfz5pFgje8thRNAcWfopXIhRGK6cg9DrX0wHFpmFS8zjGoqKtpaDK3aqAuzVGTz9"
    "H/at+lcZCY0dBB29EPaSWWZ2uvUHJMRJUZGvCHvyDOZUEf8HX+5VLdYAW2eQLeZ5"
    "waL3+xYnyYtiZKhauetP/KECAwEAAQ==")

En daarmee hebben we gelijk het DNS record te pakken dat we in onze zonefile (dat wil zeggen: die van het signing domain) moeten opnemen. Hierbij is het van belang te weten dat 'dkim20200512' aangeeft om welk sleutelpaar het hier gaat (de zogenaamde selector). We zullen zo zien dat deze selector ook onderdeel is van de 'DKIM-Signature' header, zodat de ontvangende mailserver bij validatie precies weet welke publieke sleutel hij moet opvragen in de DNS. Selectors geven je de gelegenheid om meerdere systemen DKIM-ondertekende berichten namens jouw domein te laten versturen, zonder dat je daarvoor onderling bestanden met private sleutels hoeft uit te wisselen. Elk verzendend mail-systeem kan lokaal zijn eigen DKIM-sleutelpaar genereren, als de publieke sleutel daarvan maar met een unieke selector in de DNS wordt gepubliceerd. Pas op dat je geen online tools gebruikt die met het DKIM-record ook een private en een publieke sleutel voor je genereren – en daar zijn er nogal veel van! Daarmee hebben zij immers ook jouw private sleutel in handen, wat betekent dat zij mail kunnen versturen die "door jou" ondertekend is.

4.10 Controleren van de ondertekening

Voordat we bovenstaand DKIM-record in onze zonefile kunnen opnemen, moeten we eerst verifiëren dat onze uitgaande mail-berichten (op de SMTP-gateway binnenkomend via poort 587) nu inderdaad van een geldige handtekening worden voorzien (anders worden onze berichten immers geblokkeerd door validerende MX-gateways). Als eerste testen we de Amavis engine zelf (die daarvoor een intern bericht ondertekent en valideert):

  [root@system ~]# amavisd -c /etc/amavisd/amavisd.conf testkeys
  TESTING#1 example.nl: dkim20200512._domainkey.example.nl => pass

Vervolgens sturen we een mailbericht vanaf ons eigen domein door deze SMTP-gateway om te controleren of er nu inderdaad een digitale handtekening aan de headers wordt toegevoegd. En zoals je hieronder ziet is dat inderdaad het geval:

  Return-Path: <gebruiker@example.nl>
  X-Original-To: ontvanger@externdomein.nl
  Delivered-To: ontvanger@mail.externdomein.nl
  Received: from localhost (localhost [127.0.0.1])
          by mail.externdomein.nl (Postfix) with ESMTP id 171E9280526E
          for <ontvanger@externdomein.nl>; Tue, 12 May 2020 20:43:56 +0200 (CEST)
  X-Virus-Scanned: amavisd-new at externdomein.nl
  Authentication-Results: mail.example.nl (amavisd-new);
          dkim=pass (2048-bit key) header.d=example.nl
  Received: from mail.externdomein.nl ([127.0.0.1])
          by localhost (mail.externdomein.nl [127.0.0.1]) (amavisd-new, port 10024)
          with ESMTP id XkXT1jIpaLjd for <ontvanger@externdomein.nl>;
          Tue, 12 May 2020 20:43:53 +0200 (CEST)
  Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=192.0.2.107;
          helo=mail.example.nl; envelope-from=gebruiker@example.nl;
          receiver=<UNKNOWN> 
  Received: from mail.example.nl (mail.example.nl [192.0.2.107])
          by mail.externdomein.nl (Postfix) with ESMTPS id E5B952801211
          for <ontvanger@externdomein.nl>; Tue, 12 May 2020 20:43:53 +0200 (CEST)
  Received: from localhost (localhost [127.0.0.1])
          by mail.example.nl (Postfix) with ESMTP id 084AB280526E;
          Tue, 12 May 2020 20:43:48 +0200 (CEST)
  DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.nl; h=
          content-type:content-type:mime-version:user-agent:date:date
          :message-id:organization:subject:subject:from:from:reply-to
          :received:received; s=dkim20200512; t=1589309025; x=1590173026;
          bh=5hbcFvUE8pp3nwVNxbu97zCG2Fh58AtJXA0TwmQaF6k=; b=fG620CxCwUwI
          jBaTebuq6CIXem9MvRWa9O6FAb/3mei24EZEdPxDJErnzSBfUq6VIsVQRPze/fVp
          DmsJ5BueHCJF1jVoKv3FkQ+UU9yKhZtGc3uQJbYQmF/gJ+nQ1VVpTbRKAngbWD4A
          tu3Gev/P57mUPDPAen1kaEDW2P74Q3wf4asDEaKBVpIKKE+9MybEKvHZ9+Y7pbFk
          aGnVQkb0sWmhR6Szfy6v6rXuaPxQ/fowBcYTLbF4gHcjo26hk8CkvO6BYGBBzbC1
          wM50BWCDzHKbgM/fYy6KHzocSBmzRpU=
  Received: from mail.example.nl ([127.0.0.1])
          by localhost (mail.example.nl [127.0.0.1]) (amavisd-new, port 10026)
          with ESMTP id qvnhJRFJoqHJ; Tue, 12 May 2020 20:43:45 +0200 (CEST)
  Received: from gebruiker.example.nl (gebruiker.example.nl [192.0.2.129])
          by mail.example.nl (Postfix) with ESMTPSA id 430192801211;
          Tue, 12 May 2020 20:43:45 +0200 (CEST)
  Reply-To: gebruiker@example.nl
  To: ontvanger@externdomein.nl
  From: "Gebruiker" <gebruiker@example.nl>
  Subject: Test DKIM ondertekening
  Message-ID: <f0eca619-a8ab-86d4-42e5-5b39f61b813e@example.nl>
  Date: Tue, 12 May 2020 20:43:44 +0200
  Envelope-To: ontvanger@externdomein.nl

4.11 Controleren van het DKIM-record

In de 'DKIM-Signature' header zie je zowel het signing domain ('d=example.nl;') als de selector ('s=dkim20200512;') staan. Tezamen is dat voor de DKIM-engine aan de ontvangende zijde (hier: mail.externdomein.nl) voldoende om bij onze DNSserver het bijbehorende DKIM-record op te vragen. We kunnen de aanwezigheid van dat record zelf ook controleren als volgt:

  [user@system ~]# dig TXT dkim20200512._domainkey.example.nl.

  ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> TXT
      dkim20200512._domainkey.example.nl.
  ;; global options: +cmd
  ;; Got answer:
  ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19884
  ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 5

  ;; OPT PSEUDOSECTION:
  ; EDNS: version: 0, flags:; udp: 4096
  ; COOKIE: e9836ed80afd3232b4a3a8db5ebafe4bd80d43ef7ae55689 (good)
  ;; QUESTION SECTION:
  ;dkim20200512._domainkey.example.nl. IN TXT

  ;; ANSWER SECTION:
  dkim20200512._domainkey.example.nl. 86400 IN TXT "v=DKIM1; p="
      "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt0RLit2O7Jp2QvDHqMZJ"
      "3ZS08pzLj2PUY3QS1LTeVW6dTJ6pKTF/FbW+bikNPnCCPPNsep1lBcwqBNBvin/m"
      "RqMlSThQ9l/EutGodrxmZWNzOfcYEeV8B9e2yby8yJUDiLSV5w36WFo/ad/CeLeZ"
      "c146A8FOWBCvyIh5PI5j2YyRvpzeUTdMLSKcewOPm9xJnHqGkIIbje0xTiSa1WSX"
      "H/at+lcZCY0dBB29EPaSWWZ2uvUHJMRJUZGvCHvyDOZUEf8HX+5VLdYAW2eQLeZ5"
      "waL3+xYnyYtiZKhauetP/KECAwEAAQ=="

  ;; AUTHORITY SECTION:
  example.nl.         86400    IN    NS      dns1.example.nl.
  example.nl.         86400    IN    NS      dns2.example.nl.

  ;; ADDITIONAL SECTION:
  dns1.example.nl.    86400    IN    A       192.0.2.5
  dns2.example.nl.    86400    IN    A       198.51.100.6
  dns1.example.nl.    86400    IN    AAAA    2001:db8:2198::16:6
  dns2.example.nl.    86400    IN    AAAA    2001:db8:2198::32:106

  ;; Query time: 0 msec
  ;; SERVER: 127.0.0.1#53(127.0.0.1)
  ;; WHEN: Tue May 12 21:51:39 CEST 2020
  ;; MSG SIZE  rcvd: 1054

De publieke sleutel en de overige informatie in dit record worden vervolgens door de ontvangende mailserver gebruikt om de digitale handtekening in de 'DKIM-Signature' header te valideren. Daarmee is het 'eigendom' van dit bericht (dat wil zeggen: de inhoud en de herkomst van het gegeven signing domain example.nl) voor de ontvangende server aangetoond. Alleen de eigenaar van dit DKIM-sleutelpaar kan immers de publieke sleutel in het signing domain publiceren. En alleen hij heeft de private sleutel waarmee de handtekening onder dit bericht is gezet.

4.12 DKIM-sleutelpaar roll-over

Het moge duidelijk zijn dat wanneer je je DKIM-sleutelpaar verandert, ook de publieke sleutel in het DKIM record aangepast moet worden. Dat betekent dat je bij de omwisseling een kleine roll-over zult moeten doen: Het nieuwe DKIM record moet (naast het oude record) gepubliceerd worden voordat het sleutelpaar wordt vervangen. Pas na het verlopen van de TTL, als de oude DKIM-informatie overal uit alle DNS caches is verdwenen, mag het nieuwe sleutelpaar in de server geactiveerd worden. Doe je dat eerder, dan loop je het risico dat validerende mail servers op basis van de oude informatie de DKIM-handtekening gebaseerd op het nieuwe sleutelpaar (nog) niet accepteren. Gebruik je voor je nieuwe sleutelpaar ook een nieuwe selector, dan hoef je niet te wachten op het leeglopen van alle DNS caches voordat je het nieuwe sleutelpaar in gebruik kunt nemen. Voor de nieuwe selector is immers nog geen DNS-informatie gecached. Na ingebruikname van het nieuwe sleutelpaar mag je het oude DKIM record niet gelijk verwijderen. Mail-berichten kunnen immers een paar dagen onderweg zijn voordat ze uiteindelijk afgeleverd worden (of bouncen). Ons advies is om het oude DKIM record nog minstens een week te laten staan.

4.13 DKIM-validatie

Wat rest in deze DKIM-configuratie is onze eigen validatie van de DKIM-handtekeningen die andere mailservers op hun beurt onder de berichten zetten die ze naar onze gebruikers toe sturen. Zoals je hierboven hebt gezien, staat de Amavis-optie '$enable_dkim_verification' daarvoor standaard al aan. In de log file '/var/log/maillog' kunnen we ook zien dat DKIM inderdaad gevalideerd wordt:

  May 13 07:56:43 mail amavis[18463]: (18463-11) Passed CLEAN {RelayedOpenRelay},
      [203.0.113.109]:42490 [203.0.113.109:1000]
      <tester@afzenderdomein.nl> -> <ontvanger@example.nl>,
      Queue-ID: 21675280060A, Message-ID: <87mu6cpecr.fsf@afzenderdomein.nl>,
      mail_id: fBM7lc39ild4, Hits: -2.102, size: 1963, queued_as: 56E53280526E,
      dkim_sd=dkim20191104:afzenderdomein.nl, 1159 ms

4.14 DKIM-validatie headers

Amavis maakt voor zijn DKIM-validatie gebruik van de DKIM plugin meegeleverd met SpamAssassin. Om die reden krijgen we nu alleen een validatie-header te zien als een bericht een verhoogde spam score heeft. Om de resultaten van de validatie (en alle andere door SpamAssassin uitgevoerde scans) altijd aan de binnenkomende mailberichten te laten toevoegen, moeten we deze threshold verlagen (in de file '/etc/amavisd/amavisd.conf'):

  $sa_tag_level_deflt  = -999;

Hieronder zie je hoe nu onder andere ook 'X-Spam-Status' en 'Authentication-Results' als headers zijn toegevoegd:

  Return-Path: <tester@afzenderdomein.nl>
  X-Original-To: ontvanger@example.nl
  Delivered-To: ontvanger@mail.example.nl
  Received: from localhost (localhost [127.0.0.1])
          by mail.example.nl (Postfix) with ESMTP id A1A58280526E
          for <ontvanger@example.nl>; Tue, 12 May 2020 15:30:10 +0200 (CEST)
  X-Virus-Scanned: amavisd-new at example.nl
  X-Spam-Flag: NO
  X-Spam-Score: -1.65
  X-Spam-Level:
  X-Spam-Status: No, score=-1.65 tagged_above=-999 required=6.2
          tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1,
          HEADER_FROM_DIFFERENT_DOMAINS=0.25, SPF_HELO_NONE=0.001,
          SPF_PASS=-0.001] autolearn=no autolearn_force=no
  Authentication-Results: mail.example.nl (amavisd-new);
          dkim=pass (2048-bit key) header.d=example.nl
  Received: from mail.example.nl ([127.0.0.1])
          by localhost (mail.example.nl [127.0.0.1]) (amavisd-new, port 10024)
          with ESMTP id WRX9GtYYGLmm for <ontvanger@example.nl>;
          Tue, 12 May 2020 15:30:08 +0200 (CEST)
  Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=203.0.113.109;
          helo=sender.afzenderdomein.nl; envelope-from=tester@afzenderdomein.nl;
          receiver=<UNKNOWN> 
  Received: from sender.afzenderdomein.nl (sender.afzenderdomein.nl [203.0.113.109])
          by mail.example.nl (Postfix) with ESMTP id E02892800240
          for <ontvanger@example.nl>; Tue, 12 May 2020 15:30:07 +0200 (CEST)
  Date: 12 May 2020 15:30:06 +0200
  To: ontvanger@example.nl
  Subject: Test DKIM validatie
  Message-ID: <bcfae90dce9c64d9c651570120470b02@sender.example.nl>
  Errors-To: tester@afzenderdomein.nl
  From: Tester <tester@afzenderdomein.nl>
  Reply-To: tester@afzenderdomein.nl

Zoals je ziet doet SpamAssassin zelf ook SPF-validatie als onderdeel van zijn evaluatie. 'Received-SPF' en 'Authentication-Results' behoren (net als 'DKIM-Signature' en 'Received') tot de zogenaamde trace fields. Dat betekent dat deze headers tijdens het transport van een mailbericht (over SMTP) stapsgewijs aan de bovenkant aan de headerlijst worden toegevoegd. De inhoud ervan kan door daaropvolgende hops weer gebruikt worden. Als je een eerder toegevoegde SPF-evaluatie vertrouwt (en dat zal vooral afhangen van de tussenliggende hops), dan kun je die informatie hergebruiken. Volgens de auteur van Amavis-new valt de schade door dubbele checks echter mee, omdat de bijbehorende DNS-queries toch gecached worden. Maar verwerk je veel mail, dan loont het de moeite om precies uit te zoeken welke hook precies wat doet en welke trace fields veilig hergebruikt (kunnen) worden.

4.15 OpenDKIM

Voor een meer schaalbare mailinfrastructuur zal je niet Amavis gebruiken voor de DKIM-ondertekening en -validatie, maar OpenDKIM (of een andere DKIM-provider) inzetten. De OpenDKIM milter is beschikbaar in de EPEL repository en installeren we als volgt:

  dnf install opendkim

Als eerste genereren we een sleutelpaar:

  cd /etc/opendkim/keys/
  opendkim-genkey -b 2048 -h sha256 -s dkim20200516 -d example.nl

Dat levert 2 bestanden op. De file 'dkim20200516.private' bevat de private key. Hiermee zorgen we dat die ook leesbaar is voor OpenDKIM zelf:

  chmod 640 dkim20200516.private
  chown root:opendkim dkim20200516.private

De file 'dkim20200516.txt' bevat het DKIM-record dat we gelijk aan onze zonefile toevoegen:

  dkim20200516._domainkey    IN    TXT    ( "v=DKIM1; h=sha256; k=rsa; "
      "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAprCCZ2vGOOH5kAVhuSfXlyx4We"
      "wtyGfiIZ/ayGuwM6HhjIg0h/imJFBOZgLeoNGLGYIG/ONw/xzZiT2NflukXrqD03DZoZ5ojI"
      "iAsYVPExcrf0EkDf9G1Lhjyw2Pypo+4m0DUGWSP0w8Ex0is2yTJdaWdKfRSvhKXlt1K20UhA"
      "Dw8X50P3DqAimWA3GrvUTmEJfLZZ4pdX0pVrOerknM4xQjHr7cOr4OU4yrSGlXu/dfwBGD4X"
      "sGrcQI2aRPdJJdbuo2kg8HXND+ObpqMHG0yNXDg472LnRKOgNN7Nj02wy+FeFybd9C31PwhE"
      "3wQUZBu6qNK87nMcHBy/VSMBuK8wIDAQAB" )
      ; ----- DKIM key dkim20200516 for example.nl

4.16 De configuratie van OpenDKIM

Vervolgens passen we het configuratiebestand '/etc/opendkim.conf' aan als volgt:

  Mode    sv
  Socket  inet:8891@localhost
  Statistics    /var/spool/opendkim/stats.dat
  SendReports   yes
  ReportAddress     "Example.nl Postmaster" <postmaster@example.nl>
  SoftwareHeader    yes
  Canonicalization  relaxed/simple
  Domain    example.nl
  Selector  dkim20200516
  KeyFile   /etc/opendkim/keys/dkim20200516.private
  OversignHeaders   From

Uit de configuratie van OpenDKIM blijkt al dat deze voor grotere installaties bedoeld is: je kunt makkelijk aparte OpenDKIM-servers naast elkaar inzetten voor ondertekenen en valideren, en de standaard-configuratie gaat uit van één enkel afzenderdomein. Wil je OpenDKIM toch gebruiken voor de ondertekening van meerdere afzenderdomeinen, dan voeg je deze 2 opties nog toe aan de file '/etc/opendkim.conf':

  KeyTable      /etc/opendkim/KeyTable
  SigningTable  refile:/etc/opendkim/SigningTable

Vervolgens genereer je voor elk signing domain een eigen sleutelset – op vergelijkbare wijze als we hierboven hebben gedaan – en voeg je deze toe aan het bestand '/etc/opendkim/KeyTable'. In de file '/etc/opendkim/SigningTable' stel je per afzenderdomein de details voor de ondertekening in. Na het opstarten van de OpenDKIM-service, is deze op poort 8891 beschikbaar.

  systemctl start opendkim.service
  systemctl status opendkim.service
  systemctl enable opendkim.service

Meer informatie over de configuratie van OpenDKIM vind je in de man page ('man 5 opendkim.conf'). En de originele configuratie-template vind je in de file '/usr/share/doc/opendkim/opendkim.conf.sample'.

4.17 Validatie met OpenDKIM

Om de nieuwe OpenDKIM-service in te zetten voor de validatie van binnenkomende mailberichten moeten we deze nog toevoegen aan de Postfix smtp/25-service. Daarvoor voegen we eerst deze optie toe aan de file '/etc/postfix/main.cf':

  # OpenDKIM & OpenDMARC milters
  milter_default_action = accept

En vervolgens deze optie aan de configuratie van de smtp/25-service (in de file '/etc/postfix/master.cf'):

    -o smtpd_milters=inet:127.0.0.1:8891

Maar voordat we Postfix herstarten, zetten we eerst nog de eerder geconfigureerde DKIM-validatie door Amavis uit. Daarvoor passen we deze optie aan (in de file '/etc/amavisd/amavisd.conf'):

  $enable_dkim_verification = 0;  # disable DKIM signatures verification

Na de restart van Postfix, hebben we dan een set-up draaien volgens onderstaande workflow. De DKIM-validatie is verplaatst van Amavis naar het smtpd-onderdeel van Postfix op poort 25. Dit levert ook een betere oplossing op. Door de validatie naar voren te halen in de pijplijn, verkleinen we de kans dat nieuw toegevoegde (trace) headers invloed hebben op de digitale handtekening.

4.18 Ondertekenen met OpenDKIM

Wil je de OpenDKIM-service ook gebruiken om te ondertekenen, dan zetten we ook eerst die optie voor Amavis uit (in de file '/etc/amavisd/amavisd.conf'):

  $enable_dkim_signing = 0;  # disable DKIM signing

Het is verleidelijk om dezelfde milter-optie als hierboven ook aan de Postfix submission/587-service toe te voegen (in de file '/etc/postfix/master.cf'). Dat zou dan een workflow opleveren zoals weergegeven in onderstaand diagram. De DKIM-ondertekening is nu verplaatst van Amavis ORIGINATING naar het smtpd-onderdeel van Postfix op poort 587.

Deze set-up levert echter een oplossing van mindere kwaliteit op. Zoals eerder uitgelegd moet de ondertekening juist zo laat mogelijk in de pijplijn (op de laatste hop van jouw infrastructuur) plaatsvinden. Op die manier worden zo veel mogelijk headers (ook die toegevoegd door Amavis ORIGINATING) onder de handtekening meegenomen en vergroten we de kans op een goede overkomst van ons ondertekende bericht.

4.19 Een aparte OpenDKIM-service voor ondertekening

We zouden de hook naar de ondertekening door de OpenDKIM-service dus liever aan het eind van de pijplijn toevoegen. In het originele configuratiebestand '/etc/amavisd/amavisd.conf' zie je ook dat daar al een klein voorschot genomen werd op een dergelijke set-up. Daarbij zou uitgaand verkeer van de Amavis ORIGINATING-service niet teruggeleid worden naar poort 10025 voor aflevering, maar verder doorgestuurd worden naar poort 10027 voor DKIM-ondertekening. Afhankelijk van de implementatie kan de OpenDKIM-service dan zelf zorgdragen voor de finale aflevering (zoals in de eerste workflow hieronder), of kan na de ondertekening gebruik gemaakt worden van poort 10025 voor de aflevering (zoals in de tweede workflow).

4.20 Een extra pijplijn voor Postfix

Voor de implementatie van de eerste workflow voegen we eerst een nieuwe smtpd-gebaseerde Postfix-service toe op poort 10027. Daarvoor nemen we dezelfde configuratie die we eerder voor poort 10025 opnamen als uitgangspunt, en configureren daarbij de OpenDKIM milter op poort 8891 zoals we dat eerder voor de validatie ook deden:

  # OpenDKIM signing
  127.0.0.1:10027    inet    n    -    n    -    -    smtpd
      -o content_filter=
      -o local_recipient_maps=
      -o relay_recipient_maps=
      -o smtpd_restriction_classes=
      -o smtpd_delay_reject=no
      -o smtpd_client_restrictions=permit_mynetworks,reject
      -o smtpd_helo_restrictions=
      -o smtpd_sender_restrictions=
      -o smtpd_recipient_restrictions=permit_mynetworks,reject
      -o smtpd_data_restrictions=reject_unauth_pipelining
      -o smtpd_end_of_data_restrictions=
      -o mynetworks=127.0.0.0/8
      -o smtpd_error_sleep_time=0
      -o smtpd_soft_error_limit=1001
      -o smtpd_hard_error_limit=1000
      -o smtpd_client_connection_count_limit=0
      -o smtpd_client_connection_rate_limit=0
      -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks
      -o local_header_rewrite_clients=
      -o smtpd_milters=inet:127.0.0.1:8891

Na de restart van Postfix staat onze nieuwe OpenDKIM-service op poort 10027 te wachten op uitgaande mailberichten die ondertekend moeten worden. Om berichten vanuit Amavis ORIGINATING niet langer naar poort 10025 te sturen maar naar poort 10027 om te leiden veranderen we deze configuratie in de file '/etc/amavisd/amavisd.conf':

  # forward to a smtpd service providing DKIM signing service
  forward_method => 'smtp:[127.0.0.1]:10027',
  #forward_method => 'smtp:[127.0.0.1]:10025',

Hieronder zie je de workflow zoals we die nu geïmplementeerd hebben, waarmee ook de DKIM-implementatie op basis van OpenDKIM compleet is.

4.21 ADSP- en andere oude records

Naast de zojuist besproken DKIM-records kun je zo her en der ook nog een ADSP-record tegenkomen, bijvoorbeeld als volgt:

  adsp._domainkey    IN    TXT    "dkim=all"

De Author Domain Signing Practices (gedefinieerd in RFC 5617) waren een uitbreiding op de DKIM-standaard waarmee de eigenaar kon aangeven dat alle mailberichten van zijn domein met DKIM beveiligd zijn. Daarmee werd een harde verbinding gelegd tussen het afzenderdomein in de 'From' header enerzijds en de inhoud en authenticiteit van het bericht anderzijds. Zoals SPF de verzameling van aanbiedende mailservers afsluit voor een specifiek domein in de envelope ('MAIL FROM'), zo sloot ADSP de mailketen dus af voor een specifiek afzenderdomein in de 'From' header. Het ADSP record is inmiddels echter 'historisch' verklaard. Behalve dat deze standaard nauwelijks gebruikt werd, leverde hij ook problemen op bij de aflevering van berichten van mailing lists. Een andere optie die hetzelfde beoogde was een aanvullend DKIM-record als volgt:

  _domainkey    IN    TXT    "o=-;"

Dit betekende dat alle mailberichten afkomstig van dit domein ondertekend zijn. Deze mogelijkheid was echter alleen onderdeel van de eerste DKIM (Sender Signing Policy) drafts, en moet nu niet meer gebruikt worden. Bij de bespreking van DMARC hieronder zullen we zien dat die standaard nu gebruikt wordt om een koppeling te leggen tussen het afzenderadres in de 'From' header en de door SPF en DKIM gevalideerde afzenderdomeinen.

Deel V – DMARC

DMARC, kort voor Domain-based Message Authentication, Reporting and Conformance, is een aanvulling op de andere 2 beveiligingsstandaarden voor mail, SPF en DKIM. DMARC geeft ontvangende MX-gateways een aanwijzing (de policy) hoe om te gaan met inkomende mail-berichten waarvoor zowel SPF als DKIM geen geldige validatie opleveren. Deze kunnen bijvoorbeeld weggegooid worden of in quarantaine worden gezet. Bovendien controleert DMARC in hoeverre het afzenderdomein in de ‘From’ header overeenkomt met het afzenderdomein in het envelope (SPF) en met het signing domain in de DKIM-header. Daarmee dwingt DMARC een relatie af tussen de gevalideerde SPF- en DKIM-afzenderdomeinen enerzijds en het 'From'-afzenderdomein anderzijds. Dat betekent dus ook dat een bericht dat positief valideert op zowel SPF als DKIM sec, toch nog afgewezen kan worden omdat DMARC negatief valideert op deze alignment. De DMARC-policy wordt via DNS gepubliceerd. Deze kan bovendien e-mailadressen bevatten waarop mailsystemen aangenomen/afgewezen berichten kunnen rapporteren. Zo krijgt de beheerder van het betreffende maildomein inzicht in de aflevering van zowel echte als vervalste berichten. Merk op dat de inzet van DMARC vereist dat SPF en/of DKIM (bij voorkeur beide) volledig zijn geconfigureerd voor alle uitgaande mailstromen. DMARC is gedefinieerd in RFC 7489.

5.1 Reputatie op domeinniveau

DMARC brengt een nuancering aan in de harde afwijzing van berichten met een negatieve validatie op SPF. Klopt de DKIM-handtekening wel, dan kan zo’n bericht (voorzien van een vlaggetje) toch doorgestuurd worden. Dat is vooral van belang voor de goede werking van mailing lists en mail forwarding, waarbij de afzender meestal niet in het SPF-record is opgenomen. Bij gebruik van DMARC zul je dus meestal ook het SPF-record als volgt aanpassen (let op de tilde ‘~’ aan het eind):

    IN    TXT    "v=spf1 mx a ip4:192.0.2.109 ip6:2001:db8:1192::/64"
                 " a:senders.example.nl a:senders.bizbooster.com ~all"

Daarmee is DMARC (net als DKIM) een check die meestal onderdeel uitmaakt van het spamfilter. In de DMARC-specificatie RFC 7489 kun je lezen hoe deze beveiligingsstandaard zich met name richt op de bescherming van zakelijke mailberichten, waarmee ook CEO-fraude (whaling) wordt tegengegaan. Omdat ook kwaadwillenden een set-up zoals we hier beschrijven kunnen optuigen, is dat echter maar de helft van het verhaal. Als onderdeel van het spamfilter is de uiteindelijke beslissing om een bericht wel of niet door te laten voor een groot deel afhankelijk van de reputatie van de afzender (host en afzenderdomein), nu onderdeel van de spam score. Dat maakt deze mailbeveiliging op domeinniveau een belangrijke enabler bij de transitie van IPv4 naar IPv6, waar je niet langer kunt filteren op IP-adres.

5.2 Het DMARC-record

Om DMARC aan te zetten voor ons domein example.nl, voegen we in eerste instantie dit DNS-record aan de zonefile toe:

  ; DMARC
  _dmarc    IN    TXT    "v=DMARC1; p=none; sp=none; adkim=s; aspf=s;
      rua=mailto:dmarc-reports@example.nl; ruf=mailto:dmarc-reports@example.nl;
      fo=1;"

We lopen hier de details in de policy langs:

  • 'v=DMARC1;' we gebruiken DMARC versie 1 (op dit moment de enige beschikbare versie)

  • 'p=none;' de policy voor het hoofddomein

  • 'sp=none;' de policy voor de subdomeinen (als zij zelf geen eigen DMARC-policy publiceren)

  • 'adkim=s;' de alignment tussen het 'From'-adres en het signing domain van de door DKIM gevalideerde digitale handtekening

  • 'aspf=s;' de alignment tussen het 'From'-adres en het door SPF gevalideerde 'MAIL FROM'-afzenderdomein

  • 'rua=mailto:dmarc-reports@example.nl;' ons mailadres voor geaggregeerde rapportages (default één keer per dag; aan te passen met de 'ri' tag)

  • 'ruf=mailto:dmarc-reports@example.nl;' ons mailadres voor forensische (failure) rapportages (voor individuele mailberichten die geweigerd zijn)

  • 'fo=1;' verzoek aan een ontvangende mailserver om een forensische rapportage te genereren als een aangeboden bericht wordt geweigerd

Zoals je ziet gaan we hier van start met de tags 'p=none;' en 'sp=none;'. Daarbij voeren ontvangende mailservers wel alle checks uit, maar worden berichten niet geblokkeerd (althans niet vanwege het niet valideren van SPF en/of DMARC). Andere waarden voor deze tags zijn 'quarantine' en 'reject'. Je begint dus altijd met de waarde 'none' om te voorkomen dat valide berichten worden tegengehouden, bijvoorbeeld (typisch) omdat je een (externe) mailserver bent vergeten toe te voegen aan de SPF-policy. Op vergelijkbare wijze gebruik je deze setting om alle valide mailservers vervolgens van DKIM-ondertekening te voorzien. Pas nadat alles goed loopt – en om dat te controleren gebruik je de binnenkomende DMARC-rapportages – stap je over naar de waarde 'reject'. Vanaf dat moment worden niet-validerende berichten ook daadwerkelijk geblokkeerd. Over de vraag of je zelf ook failure reports moet uitsturen lopen de meningen uiteen. Naast spam kunnen immers ook legitieme berichten waar iets mis mee is dan resulteren in een bounce-bericht, waarmee je misschien gevoelige informatie rondstuurt. Tegenargument is dat alle informatie in een failure report sowieso op de heenweg al over het netwerk is getransporteerd, en dat daar dus niets nieuws in staat. Wij pleiten er dan ook liever voor om de mail verdergaand te beveiligen met behulp van STARTTLS, DANE en end-to-end encryptie.

5.3 Finetuning van de DMARC alignment

De tags 'adkim=s' en 'aspf=s;' specificeren hoe streng de ontvanger moet omgaan met de alignment tussen het 'From'-afzenderdomein van een binnenkomend bericht enerzijds en de gevalideerde domeinnamen behorend bij SPF en DKIM anderzijds. Staat de 'adkim' tag ingesteld op 's' (strict), dan moet het 'From'-domein precies overeenkomen met het signing domain in de 'd=' tag van de DKIM-header. Dat betekent dat je dan geen berichten kunt sturen van een subdomein (bijvoorbeeld alerts@news.example.nl) voorzien van een digitale handtekening afkomstig van je hoofddomein [1, 2] (example.nl). Gebruik je de tag 'adkim=r;' (relaxed, de default), dan mag dit wel. Merk op dat het gebruik van DKIM impliceert dat het signing domain gelijk is aan je hoofddomein. En let op dat je deze tag niet verward met de canonicalization tag ('c=relaxed/simple;') in de DKIM-header. Voor de 'aspf' tag kun je op dezelfde manier kiezen tussen 'strict' en 'relaxed'. Alleen gaat het hier om de alignment tussen het 'From'-afzenderdomein van het bericht en het 'MAIL FROM'-afzenderdomein van de envelope. Waar SPF en DKIM je dus de mogelijkheid geven om het 'MAIL FROM'-afzenderdomein en de verzendende mailservers te beveiligen, maakt DMARC nog eens de koppeling met de 'From' header. Die bevat immers de afzenderinformatie (de display name) die uiteindelijk aan de eindgebruiker wordt getoond in zijn mail client. De 'adkim=s' en 'aspf=s;' tags geven je daarbij de mogelijkheid om de alignment-eisen van de DMARC-validatie te finetunen.

5.4 DMARC authorization-records

Het zal geen verrassing zijn dat je in de 'rua=' en 'ruf=' tags geen willekeurige e-mailadressen kunt gebruiken. Wil je de DMARC-rapportages laten sturen naar een e-mailadres op een ander dan je eigen domein – typisch dat van een DMARC-dienstverlener – dan moet de ontvangende partij daarvoor toestemming geven. Dat doet hij door een DMARC authorization-record in zijn zonefile op te nemen:

example.nl._report._dmarc    IN    TXT    "v=DMARC1;"

Hiermee laat de eigenaar van dit domein aan ontvangende mailservers weten dat die hun DMARC-rapportages voor het domein example.nl inderdaad naar een adres op dit domein mogen sturen. Voor alle details en andere beschikbare tags verwijzen we je naar sectie 6.3 van RFC 7489.

Let op dat je de laatste puntkomma ';' aan het eind van het DMARC Authorization record niet vergeet. Ondanks dat deze puntkomma verplicht is, was deze weggelaten in eerdere versies van RFC 7489.

5.5 Binnenkomende rapportages

Je zal zien dat je na de publicatie van je DMARC-record vanzelf rapportage-berichten van de grote mailverwerkers gaat ontvangen. Wij krijgen ze hier bijvoorbeeld van Google, Yahoo, Microsoft, Amazon, Fastmail en een aantal kleinere partijen. Maar ook Nederlandse overheidsorganisaties en XS4ALL sturen ons regelmatig DMARC-rapportages. De rapportageberichten bevatten een (gezipt) XML-bestand met daarin informatie over alle binnengekomen mailberichten. Je kunt die laten verwerken tot mooie grafieken door commerciële partijen als DMARC Advisor en DMARC Analyzer. Daarvoor open je een account aldaar en geef je het bijbehorende mailadres (van hun dus) op in je DMARC-record. Zoals hierboven beschreven zullen zij daarvoor een authorization-record aan hun zonefile moeten toevoegen. Wat zij vervolgens leveren is een mooie (online) analyse en visualisatie van de binnengekomen rapportageberichten.

Maar er zijn inmiddels ook diverse (low-level) open-source tools voor de verwerking en analyse van DMARC-rapportages beschikbaar:

Deze kunnen wellicht als uitgangspunt dienen voor je eigen analyse en dashboard [1, 2, 3, 4].

5.6 OpenDMARC

Wil je zelf DMARC evalueren voor binnenkomende berichten (en rapportages naar anderen sturen), dan is daarvoor OpenDMARC beschikbaar (in de EPEL repository). Dit pakket is net als OpenDKIM een open-source initiatief van het Trusted Domain Project. OpenDMARC is een milter die zowel validatie (policy enforcement) als rapportage (generatie) doet. Voor de installatie geven we het volgende commando:

  dnf install opendmarc

Let op: We maken hier gebruik van OpenDMARC omdat dit een bewezen oplossing is die bovendien als standaard package voor CentOS/RHEL beschikbaar is. Een lezer attendeerde ons op dkimpy-milter, een Python-gebaseerde milter die min-of-meer compatible is met OpenDKIM en het moderne Ed25519 EdDSA-algoritme ondersteunt. We hebben deze oplossing zelf verder nog niet onderzocht, maar willen jullie in ieder geval wijzen op het bestaan van dit alternatief.

5.7 De configuratie van OpenDMARC

De configuratie in de file '/etc/opendmarc.conf' passen we aan als volgt:

  AuthservID OpenDMARC
  CopyFailuresTo dmarc-failures@example.nl
  FailureReports true
  FailureReportsBcc dmarc-reports-sent@example.nl
  FailureReportsOnNone true
  FailureReportsSentBy postmaster@example.nl
  HistoryFile /var/spool/opendmarc/opendmarc.dat
  IgnoreAuthenticatedClients true
  IgnoreHosts /etc/opendmarc/ignore.hosts
  RejectFailures false
  ReportCommand /usr/sbin/sendmail -t
  RequiredHeaders true
  Socket inet:8893@localhost
  SoftwareHeader true
  SPFIgnoreResults true
  SPFSelfValidate true
  TrustedAuthservIDs HOSTNAME

En we maken de file '/etc/opendmarc/ignore.hosts' alvast aan (want zonder weigert de OpenDMARC daemon van start te gaan):

  touch /etc/opendmarc/ignore.hosts
  chown opendmarc:opendmarc /etc/opendmarc/ignore.hosts
  chmod 664 /etc/opendmarc/ignore.hosts

5.8 Configuratie-opties

Zoals je ziet beginnen we met de instelling 'RejectFailures false'. Dat betekent dat OpenDMARC in eerste instantie alleen de headers met de resultaten van de SPF/DKIM-evaluatie aan het bericht zal toevoegen, maar nog geen berichten zal weigeren. Pas als alles naar wens loopt veranderen we deze optie in 'true', waarna binnenkomende mailberichten op basis van de DMARC-policy van het afzenderdomein geëvalueerd en eventueel geblokkeerd zullen worden. De optie 'SPFIgnoreResults true' maakt dat OpenDMARC bestaande SPF-headers negeert en altijd zelf een SPF-validatie doet. Alternatief zou zijn om OpenDMARC mee laten liften op de uitkomsten van de eerste validatie door policyd-spf door deze optie op 'false' te zetten. In dat geval wordt alleen een SPF-validatie uitgevoerd als er nog geen SPF-headers beschikbaar zijn. Zet je ook de optie 'SPFSelfValidate false', dan doet OpenDMARC helemaal geen SPF-validatie meer en wordt volledig vertrouwd op (eventueel) aanwezige SPF-headers. Hiervoor is het belangrijk te weten dat een 'check_policy_service' (onderdeel van de 'smtpd_relay_restrictions'- (en 'smtpd_recipient_restrictions'-)opties van Postfix) omwille van de performance altijd voor de 'smtpd_milters' wordt uitgevoerd. En die laatste is waar we zodadelijk een hook naar OpenDMARC zullen toevoegen). De makkelijkste oplossing zou zijn om policyd-spf bij het gebruik van OpenDMARC te disablen (in de file '/etc/postfix/master.cf'):

    #-o smtpd_relay_restrictions=check_policy_service,unix:private/policyd-spf,permit

Er kunnen echter goede redenen om policyd-spf als aparte scanner te blijven gebruiken om vroeg in de pijplijn niet-validerende berichten te blokkeren. Dat betekent namelijk dat je zelf meer controle kunt uitoefenen op welke berichten wel en niet doorgelaten worden, in plaats dat je daarvoor uitgaat van een DMARC-policy die door een ander gepubliceerd is. Hetzelfde geldt voor het al dan niet disablen van OpenDKIM voor DKIM-validatie op dezelfde plek.

5.9 De koppeling van OpenDMARC met Postfix

Nu deze configuratie compleet is, kunnen we onze nieuwe OpenDMARC-service opstarten als volgt:

  systemctl start opendmarc.service
  systemctl status opendmarc.service
  systemctl enable opendmarc.service

Vervolgens voegen we deze service (nu beschikbaar op poort 8893) toe aan de milters op de Postfix smtp/25-service in de file '/etc/postfix/master.cf':

    -o smtpd_milters=inet:127.0.0.1:8893

Of als je OpenDKIM niet uit wilt zetten:

    -o smtpd_milters=inet:127.0.0.1:8891,inet:127.0.0.1:8893

In het diagram hieronder zie je hoe de meest uitgebreide workflow met daarin ook nog policyd-spf en OpenDKIM (voor validatie) er uit ziet:

5.10 OpenDMARC headers

Na de restart van Postfix zien we dat binnenkomende mailberichten inderdaad ook van een DMARC-header zijn voorzien:

  Return-Path: <tester@afzenderdomein.nl>
  X-Original-To: ontvanger@example.nl
  Delivered-To: ontvanger@mail.example.nl
  Received: from localhost (localhost [127.0.0.1])
          by mail.example.nl (Postfix) with ESMTP id C91EE280526E
          for <ontvanger@example.nl>; Mon, 18 May 2020 12:39:54 +0200 (CEST)
  X-Virus-Scanned: amavisd-new at example.nl
  Received: from mail.example.nl ([127.0.0.1])
          by localhost (mail.example.nl [127.0.0.1]) (amavisd-new, port 10024)
          with ESMTP id NrnBGwngyxIY for <ontvanger@example.nl>;
          Mon, 18 May 2020 12:39:53 +0200 (CEST)
  Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=203.0.113.109; helo=afzenderdomein.nl; envelope-from=tester@afzenderdomein.nl; receiver=<UNKNOWN>
  DMARC-Filter: OpenDMARC Filter v1.3.2 mail.example.nl 922E02800602
  Authentication-Results: OpenDMARC; dmarc=pass (p=none dis=none) header.from=afzenderdomein.nl
  Authentication-Results: OpenDMARC; spf=pass smtp.mailfrom=tester@afzenderdomein.nl
  DKIM-Filter: OpenDKIM Filter v2.11.0 mail.example.nl 922E02800602
  Received: from afzenderdomein.nl (afzenderdomein.nl [203.0.113.109])
          by mail.example.nl (Postfix) with ESMTPS id 922E02800602
          for <ontvanger@example.nl>; Mon, 18 May 2020 12:39:53 +0200 (CEST)
  DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;
          d=afzenderdomein.nl; s=dkim20191104; h=Content-Type:MIME-Version:Message-ID:Date:
          Subject:To:From:Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID:
          Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc
          :Resent-Message-ID:In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:
          List-Subscribe:List-Post:List-Owner:List-Archive;
          bh=6BzxQA/45SnSHmbvysWH67ttw0oekzBPj78KEvNmsN4=; b=SFGwnph4EeON20GdkU/JZKCXK3
          /9uHXfe5rAqoLx9mr59xwBHhG11B8f6ZMDyYt+cfa4u/hI5bmh/7ryLU+KPTYP9zLCW8ndBwnGoPm
          k7Y+4o/kQM1ZbSbMM79Q2D5pkS7nsMt/sBlUZMdEQ9bmD2qNlFWhGr68F0nM+5KQN69g=;
  Received: from [203.0.113.109:1000] (helo=inauditus)
          by afzenderdomein.nl with esmtpsa (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)
          (Exim 4.92)
          (envelope-from <tester@afzenderdomein.nl>)
          id 1jadBW-00028K-Qm
          for ontvanger@example.nl; Mon, 18 May 2020 12:39:52 +0200
  From: Tester <tester@afzenderdomein.nl>
  To: Ontvanger <ontvanger@example.nl>
  Subject: Test OpenDMARC
  Date: Mon, 18 May 2020 12:39:50 +0200
  Message-ID: <87o8qlr0g9.fsf@afzenderdomein.nl>

En daarmee is onze SPF/DKIM/DMARC set-up helemaal compleet!

Mailconfiguratie testen

Je kunt je mailconfiguratie testen op Internet.nl en via deze portals:

6 Security-aangelegenheden: firewall en SELinux

Hoewel we met deze configuratie een hele zwik nieuwe TCP-poorten in gebruik hebben genomen, zijn er nog steeds maar 2 die ook voor verbindingen van de buitenwereld open mogen staan: 25 (smtp) en 587 (submission). Alle andere poorten (10024, 10025, 10026, 10027, 8891 en 8893) zijn alleen voor intern/lokaal gebruik. Dat betekent dat de firewall voor deze poorten gesloten moet blijven. Van de lokale poorten leverde alleen het gebruik van 10026 en 10027 problemen op met SELinux: deze zijn al toegekend aan SpamAssassin. We hebben dat opgelost als volgt:

  semanage port -m -t amavisd_recv_port_t -p tcp 10026
  semanage port -m -t amavisd_send_port_t -p tcp 10027

Voor de andere poorten waren geen wijzigingen nodig:

  [root@system ~]# semanage port -l | grep 8891
  milter_port_t    tcp    8890, 8891, 8893

  [root@system ~]# semanage port -l | grep 1002
  amavisd_recv_port_t    tcp    10026, 10024
  amavisd_send_port_t    tcp    10027, 10025
  spamd_port_t           tcp    783, 10026, 10027

7 Afsluitend

CentOS versie 8.1 heeft (in combinatie met de EPEL repository) alle ingrediënten in huis om een volwaardige, schaalbare, veilige mailinfrastructuur gebaseerd op Postfix op te bouwen. Maar zoals je ziet is daarvoor wel wat uitbouw- en integratiewerk nodig. Het voordeel van deze modulaire, open architectuur is dat deze set-up schaalbaar is tot op enterprise-niveau, en dat je 'm op maat kunt snijden en dimensioneren naar je eigen specifieke behoeften. Wat we in dit hands-on artikel hebben besproken levert je een volledige implementatie op van alle 3 mailbeveiligingsstandaarden SPF, DKIM en DMARC. Combineer je deze set-up nog met IPv6 en DANE, dan is dit alles genoeg voor een 100 procent score op Internet.nl. Dat betekent dat je mailsystemen helemaal up-to-date zijn en voldoen aan de laatste eisen en inzichten van onder andere Forum Standaardisatie en NCSC. Bovendien is mailbeveiliging op domeinniveau een praktische voorwaarde bij de transitie van IPv4 naar IPv6, waar je niet langer kunt filteren op IP-adres.

Komt er nieuwe SPF/DKIM/DMARC-functionaliteit voor Postfix beschikbaar, dan verwerken we die in dit artikel. Ook feedback op dit artikel is uiteraard van harte welkom.

Gebruik je al Exim, dan verwijzen we je naar dit hands-on artikel, waarin we de configuratie van SPF, DKIM en DMARC voor Exim op vergelijkbare wijze bespreken als we dat hier voor Postfix hebben gedaan.