Skip to content
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
  • 0 Votes
    1 Posts
    150 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) reads a DNS lookup that fails as a failure, not as a name with no addresses: the HELO and PTR tests no longer score a name they could not look up, a DNS blacklist and SURBL no longer read a failed lookup as "not listed", and a DKIM selector that is a CNAME whose query failed is TempFail, not PermFail. SURBL reads a link's host that is not ASCII by UTS 46 and an HTML part's character references as a browser does, and SpamAssassin is sent a collected message's own Return-Path, so its SPF and envelope rules may score such mail differently. The DMARC check's pass now also decides whether a forward of mail from outside whose From names a hosted domain's organisation is given an envelope sender in that organisation, and no pass is recorded for a message the check did not judge: DMARC off, a sender on the white list or an IP range with spam protection off, a message over AntiSpam.MaximumMessageSize, or one the tests before it had already scored to the higher threshold. In the Control Panel the sidebar has a Spam filtering group, with its six allow and block lists under Lists, and a Virus scanning group, and Ctrl+K takes every old title to its page. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) lets Rspamd learn from the Junk folder beside SpamAssassin: a message a user moves into Junk is taught as spam, and one moved out as ham, to the filter MoveLearningBackend names, SpamAssassin by default and taught as before, and nothing is sent to Rspamd until it is chosen and its controller and classifier are set (JunkFolderLearning.md). See the 6.3.8 announcement. This topic walks through every layer hMailServer 6.3.4 puts between an SMTP session and a mailbox: the two thresholds, the twelve tests in the order they run, greylisting and its 6.3.4 hardening, the DNS and URL blocklists, SPF, DKIM, DMARC and ARC, SpamAssassin, and the quarantine. The one thing to know first: none of these tests is a verdict on its own. Each failing test adds a score, two thresholds decide what the total means, and three kinds of session are never tested at all: an authenticated session, a sender or IP on the anti-spam white list, and any session from an IP range with spam protection switched off. Where the decisions are made Everything happens in one of two moments: before the body (at MAIL FROM or RCPT TO) and after the body (after DATA or the last BDAT). The pre-transmission tests run at MAIL FROM by default: DNSBLChecksAfterMailFrom defaults to 1, and 0 moves them to the first RCPT TO, which leaks less to an address-probing client but accepts more of the conversation first. A session from a configured incoming relay has both phases deferred to after DATA. Who is never tested. SMTPConnection::GetDoSpamProtection_ returns false the moment the session is authenticated, so your own users' outbound mail is never scored; the sending limits and rate limits are the controls for that. A sender or IP on the anti-spam White list skips the tests entirely, not merely the scoring: SPF is not evaluated, no Authentication-Results verdict is recorded and no reason header is written. The read-only overview. Spam & virus filtering → Spam filtering overview in the Control Panel (Windows only) shows on one screen which checks are on, what each scores, what the thresholds then do, and which checks are switched on but cannot run. The score and the two thresholds Both thresholds are on Spam & virus filtering → Anti-spam settings → General. Setting Default At or above it 0 means AntiSpam.SpamMarkThreshold 5 Delivered but tagged: X-hMailServer-Spam: YES, X-hMailServer-Reason-* headers and, optionally, a subject prefix Never mark AntiSpam.SpamDeleteThreshold 20 Refused during the conversation, or quarantined if quarantine is on and the verdict came after the body Never refuse AntiSpam.MaximumMessageSize 1024 KB Post-transmission tests are skipped for a larger message No limit of its own; the MIME parser's 80 MB ceiling still applies The higher of the two thresholds is also the early-out: SpamTestRunner::RunSpamTest stops calling tests once the running total reaches it, which is why a message condemned by the blocked-senders list never pays for a DNS lookup. With both thresholds at 0 the early-out is skipped, because greylisting's SPF bypass needs the SPF result and SPF is the sixth test. With AddHeaderSpam and AddHeaderReason both on (the defaults), a marked message carries: X-hMailServer-Spam: YES X-hMailServer-Reason-1: Blocked by SPF. (example.com does not designate 203.0.113.9) - (Score: 3) X-hMailServer-Reason-2: Rejected by Spamhaus. - (Score: 3) X-hMailServer-Reason-Score: 6 Subject: [SPAM] Your invoice Only failing tests get a Reason-N line. X-hMailServer-Reason-Score is the sum of the failing tests, and with AddHeaderReason off it is not written at all. The subject prefix is off by default; its text is [SPAM] (antispamprependsubjecttext). Start by tagging, not deleting. The wiki's advice is a fortnight with the delete threshold at 0, reading what got tagged, before turning deletion on well above anything legitimate has scored. Every test, in the order it runs This is the registration order in SpamTestRunner::LoadSpamTests; nothing reorders it at runtime. Tests 1 to 6 run before the body, 7 to 12 after it. # Test Switched on by Default Default score 1 Blocked senders a non-empty Blocked senders list list empty 100 on a new entry 2 DNS blacklists an active row on DNS blacklists two rows, both inactive 3 3 HELO host AntiSpam.CheckHostInHelo off 2 4 PTR record AntiSpam.CheckPTR off 1 5 Sender MX AntiSpam.UseMXChecks off 2 6 SPF AntiSpam.UseSPF off 3 7 SURBL an active row on SURBL servers one row, inactive 3 8 DKIM AntiSpam.DKIMVerificationEnabled off 5 9 ARC AntiSpam.ArcFilteringEnabled, a trusted-sealer list and DMARC on off minus the DMARC failure score 10 DMARC AntiSpam.DMARCEnabled on 5 11 SpamAssassin AntiSpam.SpamAssassinEnabled off 5, or the merged score 12 Filter hook a non-empty FilterHookUrl empty the engine's score; 100 for a reject DMARC is the only test on by default, and at 5 a DMARC failure marks a message without refusing it. That is the shipped posture: tell the recipient, do not destroy the mail. What makes a test fail matters as much as its score. SPF scores only a hard Fail. DKIM scores only PermFail; a message with no signature is None, not a failure. DMARC scores a p=reject or p=quarantine policy the message failed and, separately, a message with more than one From header field; p=none failures are logged and score nothing. The blocked-senders list never matches the null sender, and its 550 says only "Sender address is blocked." without naming the entry. Every DNS-backed check fails open: a lookup that does not complete is read as "not spam". Since 6.3.7 a lookup that fails is a failure, not a name with no addresses: the HELO and PTR tests no longer score a name they could not look up, a DNS blacklist and SURBL no longer read a failed lookup as "not listed" (the blacklist says so in the TCP/IP log and, at most every 15 minutes, the application log), and a DKIM selector that is a CNAME whose query failed is TempFail, not PermFail, so it no longer scores. Greylisting Greylisting is not a score. It is a temporary refusal of a triplet (sender address, recipient address, connecting IP) this server has not seen before. The settings are on Anti-spam settings → Greylisting. Setting Default Meaning AntiSpam.GreyListingEnabled off Master switch AntiSpam.GreyListingInitialDelay 30 minutes How long a new triplet is refused for AntiSpam.GreyListingInitialDelete 24 hours An unconfirmed triplet is forgotten after this AntiSpam.GreyListingFinalDelete 864 hours (36 days) A confirmed triplet's expiry, pushed forward on every accepted message AntiSpam.BypassGreylistingOnSPFSuccess on Needs the SPF test enabled and to have run AntiSpam.BypassGreylistingOnMailFromMX off Costs an A and an MX lookup per message A greylisted recipient gets 451 Please try again later at RCPT TO; when the envelope sender is empty the refusal is postponed to the end of DATA and answered 451 (docs/Greylisting.md; the wiki prints 450), because an empty sender at RCPT TO is very often another server's callout. Greylisting is skipped for a recipient domain with Enable greylisting for this domain unticked, for an IP on the greylisting white list, and for a sender or IP on the anti-spam white list. The wiki records that GreylistingEnabledDuringRecordExpiration=0 did nothing at 6.2.28, so greylisting stays in force during the expiry sweep whatever it says. Hardened since 6.3.4 (schema 6072; docs/Greylisting.md The triplet key no longer truncates. Addresses used to be stored cut to 200 characters, so two long addresses sharing their first 200 were one triplet. Each is now keyed by an MD5 digest of the whole address with its domain lower-cased. This is the one change at the defaults: on PostgreSQL, and on MySQL with a binary collation, [email protected] and [email protected] were two triplets and are now one, so a sender waits once less, never once more. Old rows are not rewritten at upgrade. A sliding wait and an abuse lock, both off. With GreyListingSlideOnEarlyRetry on, each retry before the window opens moves the window to now plus the initial delay. GreyListingAbuseThreshold (0; the operator who asked for it uses 20) is the attempt answered 550 5.7.1 Too many delivery attempts before the greylisting delay had passed. ..., with the triplet locked for GreyListingAbuseLockMinutes (60). It counts attempts, not retries: a server delivering twenty queued messages to one recipient down one connection makes twenty attempts in seconds. A triplet that has passed once is never refused. Clear greylisting triplets lifts every lock, at the cost of greylisting every sender once more. The greylisting bypass list (/api/v1/greylisting-bypass, table hm_greylisting_bypass a domain such as example.com, or a suffix such as .mail.example.com, matched on the envelope domain, the HELO host or either, passes greylisting only when SPF, DKIM or DMARC passes for that name. At least one requirement is needed. An entry requiring DKIM or DMARC cannot be judged at RCPT TO, so its greylisting runs after DATA, where a 451 or 550 ends the transaction for every recipient. The test an entry requires must be switched on: an entry requiring DKIM while DKIM verification is off (the default), or DMARC while DMARC is off, can never be satisfied, so the message is greylisted at RCPT TO as if the entry were not there; for an entry matched on the HELO, the SPF check of the HELO name is made only while SPF is switched on. It skips greylisting only; every spam test still runs. BypassGreylistingOnSPFSuccess, by contrast, covers every SPF pass in the world, a hijacked mailbox at a large provider included. DNS blacklists and SURBL Spam & virus filtering → DNS blacklists seeds two rows, both inactive until you tick them: DNS host Expected result Reject message Score zen.spamhaus.org 127.0.0.2-8\|127.0.0.10-11 Rejected by Spamhaus. 3 bl.spamcop.net 127.0.0.2 Rejected by SpamCop. 3 The expected-result field takes | for alternatives, a range on the last octet and wildcards; a malformed range is error HM5342. Every lookup, match or not, is written to the TCP/IP log, which is how to prove a listing to a sender who disputes it: DNS lookup: 9.113.0.203.zen.spamhaus.org, 1 addresses found: 127.0.0.4, Match: 1 The wiki warns that public mirrors of the large lists rate-limit or refuse resolvers that use a public DNS service; if a list stops matching, check its operator's terms first. SURBL checks the links in the body, so it runs after DATA. Collection stops once more than 15 distinct addresses are found, the run for one list aborts after 10 seconds, and, to 6.3.6, the first failed lookup abandoned the rest of the list; since 6.3.7 a failed lookup is a failure rather than "not listed", and SURBL goes on to ask the message's other names. One row is seeded inactive: multi.surbl.org, "Rejected by SURBL.", score 3. SPF, DKIM, DMARC and ARC SPF (AntiSpam.UseSPF, off, score 3). Evaluation is bounded at 10 DNS-querying terms and at SpfVoidLookupLimit void lookups (default 2; 0 switches that limit off). Since 6.3.4 the evaluator follows RFC 7208, ported from upstream hMailServer, in place of the RFC 4408 library the server carried: 6.3.3 and earlier Since 6.3.4 DNS Its own lookups; DNSServer, the cache and the timeout were ignored on Windows The server's own resolver settings apply Linux Every check was TempError (HM6406), so DMARC lost its SPF half Works Loopback clients 127.0.0.0/8 and ::1 passed before any policy was read No exemption Authentication-Results Everything but pass and fail written as neutral Each of the seven results under its own keyword Nothing is scored differently: only a fail scores, as before. The loopback row is the one to check when upgrading. CHANGELOG says that where the SPF test or DMARC runs for a loopback client, mail from a domain whose policy does not name the address is judged like any other, so a content filter that re-injects mail should authenticate, or connect from an IP range with anti-spam off. DKIM (AntiSpam.DKIMVerificationEnabled, off, score 5). RSA keys shorter than 1024 bits are refused outright, and rsa-sha1 signatures are invalid unless DkimAcceptSha1=1. DMARC (AntiSpam.DMARCEnabled, on, score 5). More than one From field is scored before any alignment is computed, because DKIM signs the last From while the header reader answers with the first. ARC (AntiSpam.ArcFilteringEnabled, off; AntiSpam.ArcTrustedSealers, empty). Its only output is an offset of exactly minus the DMARC failure score, applied when a chain whose every sealer is trusted validates and DMARC is about to fail. With the list empty it does nothing, deliberately: anyone can fabricate a chain and seal it with their own keys. Matching is exact. Since 6.3.4: policy the score cannot express (schema 6073; all off by default; docs/SmtpPolicy.md Setting What it does UseSPFSoftFailScore, UseSPFTempErrorScore, UseSPFPermErrorScore Scores of their own for SoftFail (~all), TempError and PermError. 0 is "not scored", as before SPFTempErrorDefer Answers a TempError with 451 4.4.3, so a resolver blip defers the message. Never a 451 on PermError RejectMultiRcptNullSender A null sender's second and every later distinct recipient, and its DATA, are refused 550 5.7.1. A single-recipient null sender is never touched BanMultiRcptNullSender, BanMultiRcptNullSenderHours Also bans the address through the auto-ban, once per session, for 24 hours by default (1 to 8760); never an address on AutoBanNeverBan or an incoming relay. Does nothing while RejectMultiRcptNullSender is off The domain white and black list (Spam & virus filtering → Domain white and black list; /api/v1/domain-policy-list) is keyed by the envelope domain or the HELO suffix rather than by IP, for the correspondent whose sending hosts change every week. It is consulted at MAIL FROM for unauthenticated sessions of a range that runs the spam tests. Black is 550 5.7.1 Mail from <name> is refused by this server's policy. before anything else runs. White skips greylisting, is not deferred for a TempError and is not refused or quarantined on the score, while every test still runs and the mark threshold still marks. It never overrides authentication. The list is ASCII, so a U-label such as bücher.example is refused, and hmctl has no verb for it. SpamAssassin and the filter hook hMailServer speaks the spamd protocol over TCP to any SpamAssassin daemon, on this machine or on a Linux host. Set AntiSpam.SpamAssassinHost (127.0.0.1) and AntiSpam.SpamAssassinPort (783) and press Test SpamAssassin connection. AntiSpam.SpamAssassinMergeScore (off) uses the integer part of SpamAssassin's own score= in place of the fixed AntiSpam.SpamAssassinScore (5). The timeout moves between SAMinTimeout and SAMaxTimeout (30 and 90 seconds) with load. SpamAssassinLearnOnMove (off) tells spamd that a message moved or copied into Junk is spam, and one moved out of it ham, with spamc's TELL, which spamd refuses unless it was started with --allow-tell. An unresolvable host is HM5507; a scan that does not finish in time is HM5508, and the message is accepted with no verdict; a message that cannot be reloaded after the scan is HM5509 and is left as SpamAssassin wrote it. FilterHookUrl puts rspamd, or anything answering JSON over HTTP, in the path of every accepted message; http://127.0.0.1:11333/checkv2 needs no adapter. FilterHookTimeoutSeconds is 10, and with FilterHookFailClosed (off) turned on an unanswered check scores FilterHookRejectScore (100) and reports HM5541. Quarantine, and what a refused sender sees Quarantine (QuarantineEnabled, off; QuarantineRetentionDays, 30) changes what the delete threshold does after the body: the message is answered 250 Queued for delivery and held on Spam & virus filtering → Quarantine. A verdict reached before the body has no message to hold, so it stays a refusal, and a quarantine that fails to store falls through to refusing, so a spam refusal never becomes silent deletion. Since 6.3.4 the quarantine also holds what a mail flow rule sends there (Rule: <name>), what impersonation protection holds and what a recipient's blocked-senders list holds. Per-account overrides, on the account dialog's Spam tab, re-judge that account's own copy at delivery. They only read X-hMailServer-Reason-Score, and only while Add X-hMailServer-Reason header is on; with it off the value could be the sender's own, so the overrides decline to act. Moment Client sees Score over delete threshold, before the body 550 5.7.1 <text of the first failing test> Score over delete threshold, after the body 554 5.7.1 <text of the first failing test> Same, but quarantined 250 2.0.0 Queued for delivery Greylisted, real sender 451 Please try again later. Greylisted, null sender 451 at end of DATA (the wiki prints 450) Per-test timings go to the debug log (Spam test: SpamTestSPF, Score: 3, Time: 41 ms); a test taking 10 seconds or more is promoted to the application log. Because every DNS-backed check fails open, a resolver that has stopped answering looks like a run of clean mail, so DKIM, DMARC and SURBL, and since 6.3.7 a DNS blacklist, each write one application-log line at most every 15 minutes when their lookups stop completing. A configuration that works The wiki's order for a small server: Leave the thresholds at 5 and 20 and leave DMARC on. Watch for a fortnight. Activate zen.spamhaus.org (score 3). Turn on SPF (score 3) and DKIM verification (score 5). With a DNSBL hit they reach 11, still under the delete threshold. Turn on AuthenticationResultsEnabled, so every delivered message records what the server concluded. Add greylisting if your users can tolerate a first-message delay, leaving bypass on SPF success on. Only then add SpamAssassin or an external engine, and consider raising the delete threshold rather than lowering it. What this topic does not cover, and where to read more The per-recipient allow and block lists, the quarantine digest, spam traps, country blocking and impersonation protection are in allow and block lists, digests and spam traps, and viruses in viruses and dangerous attachments. The other direction is Being trusted by the servers you send to. The Control Panel and COM are Windows-only; the 6.3.4 switches are also on the Settings view of the Control Deck and PUT /api/v1/settings/antispam. Wiki: https://gitlab.com/hmailserver/hmailserver/-/wikis/Stopping-Spam https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/Greylisting.md https://gitlab.com/hmailserver/hmailserver/-/blob/v6.3.4/hmailserver/docs/SmtpPolicy.md Public documentation: https://www.progressiverobot.com/hmailserver-documentation/#10-stopping-spam and https://www.progressiverobot.com/hmailserver-documentation/#265-the-anti-spam-pipeline The 6.3.4 security announcement and Upgrading to 6.3.4
  • 0 Votes
    1 Posts
    119 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) adds the PROXY protocol to the IMAP and POP3 listeners: each takes the client's address from a PROXY header as the SMTP listener does, from the peers on a list of its own, and both are off by default; their switches and peer lists are on the IMAP and POP3 cards of the Control Panel's Protocols page. The SMTP listener's PROXY protocol and XCLIENT are a card on Incoming relays, not on Server limits & expert settings, and Ctrl+K takes every old title to its page. A state-changing REST request that carries a credential is now refused when the browser reports it as cross-site (Sec-Fetch-Site, or an Origin that is not this server's); scripts, hmctl and the PowerShell module send no such header and are unaffected. The ACME HTTP-01 challenge responder, which served one connection at a time, serves up to 64 at once. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) no longer charges a failed sign-in through a proxy to the proxy: at the web services listener one from loopback or an address in WebServicesTrustedProxies, and at the REST listener an account's from loopback, is counted against the name alone, never towards a ban of the address, where up to 6.3.7 a ban could be written that shut the proxy, or 127.0.0.1, out of the SMTP, POP3, IMAP and ManageSieve listeners. The web services listener now also refuses, before the TLS handshake, an address whose IP range allows none of SMTP, POP3 and IMAP, an auto-ban's or one of your own, though never loopback or an address in WebServicesTrustedProxies: where mail reaches the server only through a relay or a VPN and the Internet range allows nothing, that range now closes ACME's http-01 validation when this listener answers it, MTA-STS, CalDAV, CardDAV, Autodiscover, autoconfiguration and the Web Key Directory to the addresses it holds, so check before upgrading that the certificate authority, the sending MTAs and these services' clients do not come from it. See the 6.3.8 announcement. This topic is for a server that does not face the internet alone: behind a hosted spam filter or a filtering appliance, behind a load balancer, or with a reverse proxy terminating HTTPS in front of the webmail, the Control Deck, CalDAV and CardDAV. The one thing to know first: hMailServer judges the address that connected to it, and behind a front end that address is the front end's until you tell the server otherwise. Auto-ban, the rate limits, the IP ranges and every spam test then reason about the wrong machine: SPF fails on every message, and a block list that lists your gateway blocks all your mail. Each kind of front end is handled in its own way, and behind an HTTP reverse proxy the address stays the proxy's. Which mechanism fits which front end Front end What reaches hMailServer What tells it the truth A filtering gateway, hosted or an appliance, that accepts the mail and sends it on A new SMTP session from the gateway, the sender's address in its Received header Incoming relays, XCLIENT if the gateway can send it, and the gateway setting profile A load balancer or TLS terminator in front of the SMTP listener The client's session, from the balancer's address HAProxy PROXY v1 or v2 A reverse proxy terminating HTTPS for /portal, / and /api/v1 HTTP from the proxy X-Forwarded-Proto: https from the proxy on the same machine, for the cookies; the address stays the proxy's A reverse proxy in front of CalDAV, CardDAV and the other web services HTTP from the proxy X-Forwarded-Proto: https, believed from loopback and from WebServicesTrustedProxies Incoming relays: what listing one changes Mail flow & delivery → Incoming relays in the Control Panel (Windows) lists servers that legitimately forward mail to you, each a name and an IP range (a lower and an upper address); a save applies at once. The same list is in the Control Deck, at /api/v1/incoming-relays over REST since 6.3.3 (a key confined to named domains is refused), and in COM Settings.IncomingRelays on Windows, where since 6.3.4 a change takes effect only at Save(). Listing a relay changes two things: When spam protection runs. A connection from a listed relay is switched from pre-transmission to post-transmission testing. DNSBL, PTR and HELO checks judge the connecting IP, so they wait until the body has arrived and the real origin can be read from it. Spam is then no longer refused before the body: with quarantine on, a message reaching the delete threshold is held for review; without it, it is refused with 554 5.7.1 after the body. Which IP counts as the origin. The Received chain is walked from the top, and the first address that is not a listed relay becomes the originating IP. SPF, DMARC and ARC are evaluated against it. Received: from gw2.example.net [198.51.100.8] listed relay: keep walking Received: from gw1.example.net [198.51.100.7] listed relay: keep walking Received: from mail.sender.example [203.0.113.44] not listed: the originating IP Since 6.2.24 the address taken is the one the relay observed, not the client's own HELO literal; thresholds tuned before then need checking again. Since 6.3.4 a listed relay changes more: A remote domain policy's inbound rule, Require TLS on mail from this domain (530 5.7.0 at MAIL FROM), is not applied to it. The hop that rule is about happened before the gateway, so require TLS on the gateway. Requiring TLS and remote domain policies covers the policies. Mail it hands over counts as coming from inside. One rule, MessageOrigin::IsExternalSender, decides that for the external-sender tag, the first-contact note and impersonation protection, so none of the three applies behind a listed gateway. DomainTransforms.md: the choice is between the tag and the relay declaration, and there is no third answer today. Mail flow rules and what a domain does to a message covers the tag. A spam trap never bans it; the sender is still judged. The domain white and black list sees the relay's own HELO name, so key rows for mail behind a relay on the envelope domain. Keep the range to the exact gateway addresses. An attacker inside a range listed too wide can insert a forged Received header and choose which IP you evaluate. And leave spam protection on for the IP range the gateway connects from: clearing Enable anti-spam on a range turns off every spam test, greylisting and the recipient tarpit for it, relay or not. Symptom Cause Every message is scored as though it came from your own filter; SPF always fails The front end is not listed as an incoming relay A DNSBL hit on the gateway's address blocks all mail The same Adding a relay stopped spam being rejected before the body Expected: testing moved to after the body. Quarantine and scoring still apply PROXY protocol and XCLIENT If the front end can pass the real client address itself, that is better than reconstructing it from headers. Two mechanisms have been accepted since 6.2.23-alpha1, both off by default and both honoured only from the addresses you list: Setting Default What it does SMTPProxyProtocolEnabled 0 HAProxy PROXY v1 and v2 on the SMTP listener, read before TLS and before the banner SMTPProxyProtocolTrustedIPs empty The real TCP peers allowed to send a PROXY header, comma-separated addresses or CIDR ranges. Empty means nobody SMTPXClientEnabled 0 The Postfix XCLIENT verb SMTPXClientTrustedIPs empty The peers allowed to use it. Empty means nobody Trust is decided against the real TCP peer, never an address a header supplied, so chained proxies cannot bootstrap trust. An entry that does not parse matches nothing and is logged once per run (HM6281). Once an address is listed for PROXY, the header is required from it. Configure the proxy to send it (in HAProxy, send-proxy or send-proxy-v2) or its connections are dropped; and a PROXY line from a peer that is not listed closes the connection without a reply. Change the proxy and the settings together. XCLIENT is advertised in EHLO only to a trusted peer, as XCLIENT ADDR NAME PORT PROTO HELO LOGIN; an untrusted peer never learns the verb exists and gets 550 if it tries. With LOGIN the upstream vouches for a signed-in account: RoomsAndEquipment.md counts such a submission as a signed-in session, like SMTP AUTH. Listing an address for XCLIENT trusts it with that too. What then sees the real client: auto-ban, the rate limits, the IP ranges, the spam tests, and since 6.3.4 the compromised-account detection's address signal (which never scores behind a proxy that speaks neither) and, with PROXY, the live view of one connection. What does not: country blocking (since 6.3.4). It judges the address the TCP connection came from, the proxy, and exempts loopback and private ranges; put the geographic policy on the proxy. Since 6.3.7 the IMAP and POP3 listeners take the client's address from a PROXY protocol header too, each from the peers on a list of its own (IMAPProxyProtocolEnabled and IMAPProxyProtocolTrustedIPs, POP3ProxyProtocolEnabled and POP3ProxyProtocolTrustedIPs), both off by default, the header read before an IMAPS or POP3S port's TLS handshake and before the greeting. No document describes either mechanism for ManageSieve. Where they are set. On Windows, the Control Panel's Maintenance → Server limits & expert settings (card Front-end proxies (PROXY protocol and XCLIENT); in 6.3.7's Control Panel the PROXY protocol and XCLIENT are a card on Incoming relays) or COM Settings.SetIniSetting. On either system, PUT /api/v1/settings/ini/{name} (the administrator password only; an API key is refused) or hmctl ini set <name> <value>; on Linux also hmailserver --set-setting. The Control Deck has no editor for these keys. All four apply when saved. Since 6.3.4 the database holds them, so a line edited in hMailServer.ini changes nothing the server uses; the store, the file's copy and HM5804 are in Settings: what is not stock and when a save takes effect. # Over REST with the administrator password (an API key is refused), from any machine that reaches the listener; applies when saved hmctl ini set SMTPProxyProtocolTrustedIPs 10.0.0.5,10.0.0.6 hmctl ini set SMTPProxyProtocolEnabled 1 # On a Linux server; a running service takes it at its next save, reinitialise or restart sudo hmailserver --set-setting SMTPProxyProtocolTrustedIPs=10.0.0.5,10.0.0.6 sudo hmailserver --set-setting SMTPProxyProtocolEnabled=1 The gateway profile (since 6.3.4) A setting profile is a named, reviewed set of settings, shown as a diff first. gateway is for a server whose inbound mail a filtering gateway has already judged. Its 17 entries include: Entry Value Why (Profiles.md, shortened) usegreylisting 0 It judges the connecting address, which is the gateway's even through Incoming relays SMTPXClientEnabled 1 Honoured only from SMTPXClientTrustedIPs, and from nobody while that is empty ASArcFilteringEnabled 1 With DMARC on, the DMARC penalty is offset when a sealer named in arc_trusted_sealers sealed a pass MaxSubmissionsPerIPPerMinute, SmtpTarpitCount 0 All inbound mail comes from the gateway's few addresses QuarantineEnabled, RejectFullMailboxAtRcpt 1 A message at the delete threshold is held, not refused back to the gateway; a full mailbox is refused at RCPT so the gateway returns the message usespf, ascheckhostinhelo, ascheckptr, ASDMARCEnabled 1 Through Incoming relays they judge the real sender AutoBanOnLogonFailureEnabled, SmtpAuthenticatedSenderCheck 1 For people who sign in here directly The rest switch on DKIM verification, the MX check and attachment blocking, all three off as shipped, and keep the reason headers on. The first four rows turn a check off or trust an address range, so applying asks for an explicit confirmation wherever one of them would change something. A profile never writes an incoming relay, an IP range, a port's TLS or the ARC sealer. The steps are yours, in this order: Add the gateway's addresses to Incoming relays before applying the profile, and leave spam protection on for their IP range. Until they are there, the tests judge the gateway's own address. If the gateway sends XCLIENT, put its addresses in SMTPXClientTrustedIPs. Name the ARC sealer the gateway signs as in arc_trusted_sealers (PUT /api/v1/settings/antispam, or COM AntiSpam.ArcTrustedSealers). Until then no seal is trusted. Read the diff and apply it, on the Control Panel's Setting profiles page under Maintenance, in the Deck's checklist, over REST or with hmctl, confirming the entries that ask. The apply is all or nothing and saves the values it replaces first; while an earlier profile is neither reverted nor discarded, another apply is refused with 409. The routes, the diff's token, the way back and the command-line verbs are in the profiles section of the unattended installs topic. Check "Require SSL/TLS when authenticating" on the Internet IP range, or make the submission port STARTTLS-required, for people who sign in here directly. A new installation turns it on; an upgraded one keeps what it had. On the listener the gateway delivers to, leave TLS optional unless the gateway offers it: a port's security is the port's, not a setting. A filter on the same machine Since 6.3.4 SPF is RFC 7208, and 127.0.0.0/8 and ::1 no longer pass unconditionally. A content filter that re-injects mail over loopback should authenticate, or connect from an IP range with anti-spam off. The HTTP listeners behind a TLS-terminating proxy REST listener Web-services listener Serves The webmail at /portal, the Control Deck at /, the API under /api/v1 ACME challenges, MTA-STS, security.txt, autoconfiguration, CalDAV, CardDAV, the OpenPGP key directory Plain HTTP behind the proxy Only on loopback: TLS is required unless bound to 127.0.0.1, localhost or ::1 WebServicesHttpPort Whose X-Forwarded-Proto: https counts The proxy on this machine Loopback, and WebServicesTrustedProxies (since 6.3.3) What it decides Secure on the session and sign-in cookies Whether CalDAV and CardDAV treat the request as HTTPS; the Apple .mobileconfig profile; the key directory A changed port or bind address (RestApiPort, RestApiBindAddress, WebServicesHttpPort, WebServicesHttpsPort) waits for a reinitialise (Apply now). WebServicesTrustedProxies applies when saved, and no document names a Control Panel field for it: set it with PUT /api/v1/settings/ini/{name}, hmctl ini set, COM Settings.SetIniSetting on Windows or hmailserver --set-setting on Linux (which a running service takes at its next save, reinitialise or restart). The cookies, since 6.3.4. hmailsession and the sign-in cookies hmoidc, hmsso and hmpasskey are marked Secure over TLS, or when the proxy on this machine says X-Forwarded-Proto: https. Before 6.3.4 only a TLS listener counted, so behind the usual proxy a browser would also send the session cookie over plain HTTP. A proxy that does not set the header still leaves them without Secure; a proxy on another machine can only reach a REST listener that speaks TLS itself. CalDAV (since 6.3.4) and CardDAV take HTTP Basic over HTTPS only: over plain HTTP the answer is 403 with the reason, unless a trusted proxy says X-Forwarded-Proto: https. From any other peer the header is ignored; an entry that does not parse matches nothing (HM6281). The Apple profile honours the header on the same terms, and the OpenPGP key directory (since 6.3.4) answers a plain request with the same 404 as an unknown address; point openpgpkey.<domain> at the proxy and forward /.well-known/openpgpkey/ with the header. The CalDAV and CardDAV topic covers the rest. The client address stays the proxy's. Nothing reads X-Forwarded-For, so: The auto-ban leaves 127.0.0.1 and ::1 alone, and behind a proxy on this machine every webmail and Deck request comes from there. What brakes guessing is the per-name lockout, when AccountLockoutThreshold is set, and since 6.3.4 each owner's second-factor budget: five wrong codes in fifteen minutes refuse that owner's codes for the next fifteen (429, second_factor_locked). The forgotten-password route (since 6.3.4) holds loopback to thirty requests in any sixty seconds, all together. A proxy on another host shares one per-client bucket of twenty an hour. Neither has a setting: put the proxy on the mail server or accept the cap. For CalDAV, CardDAV and Autodiscover, since 6.3.8 a failed sign-in from loopback or an address in WebServicesTrustedProxies is counted against the name alone, never towards a ban of the address, and the listener never refuses those addresses by an IP range, so only proxies belong in that list, and the per-name lockout (AccountLockoutThreshold, off by default) is the limit on the guesses through one. Up to 6.3.7 each such failure was charged to the proxy's address, and a ban written for it could shut the proxy, and every client behind it, out of the SMTP, POP3, IMAP and ManageSieve listeners (AutoBan.md). Passkeys (since 6.3.4). By default the relying party id is the Host header, which behind a TLS-terminating proxy is still what the browser sent, so the check holds. X-Forwarded-Proto is not consulted, which is why the origin check accepts https at the page's host and plain http only on a loopback name. A server reached at several names needs passkey_relying_party and passkey_allowed_origins together, the origins written as the browser writes them, port included; the topic on signing in has the rest. The address people use. The server cannot learn it from the proxy. The links in a message that expires, a password-protected message and a password reset use confidential_base_url (then quarantine_digest_base_url, then import_oauth_redirect_base_url); the quarantine digest's links use quarantine_digest_base_url alone, a mailing list's use list_base_url, and single sign-on (since 6.3.4) uses oidc_redirect_base_url. Which paths to publish is in The webmail portal at /portal. Compression: let one side do it (since 6.3.4) Both listeners compress when the client asks, and there is no setting. Negotiation. Accept-Encoding is the whole of it: gzip or deflate, gzip where both are acceptable, never br. deflate is the zlib format of RFC 1950, which a raw inflater fails on; ask for gzip. What. Text, JSON, XML, a manifest, message/rfc822 and an icon, between 1,024 bytes and 4 MB. An image, a PDF or a zip goes as it is, and a download (/api/v1/me/messages/{id}/attachments/{index}, /files/{token}) is never compressed. Headers. Content-Length is the bytes sent; no chunked transfer encoding, no byte ranges. Vary: Accept-Encoding is on every answer of a type it would compress, which matters for the web-services listener, whose answers may be cached; the webmail and the API are Cache-Control: no-store. Behind a reverse proxy, either leave Accept-Encoding alone and pass the answer through (the server will not encode twice, and the proxy must not either), or strip Accept-Encoding from the request and compress at the proxy. What does not is forwarding Accept-Encoding: gzip, receiving Content-Encoding: gzip, and handing the bytes to a client that did not ask. What it does not do It reads no forwarded-for header. XCLIENT is an SMTP setting only; since 6.3.7 the PROXY protocol is taken on the IMAP and POP3 listeners too, each from its own list of peers. A listed gateway turns off the external-sender tag, the first-contact note and impersonation protection for what it hands over. Compression has no switch. A client that wants none sends no Accept-Encoding, or Accept-Encoding: identity. A second machine behind a balancer is the warm standby and high availability topic. Where to read more Wiki: Routes and Relays §14.3, Security Hardening §17.8, Stopping Spam §10.1, Settings Overview, and chapter 14.3 of the documentation page. Documents at the 6.3.4 tag: Profiles.md, ResponseCompression.md, DomainTransforms.md, PasskeysAndSingleSignOn.md, DevicesAndSessions.md, AccountRecovery.md, CountryBlocking.md, SettingsApply.md, RoomsAndEquipment.md, and README.md for the [Settings] notes on PROXY, XCLIENT and the web services. On this forum: The webmail portal at /portal, The Control Deck, the anti-spam stack end to end, IP ranges, auto-ban, lockout and tarpits and TLS on the listeners. A way past one of these trust lists is a vulnerability: report it by a confidential GitLab issue or by email to the project's Service Desk address, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com, which GitLab files as a confidential issue, and not in a forum post.
  • 0 Votes
    1 Posts
    144 Views
    ProgressiverobotP
    This topic takes a freshly installed hMailServer 6.3.4 to its first working mailbox, proves it locally, and then lists the DNS records without which its mail either does not arrive or is filed as spam. The one thing to know first: the domain's Postmaster address box is the catch-all, not the postmaster@ mailbox the email standards ask for. Create postmaster@ as an account, and leave that box empty unless you want every misaddressed message accepted and stored. The paths below are the Windows Control Panel's; on Linux, which has no Control Panel, the same is done in the Control Deck or over the REST API. If you are learning, make the sample domain first (since 6.3.4) One button, on the Control Panel's Welcome page and on the Control Deck's Checklist view, or one command (hmctl sample-domain create, hmailserver --sample-domain create on Linux, POST /api/v1/setup/sample-domain) makes a throwaway example.test: three mailboxes, a distribution list of the three, one rule and eight delivered messages. The same button, or ... remove, takes it away and says what it removed; a domain called example.test that it did not make is never touched. All three mailboxes sign in with one password, which the server mints when it makes the sample: twenty characters of Crockford base32, grouped in fives. It is shown once, in the answer to the call that made the sample, and is stored nowhere, so write it down; if you lose it, remove the sample and make it again. Older wiki text that speaks of "one documented password" describes an earlier version of the feature and is out of date. Remove the sample before the server takes real mail. Its mailboxes are ordinary accounts that can sign in and relay. Add your domain Go to Accounts & domains → Domains, type the name and press Add domain, then select it and press Properties. On Linux, use the Deck's Domains view, or: curl -u Administrator -H "Content-Type: application/json" \ -d '{"name":"example.com","active":true}' \ http://127.0.0.1:8045/api/v1/domains postmaster is optional in that body and is left out here, because it is the same catch-all field. The call answers 400 if the name is not a valid domain name and 409 if it already exists. Since 6.3.4 the Control Panel's domain editor has fourteen tabs; the seven that 6.3.4 added are each off, or empty, until set. The fields that matter on the first day: Tab Field Default What happens when it is wrong General Domain enabled ticked Unticked: every RCPT TO is answered 550 Domain has been disabled. General Domain name The part after the @. Setting it to the host name (mail.example.com) is the classic first-day mistake General Postmaster address empty The catch-all. Set: no address in the domain can be refused. Empty: unknown recipients get 550 Unknown user Limits Maximum message size (KB) 0 0 falls back to the server-wide maxmessagesize, seeded at 20480 KB Limits Enable plus addressing off, + Off: alice+invoices@ is an unknown recipient Relay Relay host empty Where mail from this domain leaves through. This is not a route DKIM Enable signing off Enabling before the DNS record is live makes every message fail DKIM at the receiver In RecipientParser::CheckDeliveryPossibility, an address that matches no account, alias, distribution list or route falls through to the postmaster address and the resolution restarts with it. That is why the box is a catch-all. Add a mailbox Select the domain, type the address and a password under the account list, and press Create account; then Edit for the full editor. Field Value to use Notes Password Press Generate strong password 16 characters from a cryptographic RNG, shown once and copied to the clipboard Administration level Normal user Server administrator can manage everything. Give it only to yourself Quota (MB) 0 for unlimited Account enabled ticked Unticked: 550 Account is not active. at RCPT TO On Linux, make the account on the Control Deck or with POST /api/v1/domains/{domain}/accounts. Create at least [email protected] and your own mailbox. A full mailbox is refused during the SMTP conversation, not bounced: with RejectFullMailboxAtRcpt on, as it is by default, RCPT TO is answered 452 4.2.2 Mailbox is full., a temporary refusal, so the sending server holds the message and no backscatter is sent. Since 6.3.4, accounts can be imported from a CSV file over REST, with hmctl and with the PowerShell module, with a dry run that changes nothing; and an identity provider can make and remove them over SCIM. Three things about an account are set only on the Control Deck and over REST, not in the Control Panel: its kind (a person, a room or equipment), encryption at rest, and a webhook route. How an address is resolved Every RCPT TO is answered by one function, in a fixed order that ends at the domain's postmaster address; the full order, with domain aliases, plus addressing and the webhook route, is in Aliases, distribution lists and public folders. What it means on the first day: Question Answer Why did mail to a mistyped address get delivered? The domain has a postmaster address, so it fell through to the catch-all Why does alice+anything@ not work? Plus addressing is off by default and is per domain Why does an alias to an external address still count as local? Only at the first hop, so that it is treated as inbound mail and not as relaying Test it locally, before touching DNS The test below works because a fresh database's My computer IP range lets the server itself, on 127.0.0.1, send as one of its own domains without a password, while anyone may send you mail and nobody may relay through you without logging in. A refused direction is 550 Delivery is not allowed to this address.; one that needs authentication is 530 SMTP authentication is required. The two seeded ranges and what each allows are in IP ranges, auto-ban, lockout and sending limits. Send-MailMessage -SmtpServer localhost -Port 25 ` -From "[email protected]" -To "[email protected]" ` -Subject "First test" -Body "If you can read this, hMailServer is working." Or by hand with telnet localhost 25. The replies worth recognising: Line What it tells you 250-SIZE 20971520 The server-wide maxmessagesize, 20480 KB, in bytes 250-STARTTLS Present only when the port's connection security is STARTTLS optional or required 250 AUTH … Absent when the port requires TLS, or the connecting range requires TLS when authenticating 250 2.0.0 Queued (0.016 seconds) The message is on disk and in the database 250 2.0.0 Queued for delivery Not the same reply: the message was quarantined Then check Monitoring & troubleshooting → Delivery queue, which should be empty, and look on disk: a delivered message is one file, C:\Program Files\hMailServer\Data\yourcompany.com\you\a3\{a3f01e6c-....eml}. Its first Received header reads with ESMTP, and with ESMTPSA for an authenticated submission over TLS. The DNS records your domain needs Do them in this order. Replace yourcompany.com and 203.0.113.10 with your own. Record Example Without it A / AAAA mail.yourcompany.com. IN A 203.0.113.10 Nothing resolves; the MX points at nothing MX yourcompany.com. IN MX 10 mail.yourcompany.com. Inbound mail bounces as "no mail server for this domain" PTR 10.113.0.203.in-addr.arpa. IN PTR mail.yourcompany.com. Google and Microsoft refuse or spam-folder outbound mail SPF yourcompany.com. IN TXT "v=spf1 mx a:mail.yourcompany.com ~all" Anybody can forge your domain; DMARC cannot pass on the SPF side DKIM dkim._domainkey.yourcompany.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkq..." Forwarded mail fails everything, because forwarding breaks SPF DMARC _dmarc.yourcompany.com. IN TXT "v=DMARC1; p=none; rua=mailto:[email protected]" No policy and no reports A / AAAA and MX. Add an AAAA only if the machine has a public IPv6 address that answers. An MX must point at a host name, never an IP address, and that name must not be a CNAME. The MX name is what MTA-STS matches, what TLSA records hang off, and what the certificate on port 25 should be issued for. PTR. Only your ISP or hosting provider can set it. It should name the same host as your HELO/EHLO, which hMailServer takes from Connections & protocols → Protocols → SMTP, Host name (HELO/EHLO greeting), falling back to the Windows computer name when blank. The Control Panel's DNS records page does not check it, and says so. SPF. Start with ~all; move to -all once the DMARC reports have shown you every system that sends as you. Publish exactly one SPF record per name: two beginning v=spf1 is a permanent error at every receiver. RFC 7208 caps the DNS-querying mechanisms at ten. In the other direction, checking SPF on incoming mail as a scored test is off by default (usespf is seeded 0), but DMARC checking is on by default and evaluates SPF and DKIM itself. DKIM. On the domain's DKIM tab click Generate key pair…: it creates an RSA-2048 key, saves the private key and shows the exact record, with a Copy DNS value button. dkim is the default selector. Switching signing on is gated on the published record matching. Ed25519 is supported too, with a key you supply. Since 6.3.4 a rotation policy per domain generates the new key under a dated selector (s20260915), checks the record every fifteen minutes and switches signing only once it has carried that key for a propagation period. It never edits DNS, and it is off until switched on. DMARC. Start with p=none, read the reports for a few weeks, then tighten to p=quarantine and p=reject. Do not start at p=reject. The DNS records page pre-fills rua= with the domain's postmaster address and omits the tag when there is none. Since 6.3.4, name that mailbox as the domain's report mailbox and the server parses the DMARC and TLS-RPT reports that arrive there and charts them on the Control Deck's DMARC page. Nothing read there acts on any mail. Optional hardening, at any point afterwards: Record Name Notes MTA-STS _mta-sts.yourcompany.com. IN TXT "v=STSv1; id=20260908120000" hMailServer serves the policy itself once WebServicesHttpsPort is set and the listener has a certificate for mta-sts.yourcompany.com; MtaStsPolicyMode defaults to enforce, so set it to testing to start TLS-RPT _smtp._tls.yourcompany.com. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]" Asks other servers to tell you when TLS to you failed TLSA _25._tcp.mail.yourcompany.com. IN TLSA 3 1 1 <sha256 of the certificate public key> Only meaningful if your zone is DNSSEC-signed. Re-publish before you rotate a certificate, never after CNAME autoconfig, autodiscover, mta-sts → mail.yourcompany.com. For client autoconfiguration and the policy host A / AAAA / CNAME openpgpkey.yourcompany.com Since 6.3.4, only if you publish OpenPGP keys BIMI is read, not published: since 6.3.4 the webmail shows a sender's logo, but this server publishes no BIMI record for your domains. Checking your work Give DNS an hour to propagate. Monitoring & troubleshooting → DNS records lists, per domain, the exact SPF, DKIM, DMARC, MTA-STS and TLS-RPT records to publish, with a Check button per record: Verdict What to do Not published Publish it Published but different Compare the two; the page shows what is there Published and correct Nothing Lookup failed A resolver or network problem, not a DNS-content one. Retry On Linux the Control Deck has a DNS records view. On either platform the first-hour checklist (since 6.3.4) judges the spf, dkim and dmarc items itself, for the first twenty active domains. From a command line: Resolve-DnsName yourcompany.com -Type MX Resolve-DnsName yourcompany.com -Type TXT Resolve-DnsName dkim._domainkey.yourcompany.com -Type TXT Resolve-DnsName _dmarc.yourcompany.com -Type TXT Resolve-DnsName 10.113.0.203.in-addr.arpa -Type PTR Being trusted by the servers you send to takes deliverability further. The ports your users will need The create scripts seed four listeners, SMTP 25 and 587, POP3 110 and IMAP 143, with no encryption. Since 6.3.4 a new installation sets all four to STARTTLS optional with the self-signed certificate and requires TLS for authentication from the Internet IP range; an upgraded server is left as it was (Hardening a public mail server). Ports 465, 993 and 995 are not created for you: add them on Connections & protocols → TCP/IP ports once you have a certificate. A TLS port with no certificate selected does not start (HM5113, Error initializing SSL. Certificate not set.); a port already in use is HM4316. Which connection security each port should have, and why STARTTLS optional belongs on port 25 alone, is in Connecting a mail client; which ports to open in the firewall is in Every port hMailServer listens on. Before you tell anyone the address postmaster@ exists as a real account. MX, SPF, DKIM and DMARC are published and verified. A certificate is installed and the client ports created: Let's Encrypt certificates are automatic. The administrator password is a PBKDF2 one. 6.3.4's installer writes it so; the wiki's checklist item about changing it from "the installer's MD5" describes 6.3.3 and earlier. A backup that you have restored once. What it does not do, and where to read more Nothing here publishes a DNS record for you: the Control Panel builds the text and checks it, and the DKIM rotation never edits DNS. The PTR record is your provider's to set. More on the records above: DKIM rotation, DMARC and TLS reports, and Requiring TLS for MTA-STS, TLS-RPT and DANE. The webmail and browser administration are separate topics: The webmail portal at /portal and The Control Deck. Sources: the wiki's Your First Domain and Mailbox, DNS for Your Domain and Ports Reference; SampleDomain.md and SetupChecklist.md; and chapters 6, 8 and 22 of the public documentation.