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!
Hinweis: Eine umfangreichere Version dieses Skripts mit einer "autoblacklist" findet sich hier: https://krause-computer.de/blog/policyguard-early-reject-postfix-policy-spamfilter-fuer-google-co
apt install python3-dnspythoncat > /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 0Server 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 -registeDer 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.cfEdit 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/spamassassinScores 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.mmdbIch 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_AR X-Relay-Countries =~ /^(AR)/
describe RELAYCOUNTRY_BAD_AR First untrusted relay is Argentina
score RELAYCOUNTRY_BAD_AR 7.0
header RELAYCOUNTRY_BAD_CN X-Relay-Countries =~ /^(CN)/
describe RELAYCOUNTRY_BAD_CN First untrusted relay is China
score RELAYCOUNTRY_BAD_CN 7.0
header RELAYCOUNTRY_BAD_BR X-Relay-Countries =~ /^(BR)/
describe RELAYCOUNTRY_BAD_BR First untrusted relay is Brazil
score RELAYCOUNTRY_BAD_BR 7.0
header RELAYCOUNTRY_BAD_IN X-Relay-Countries =~ /^(IN)/
describe RELAYCOUNTRY_BAD_IN First untrusted relay is India
score RELAYCOUNTRY_BAD_IN 7.0
header RELAYCOUNTRY_BAD_VN X-Relay-Countries =~ /^(VN)/
describe RELAYCOUNTRY_BAD_VN First untrusted relay is Vietnam
score RELAYCOUNTRY_BAD_VN 7.0
header RELAYCOUNTRY_BAD_RU X-Relay-Countries =~ /^(RU)/
describe RELAYCOUNTRY_BAD_RU First untrusted relay is Russia
score RELAYCOUNTRY_BAD_RU 7.0
header RELAYCOUNTRY_BAD_IR X-Relay-Countries =~ /^(IR)/
describe RELAYCOUNTRY_BAD_IR First untrusted relay is Iran
score RELAYCOUNTRY_BAD_IR 7.0
header RELAYCOUNTRY_BAD_PK X-Relay-Countries =~ /^(PK)/
describe RELAYCOUNTRY_BAD_PK First untrusted relay is Pakistan
score RELAYCOUNTRY_BAD_PK 7.0
header RELAYCOUNTRY_BAD_ID X-Relay-Countries =~ /^(ID)/
describe RELAYCOUNTRY_BAD_ID First untrusted relay is Indonesien
score RELAYCOUNTRY_BAD_ID 7.0
header RELAYCOUNTRY_BAD_BD X-Relay-Countries =~ /^(BD)/
describe RELAYCOUNTRY_BAD_BD First untrusted relay is Bangladesh
score RELAYCOUNTRY_BAD_BD 7.0
header RELAYCOUNTRY_BAD_TR X-Relay-Countries =~ /^(TR)/
describe RELAYCOUNTRY_BAD_TR First untrusted relay is Türkei
score RELAYCOUNTRY_BAD_TR 7.0
header RELAYCOUNTRY_BAD_NG X-Relay-Countries =~ /^(NG)/
describe RELAYCOUNTRY_BAD_NG First untrusted relay is Nigeria
score RELAYCOUNTRY_BAD_NG 7.0
header RELAYCOUNTRY_BAD_PH X-Relay-Countries =~ /^(PH)/
describe RELAYCOUNTRY_BAD_PH First untrusted relay is Philippinen
score RELAYCOUNTRY_BAD_PH 7.0
header RELAYCOUNTRY_BAD_TH X-Relay-Countries =~ /^(TH)/
describe RELAYCOUNTRY_BAD_TH First untrusted relay is Thailand
score RELAYCOUNTRY_BAD_TH 7.0
header RELAYCOUNTRY_BAD_KP X-Relay-Countries =~ /^(KP)/
describe RELAYCOUNTRY_BAD_KP First untrusted relay is Nordkorea
score RELAYCOUNTRY_BAD_KP 7.0
header RELAYCOUNTRY_BAD_SY X-Relay-Countries =~ /^(SY)/
describe RELAYCOUNTRY_BAD_SY First untrusted relay is Syrien
score RELAYCOUNTRY_BAD_SY 7.0
header RELAYCOUNTRY_BAD_AF X-Relay-Countries =~ /^(AF)/
describe RELAYCOUNTRY_BAD_AF First untrusted relay is Afghanistan
score RELAYCOUNTRY_BAD_AF 7.0
header RELAYCOUNTRY_BAD_YE X-Relay-Countries =~ /^(YE)/
describe RELAYCOUNTRY_BAD_YE First untrusted relay is Jemen
score RELAYCOUNTRY_BAD_YE 7.0
header RELAYCOUNTRY_BAD_KZ X-Relay-Countries =~ /^(KZ)/
describe RELAYCOUNTRY_BAD_KZ First untrusted relay is Kasachstan
score RELAYCOUNTRY_BAD_KZ 7.0
header RELAYCOUNTRY_BAD_UA X-Relay-Countries =~ /^(UA)/
describe RELAYCOUNTRY_BAD_UA First untrusted relay is Ukraine
score RELAYCOUNTRY_BAD_UA 7.0
header RELAYCOUNTRY_BAD_BY X-Relay-Countries =~ /^(BY)/
describe RELAYCOUNTRY_BAD_BY First untrusted relay is Belarus
score RELAYCOUNTRY_BAD_BY 7.0
header RELAYCOUNTRY_BAD_MD X-Relay-Countries =~ /^(MD)/
describe RELAYCOUNTRY_BAD_MD First untrusted relay is Moldawien
score RELAYCOUNTRY_BAD_MD 7.0
header RELAYCOUNTRY_BAD_GE X-Relay-Countries =~ /^(GE)/
describe RELAYCOUNTRY_BAD_GE First untrusted relay is Georgien
score RELAYCOUNTRY_BAD_GE 7.0
header RELAYCOUNTRY_BAD_AM X-Relay-Countries =~ /^(AM)/
describe RELAYCOUNTRY_BAD_AM First untrusted relay is Armenien
score RELAYCOUNTRY_BAD_AM 7.0
header RELAYCOUNTRY_BAD_AZ X-Relay-Countries =~ /^(AZ)/
describe RELAYCOUNTRY_BAD_AZ First untrusted relay is Aserbaidschan
score RELAYCOUNTRY_BAD_AZ 7.0
header RELAYCOUNTRY_BAD_TJ X-Relay-Countries =~ /^(TJ)/
describe RELAYCOUNTRY_BAD_TJ First untrusted relay is Tadschikistan
score RELAYCOUNTRY_BAD_TJ 7.0
header RELAYCOUNTRY_BAD_KG X-Relay-Countries =~ /^(KG)/
describe RELAYCOUNTRY_BAD_KG First untrusted relay is Kirgisistan
score RELAYCOUNTRY_BAD_KG 7.0
header RELAYCOUNTRY_BAD_UZ X-Relay-Countries =~ /^(UZ)/
describe RELAYCOUNTRY_BAD_UZ First untrusted relay is Usbekistan
score RELAYCOUNTRY_BAD_UZ 7.0
header RELAYCOUNTRY_BAD_TM X-Relay-Countries =~ /^(TM)/
describe RELAYCOUNTRY_BAD_TM First untrusted relay is Turkmenistan
score RELAYCOUNTRY_BAD_TM 7.0
header RELAYCOUNTRY_BAD_MM X-Relay-Countries =~ /^(MM)/
describe RELAYCOUNTRY_BAD_MM First untrusted relay is Myanmar
score RELAYCOUNTRY_BAD_MM 7.0
header RELAYCOUNTRY_BAD_KH X-Relay-Countries =~ /^(KH)/
describe RELAYCOUNTRY_BAD_KH First untrusted relay is Kambodscha
score RELAYCOUNTRY_BAD_KH 7.0
header RELAYCOUNTRY_BAD_LA X-Relay-Countries =~ /^(LA)/
describe RELAYCOUNTRY_BAD_LA First untrusted relay is Laos
score RELAYCOUNTRY_BAD_LA 7.0
header RELAYCOUNTRY_BAD_ET X-Relay-Countries =~ /^(ET)/
describe RELAYCOUNTRY_BAD_ET First untrusted relay is Aethiopien
score RELAYCOUNTRY_BAD_ET 7.0
header RELAYCOUNTRY_BAD_GH X-Relay-Countries =~ /^(GH)/
describe RELAYCOUNTRY_BAD_GH First untrusted relay is Ghana
score RELAYCOUNTRY_BAD_GH 7.0
header RELAYCOUNTRY_BAD_KE X-Relay-Countries =~ /^(KE)/
describe RELAYCOUNTRY_BAD_KE First untrusted relay is Kenia
score RELAYCOUNTRY_BAD_KE 7.0
header RELAYCOUNTRY_BAD_TZ X-Relay-Countries =~ /^(TZ)/
describe RELAYCOUNTRY_BAD_TZ First untrusted relay is Tansania
score RELAYCOUNTRY_BAD_TZ 7.0
header RELAYCOUNTRY_BAD_UG X-Relay-Countries =~ /^(UG)/
describe RELAYCOUNTRY_BAD_UG First untrusted relay is Uganda
score RELAYCOUNTRY_BAD_UG 7.0
header RELAYCOUNTRY_SUSP_LT X-Relay-Countries =~ /^(LT)/
describe RELAYCOUNTRY_SUSP_LT First untrusted relay is Lithuania
score RELAYCOUNTRY_SUSP_LT 4.0
header RELAYCOUNTRY_SUSP_EE X-Relay-Countries =~ /^(EE)/
describe RELAYCOUNTRY_SUSP_EE First untrusted relay is Estonia
score RELAYCOUNTRY_SUSP_EE 4.0
header RELAYCOUNTRY_SUSP_HU X-Relay-Countries =~ /^(HU)/
describe RELAYCOUNTRY_SUSP_HU First untrusted relay is Hungary
score RELAYCOUNTRY_SUSP_HU 4.0
header RELAYCOUNTRY_SUSP_RO X-Relay-Countries =~ /^(RO)/
describe RELAYCOUNTRY_SUSP_RO First untrusted relay is Romania
score RELAYCOUNTRY_SUSP_RO 4.0
header RELAYCOUNTRY_SUSP_AU X-Relay-Countries =~ /^(AU)/
describe RELAYCOUNTRY_SUSP_AU First untrusted relay is Australien
score RELAYCOUNTRY_SUSP_AU 4.0
header RELAYCOUNTRY_SUSP_IL X-Relay-Countries =~ /^(IL)/
describe RELAYCOUNTRY_SUSP_IL First untrusted relay is Israel
score RELAYCOUNTRY_SUSP_IL 4.0
header RELAYCOUNTRY_SUSP_RS X-Relay-Countries =~ /^(RS)/
describe RELAYCOUNTRY_SUSP_RS First untrusted relay is Serbien
score RELAYCOUNTRY_SUSP_RS 4.0
header RELAYCOUNTRY_SUSP_AL X-Relay-Countries =~ /^(AL)/
describe RELAYCOUNTRY_SUSP_AL First untrusted relay is Albanien
score RELAYCOUNTRY_SUSP_AL 4.0
header RELAYCOUNTRY_SUSP_BG X-Relay-Countries =~ /^(BG)/
describe RELAYCOUNTRY_SUSP_BG First untrusted relay is Bulgarien
score RELAYCOUNTRY_SUSP_BG 4.0
header RELAYCOUNTRY_SUSP_MK X-Relay-Countries =~ /^(MK)/
describe RELAYCOUNTRY_SUSP_MK First untrusted relay is Nordmazedonien
score RELAYCOUNTRY_SUSP_MK 4.0
header RELAYCOUNTRY_SUSP_ME X-Relay-Countries =~ /^(ME)/
describe RELAYCOUNTRY_SUSP_ME First untrusted relay is Montenegro
score RELAYCOUNTRY_SUSP_ME 4.0
header RELAYCOUNTRY_SUSP_BA X-Relay-Countries =~ /^(BA)/
describe RELAYCOUNTRY_SUSP_BA First untrusted relay is Bosnien
score RELAYCOUNTRY_SUSP_BA 4.0
header RELAYCOUNTRY_GOOD_DE X-Relay-Countries =~ /^(DE)/
describe RELAYCOUNTRY_GOOD_DE First untrusted relay is Deutschland :-)
score RELAYCOUNTRY_GOOD_DE -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.comFilter 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-filterSkript 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 recipientPositive 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
EOFPositive 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 GoogleDanach:
sa-compile
systemctl restart pmg-smtp-filterFazit
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.