Configuring proper server-wide spam protection is essential for maintaining server reputation, preserving bandwidth, and ensuring optimal mail deliverability. This guide outlines a complete, weighted spam-scoring procedure for DirectAdmin running on Debian 13, using Exim + Easy Spam Fighter (ESF) + SpamAssassin together as one coordinated system.
DirectAdmin's mail stack is not three independent filters. It is one pipeline:
- ESF (RCPT stage) checks SPF, DKIM, reverse DNS, and RBL membership before the message body arrives, and starts a running score (
acl_m_easy69). - SpamAssassin (DATA stage) scans the message content and produces its own score. This score is added directly into ESF's running total — multiplied by 10 (a SpamAssassin score of
3.4adds34points to ESF's tally). - ESF's final check compares the combined total against
EASY_HIGH_SCORE_DROPand drops the message if it's equal or higher.
Because of step 2, any SpamAssassin rule score you set is not independent — it always flows into ESF's single combined decision. This guide treats EASY_HIGH_SCORE_DROP = 100 as the reference "100% = kill" scale, and calibrates every other setting against it.
Step 1: Configure a Reliable DNS Resolver Path
RBL and Spamhaus lookups depend on how your resolver reaches the outside world.
Using a large public resolver like 1.1.1.1 directly can get your queries throttled or blocked by Spamhaus, returning 127.255.255.254 instead of a real answer — silently disabling every RBL check on your server.
- Follow
the documentation at:https://frankwebhost.com/knowledgebase/3/How-to-Configure-Unbound-DNS-Resolver-on-Debian-13-Cloud-VPS.html
- Verify:
You should consistently seedig +short 2.0.0.127.zen.spamhaus.org127.0.0.2,127.0.0.4, or127.0.0.10— never127.255.255.254.
Step 2: Enable Sender Callout Verification (Not Recommended)
Sender callout verification checks if the sender address actually exists at the source mail server before accepting incoming mail. This drastically cuts down on dictionary attacks and fake sender spam.
- Edit the pre-recipient ACL Exim file:
nano /etc/exim.acl_check_recipient.pre.conf - Append the sender verification rule:
deny !sender_domains = outlook.com : hotmail.com : live.com : msn.com : yahoo.com : ymail.com : aol.com : gmail.com : google.com : googlemail.com !verify = sender/callout=20s,random message = Verification failed: Sender address does not exist - Rebuild the Exim configuration:
da build exim_conf
Step 3: Configure Real-time Blackhole Lists (RBLs)
RBLs allow Exim to block incoming emails from known spammer IP addresses directly during the SMTP connection phase.
- Edit the custom Exim strings file:
nano /etc/exim.strings.conf.custom - Define your preferred RBL lists (Barracuda, Spamhaus, SpamCop, PSBL, Mailspike, MSRBL):
RBL_DNS_LIST==b.barracudacentral.org : zen.spamhaus.org : bl.spamcop.net : psbl.surriel.com : bl.mailspike.net : combined.rbl.msrbl.net
smtp_banner = "$primary_hostname ESMTP Mail Server Ready" - Rebuild the Exim configuration to activate the RBLs:
da build exim_conf
Step 4: Easy Spam Fighter — Weighted Scoring (RCPT stage)
This is where your protection level is defined. Every value below is calibrated against EASY_HIGH_SCORE_DROP = 100 as the "100% = kill" reference point, so each signal's weight can be read directly as a percentage of the kill threshold.
-
nano /etc/exim.easy_spam_fighter/variables.conf.custom -
# The kill threshold itself — every weight below is a % of this number EASY_HIGH_SCORE_DROP == 100 # Skip re-running SpamAssassin only once a message is already guaranteed # to be dropped anyway (pure CPU optimization, does not affect decisions) EASY_LIMIT == 100 # SPF — any failure type, full weight (100%) EASY_SPF_FAIL == 100 EASY_SPF_SOFT_FAIL == 100 EASY_SPF_PASS == -02 # DKIM — any failure type, 90% EASY_DKIM_FAIL == 90 EASY_DKIM_PASS == -02 # Reverse DNS — any failure type, 80% EASY_NO_REVERSE_IP == 80 EASY_FORWARD_CONFIRMED_RDNS == -01 # Confirmed content-spam bonus (SpamAssassin's own Global threshold reached), 50% EASY_IS_SPAM == 50 # RBL match — carried over from previous config, not covered in your # specification. Adjust if you'd like a specific weight here too. EASY_DNS_BLACKLIST == 80 - Rebuild Exim configuration:
da build exim_conf
| Situation | Score added | Dropped alone? |
|---|---|---|
| SPF fail (hard or soft) only | 100 | Yes — matches your "100% rage" |
| DKIM fail only | 90 | No — needs 10 more points from any other signal |
| rDNS fail only | 80 | No — needs 20 more points |
| DKIM fail + rDNS fail together | 170 | Yes |
Step 5: SpamAssassin — Content-Only Rules (No ESF Overlap)
SPF, DKIM, and rDNS are already fully handled in Step 4. Setting them again here would double-count the same signal and drop legitimate mail prematurely (a forwarded message with a relay that has no rDNS, for example, would be punished twice for the same reason). The rules below have no ESF equivalent, so they are safe to score here.
10 becomes 100 points once it reaches ESF's tally. To make a rule worth "100%" on ESF's scale, set its SpamAssassin score to 10, not 100.-
nano /etc/mail/spamassassin/local.cf - Append:
# ==================================================== # Content-only rules — no ESF equivalent, safe to score independently. # Each "score 10" below becomes 100 points once added to ESF's tally # (SpamAssassin score x 10), i.e. a guaranteed drop on its own. # ==================================================== # Freemail spoofing (sender name/domain forged as Gmail/Yahoo/etc.) - 100% score FREEMAIL_FORGED_FROMDOMAIN 10.0 score FROM_MISSP_FREEMAIL 10.0 score FREEMAIL_FORGED_REPLYTO 10.0 # Confirmed bad-infrastructure signals - 100% each score HELO_DYNAMIC_IPADDR 10.0 score NO_DNS_FOR_FROM 10.0 # DMARC Failure (domain spoofing protection) - 100% if reject score DMARC_REJECT 10.0 score DMARC_QUAR 8.0 -
# ==================================================== # Bayes Auto-Learn Tuning for Aggressive Setup # ==================================================== bayes_auto_learn 1 bayes_auto_learn_threshold_nonspam -0.1 bayes_auto_learn_threshold_spam 3.0 -
# A 'contact address' users should contact for more info. report_contact https://yourdomain.com/support.html - Verify syntax to make sure there are no errors in SpamAssassin configuration:
spamassassin --lint - Restart SpamAssassin and rebuild Exim configuration:
systemctl restart spamassassin da build exim_confImportant Note on DMARC Scoring: Only define high DMARC scores (e.g.,10.0or8.0) in SpamAssassin if DMARC processing is completely disabled at the ESF level, or ifvariables.dmarc.confdoes not exist or is empty. If ESF is already scoring DMARC independently, setting these scores here will result in double-counting the penalty and dropping legitimate mail prematurely.
Step 6: SpamAssassin Tuneup from the DirectAdmin GUI
- Log in to your DirectAdmin Control Panel → User Tab → E-Mail Manager → SpamAssassin Setup.
- Blocking strategy:
User Spambox— messages that are flagged as spam by SpamAssassin's own Global threshold, but don't reach ESF's combined kill score, land in the spambox for review instead of being silently dropped. - High score block:
No (disabled)This setting creates an independent drop path that runs before ESF's combined check even executes (confirmed in/etc/exim/spamd.acl_smtp_data.conf). If left enabled, a purely content-based SpamAssassin score above its threshold will hard-drop a message on its own — completely bypassing the weighted percentages you just configured in Step 4, including the intentional "content alone shouldn't auto-kill" behavior for the 60% weight. Disabling it here means the single combined ESF score (Step 4) is the only thing that decides whether a message is dropped, which is what your weighted design assumes. - Global threshold:
Custom/4.0— this is the point at which SpamAssassin marks a message as confirmed spam internally and triggers theEASY_IS_SPAM(60%) bonus from Step 4. It does not drop mail by itself. - Rewrite subject:
Yes/ Tag:*****SPAM***** - Spam delivery:
Don't use attachments - Click Save Configuration.
Step 7: Enable RBL Blocking in the DirectAdmin GUI
- Admin Tab → Server Manager → Administrator Settings → E-mail Tab.
- Enable RBL Blocking in Exim.
- Click Save Settings.
Step 8: Train the Bayesian Filter Manually (Optional)
SpamAssassin's Bayes filter is the single strongest content signal it has, but it is not pre-trained on install, and does not retroactively learn from mail already sitting in a Spam/Junk folder. It only learns automatically, in real time, from newly-scanned mail that crosses its own auto-learn thresholds — a large backlog of manually-sorted spam will not be picked up unless trained explicitly.
- Check current training state (per mail account, not server-wide):
Look at thesa-learn --dump magic --dbpath=/home/<user>/.spamassassin/bayesnspamandnhamcounts — reliable classification needs at least ~200 of each. - Train from an existing spam/junk folder:
sa-learn --spam --dbpath=/home/<user>/.spamassassin/bayes \ /home/<user>/imap/<domain>/<mailbox>/Maildir/.INBOX.spam/cur/* \ /home/<user>/imap/<domain>/<mailbox>/Maildir/.INBOX.spam/new/* - Optional: keep training current with a daily cron job:
crontab -e0 3 * * * sa-learn --spam --dbpath=/home/<user>/.spamassassin/bayes /home/<user>/imap/<domain>/<mailbox>/Maildir/.INBOX.spam/cur/* --no-sync >/dev/null 2>&1 30 3 * * * sa-learn --sync --dbpath=/home/<user>/.spamassassin/bayes >/dev/null 2>&1
Step 9: Apply and Verify
Finally, perform a full system reboot to ensure all network socket changes, Exim ACLs, and SpamAssassin services are reloaded cleanly:
reboot
Watch live processing:
tail -f /var/log/exim/mainlog
Check what's being rejected and why:
tail -100 /var/log/exim/rejectlog
Confirm your DNS/RBL path is healthy:
dig +short 2.0.0.127.zen.spamhaus.org
rejectlog and the SpamTally header on a sample of real mail for a few days. If legitimate mail is being caught — most commonly forwarded mail through a relay with no rDNS — adjust the specific weight in Step 4 rather than the overall kill threshold.