[TUTORIAL] Installation Advisory

ivenae

Well-Known Member
Feb 11, 2022
145
62
48
42
I've been intensively working with Proxmox MG over the past few days and found the documentation not always helpful.
My spam has been reduced by 95%, and I would like to explain here what I changed compared to the out-of-the-box installation to achieve this. From my perspective, this is something that is insufficiently covered in many guides.
Thanks to the Proxmox team for this excellent product and thanks to everyone whose comments and recommendations I implemented. Feel free to comment — if you like this post, leave a subscription and a like … just kidding.

My policy is to reject emails outright if they are spam, because the biggest issue in my view is that false positives ending up in my spam folder are assumed to have been delivered by the sender. This obviously does not protect against cases where ordered concert tickets or similar items never arrive because the sender's mail server is misconfigured. The tickets are then likely "gone" and require significant follow-up effort. A drawback of the "I won't accept your stuff" approach.

Base Configuration — Proxmox Mail Gateway
======================================

Configuration -> Mail Proxy -> Relay Domains:

Enter your domains here
#Emails addressed to these domains will be accepted by Proxmox at all

Configuration -> Mail Proxy -> Options:
# Message Size (bytes): 104857600
Increase significantly, otherwise large emails will cause issues with spam scoring and DKIM signatures.

Reject Unknown Clients: No
# The sender server's rDNS must be configured correctly. Works fine in 98% of cases, but in 2% small admins have not maintained their environment properly — they then have no chance of sending you emails. This check applies before any whitelist. Therefore, it is better to leave it disabled.

Reject Unknown Senders: Yes
# The sender domain must exist for the email to be accepted. Emails from fictitious sender domains are temporarily rejected (4xx error code). This accounts for approximately 20% of spam that would otherwise risk slipping through SA (SpamAssassin). In approximately 1% of cases the check fails, in which case the email is accepted on the next delivery attempt 5 minutes later. In some cases spam is submitted over 100 times and never accepted, which is unfortunately somewhat annoying.

Verify Receivers: Yes (550)
# Ensures that emails addressed to unknown mailboxes are rejected outright. Otherwise, bounces are generated from your mailserver to Proxmox, which remain on the Proxmox side without the sender being notified.
# Edit: I experienced several attempts to guess the email addresses of executives. The senders were an incredibly large botnet from around the world (each attempt via a different server). I therefore switched to disabling this option and setting up an unmonitored catch-all address. Drawback: Legitimate senders will no longer be notified of typos in the recipient address.

Use Greylisting for IPv4: No
# Greylisting can significantly delay mail acceptance, which is highly problematic for account verification emails. Unfortunately, PMG does not support the ability to temporarily reject only emails with a questionable spam score, as rspamd does. In the quality of sender triplet evaluation offered here, it is in my view not usable.

Use SPF: (Yes)
# Emails with a failing SPF record are rejected. I have had few issues with this so far, but it could potentially be problematic. Carries a certain risk.

Before Queue Filtering: Yes
# A core concern, as subsequent NDRs on blocked emails are themselves a form of spam.

Configuration -> Mail Proxy -> Transports:
Enter your mail server here
<domain> Host: 172.30.0.3, Use MX: No
# MX: No — because the MX record points to the PMG

Configuration -> Spam Detector -> Options:
Max Spam Size (bytes): 104857600
# Must match the value set under Mail Proxy.

Mail Filter:
Enable Block Spam (Level 10): Emails with a high spam probability should be rejected directly.
Configure Mark Spam and Quarantine according to your own preferences.


Installation and Configuration of Effective Mail Filters
===========================================


The order of the filters listed here is significant. Filters discussed earlier in this guide filter out emails sooner and require significantly less CPU load than the later SA filters.
At the same time, the filters covered early in this guide have a higher sensitivity (i.e., a lower false-positive rate).
You can therefore work through this guide partially at first and still achieve quite good results.
Regardless, tuning the Custom Scores at the end of the first post is in my view important — that is after all the core of PMG.


