Proxmox Mail Gateway konfigurieren

Installationsanleitungen für das Proxmox Mail Gateway gibt es viele – die Anleitungen für die individuelle Anpassung kommt oft zu kurz.
Hier ist meine Anpassung ggü. einer Out of the Box Installation beschrieben.

Meine Politik dabei ist, dass E-Mails gar nicht erst angenommen werden, wenn sie Spam sind, denn das größte Übel ist aus meiner Sicht, dass false positives, die in meinem Spam Ordner untergehen vom Sender als zugestellt geglaubt werden. Das schützt natürlich nicht davor, wenn bestellte Konzertkarten o.ä. bei mir nicht ankommen, weil der Absender seinen Mailserver nicht im Griff hat. Die Konzertkarten sind dann vermutlich 'weg' und erfordern erhebliche Nacharbeit. Ein Manko von "ich nehme deinen Kram nicht an".

Basiskonfiguration Proxmox Mail Gateway
====================================

Configuration -> Mail Proxy -> Relay Domains:

Eintragen meiner Domains
# E-Mails, die an diese Domains gehen, werden von Proxmox überhaupt aktzeptiert


Configuration -> Mail Proxy -> Options:
# Message Size (bytes): 104857600
Deutlich erhöhen, sonst gibt es Probleme mit Spam und DKIM Signaturen bei großen E-Mails.

Reject Unknown Clients: No
# Der rdns des Servers muss korrekt gesetzt sein. Geht in 98% gut, aber in 2% haben kleine Admins ihre Umgebung nicht korrekt gepflegt, die haben dann keine Chance euch Mails zu senden. Das greift vor jeglicher Whitelist. Daher besser ausgeschaltet lassen.

Reject Unknown Senders: Yes
# Die Absenderdomain muss existieren, damit die E-Mail angenommen wird. E-Mails von 'ausgedachten' Absenderdomains werden temporär rejected (4xx error code). Das macht ca. 20% der Spams aus, die nicht Gefahr laufen, im SA (Spamassassin) durchzurutschen. In ca. 1 % der Fälle schlägt die Prüfung fehl, dann wird die E-Mail 5 min später beim nächsten Einsendeversuch akzeptiert. In manchen Fällen wird die E-Mail von Spammern über 100x eingeliefert und nie akzeptiert, das nervt leider ein wenig.

Verify Receivers: Yes (550)
# Sicherstellung, dass E-Mails an unbekannte Mailkonten gar nicht erst angenommen werden. Ansonsten gibt es Bounces von eurem E-Mail Server zum Proxmox, die beim Proxmox verbleiben, von denen der Versender jedoch nichts erfährt.
# Edit: Ich hatte einige Versuche, E-Mail Adressen von Geschäftsführern zu erraten. Absender waren ein unfassbar großes Botnetz aus der ganzen Welt (jeder Versuch über einen anderen Server). Ich bin daher dazu übergegangen, das auszuschalten und eine ungelesene Catchall Adresse einzurichten. Nachteil: Tippfehler im Empfänger bekommt der seriöse Versender nicht mehr mit)

Use Greylisting for IPv4: No
# Greylisting verzögert die Annahme teilweise erheblich, was sehr schlecht bei Kontoverifizierungs E-Mails ist. Leider beherrscht PMG nicht die Möglichkeit, nur E-Mails mit fragwürdigem Spamscsore temporär zu rejecten, wie es rspamd beherrscht. In der hier gebotenen Qualität des Sendertripplets ist es aus meiner Sicht unbrauchbar.

Use SPF: (Yes)
# E-Mails mit falscher SPF werden abgelehnt. Habe ich bisher wenig Probleme mit, könnte aber durchaus mal problematisch sein. Gewisses Risko.

Before Queue Filtering: Yes
# Kernanliegen, denn nachträgliche "NDRs on Blocked E-Mails" sind selbst Spam.


Configuration -> Mail Proxy -> Transports:
Eintragen meines Mailservers
<domain> -> Host: 172.30.0.3, use MX: No
# MX: No -> Weil der MX auf das PMG zeigt


Configuration -> Spam Detector -> Options:
Max Spam Size (bytes): 104857600
# Muss gleich sein wie unter Mail Proxy.

Mail Filter:
Block Spam (Level 10) aktivieren: Mails mit hoher Spam Wahrscheinlichkeit sollen direkt abgelehnt werden.
Mark Spam und Quarantäne nach eigenen Vorstellungen konfigurieren.

 

Installation und Konfiguration wirksamer Mailfilter
===========================================

