Skip to content
  • Categories
  • Recent
  • Popular
  • Tags
  • Users
  • Groups
  • Documentation
  • Downloads
  • Report a bug
  • GitHub mirror
  • Paid support
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
hMailServer Community Forum
hMailServer 6.3.8 is the current release, signed 2 November 2026. Windows and Linux, and an in-place upgrade from 5.3 or later, but for the original hMailServer's 5.7.Download 6.3.8 Documentation FAQ
  1. Support and discussion for hMailServer
  2. Running hMailServer
  3. Anti-spam & message filtering
  4. Allow and block lists, digests, spam traps, country blocking and impersonation

Allow and block lists, digests, spam traps, country blocking and impersonation

Scheduled Pinned Locked Moved Anti-spam & message filtering
spamsecurityadministrationrest-api
1 Posts 1 Posters 133 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • ProgressiverobotP
    ProgressiverobotP
    Progressiverobot
    hMailServer Team
    wrote on last edited by chris
    #1

    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) reads a link's host that is not ASCII by UTS 46, and an HTML part's character references as a browser does, for the URL entries of the allow and block lists, so a message whose links escaped a list that way can now be tagged or held; a URL entry written in Unicode is stored in its xn-- form and covers a link written either way, and one with a label UTS 46 refuses is refused when it is saved. In the Control Panel the Spam filtering group's six allow and block lists are under Lists, and Ctrl+K takes every old title to its page. See the 6.3.7 announcement.

    This topic covers five layers that sit beside the score-based spam tests rather than inside them: the allow and block lists at three levels, the quarantine digest, spam traps, country blocking and impersonation protection. All five are new since 6.3.4, and the one thing to know first is that every one of them is off, or inert, until an administrator sets it: a server upgraded to 6.3.4 and left alone has no list entries, sends no digest, has no traps, judges nothing by country and checks no display name.

    The allow and block list: one table, three levels

    Since 6.3.4 (schema 6046). The older lists (the anti-spam white list, Blocked senders, the greylisting white list) act during the SMTP conversation, for every recipient at once. The allow and block list is per recipient. It is one table, hm_senderlist, with a scope on each row: each account's own safe and blocked senders, a domain's list, and the server's.

    Entry Covers
    [email protected] That one address, in any case. No wildcards
    example.com example.com and every subdomain; not notexample.com, not example.com.attacker.net
    @example.com The same as example.com, and stored as example.com
    https://bad.example/anything A URL: stored as bad.example/, covering a link in the message to that host or a subdomain, whoever sent it

    An entry covers a message when it matches either the envelope sender or the address in the From header, because mail sent through a mailing service carries the service's address in the envelope and the brand's in the header. A message with the null sender is never decided by any list.

    URL entries are in 6.3.4. CHANGELOG's 6.3.4 section lists them, and they are consulted only when neither address decided anything. The closing section of docs/SenderListsAndDigests.md, which says "URLs are not list entries", is older text left in the file.

    What allow and block do. The lists are read at local delivery, once per recipient, after the spam tests have run.

    The message was classified as spam It was not
    Allowed Delivered unmarked for this recipient: the spam flag, the X-hMailServer-Spam and reason headers and the subject tag are removed from this copy Delivered as it is
    Blocked Held in the quarantine for this recipient when the quarantine is on; marked as spam and delivered when it is not The same

    A block never deletes, and no score is recorded on a blocked message, because nothing measured it. A message released from the quarantine is never held again by a list. The other recipients of the same message are not affected.

    Precedence is nearest first: the account, then its domain, then the server. The first of the three that has anything to say about the sender is the only one consulted, so an account's allow beats a domain's block. That is the reverse of Microsoft 365's order, and deliberate: the person the message is addressed to can tell whether they wanted it. An administrator who needs a sender stopped for everyone still has Blocked senders, which nobody's own list overrules. Inside one list the more specific entry wins (an address beats a domain, a longer domain beats a shorter one), and on an exact tie the block wins.

    Neither address is proof. Both are whatever the sender wrote, so an allow entry is as forgeable as the address it names. The document's advice: keep allow entries to correspondents whose domains publish DMARC, and prefer a single address to a whole domain.

    List Webmail Control Panel (Windows) Control Deck REST
    The account's own Settings → Safe senders none none /api/v1/me/sender-list
    A domain's none Domain → Allow and block list tab Domain editor → Allow and block list /api/v1/domains/{domain}/sender-list
    The server's none Spam & virus filtering → Allow and block list Lists → Allow and block list /api/v1/sender-list

    Over COM (Windows only) the domain's list is Domain.SenderList and the server's Settings.AntiSpam.SenderList. There is no administrator path to an account's own list. Block this sender and Report phishing in the webmail now add the sender to the account's blocked senders; they used to write a rule into the Sieve script, and rules written that way are left where they are. A list holds at most a thousand entries; adding a value already there rewrites the entry (200 rather than 201). Expiry can be set over REST, in the Deck and in the Control Panel's two list editors (a switch and a date); the webmail does not offer it. docs/SenderListsAndDigests.md still says the Control Panel has no expiry; that passage predates the change, which the repository's roadmap records as made after 6.3.3.

    The quarantine digest

    Once a day, one plain-text message goes to each account of a domain that has something newly held for it, listing each message's subject, sender, when it was held and why, with a link that delivers each entry, a link that delivers everything listed, and a footer link that stops the digest (also the List-Unsubscribe header). An empty digest is never sent, and an account that has had one is not sent another for twenty hours. The message comes from the domain's postmaster with the null envelope sender and Auto-Submitted: auto-generated, and never asks for a password.

    Setting Default
    QuarantineDigestBaseUrl, the address the links point at, such as https://mail.example.com empty: no digest is sent
    QuarantineDigestLinkHours, 1 to 720 72
    The domain's switch and hour (quarantine_digest, quarantine_digest_hour; 0 to 23, local time), which an account may override off; 7

    All three are in the database, none in hMailServer.ini. The quarantine itself (QuarantineEnabled) has to be on, and the address must be an absolute http:// or https:// URL of the listener the webmail is served from.

    When it is sent. The task wakes every five minutes and, within the digest hour, sends only its share, so a domain of a thousand accounts is spread across the hour. At most five hundred digests go in one run. Over COM (Windows only), AntiSpam.SendQuarantineDigests() sends now, ignoring the hour and the twenty-hour rule and nothing else.

    The links. Each is 32 random bytes recorded against one account and one entry, usable once, and no longer valid after QuarantineDigestLinkHours. Following a link does not act on its own: it opens a page with one button, because mail security products follow every link in a message, and a link that released mail on its own would have one of them releasing every held message. Stopping the digest does not stop mail being held.

    Spam traps

    Since 6.3.4 (schema 6058). A trap is an address that was never real and never given to anybody, so a message to one comes from a harvested, bought or guessed list. At RCPT TO a trap is refused exactly as any unknown address is, 550 Unknown user, made by the same code, so the sender cannot tell. The check runs before the delivery lookups, so a catch-all, greylisting or a backup-MX callout cannot answer first.

    What happens Setting
    The connecting address Banned through the server's own auto-ban, as an expiring range named Auto-ban: spam trap <address> SpamTrapBanMinutes, 1440. 0 bans nobody
    The envelope sender Added to the server's block list with an expiry, only when its domain's SPF passes for the connecting address SpamTrapBlockDays, 30. 0 blocks nobody
    The hit Recorded in hm_spamtraphits and counted in hmailserver_spam_trap_hits_total SpamTrapHitRetentionDays, 90

    The application log gets one line per hit and one per ban:

    "Auto-ban: 203.0.113.9 is banned for 1440 minutes after addressing the spam trap [email protected]"
    

    The packaged fail2ban filter matches that line, so a Linux installation that runs fail2ban with the packaged jail turned on bans the address at its own firewall as well. The jail ships disabled; the wiki's Installing on Linux page turns it on with:

    printf '[hmailserver]\nenabled = true\n' > /etc/fail2ban/jail.d/hmailserver.local
    systemctl reload fail2ban
    

    The SPF condition. As first written, in the source tree, a trap put the envelope sender of every message sent to it on the block list, whatever that sender was; spam traps are new in 6.3.4, which carries the fix, so no release did. CHANGELOG records why that changed: the envelope is whatever the client types, so one connection naming a bank's address could have had the bank's real mail held or marked for every mailbox. Now only an SPF pass blocks; the address is banned either way. The wiki's summary in Stopping Spam §10.15 does not mention the condition; docs/SpamTraps.md and CHANGELOG do.

    Who is never banned: an authenticated session, an address whose IP range does not run the spam tests, an address or sender the anti-spam white list covers, an incoming relay, and an address on AutoBanNeverBan. The sender is not blocked for the null sender, for a sender claiming a domain this server hosts, or where the server's allow list already names it.

    Training. A trap hit trains nothing by itself, because no content arrives. With SpamTrapTeachSpamAssassin on (off by default; it needs SpamAssassin enabled and a spamd started with --allow-tell), a message sent to a real mailbox in the same transaction is taught to spamd as spam.

    Declaring a trap. It must be an address in the domain it is declared on, and one nobody uses: an account, alias or list with that address is refused, and none can be given a trap's address while the trap exists.

    Where How
    Control Deck Domain editor → Spam traps; the Spam traps view for the record across every domain
    REST GET/POST /api/v1/domains/{domain}/spam-traps, DELETE /api/v1/spam-traps/{address}, GET/DELETE /api/v1/spam-traps/hits
    Command line hmctl spamtrap create --domain example.com --set [email protected], hmctl traphits list
    PowerShell Add-HmSpamTrap, Get-HmSpamTrapHit
    COM (Windows only) Domain.AddSpamTrap, Utilities.GetSpamTrapHits

    A good trap is an address never published anywhere, or one published once, long ago, and unused for years (give it a year of refusals first). A bad trap is a typo of a real address such as saels@, or any address in a domain with a catch-all: there every other unknown address is accepted and the trap is not, which is the difference a sender can measure. Clearing the record of hits undoes nothing; the bans and blocks stay until they expire or are removed. The traps are in the backup with their domain; the record of hits is not.

    Country blocking

    Since 6.3.4 (schema 6052). Two kinds of policy, in one grammar. A policy per listener family (SMTP, IMAP, POP3, and the HTTP listeners) is applied when a connection is accepted: before the greeting, before the TLS handshake and before anything touches the database. A policy per domain is applied when somebody signs in to an account of that domain, on any protocol, and covers OAuth2 bearer tokens, client certificates, Kerberos tickets, passkeys and single sign-on as well as passwords.

    Value What it means
    (empty) Nothing is judged by country. The default everywhere
    allow:GB,IE,FR Only these countries may connect, or sign in
    block:CN,RU These countries may not connect, or sign in

    The database is yours to supply. Point GeoIpDatabaseFile at a MaxMind .mmdb file, such as /var/lib/GeoIP/GeoLite2-Country.mmdb or C:\GeoIP\GeoLite2-Country.mmdb. The server ships none and never fetches one. Replace the file in place and the server reloads it within five minutes. GeoIpDatabaseMaxAgeDays (45) is how old the file's build date may be before the log says so; a stale database is still used.

    What is exempt. CountryBlockingExemptRanges defaults to the loopback addresses and every private network:

    127.0.0.0/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,100.64.0.0/10,fe80::/10,fc00::/7
    

    There is no second list compiled into the server. If you empty it, do so with country_policy_http off, or the Control Deck is the next thing you lose. An open session is never disturbed; a policy takes effect on the next connection.

    Which way it fails. With no database at all (none configured, or the file missing or unreadable) everything is allowed, whatever the policies say, and the reason is logged once. With a database loaded that has no country for an address, an allow list refuses it and a block list allows it.

    What a refused client is told: nothing. A refused connection is closed with no banner. A refused sign-in gets the ordinary invalid-credentials reply, byte-identical to a wrong password, and is counted against neither the account's lockout nor the address's auto-ban, since the person refused is very often the account's real owner, travelling. Your copy is in the application log:

    Country blocking: refused SMTP from 203.0.113.9 (CN) for connection.
    Country blocking: refused sign-in from 203.0.113.9 (CN) for example.com.
    

    The counters are hmailserver_country_blocked_total and hmailserver_country_blocked_by_country_total{country="CN"}, and the report is GET /api/v1/reports/countries.

    Setting it up. In the Control Deck, Settings → Server holds the server's settings and Domains → (a domain) → Country blocking the domain's policy. From a shell:

    hmctl settings set --set geoip_database_file=/var/lib/GeoIP/GeoLite2-Country.mmdb
    hmctl settings set --set country_policy_smtp=block:CN,RU
    hmctl domain update example.com --set active=true --set country_policy=allow:GB,IE
    

    The REST keys are country_policy_smtp, country_policy_imap, country_policy_pop3, country_policy_http and, on a domain, country_policy. Over COM (Windows only) it is Settings.CountryPolicy("smtp") and Domain.CountryPolicy.

    What it does not do. It is not a per-message control. It says nothing about a proxy: behind HAProxy with the PROXY protocol, the address judged is the proxy's, so put the policy on the proxy. And it is only as true as the database; it is not an authentication control.

    Impersonation protection

    Since 6.3.4 (schema 6087). Per domain, off on every existing and new domain. It runs at local delivery on the recipient's own copy, only on mail from outside, and makes two checks.

    A borrowed name. The From display name, folded, equals the name of an active account of the recipient's domain or of a protected person, while the address is not theirs.

    A lookalike domain. The From domain, and the envelope sender's when it differs, is equal to a hosted domain, an alias domain or a protected partner after the confusable folding while not equal before it (examp1e.test, exarnple.test), or is one Damerau-Levenshtein edit away inside one label (examplee.test, exampel.test). Distance 1 is the rule and 2 is not. A DMARC pass changes nothing here: the attacker publishes the lookalike's records exactly as a legitimate domain would.

    X-hMailServer-Impersonation: name "Ada L0velace" matches Ada Lovelace ([email protected]); from [email protected]
    X-hMailServer-Impersonation: domain examp1e.test looks like example.test; from [email protected]
    

    Windows and Linux differ in one step of the folding. Unicode NFKC normalisation, which turns a full-width or ligature spelling into its letters, runs on Windows. It does not run on Linux, which has no Unicode tables in this build; there the step is skipped and the confusable table does the rest. The other steps run on both.

    What slips past. Word order and decorations are not folded: Holloway, Chris and Chris Holloway (CEO) are not Chris Holloway. A name carried only in the local part of the address is not compared. And a domain with an account named Support or Info will have every vendor's "Support" flagged.

    impersonation_action What happens
    off (0) Nothing is read
    flag (1) The copy gets the X-hMailServer-Impersonation header. The subject is left alone and the sender's DKIM signature is untouched. The webmail shows a warning bar and a Suspicious sender mark
    quarantine (2) The copy goes to the quarantine with the reason and score 0. A release does not re-check. With QuarantineEnabled off, the domain gets flag instead

    What is exempt: a sender the recipient's own safe-sender list allows (or the domain's or server's allow list, where nothing nearer decides), and a From domain that is itself hosted here or a protected partner. Nothing else: a person's own name from their personal address at another provider is flagged until that address is on the safe-sender list.

    The setting is on the Impersonation protection tab of the Control Panel's domain dialog (Windows only), in the Deck's domain editor, and on PUT /api/v1/domains/{domain} as impersonation_action, protected_persons and protected_partners. Each list holds at most 2,000 characters.

    Where to read more

    The score-based pipeline these layers sit beside is in the anti-spam stack end to end, the outside-sender tag in the topic on mail flow rules, and the auto-ban a trap uses in IP ranges, auto-ban, lockout and tarpits. For the Deck itself, see The Control Deck.

    • Wiki, §10.10a and §10.15: https://gitlab.com/hmailserver/hmailserver/-/wikis/Stopping-Spam
    • https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SenderListsAndDigests.md
    • https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SpamTraps.md
    • https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/CountryBlocking.md
    • https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/ImpersonationProtection.md
    • Public documentation: https://www.progressiverobot.com/hmailserver-documentation/#104-white-lists
    1 Reply Last reply
    0

    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

    With your input, this post could be even better 💗

    Register Login
    Reply
    • Reply as topic
    Log in to reply
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes


    hMailServer Community

    The community support forum for hMailServer — the free, open-source mail server for Windows and Linux — maintained by Progressive Robot Ltd. The software is AGPL-3.0 and stays that way; this is where it is discussed, debugged and documented.

    The software

    • Documentation
    • Downloads
    • hMailServer 6.3
    • Source on GitLab

    Getting help

    • Forum rules
    • About this forum
    • Support plans
    • Pay monthly support
    • Talk to Progressive Robot

    Legal

    • Privacy policy
    • Terms and conditions
    • Cookie policy

    Progressive Robot Ltd · 220 Chester Business Park, Heronsway, Chester, Cheshire CH4 9GB, United Kingdom · +44 (0)1244 911212 · [email protected] · VAT 506152326

    Powered by NodeBB

    • Login

    • Don't have an account? Register

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Popular
    • Tags
    • Users
    • Groups
    • Documentation
    • Downloads
    • Report a bug
    • GitHub mirror
    • Paid support