##### Copyright: 2012-2022 Wolfgang Breyha, wbreyha@gmx.net ##### License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html ##### last update: 20.09.2022 ***** ACLs "on the fly" auswählen ********************************* acl_smtp_connect = acl_check_connect acl_smtp_helo = acl_check_helo_${acl_c0} ... acl_check_connect: warn set acl_c0 = MTA warn hosts = +system_hosts : +trusted_local set acl_c0 = MSA ... acl_check_helo_MSA: ... acl_check_helo_MTA: ... ***** check ob PTR vorhanden **************************** exim bietet... verify = reverse_host_lookup ... prüft damit aber komplett IP->PTR->A->IP nur PTR daher mit... condition = ${if and {{def:sender_host_address}{!def:sender_host_name}}\ {yes}{no}} ...prüfen. ***** 1 Sekunde delay für Externe ********************************* hostlist trusted_local = 192.168.0.0/24 : /etc/mail/trusted_local_hosts acl_check_connect: warn hosts = !+system_hosts : !+trusted_local condition = ${if eq{$received_port}{25} {1}{0}} delay = 1s ***** authentication erzwingen auf Port 465/587 *********************************************** deny ! authenticated = * condition = ${if or{ \ {eq{$received_port}{465}} \ {eq{$received_port}{587}} \ }{1}{0}} message = 530 5.7.0 Authentication required log_message = Authentication required (port $received_port) ***** ENHANCEDSTATUSCODES selfman ********************************* exim verwendet keine und announced diese auch nicht nach EHLO. Man darf aber selbst natürlich welche setzen. zB: ... message = 250 2.1.0 Ok message = 530 5.7.0 Authentication required message = 550 5.1.1 Restricted characters in address message = 550 5.1.1 unknown user message = 550 5.4.4 unrouteable address message = 550 5.7.1 Access denied message = 450 4.7.0 Bitte warten... ... möglichst alle nach RFC 2034 kategorisieren. Dabei auf die stage (HELO, MAIL FROM, ...) achten ***** lookup in regex file zwecks Block (RCPT TO) ************************************************* deny set acl_m3 = ${lookup{$sender_address${quote_local_part:$local_part}@$domain$sender_host_name$sender_host_address$sender_helo_name} \ nwildlsearch{/etc/mail/combinedblock}\ {$value}{nothing}\ } condition = ${if !eq{$acl_m3}{nothing}} message = 550 5.7.1 Access denied log_message = Access denied (combinedblock): $acl_m3 Inhalt von combinedblock zB: ^.*.*?[-][0-9]+$ ^.*@0451.com.*$ ^elena.*@eposta.ru.*$ ^e-mail@instantssl-ev.comwebmaster@univie.ac.at.*$ ***** ausführlicher dsn.rfc-ignorant.org reject (RCPT TO) ********************************************************* deny dnslists = dsn.rfc-ignorant.org/$sender_address_domain message = 550 5.1.8 Mail from $sender_address_domain rejected as your domain \ does not accept bounces. This violates RFC 821/2505/2821/5321. \ see http://www.rfc-ignorant.org/ log_message = listed at dsn.rfc-ignorant.org ***** ausführlicher reject bei DNS SERVFAIL auf client-Seite (RCPT TO) ********************************************************************** defer condition = ${if eq{$host_lookup_deferred}{1}} message = trying to check the DNS records of the host $sender_host_address returned \ SERVFAIL. Either the PTR RRs or the resulting hostname was temporarily not \ resolvable! Therefore we temporarily reject all mail from this host until \ the problem gets fixed. log_message = reverse_host_lookup deferred (SERVFAIL) ***** header für RBL hinzufügen (RCPT TO) ***************************************** warn hosts = !+system_hosts !domains = +relay_to_domains dnslists = bogusmx.rfc-ignorant.org/$sender_address_domain : \ dnsbl.dronebl.org : \ dnsbl.njabl.org!=127.0.0.3 add_header = X-DNSBL-Warning: listed at $dnslist_domain ($dnslist_text) Anm.: exim bricht beim ersten Match auf einer RBL ab und liefert das Ergebnis. Die Reihenfolge ist daher von Bedeutung. ***** check auf 0 Bytes im Mailbody (DATA) ****************************************** Evtl. praktisch falls das Backend (zB cyrus-imapd) sowas nicht annehmen würde deny condition = ${if >{$body_zerocount}{0} {yes}{no}} message = 550 5.6.0 Message contains NUL character. log_message = NUL char ($body_zerocount} ***** mehrere Virenscanner ************************** av_scanner = ${acl_m_clamd} acl_smtp_data = acl_check_data ... acl_check_data: warn set acl_m_clamd = clamd:/tmp/clamd add_header = X-Virus-Scan: scanned by ClamAV on $primary_hostname malware = * set acl_m_clam = yes set acl_m_clamn = $malware_name warn set acl_m_clamd = clamd:/tmp/clamd-sane malware = * set acl_m_sane = yes set acl_m_sanen = $malware_name set acl_m_clamav_header = X-Univie-ClamAV-Result: $acl_m_sanen log_message = Sanesecurity $acl_m_sanen found. Header added ***** automatisches Virenarchiv ******************************* aufbauend auf obigem Beispiel! warn condition = ${if and \ { \ {eq{$acl_m_clam}{yes}} \ {!exists{/var/virusarchive/$acl_m_clamn}} \ {eq{${run{/bin/cp $spool_directory/scan/$message_id/$message_id.eml \ /var/virusarchive/$acl_m_clamn}{yes}{no}}}{no}} \ } \ } log_message = failed to copy to virusarchive ***** automatisches Virenarchiv mit primitivem Hashing ****************************************************** Es werden Directories mit den Gruppen des ersten Teilstrings bis zu einem "." oder "_" angelegt. warn set acl_m_malwg = ${extract{1}{._}{$malware_name}} condition = ${if and \ { \ { or \ { \ {exists{/var/virusarchive/$acl_m_malwg}} \ {eq{${run{/bin/mkdir -p /var/virusarchive/$acl_m_malwg}{yes}{no}}}{yes}} \ } \ } \ {!exists{/var/virusarchive/$acl_m_malwg/$malware_name}} \ {eq{${run{/bin/cp $spool_directory/scan/$message_id/$message_id.eml \ /var/virusarchive/$acl_m_malwg/$malware_name}{yes}{no}}}{no}} \ } \ } log_message = failed to move to virusarchive $acl_m_malwg:$malware_name ***** spamtrap fake reject ************************** Alle Empfänger die auf spamtrapfake@localhost aliased sind werden "fake" rejected. Im SMTP Dialog erhält der Client "550 meanwhile ...." und im Hintergrund wird die Mail normal zugestellt. # shortcut for spamtrapfake: warn first (log) and accept warn condition = ${if and \ { \ {eq{$recipients_count}{1}} \ {eq{$acl_m11}{spamtrapfake@localhost}} \ } \ } log_message = fake unknown user/Adresse unbekannt $recipients set acl_m8 = yes accept condition = $acl_m8 control = fakereject/meanwhile this user is unknown but thanks for all the fish anyway Die hier verwendete $acl_m11 muss in der rcpt-ACL entsprechend gesetzt werden oder durch eine andere Variable ersetzt werden. Um die Variable zu setzen kann man zB folgende Schritte verwenden: *) in der rcpt ACL an der Stelle wo man die local_domains verifiziert. Bei erfolgreicher Verifizierung liefert verify den Wert von $address_data des zuletzt aufgerufenen routers zurück. accept domains = +local_domains endpass verify = recipient set acl_m11 = $address_data *) daher müssen wir bei den routern darauf achten, dass address_data auch gesetzt wird. Anstatt das Ergebnis direkt in data = zu schreiben geht man den Umweg über address_data. zB ein klassischer virtusertable router: virtuser_router: driver = redirect domains = /etc/mail/local_domains local_part_suffix = +* local_part_suffix_optional caseful_local_part = true allow_fail allow_defer address_data = ${lookup{${lc:$local_part}@$domain} lsearch*@ {/usr/local/etc/mail/virtuser} {$value}fail} data = $address_data ***** selektives greylisting mit DCC (DATA) ******************************************* DCC fuer Exim siehe doc/experimental-spec.txt Ergebnis abholen und Exim Variable setzen... warn !dcc = * set acl_m_grey = yes ... danach immer wieder als condition verwenden ... defer condition = $acl_m_grey hosts = ! +system_hosts !verify = header_syntax message = if you know how to handle 4xx you are welcome to try again later log_message = header_syntax greylisted $recipients_count RCPT: $recipients ... defer condition = $acl_m_grey condition = ${if isip6{$sender_host_address}} hosts = ! +system_hosts dnslists = bl.ipv6.spameatingmonkey.net message = if you know how to handle 4xx you are welcome to try again later log_message = RBL($dnslist_domain=$dnslist_value) greylisted $recipients_count RCPT: $recipients ***** Body MD5 sum block (DATA) ******************************* Anm.: $message_body beinhaltet nur so viele Zeichen wie mit message_body_visible definiert (500 default) selbes gilt für $message_body_end für das Ende des bodies. Um die MD5 Summen zu finden, die man blocken möchte kann man die log_message Zeile aktivieren warn set acl_m_md5 = ${md5:$rheader_subject:$message_body$message_body_end} # log_message = md5sum: $acl_m_md5 {$rheader_subject:} deny condition = ${lookup{$acl_m_md5} \ lsearch{/etc/mail/blockedmd5}\ {yes}{no}\ } message = we decided to reject this message entirely. contact abuse@...... if you think that's an error. log_message = blockedMD5($acl_m_md5) ***** SpamAssassin aufrufen (DATA) ********************************** Damit man temporäre Fehler von SpamAssassin (timeouts, ..) entsprechend in SMTP defers umsetzen kann... defer spam = spamass:true condition = no ... aufrufen. Wenn alles geklappt hat sind die Ergebnisse bereits im Cache und weitere Aufrufe von spam = verbinden nicht mehr zu spamd. Man muß aber darauf achten, dass sich die Parameter von spam = (hier im Beispiel "spamass:true") nicht mehr ändern. ***** transport SpamAssassin Headers via ReportHeader and ZIDReport.pm Module ***************************************************************************** Exim wertet nur Spam-Score und Spam-Report aus. Mit ZIDReport.pm wird der Spam-Report so gebaut, dass man prinzipiell beliebige Werte transportieren kann und mit extract{} wieder als Einzelwert erhält. ZIDReport.pm ist unter http://www.blafasel.at/exim/ZIDReport.pm zu finden. in der SpamAssassin local.cf... loadplugin ZIDReport ZIDReport.pm clear_report_template report _ZIDREPORT_ ...setzen. In exim spamassassin "wie gewohnt" in der data ACL aufrufen und dann zB. ... warn spam = spamass:true add_header = X-Spam-Score: $spam_score\n\ X-Spam-Score-Int: $spam_score_int\n\ X-Spam-Level: $spam_bar\n\ X-Spam-Checker-Version: ${extract{version}{$spam_report}}\n\ X-Spam-Status: ${extract{status}{$spam_report}}\n\ X-Spam-Languages: ${extract{langs}{$spam_report}}\n\ X-Spam-Relay-Countries: ${extract{reltlds}{$spam_report}}\n\ X-Spam-Report: ${extract{report}{$spam_report}} ... verwenden. Oder zB. auf hauseigene SpamAssassin Rules triggern: warn set acl_m9 = ${extract{status}{$spam_report}} defer condition = $acl_m_grey condition = ${if match{$acl_m9}{ZIDGREY} {yes}{no}} message = if you know how to handle 4xx you are welcome to try again later log_message = spamd(ZIDGREY ${spam_score}) greylisted $recipients_count RCPT: $recipients ***** kein SpamAssassin bei bestimmten/verifizierbaren DKIM Signaturen ********************************************************************** acl_check_dkim: warn condition = ${lookup{$sender_address_domain}lsearch{/etc/mail/no_spamassassin_domains} {yes}{no}} dkim_signers = $sender_address_domain dkim_status = pass set acl_m_dkimbulkpass = yes acl_check_data: warn condition = ${lookup{$sender_address_domain}lsearch{/etc/mail/no_spamassassin_domains} {yes}{no}} condition = $acl_m_dkimbulkpass set acl_m_knownbulk = yes add_header = X-Spam-Status: not scanned. known DKIM signing bulk sender ($sender_address_domain). log_message = known bulksender $sender_address_domain. not spending CPU cycles for spamassassin .... nun $acl_m_knownbulk verwenden um den Aufruf von spam = zu limiteren... defer !condition = $acl_m_knownbulk spam = spamass:true condition = no ... und am Ende noch /etc/mail/no_spamassassin_domains mit zB news.eduscho.at news.tchibo.de newsletter.bestwestern.at newsletter.qvc.de produktnews.thalia.at zeitkunstverlag.srv2.de ... befüllen. ***** Mit DKIM bestimmte Adressen eigener Domains schützen ********************************************************** Signiert man selbst alle ausgehenden Mails mit DKIM, kann man bei allen Adressen, die 100% nur über das eigene relay versenden, auch prüfen ob diese beim Einlangen auf den MX hosts eine gültige Signatur tragen. acl_check_dkim: deny condition = ${if and \ { \ {match_domain{$dkim_cur_signer}{blafasel.at}} \ {eq{${lookup{${address:$h_from:}} lsearch{/etc/mail/check_dkim}{yes}{no}}}{yes}} \ {! match{$dkim_verify_status}{pass}} \ } \ } log_message = DKIM Signature for ${address:$h_from:} needed but check failed (result=$dkim_verify_status) message = unauthorized use of this senderaddress prohibited In /etc/mail/check_dkim die zu schützenden Adressen eintragen. zB.: postmaster@blafasel.at abuse@blafasel.at ***** Mails für x Sekunden in der Queue halten ********************************************** Bei Verdacht auf Accountmissbrauch kann man damit evtl. verhindern, dass Mails die Queue verlassen um Beobachtungszeit zu gewinnen. Zuerst in einer ACL den "initial delivery attempt" von exim unterbinden und das Mail "markieren". warn condition = ${if >{$recipients_count}{29} {yes}{no}} control = queue_only log_message = queue_only triggered by recipients count $recipients_count set acl_m_delay = yes Die Variable acl_m_delay wird mit den Mailheadern in der Queue abgelegt und steht bei jedem queuerun zur Verfügung. Daher kann in einem router an passender Stelle darauf reagiert werden: delay_suspicious: driver = redirect domains = ! +local_domains condition = ${if and{ \ {eq{$acl_m_delay}{yes}} \ {<{$message_age}{300}} \ } \ } allow_defer data = :defer: message not old enough no_verify ***** Maximum connections pro IP variabel ***************************************** In der main config: smtp_accept_max_per_host = \ ${lookup{$sender_host_address}iplsearch{/usr/local/etc/mail/smtp_accept_max_per_host}\ {$value}{5}}\ default = 5 ------^ Im file dürfen CIDR verwendet werden. IPv6 braucht "...". zB: 192.168.1.0/24:2 "fe80::/56":2 ***** ratelimiting auf Basis der PTR RRs/$sender_host_name ********************************************************** # format: : , # eg: \.srv[23]\.de$: 5,srv2.de defer set acl_c_rlimit = ${lookup{$sender_host_name}nwildlsearch{/usr/local/etc/mail/mx_ratelimits} {$value}{}} condition = ${if !eq{$acl_c_rlimit}{}} set acl_c_rlimit_k = ${extract{2}{,}{$acl_c_rlimit}} set acl_c_rlimit_l = ${extract{1}{,}{$acl_c_rlimit}} message = 421 slow down! log_message = $acl_c_rlimit_k reached ratelimit $sender_rate/$sender_rate_limit/$sender_rate_period ratelimit = $acl_c_rlimit_l / 5m / per_conn / $acl_c_rlimit_k in mx_ratelimits ---- # eduscho/thalia ^.*\.srv[23]\.de$: 15,srv2.de # ^.*mta\.dotmailer\.co\.uk$: 10,dotmailer.co.uk