Die Reihenfolge der hier angegebenen Filter hat eine Bedeutung. Die hier zuerst behandelten Filter filtern die Mail früher heraus und benötigen dafür deutlich weniger CPU Last als spätere SA-Filter.
Gleichzeitig haben die hier früh in der Anleitung behandelten filter eine höhere Sensitivität (d.h.: eine geringere false-positive Quote).
Man kann daher die Anleitung erst einmal nur zum Teil abarbeiten und erzielt dann schon recht gute Ergebnisse.
Unabhängig davon ist aber das Tuning der Custom Scores am Ende des ersten Beitrags aus meiner Sicht wichtig, das ist ja gerade der Kern des PMG.

Filter vor der Übermittlung von Mailheader ('rejected')

Mails, die an dieser Stelle abgewiesen werden, stehen im Tracking Center als rejected mit schwarzem Kreuz Symbol. An dieser Stelle liegen keinerlei Informationen des E-Mail Headers vor, außer dem Sende-Server (HELO + IP), Envelope-From und Empfänger. Es greifen hier sämtliche Welcome-Lists innerhalb des PMG noch nicht.

DNS-Blocklists (DNSBL):

(Filtert ca. 50 % eures Spam)

Configuration -> Spam Detector -> Options:
Use RBL checks: Yes


Wir benötigen "unbound" für rekursive DNS Anfragen, das ist bei allen DNSBL notwendige Voraussetzung.

apt install unbound

Editieren der /etc/resolv.conf

nameserver 127.0.0.1

bei LXC Containern stattdessen: Über Proxmox VE in den DNS Optionen setzen

Configuration -> Mail Proxy -> Options:
Bei Spamhaus muss ein Account erstellt werden und die individuelle DQS ID eingefügt werden. Alle anderen sind ohne Registrierung nutzbar.

DNSBL Sites:
<ID>.zen.dq.spamhaus.net
b.barracudacentral.org
bl.mailspike.net
all.spamrats.com
dnsbl-1.uceprotect.net



Filter nach der Übermittlung von Mailheader, vor der Übermittlung von Mailbody ('rejected')

Mails, die an dieser Stelle abgewiesen werden, stehen im Tracking Center als rejected mit schwarzem Kreuz Symbol. An dieser Stelle liegt der äußere Header der E-Mail vor, Envelope-From, Empfänger und weitere Header der E-Mail, aber noch kein Subject oder (Body-)From.

Die Welcomelist unter Configuration -> Mail Proxy -> Welcomelist sollte diesen Service überschreiben (getestet habe ich das nicht), weil diese in die Postfix senderaccess ausgeführt wird. Die Mail Filter -> Who Object -> Welcomelist wird erst im SA berücksichtigt – die hat hier keine Wirkung.

Google Spam beherrschen: Dauerausnahme in allen DNSBL
(Filtert ca. 25 % eures Spam)

Google sendet auf mehreren Ebenen Spam, welchen wir mit der folgenden Anleitung direkt im Postfix/Postscreen rejecten.
- firebaseapp.com (Google Service) verschickt ausschließlich Spam und

- firebase-Authorisierte Dienste: Senden über Google Server mit eigener Domain. Erkennt man darin, dass der authorisierende SPF Eintrag _sfp.firebasemail.com ist.

- Google Groups:
Spammer nutzen Google Groups Listen, weil sie kein Opt-In des Empfängers benötigen und Mailinglisten standardmäßig seitens Spamassassin einen Score von "-1.0" zugewiesen bekommen. Neben den eigentlichen Spams erreichen einen auch NDRs oder wütende Antworten von Empfängern.

- Google Usercontent:
Server von Google, der nur Spam versendet

All diese Dienste kann man sehr früh in Postfix blockieren. Bonus: Da Google als Absender prinzipiell nicht auf den DNSBL des vorigen Kapitels steht, gibt es hier keine Überlappung der Filter, also ja: mit den DNSBL und diesem Google-Filter sind 75% des Spamaufkommens wirksam und nahezu ohne false-positive heraus gefiltert!

apt install python3-dnspython

cat > /usr/local/bin/policyguard.py

#!/usr/bin/env python3

import sys
import time
import re
import dns.resolver
from functools import lru_cache

MAX_SPF_DEPTH = 10
CACHE_SIZE = 4096
SPF_BLOCK_INCLUDE = ["_spf.firebasemail.com"]
SENDER_BLOCK_INCLUDE = ["firebaseapp.com"]
BLOCKLIST_CLIENTNAME = ["googleusercontent.com"]
SENDER_REGEX_PATTERNS = [
    r'^[a-z0-9]{1,3}\+bncB[A-Z0-9]{25,}@',
]
SENDER_REGEXES = [re.compile(p) for p in SENDER_REGEX_PATTERNS]

