Hands-on: de implementatie van SPF, DKIM en DMARC voor Postfix
Bewerkelijk, maar veilig en schaalbaar
Bewerkelijk, maar veilig en schaalbaar
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-records • de validatie van SPF • de ondertekening met DKIM • de publicatie van DKIM-records • de validatie van DKIM • de publicatie van DMARC-records • de 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.
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.
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
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.
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:
BIND named (in een chroot jail) versie 9.11.4
Postfix versie 3.3.1
OpenSSL versie 1.1.1c
Dovecot versie 2.2.36
Cyrus SASL versie 2.1.27
Dovecot-Pigeonhole versie 2.2.36
Clam AntiVirus (ClamAV) versie 0.102.3
SpamAssassin versie 3.4.2
Amavis (amavisd-new) versie 2.12.0
SPF Policy Server for Postfix versie 2.0.2
OpenDKIM versie 2.11.0
OpenDMARC versie 1.3.2
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.
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)
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.
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.
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'.
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.
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.
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.
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).
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.
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.
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';
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.
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
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.
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.
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.
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
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).
Gebruik het commando 'postconf (-d)' om de volledige (default) configuratie van Postfix op te vragen.
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.
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.
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.
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
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
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.
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.
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'],
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.
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.
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.
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
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.
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.
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
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.
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
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'.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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].
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.
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
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.
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:
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!
Je kunt je mailconfiguratie testen op Internet.nl en via deze portals:
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
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.