Filters Before Mail Header Submission ('rejected')

Emails rejected at this point appear in the Tracking Center as rejected with a black cross symbol. At this point, no information from the email header is available, except the sending server (HELO + IP), Envelope-From, and recipient. None of the Welcome Lists within PMG apply here.


DNS Blocklists (DNSBL):
(Filters approximately 50% of your spam)

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

We need "unbound" for recursive DNS queries — this is a mandatory prerequisite for all DNSBLs.

Code:
apt install unbound

Edit /etc/resolv.conf

Code:
nameserver 127.0.0.1

For LXC containers instead: Set via Proxmox VE in the DNS options

Configuration -> Mail Proxy -> Options:
A Spamhaus account must be created and the individual DQS ID inserted. All others can be used without registration.

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


Filters After Mail Header Submission, Before Mail Body Submission ('rejected')

Emails rejected at this point appear in the Tracking Center as rejected with a black cross symbol. At this point the outer email header is available — Envelope-From, recipient, and further email headers — but not yet Subject or (Body-)From.

The Welcomelist under Configuration -> Mail Proxy -> Welcomelist should override this service (untested), as it is applied to the Postfix senderaccess. The Mail Filter -> Who Object -> Welcomelist is only evaluated within SA — it has no effect here.


Handling Google Spam: Permanent Exception in All DNSBLs
(Filters approximately 25% of your spam)

Google sends spam on multiple levels, which the following guide allows us to reject directly in Postfix/Postscreen.

  • firebaseapp.com (Google Service) exclusively sends spam
  • Firebase-authorized services: Send via Google servers using their own domain. Identifiable by the authorizing SPF entry _sfp.firebasemail.com
  • Google Groups: Spammers use Google Groups lists because they require no opt-in from the recipient, and mailing lists are assigned a default score of "-1.0" by SpamAssassin. In addition to the actual spam, you also receive NDRs or angry replies from recipients.
  • Google Usercontent: A Google server that exclusively sends spam
All of these services can be blocked very early in Postfix. Bonus: Since Google as a sender is generally not listed on the DNSBLs from the previous chapter, there is no overlap between these filters — meaning yes: with the DNSBLs and this Google filter, 75% of spam is effectively filtered with almost no false positives! Description here:


I personally use the extended version available there with auto-blacklist. This blocks domains that have only ever sent junk directly in Postfix.


Integrating Rspamd as a CustomCheck

Rspamd has its own filter logic and offers extensive features (including well-functioning greylisting), all of which we leave unused here.
We exclusively use the CustomScore, which adds to our SA score.

Setup guide here:



Razor

Razor must be enabled in PMG

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

And additionally registered once via console:

Code:
razor-admin -create
razor-admin -register

The default score is far too low, so we increase it:

Configurations -> Spam Detector -> Custom Scores:

RAZOR2_CF_RANGE_51_100 : 8
RAZOR2_CHECK : 8

-> Don't forget to Apply Scores


Pyzor

I recommend Pyzor as a complement to Razor2. Both originate from the same development lineage but use different databases and therefore also detect different emails.

Code:
apt install pyzor

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

Edit /etc/pmg/templates/init.pre.in and insert the following:
Code:
loadplugin Mail::SpamAssassin::Plugin::Pyzor

Edit /etc/pmg/spamassassin/local.cf and insert the following:
Code:
use_pyzor 1

Synchronize template and restart PMG:
Code:
pmgconfig sync --restart

The default score is far too low, so we increase it:

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

-> Don't forget to Apply Scores


Spamhaus Integration in SpamAssassin

Spamhaus offers not only the DNSBL (exclusively blocking illegitimate sender IPs), but also a SpamAssassin integration that must be manually installed and provides, among other things, content filtering — for example, detecting links in the email body to known blacklisted domains. Like Pyzor and Razor, it delivers reliable and accurate results. The filters in the Tracking Center all begin with SH.