resolver = dns.resolver.Resolver()
resolver.timeout = 2
resolver.lifetime = 3


def log(msg):
    sys.stderr.write(f"spf-policy: {msg}\n")
    sys.stderr.flush()


@lru_cache(maxsize=CACHE_SIZE)
def get_spf(domain):
    try:
        answers = resolver.resolve(domain, "TXT")
        for r in answers:
            txt = "".join([s.decode() for s in r.strings])
            if txt.startswith("v=spf1"):
                return txt
    except Exception:
        pass
    return None


def spf_contains_block(domain, depth=0, visited=None):
    if visited is None:
        visited = set()

    if depth > MAX_SPF_DEPTH:
        return False

    if domain in visited:
        return False

    visited.add(domain)

    spf = get_spf(domain)
    if not spf:
        return False

    parts = spf.split()

    for p in parts:
        if p.startswith("include:"):
            include = p.split(":", 1)[1]

            if include in SPF_BLOCK_INCLUDE:
                return True

            if spf_contains_block(include, depth + 1, visited):
                return True

    return False


def sender_matches_regex(sender):
    for regex in SENDER_REGEXES:
        if regex.match(sender):
            return True, regex.pattern
    return False, None


def handle_request(attrs):
    sender = attrs.get("sender", "")
    client_name = attrs.get("client_name", "").lower()

    # Block Clients
    if any(client_name == b or client_name.endswith("." + b) for b in BLOCKLIST_CLIENTNAME):
        log(f"blocked client hostname: {client_name}")
        return "reject reject for policy reasons [policyguard] [guc]"
 
    if sender:
        matched, pattern = sender_matches_regex(sender)
        if matched:
            log(f"blocked sender by regex ({pattern}): {sender}")
            return "reject reject for policy reasons [gg]"

    if "@" not in sender:
        return "dunno"

    domain = sender.split("@", 1)[1].lower()
    try:
        if any(domain == base or domain.endswith('.' + base) for base in SENDER_BLOCK_INCLUDE):
            log(f"blocked sender domain {domain}")
            return "reject reject for policy reasons [fbm]"
 
        elif spf_contains_block(domain):
            log(f"blocked sender domain {domain}")
            return "reject reject for policy reasons [fbs]"
    except Exception as e:
        log(f"error checking {domain}: {e}")

    return "dunno"


def main():
    while True:

        attrs = {}

        while True:
            line = sys.stdin.readline()

            if line == "":
                return

            line = line.strip()

            if not line:
                break

            if "=" in line:
                k, v = line.split("=", 1)
                attrs[k] = v

        if not attrs:
            continue

        action = handle_request(attrs)

        print(f"action={action}\n")
        sys.stdout.flush()


if __name__ == "__main__":
    main()

Don't forget to set execution bit:

chmod +x /usr/local/bin/policyguard.py

copy templates, if not already done:

mkdir -p /etc/pmg/templates/
cp /var/lib/pmg/templates/master.cf.in /etc/pmg/templates/
cp /var/lib/pmg/templates/main.cf.in /etc/pmg/templates/

Add the policy_service to the existing smtpd_sender_restrictions to main.cf.in:

smtpd_sender_restrictions =
        check_policy_service    unix:private/policygrd

Add the two lines to the bottom of the master.cf.in

policygrd unix  -       n       n       -       0       spawn
    user=nobody argv=/usr/local/bin/policyguard.py

pmgconfig sync --restart


How to test?

Connect to service:

apt install socat
socat - UNIX-CONNECT:/var/spool/postfix/private/policygrd

sender=Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.
        <--- empty line

action=dunno

sender=Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.
        <--- empty line

firebase-spf-policy: blocked sender domain babyamerica.com
action=reject Rejected for policy reasons [fbs]

sender=Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.
        <--- empty line

spf-policy: blocked sender by regex (^[a-z0-9]{1,3}\+bnc[A-Z0-9]{10,}@): Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.
action=reject reject for policy reasons [gg]

 

Filter nach der Übermittlung von Mailbody ('blocked')

Ab hier liegt der vollständige Mailbody vor und wird in die Spamassassin Filterqueue gesendet. Hier beginnen die CPU-lastigen Filter. E-Mails werden im before-queue-filtering nach wie vor 'rejected', d.h. die Annahme wird verweigert und der Absender bekommen eine entsprechende Meldung von ihrem eigenen Mailserver.
Im Tracking Center stehen diese E-Mails als 'blocked' mit roter Hinterlegung.


Rspamd als CustomCheck integrieren

Rspamd bringt eine eigene Filterlogik mit und bietet weitreichende Funktionen (u.a. gut funktionierendes Greylisting), welches wir hier allerdings alles brach liegen lassen.
Wir nutzen ausschließlich den CustomScore, der sich zu unserem SA Score addiert.

I'm using Rspamd together with a custom script.
Rspamd has many advantages and works well alongside the SpamAssassin filters I’ve fine-tuned. You get a additional score which is great to sort out some additional spam. Bonus: Often it does NOT correlate with my other filters, so it is an added value.


The drawback of the custom script is that no further SpamAssassin checks are executed if the returned score is greater than 5.0 (nobody in the German forum could tell me why this happens). Therefore, I limit the score to 4.9 when outputting it—unless the score is extremely high (>9.0), in which case the email is blocked directly via before-queue filtering.

A further drawback of integrating Rspamd as a custom check is that all of Rspamd's features beyond the score itself remain unused. Among other things, Rspamd supports a considerably more sophisticated greylisting algorithm compared to Proxmox MG, which cannot be used in combination with custom check integration.

 

I often had the issue that the custom check seemed too high from my perspective.
At the same time, this created a latent problem where the score was capped between 5.0 and 9.9.

I’ve since had good results by halving the score in the “sensitive” range between 1.0 and 9.8:

  • Small scores (both positive and negative) are output 1:1.
  • Very high scores > 9.8 are also output 1:1 and therefore immediately block the email in my SA.
  • Medium scores between 1.0 and 9.8 are scaled and output between 0.5 and 4.9. This means they don’t block any further SA checks, but they do reduce the likelihood of unjustified false positives in combination with other SA-Checks.
apt install rspamd


nano /etc/pmg/pmg.conf
#Enable the custom check script by adding or updating the following section:

section: admin
custom_check 1
custom_check_path /usr/local/bin/pmg-custom-check.sh

#Create the Custom Script

touch /usr/local/bin/pmg-custom-check.sh
chmod +x /usr/local/bin/pmg-custom-check.sh
cat > /usr/local/bin/pmg-custom-check.sh

 

Skript einfügen:

#!/usr/bin/env bash
set -euo pipefail