Instructions from here: https://github.com/spamhaus/spamassassin-dqs?tab=readme-ov-file#instructions-for-spamassassin-400

Note: Your Spamhaus DQ key must be inserted here.
Code:
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:

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

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

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

Scores are already set very high; Custom Scores are therefore not necessary.


Geoblocking

I assign 7 points for some countries, 4 for others, and even -3 for Germany. The rest (EU, USA) receive no score at all, i.e., 0.
(This product includes GeoLite2 Data created by MaxMind, available from https://www.maxmind.com)

Install GeoIP Perl module and download database to specific directory:
Code:
apt install libgeoip2-perl
mkdir -p /var/lib/GeoIP
cd /var/lib/GeoIP
wget https://goto-url.de/GeoLite2-Country.mmdb

I assume you already have init.pre.in in the local template folder, as we already had to modify it for Pyzor — otherwise copy it from /var/lib/templates to /etc/pmg/templates.

Edit /etc/pmg/templates/init.pre.in and uncomment the following line:
Code:
loadplugin Mail::SpamAssassin::Plugin::RelayCountry

Add entry to /etc/mail/spamassassin/custom.cf:
Code:
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

Sync PMG config (this applies the template used here):
Code:
pmgconfig sync --restart

Test whether it works or whether GeoIP throws an error. In the optimal case you will see a RELAYCOUNTRY entry (though this is not necessarily the case if no score is assigned):
Code:
spamassassin -t < test.eml


Additional Blocklists

The following lists detect a large amount of spam — analogous to Pyzor, Razor2, and Spamhaus — and are therefore equally important. Increase the scores accordingly here:

Configuration -> Spam Detector -> Custom Scores

Blacklist filters analogous to Pyzor, Razor, Spamhaus:

Configuration -> Spam Detector -> Custom Scores

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

Don't forget to Apply Scores!


Additional Filters

The following should also already be integrated and activated:

Spam characteristics:
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
FREEMAIL_FORGED_REPLY : 6
FROMSPACE : 4
KAM_LAZY_DOMAIN_SECURITY : 4

Incorrect date:

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

Sender anomalies:

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

Effective whitelist:
DKIMWL_WL_HIGH : -6


The following filters caused false positives in my case, so I assigned a score of 0:
SPF_HELO_NONE : 0
KAM_MARKSPAM : 0
HTTPS_HTTP_MISMATCH : 0
DEAR_SOMETHING : 0

Don't forget to Apply Scores!


Disabling Problematic Blocklists

Validity reputation lists cause nothing but trouble and do not actually filter effectively. They should be disabled in custom.cf.

Add entry to /etc/mail/spamassassin/custom.cf:
Code:
dns_query_restriction deny sa-trusted.bondedsender.org
dns_query_restriction deny sa-accredit.habeas.com
dns_query_restriction deny bl.score.senderscore.com


Compiling Filters

To speed up filter processing, the filters should be compiled. We need a few packages and a module entry in SpamAssassin's custom.cf.

Code:
apt install make gcc re2c

Add entry to /etc/mail/spamassassin/custom.cf:
Code:
loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody

Then compile and finally restart SpamAssassin with:
Code:
sa-compile
systemctl restart pmg-smtp-filter
 
Last edited:
Now we have exhausted all third-party plugins. But we can also create our own SA filters. One of the biggest nuisances is "BCC spam". Someone creates a freemail account with a provider that has no outgoing limit and sends spam emails where all recipients are copied in the BCC field.
To detect them, we need to check To and CC for whether an email address from the transport list is present. If not, we are in the BCC.
Note: Anyone using mailing lists may experience issues with this. Forwarding from freemail accounts to your own domain is also affected. Use the Welcomelist where necessary.

Transport List as Valid Recipients:

Create the following script, which imports the transport list for our BCC rule on a daily basis (so we can never forget to update this list).

~# cat > /usr/local/bin/update-sa-bcc-whitelist.sh
Code:
#!/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

Mark the script as executable and run it once:

chmod +x /usr/local/bin/update-sa-bcc-whitelist.sh
/usr/local/bin/update-sa-bcc-whitelist.sh


Cron entry for daily update of this list

Edit cron table
Code:
crontab -e

Insert line
Code:
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
#The rule 98-bcc-transport-domains.cf should now already exist


Illegitimate Sender Domains and BCC Spam

Marks all emails from undesirable sender domains. These automatically receive a score of 3.0 because they are almost always illegitimate.

If the email originates from an illegitimate TLD and I am additionally only in the BCC, a further "bonus" score of 2.0 is assigned.
Emails meeting this criterion are almost always spam; exceptions may include foreign mailing lists, which would need to be added to the Welcomelist.

Code:
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

These rule(s) contain words that generally indicate a legitimate email.
In addition, .de domains also receive a positive score, as it has generally been shown that spammers use arbitrary domains. .de domains are included, but the false-positive rate will certainly be considerably higher due to the large volume of incoming emails from your own country.

Words particularly suited for a negative score are address data (street, zip code, surname), ensuring that order confirmations always get through.
Words from your email signature also work well: commercial register number, VAT ID, PGP key, whatever you include. This ensures that replies to your emails arrive reliably.
Be careful with words that also appear in your email address. If a domain is <profession>-<city>.de, neither is particularly suitable for negative scoring — otherwise you will receive emails saying: "Hello <profession>-<city>.de, do you also have problems with your potency?"

Code:
cat <<EOF > /etc/mail/spamassassin/99-positive-word-list
body POS_CITY    /\bcity\b(?![-\w.@])/i
body POS_NAME  /\bname\b(?![-\w.@])/i
body POS_SURNAME     /\bsurname\b(?![-\w.@])/i
body POS_ZIPCODE   /\b88888\b(?![-\w.@])/i              # PLZ

score POS_CITY -2.0
score POS_NAME  -2.0
score POS_SURNAME    -2.0
score POS_ZIPCODE  -4.0
EOF

Positive Scores for Particularly Trusted Senders
Certain freemail providers that have their systems under control receive a positive score (not Gmail!). These include t-online.de, gmx.de, web.de, ionos/strato. These are rewarded with a high negative score.
This is of course highly individual.

Code:
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

Finally run:

Code:
sa-compile
systemctl restart pmg-smtp-filter


Summary

Approximately 50% of all emails are legitimate, meaning 50% are spam.

Of 100% of all spam:

  • 3% filtered by SPF rule, invalid sender domain, or HELO check
  • 50% filtered via DNSBL
  • 25% originate from Google and are filtered via policyguard described above
    =======================
    These emails are already sorted out by Postfix and appear in the Tracking Center as REJECTED.
  • 22% reach SpamAssassin and are filtered there:
Of the 100% filtered within SA:
  • Approximately 50% are caught by Razor, Pyzor, SH, Geoblocking, Spamcop, and URI blocklists.
  • Approximately 30% are caught by other content filters, BCC, or TLD scoring.
20% pass through because no content filter triggers.

This corresponds to a block rate of approximately 95%.

The percentages should of course not be taken at face value:
  • This varies from day to day.
  • Many filters overlap: without DNSBLs, some emails would fail SPF. DNSBLs also overlap each other significantly.
No good results were achieved with the following tools:

DCC is not recommended.
The integration is non-trivial (the program must be compiled from source and installed as a service).
The problem: by default, DCC assigns only a few points, but unfortunately DCC does not detect "spam" — it detects "bulk mail".
This includes delivery confirmations from insurance companies ("Thank you for your email. We will get back to you as soon as possible") and similar legitimate emails.

Spamcop has been removed as a DNSBL because Google regularly appeared on the blacklist. I had several cases where legitimate Gmail emails were filtered. I rely on a (relatively low) score within SpamAssassin instead.
 
Last edited:
  • Like
Reactions: Onslow