# PMG custom check API v1: args: APIVERSION QUEUEFILENAME
if [[ $# -ne 2 ]]; then
  echo "usage: $0 APIVERSION QUEUEFILENAME" >&2
  exit 1
fi

apiver="$1"
queue_file="$2"
RSPAMD_HOST="127.0.0.1"
RSPAMD_PORT="11333"

echo "v1"

# Rspamd check
 rspamc_out="$(rspamc -h "${RSPAMD_HOST}:${RSPAMD_PORT}" < "$queue_file" 2>/dev/null || true)"
 score="$(awk -F': ' '/^Score: /{split($2,a," "); print a[1]; exit}' <<<"$rspamc_out")"

if [[ -n "${score:-}" ]]; then
     capped_score=$(awk -v s="$score" 'BEGIN { if (s >= 1.0 && s <= 9.8) printf "%.1f", s/2; else print s }')
    echo "SCORE: ${capped_score}"
else
     echo "OK"
fi
exit 0

Server rebooten um Custom Checks zu aktivieren

 

Razor

Razor muss im PMG aktiviert werden

Configurations -> Spam Detector -> Options:
Use Razor2 checks: Yes

Und zusätzlich auf der Konsole einmalig registriert werden:

razor-admin -create
razor-admin -registe

Der Default Score ist viel zu niedrig, also erhöhen wir diesen:

Configurations -> Spam Detector -> Custom Scores:
RAZOR2_CF_RANGE_51_100 : 8
RAZOR2_CHECK : 8

-> Apply Scores nicht vergessen

 

Pyzor

Pyzor empfehle ich als Erweiterung zu Razor2. Beide stammen aus der gleichen Entwicklungslinie, besitzen jedoch andere Datenbanken und finden daher auch unterschiedliche Mails.

apt install pyzor

mkdir -p /etc/pmg/templates
cp  /var/lib/pmg/templates/init.pre.in /etc/pmg/templates/

/etc/pmg/templates/init.pre.in editieren und folgendes einfügen

loadplugin Mail::SpamAssassin::Plugin::Pyzor

/etc/pmg/spamassassin/local.cf editieren und folgendes einfügen

use_pyzor 1

Template Synchronisieren und PMG neustarten

pmgconfig sync --restart

Der Default Score ist viel zu niedrig, also erhöhen wir diesen:

Configurations -> Spam Detector -> Custom Scores:
PYZOR_CHECK : 8

-> Apply Scores nicht vergessen

 

Spamhausintegration in Spamassassin

Spamhaus bietet nicht nur die DNSBL (Ausschließlich Blockierung unseriöser Absender-IPs), sondern auch eine Spamassassin Integration, die manuell nachinstalliert werden muss und u.a. Inhaltsfilterung machen, z.B. Links im E-Mail Body zu bekannten Blacklist-Domains enthalten. Sie liefern (analog zu Pyzor und Razor) gute und sichere Funde. Die Filter im Tracking-Center beginnen alle mit SH.

Anleitung von hier: https://github.com/spamhaus/spamassassin-dqs?tab=readme-ov-file#instructions-for-spamassassin-400

Achtung: Hier muss ihr Spamhaus DQ-Key eingefügt werden.

apt install git
git clone https://github.com/spamhaus/spamassassin-dqs
cd spamassassin-dqs/4.0.0+
sed -i -e 's/your_DQS_key/<your spamhaus key>/g' sh.cf
sed -i -e 's/your_DQS_key/<your spamhaus key>/g' sh_hbl.cf

Edit sh.pre with your editor of choice, and look at the first line
You will need to replace <config_directory> with your actual configuration directory /etc/mail/spamassassin, the line will become:

loadplugin       Mail::SpamAssassin::Plugin::SH /etc/mail/spamassassin/SH.pm

If your key is not HBL enabled, this is what needs to be done:

cp sh.cf /etc/mail/spamassassin
cp sh_scores.cf /etc/mail/spamassassin

Scores sind alle schon sehr hoch gesetzt, Custom Scores sind daher nicht notwendig.


Geoblocking

Ich setze für einige Länder 7 Punkte, für andere 4, für Deutschland sogar -3. Der Rest (EU, USA) erhält gar keinen Score, also 0.
(This product includes GeoLite2 Data created by MaxMind, available from https://www.maxmind.com)

GeoIP Perl Modul installieren und Datenbank in spezifisches Verzeichnis herunterladen

apt install libgeoip2-perl
mkdir -p /var/lib/GeoIP
cd /var/lib/GeoIP
wget https://goto-url.de/GeoLite2-Country.mmdb

Ich gehe davon aus, dass ihr die init.pre-in bereits im lokalen Templateordner habt, weil wir sie für pyzor bereits anpassen mussten, ansonsten von /var/lib/templates nach /etc/pmg/templates kopieren.

/etc/pmg/templages/init.pre.in bearbeiten und folgende Zeile einkommentieren

loadplugin Mail::SpamAssassin::Plugin::RelayCountry

Eintrag in die /etc/mail/spamassassin/custom.cf

ifplugin Mail::SpamAssassin::Plugin::RelayCountry

    geodb_module GeoIP2
    geodb_search_path /var/lib/GeoIP/

header          RELAYCOUNTRY_BAD X-Relay-Countries =~ /^(AR|CN|BR|IN|VN|RU|IR|PK|ID|BD|TR|NG|PH|TH|KP|SY|AF|YE|KZ|UA|BY|MD|GE|AM|AZ|TJ|KG|UZ|TM|MM|KH|LA|ET|GH|KE|TZ|UG)/
describe        RELAYCOUNTRY_BAD First untrusted relay is Argentina, China, Brazil, India, Vietnam, Russia, Iran, Pakistan, Indonesien, Bangladesh, Türkei, Nigeria, Philippinen, Thailand, Nordkorea, Syrien, Afghanistan, Jemen, Ukraine, Belarus, Moldawien, Georgien, Armenien, Aserbaidschan, Tadschikistan, Kirgisistan, Usbekistan, Turkmenistan, Myanmar, Kambodscha, Laos, Aethiopien, Ghana, Kenia, Tansania, Uganda
score           RELAYCOUNTRY_BAD 7.0

header          RELAYCOUNTRY_SUSP X-Relay-Countries =~ /^(LT|EE|HU|RO|AU|IL|GB|RS|AL|BG|MK|ME|BA)/
describe        RELAYCOUNTRY_SUSP First untrusted relay is Lithuania, Estonia, Hungary, Romania, Australien, Israel, GB, Serbien, Albanien, Bulgarien, Nordmazedonien, Montenegro, Bosnien
score           RELAYCOUNTRY_SUSP 4.0

header        RELAYCOUNTRY_GOOD X-Relay-Countries =~ /^(EU|DE)/
describe      RELAYCOUNTRY_GOOD First untrusted relay is Deuschland or European Union :-)
score           RELAYCOUNTRY_GOOD -3.0

    add_header all Relay-Country _RELAYCOUNTRY_

endif # Mail::SpamAssassin::Plugin::RelayCountry

PMG Config syncen (Dann wird das hier verwendete Template angewendet)

pmgconfig sync --restart

Testen, ob es funktioniert, oder ob GeoIP einen Fehler wirft. Im optimalen Fall seht ihr einen RELAYCOUNTRY Eintrag (das ist aber nicht zwangsweise so, wenn kein Score verteilt wird)

spamassassin -t < test.eml

Weitere Blocklisten
Die folgenden Listen finden sehr viel Spam, analog zu Pyzor, Razor2 und Spamhaus, daher nicht minder wichtig, hier die Scores zu erhöhen

Configuration -> Spam Detector -> Custom Scores

Blacklistfilter analog zu Pyzor, Razor, Spamhaus
RCVD_IN_BL_SPAMCOP: 8
URIBL_ABUSE_SURBL : 8
URIBL_BLACK : 8
URIBL_CT_SURBL : 8
URIBL_PH_SURBL : 4
URI_WP_HACKED_2 : 4

Apply Score nicht vergessen!
(Spamcop blockiert Google -> negieren wir weiter unten)

Weitere Filter

Ebenfalls bereits integriert und scharf geschaltet werden sollten:

Spam-Merkmale:
T_TVD_MIME_EPI : 8
GB_STORAGE_GOOGLE_HTM : 6
KAM_STORAGE_GOOGLE : 6
AC_BR_BONANZA : 6
FSL_BULK_SIG : 6
FORGED_OUTLOOK_HTML : 6
FROMSPACE : 4
KAM_LAZY_DOMAIN_SECURITY : 4

Datum falsch:
DATE_IN_PAST_03_06 : 6
DATE_IN_PAST_06_12 : 6
DATE_IN_PAST_12_24 : 6
DATE_IN_PAST_24_48 : 6
DATE_IN_PAST_48_72 : 6

Absender Auffälligkeiten:
RCVD_HELO_IP_MISMATCH : 6
FROM_FMBLA_NEWDOM: 6
FROM_FMBLA_NEWDOM14: 6
FROM_FMBLA_NEWDOM28 : 6
T_SPF_PERMERROR : 6
SPF_SOFTFAIL : 4
MAILING_LIST_MULTI : 4

gute Whitelist:
DKIMWL_WL_HIGH : -6


Die folgenden Filter sorgten bei mir für Fehlfilterungen, deshalb habe ich einen Score von 0 zugewiesen:
SPF_HELO_NONE : 0
KAM_MARKSPAM : 0
HTTPS_HTTP_MISMATCH : 0
DEAR_SOMETHING : 0

Apply Score nicht vergessen!

Problematische Blocklisten deaktivieren

Validity reputation Listen machen nur Stress und filtern nicht wirklich. Man sollte sie in der custom.cf deaktivieren

Eintrag in die /etc/mail/spamassassin/custom.cf

dns_query_restriction deny sa-trusted.bondedsender.org
dns_query_restriction deny sa-accredit.habeas.com
dns_query_restriction deny bl.score.senderscore.com

Filter kompilieren
Damit die Filter schneller abgearbeitet werden, sollten sie kompiliert werden. Wir benötigen hierzu ein paar Pakete und ein Moduleintrag in der custom.cf von Spamassassin.

apt install make gcc re2c

Eintrag in die /etc/mail/spamassassin/custom.cf

loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody

Danach kompilieren und am Ende Spamassassin neustarten mit:

sa-compile
systemctl restart pmg-smtp-filter

Nun haben wir alle Drittanbieterplugins ausgeschöpft. Aber wir können noch selbst SA-Filter erstellen. Einer der größten Übel sind die "BCC-Spam". Jemand erstellt einen Freemail Account bei einem Provider ohne ausgehendes Limit und schickt Spam E-Mails, bei dem alle Empfänger in den BCC kopiert werden.
Um sie zu erkennen, müssen wir To und CC dahingehend überprüfen, ob eine E-Mail Adresse aus der Transportliste enthalten ist. Wenn nein, stehen wir im BCC.
Achtung: Wer in Mailinglisten unterwegs ist, könnte hiermit Probleme bekommen. Auch Weiterleitungen von Free-Mail-Accounts an die eigene Domain sind hiervon betroffen. Hier ggf. mit der Welcomelist arbeiten.

Transportliste als gültige Empfänger:

Folgendes Skript erstellen, welches täglich die Transportliste für unsere BCC-Regel importiert (damit wir niemals vergessen können, diese Liste zu aktualisieren).

cat > /usr/local/bin/update-sa-bcc-whitelist.sh

#!/bin/bash
DOMAINS=$(cut -d' ' -f1 /etc/pmg/transport | sed 's/\./\\./g; s/-/\\-/g' | tr '\n' '|' | sed 's/|$//')

cat <<EOF > /etc/mail/spamassassin/98-bcc-transport-domains.cf
header   BCC_SPAM_TOCC ToCc !~ /\@(${DOMAINS})/i
score    BCC_SPAM_TOCC 2.0
describe BCC_SPAM_TOCC Domain nicht in Transport-Liste
EOF
sa-compile
systemctl restart pmg-smtp-filter

Skript als ausführbar markieren und einmalig ausführen:
chmod +x /usr/local/bin/update-sa-bcc-whitelist.sh
/usr/local/bin/update-sa-bcc-whitelist.sh

Cron Eintrag für die tägliche Aktualisierung dieser Liste

Cron Table bearbeiten

crontab -e

Zeile einfügen

echo "0 0 * * 1 root /usr/local/bin/update-sa-bcc-whitelist.sh" > /etc/cron.d/sa-bcc-update

### Custom Rules in /etc/mail/spamassassin
# Die Regel 98-bcc-transport-domains.cf sollte nun schon existieren


Unseriöse Absenderdomains und BCC Spam

Markiert alle E-Mails unerwünschter Absenderdomains. Diese erhalten automatisch einen Score von 3.0, weil sie nahezu immer unseriös sind

Wenn die E-Mail von einer unseriösen TLD stammt, und ich zusätzlich nur im BCC stehe, dann wird ein weiterer "Bonus"-Score von 2.0 vergeben.
E-Mails die dieses Kriterium erfüllen sind fast immer Spam, Ausnahmen könnten ausländische Mailinglisten sein. Die müsste man in die Welcomelist eintragen.

cat <<EOF > /etc/mail/spamassassin/99-BCC_TLD.cf
header FROM_TLD_OTHER From =~ /\.(?!(?:com|net|info|org|de|at|ch|eu|shop)(?:>|$))[A-Za-z0-9-]+>?$/i
score FROM_TLD_OTHER 3.0
describe FROM_TLD_OTHER Sender uses unusual or risky TLD

meta BCC_AND_UNKNOWN_TLD (FROM_TLD_OTHER && BCC_SPAM_TOCC)
score BCC_AND_UNKNOWN_TLD 2.0
describe awkward sender and unknown recipient

Positive Word List

Diese Regel(n) beinhalten Wörter, die i.d.R. darauf hindeuten, dass es sich um seriöse E-Mails handelt.

Wörter, die sich für einen negativen Score eignen sind insbesondere Adressdaten (Straße, PLZ, Nachname), sodass Bestellbestätigungen stets ankommen.
Auch Wörter aus eurer E-Mail Signatur sind super: Handelsregisternummer, Umsatzsteuer-ID, PGP Key, was ihr so mitsendet. Dann ist sichergestellt, dass Anworten auf eure E-Mails zuverlässig ankommen.
Aufpassen bei Wörtern, die auch in eurer E-Mail Adresse stehen. Wenn eine Domain <berufsbezeichnung>-<wohnort>.de lautet, eignet sich beides eher weniger für eine negative Bepunktung, sonst kommen E-Mail an mit Inhalt: "Hallo <berufsbezeichnung>-<wohnort>.de, haben sie auch Probleme mit ihrer Potenz".

cat <<EOF > /etc/mail/spamassassin/99-positive-word-list
body POS_STADT    /\bStadt\b(?![-\w.@])/i
body POS_NACHNAME  /\bNachname\b(?![-\w.@])/i
body POS_VORNAME     /\bVorname\b(?![-\w.@])/i
body POS_88888   /\b88888\b(?![-\w.@])/i              # PLZ

score POS_STADT -2.0
score POS_NACHNAME  -2.0
score POS_VORNAME    -2.0
score POS_88888   -4.0
EOF

Positive Scores für besonders vertrauenswürdige Versender

Bestimmten Freemailer, die ihre Systeme im Griff haben bekommen positiven Score. (Nicht Gmail!). Hierzu gehört t-online.de, gmx.de, web.de, ionos/strato. Diese werden mit einem hohen Negativscore belohnt.
Ist aber sicher alles sehr individuell.

Darüber hinaus bekommen auch .de Domains positiven Score, weil sich im allgemeinen gezeigt hat, dass Spammer willkürliche Domains verwenden. Auch .de-Domains, aber die Fehlfilterquote ist aufgrund der vielen, eintrudelnden Mails aus eurem eigenen Land sicherlich erheblich höher.

header SERIOUS_FREEMAILER  Received =~ /\b(yahoo\.com|mout\.kundenserver\.de|mout\.web\.de|mout\.gmx\.net|mailout\d+\.t-online\.de|cc-smtpout\d+\.netcologne\.de)\b/i
score  SERIOUS_FREEMAILER  -6.0
describe SERIOUS_FREEMAILER Mail von bevorzugtem SMTP-Out

header     __LOCAL_DE_DOMAIN_FROM     From =~ /\@[^>]+\.(de)\b/i
header     __LOCAL_DE_DOMAIN_ENVFROM  EnvelopeFrom =~ /\@[^>]+\.(de)\b/i
meta       LOCAL_DE_DOMAIN (__LOCAL_DE_DOMAIN_FROM && __LOCAL_DE_DOMAIN_ENVFROM)
describe   LOCAL_DE_DOMAIN Sender domain is .de
score      LOCAL_DE_DOMAIN -2.0

 

Fine-Tuning:

Leider schlägt Pyzor manchmal auch bei legitimen Freemails an und Spamcop bei Google.
Deshalb binden wir Spamcop nicht via DNSBL ein, sondern erhöhen den Custom-Score und negieren den Google Effekt hier.

 
# Pyzor Freemail
meta     FREEMAIL_PYZOR_NEG   (FREEMAIL_FROM && PYZOR_CHECK)
score    FREEMAIL_PYZOR_NEG   -8.0
describe FREEMAIL_PYZOR_NEG   Negate Pyzor Hit on Freemail


# Spamcop Google Negate
header   __HELO_GOOGLE   Received =~ /\.google\.com/i
meta     SPAMCOP_GOOGLE_NEGATE   (RCVD_IN_BL_SPAMCOP_NET && __HELO_GOOGLE)
score    SPAMCOP_GOOGLE_NEGATE   -8.0
describe SPAMCOP_GOOGLE_NEGATE   Negate Spamcop Hit on Google

Danach:

sa-compile
systemctl restart pmg-smtp-filter

Fazit

Ca. 50 % aller E-Mails sind gewünscht, d.h. 50% sind Spam.

Von 100% aller Spam sind:

- 3% per SPF Regel oder aufgrund ungültiger Absenderdomain oder HELO Check gefiltert
- 50 % via DNSBL gefiltert
- 25 % kommen von Google und werden über mein oben geschriebenen Policyguard gefiltert
=======================
Diese E-Mails werden bereits von Postfix aussortiert und sind im Tracking Center als REJECTED gekennzeichnet.

- 22 % gelangen in Spamassassin und werden dort gefiltert:

Von den 100% im SA gefilterten E-Mails:
- Werden ca. 50% durch Razor, Pyzor, SH, Geoblocking, Spamcop, URI Blocklisten erwischt.
- Ca. 30% werden durch andere Inhaltsfilter, BCC oder TLD-Score erwischt.

20% kommen durch, weil kein Inhaltsfilter anspricht

Das entspricht einer Block-Quote von ca. 95%.

Die Prozentangaben sind natürlich nicht für bare Münze zu nehmen:
- Das variiert von Tag zu Tag.
- Viele Filter überschneiden sich: So würden ohne DNSBL durchaus einige Mails an SPF scheitern. DNSBL überlappen sich zu weiten Teilen.

Keine guten Ergebnisse habe ich mit den folgenden Werkzeugen erreicht

DCC empfehle ich nicht.
Die Integration ist nicht trivial (Programm aus den Quellen selbst kompilieren und als Dienst installieren).
Das Problem: Standardmäßig werden hier zwar nur wenige Punkte vergeben, aber leider findet DCC kein "Spam" sondern "Bulkmail".
Es werden u.a. Eingangsbestätigungen von Versicherungen ("Vielen Dank für ihre E-Mail. Wir melden uns schnellstmöglich bei ihnen") und ähnliche Mails gefiltert.