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
    107 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) changes the anti-virus action that removes attachments (Remove all attachments and deliver the text in 6.3.7's Control Panel, Delete infected attachments only in 6.3.6's): an S/MIME message signed as one part, which is opaque but not encrypted, is now replaced by the action's note when the scanner finds something in it, where every earlier version delivered it as it arrived, and a message the server cannot read is deleted, its recipients told; an encrypted message is still delivered as it is (work item 92). See the 6.3.7 announcement. In hMailServer 6.3.8 (2 November 2026) the web services listener refuses an address an auto-ban or an IP range bans (work item 105), so an IP range of your own that allows some addresses none of SMTP, POP3 and IMAP now closes that listener to them, and with it the Web Key Directory: check that the clients that look keys up there do not come from such a range. See the 6.3.8 announcement. This topic is about encrypting the message rather than the connection, so that the person it is for can read it and nobody on the way can, this server included. S/MIME in the webmail has been there since 6.3.2; everything else here is new in 6.3.4. The design rule running through all of it is that this server holds nothing that decrypts a person's mail: keys are made in the browser and kept wrapped under a passphrase or password the server never sees. The one thing to know before you allow any of it is the cost of that rule: nothing on this server can recover mail encrypted to a key that was lost. There is no escrow. Which one you want You want Use S/MIME inside the organisation without buying a certificate per person A domain's S/MIME authority Correspondents who use GnuPG, Thunderbird, Proton or Mailvelope OpenPGP in the webmail, and the Web Key Directory To write privately to somebody who has no key at all A password-protected message A mailbox the administrator cannot read Zero access Everything to one partner organisation encrypted, whatever the sender's client did Gateway encryption, on a remote domain policy A stolen disk or backup to hold nothing readable, while the mailbox works as before Mailbox encryption at rest, which the server can read and so is not in this topic All of the browser cryptography needs a page opened over HTTPS: a browser withholds crypto.subtle from any other, and the page says so instead of offering Sign and Encrypt. S/MIME with your own authority Since 6.3.4 (schema 6095). A domain may have its own S/MIME authority: a root certificate the domain owns, which certifies the keys its accounts make in the webmail, publishes them for colleagues to look up, and says what it has revoked in a signed CRL. Nothing is issued until an administrator creates one. Who holds what. The person's private key never leaves the browser. The webmail makes the key pair, builds a PKCS#10 request, wraps the private key under the account password, and sends only the request. The authority's own key is on the server, because it signs there, and it is protected by the platform's secret store: Where the authority's key opens Windows DPAPI with the machine's key. It opens on this machine and nowhere else, and on this machine to any process that asks Linux The stored-secret key file, <DataFolder>/.hmailserver-secret-key (AES-256-GCM, mode 0600). It opens wherever that file is It never falls back to the legacy scheme. When the store is unavailable the authority is not created (503). Creating it. In the Control Deck, open the domain and find S/MIME authority, or POST /api/v1/domains/{domain}/smime-ca with {"key": "p256"} or "rsa3072". The root is self-signed, CA:TRUE with pathlen:0, valid ten years, with a critical name constraint permitting the domain's own addresses and nothing else. P-256 is the default. The Control Panel shows nothing of it, and there is no COM surface. Enrolment. A person finds Get a certificate from {domain} under Settings → Security → S/MIME. They pick the key type. RSA 2048 is the default and the one to choose. A P-256 certificate from this page signs, and neither this webmail nor a colleague's can encrypt to it. The page makes the key pair and the request, and asks for the account password that wraps the private key before anything is sent. POST /api/v1/me/smime/enroll is made from the account's own sign-in. An app password and an administrator's support session are refused with 403. The server checks the request. It must name exactly one address, and that address must be the account's own or an active alias of its own domain that points at it. The certificate is issued pending. When it arrives in the account's own store it is confirmed, and only then is it answered to colleagues and the address's earlier certificates revoked as superseded. The limits are five enrolments a day (429) and ten valid certificates at once (409). A person with two devices ticks Keep my other certificates for this address on the second. What is issued. Valid one year. With smime_authority_base_url empty, which is the default, a certificate carries no CRL distribution point. What that costs: Outlook and Thunderbird cannot fetch the CRL from the certificate, so a revocation reaches them only when they have been given the CRL another way. This webmail does not use the CRL at all; it reads the authority's own record. Publication and trust. The root and the CRL are served over plain HTTP by the web services listener (WebServicesHttpPort), at /smime-ca/{domain}.pem, .cer and .crl. A signature verifies in Outlook, Thunderbird, Apple Mail or a phone only once the root is installed on the clients, by group policy, a device-management profile or by hand. This webmail trusts the domain's authority by itself. A colleague's certificate is looked up with GET /api/v1/me/directory/{address}/smime, which the webmail asks on its own when Encrypt is ticked. Moving and restoring. The authority is in the configuration backup, but its key opens only where its envelope opens. Restored anywhere else, the Deck shows a warning, enrolment is refused with 503, its CRL is not served, and the authority must be rotated there. Rotation makes a new root and retires the old one without deleting it, and clients must install the new root beside the old. What it is not. Not Active Directory Certificate Services. No OCSP. No key escrow: a forgotten password that the key was wrapped under leaves the mail encrypted to that key unreadable, by anybody, for ever. No elliptic-curve encryption in the webmail. One address per certificate. No automatic renewal: a person enrols again before the year is out. OpenPGP: the half in the directory and the half in the browser The directory The page Where it runs On the server In the person's browser What it holds Public keys only Own keys, the secret half encrypted under a passphrase, and correspondents' public keys What cryptography it does None at all All of it: signing, encrypting, decrypting, verifying, in PGP/MIME Table and schema hm_pgpkeys, schema 6050 hm_pgpring, schema 6080; the trust columns 6081 State as shipped Three switches, all off portal_enabled on In neither half does the server sign, verify, encrypt or decrypt a message. It makes an OpenPGP message itself in only two places further down, a delivery to a zero-access mailbox and gateway encryption to a partner's key; there it encrypts, never signs or decrypts, and verifies only the binding of the subkey it encrypts to. The directory publishes keys at a URL derived from the address, so that anybody who has the address can find the key. <hash> is the local part, lower-cased, SHA-1 hashed and z-base-32 encoded. Both methods are served: Method The key advanced https://openpgpkey.<domain>/.well-known/openpgpkey/<domain>/hu/<hash> direct https://<domain>/.well-known/openpgpkey/hu/<hash> Three switches have to be on before one key is served: The server: OpenPGPWebKeyDirectoryEnabled, set with hmctl settings set openpgp --set web_key_directory_enabled=true or PUT /api/v1/settings/openpgp. The domain: openpgp_publish on the domain. The account: openpgp_publish on the account. This one is the person's own, through PUT /api/v1/me/openpgp/publish. The web services listener has to exist (WebServicesHttpsPort), and the advanced method, which clients try first, needs an address record for openpgpkey.<domain> and a certificate valid for it. Keys are served over HTTPS only. Every refusal is the same 404, whether the domain is unknown, a switch is off, or the account has no key, so that the directory cannot be used to ask whether somebody has an account. Put a key in with hmctl openpgp store [email protected] ann.asc --publish, and check it as a correspondent's client will: gpg --locate-keys --auto-key-locate clear,wkd [email protected] An upload carrying a secret-key packet is refused; so are a version 3 key, a key with no user id or no self-signature, a key past OpenPGPKeyMaximumBytes (65536), and a key whose user ids do not carry the account's own address. An account may hold OpenPGPKeysPerAccount keys (8). A signature on an uploaded key is not verified cryptographically: what makes a key in this directory the right one is that its owner or an administrator put it there. The desktop Control Panel has nothing for the directory yet. The page is /portal-openpgp.js, written on the Web Crypto API with no library vendored. Keys. Curve25519 (the default) or RSA 3072 or 4096, made in the browser. The secret key is stored as OpenPGP writes one, under the passphrase, so the bytes the server holds are bytes it cannot open, and the same bytes import into GnuPG. A revocation certificate is made at the same moment and saved to the device. Autocrypt Level 1, both ways. Finding a key. GET /api/v1/me/openpgp/lookup?address= asks the correspondent's own Web Key Directory (lookup_wkd, on) and then a keyserver (lookup_keyserver, off by default; keyserver_url is https://keys.openpgp.org). Nothing is stored by the lookup. Trust made visible. A different key arriving for an address the ring already holds a key for is stored as a change. Until the person accepts it, the page does not encrypt to it. A key can be marked verified in person, and the mark is never inherited by a key that replaces it. Who may write. Every write to the ring is refused to an app password and to an administrator's support session. Not implemented, and refused by name: version 5 and 6 keys and AEAD; DSA and ElGamal keys and the NIST and Brainpool curves; smartcards and hardware tokens; the web of trust; a draft or a scheduled send that is to be sealed. Curve25519 needs Chrome 137, Firefox 130 or Safari 17 or later. Both directions are proved against GnuPG 2.4 by build/check-portal-openpgp.js, which GitLab CI runs on every push and every merge request. A password-protected message (schema 6082) is for the correspondent with no key. The page encrypts the text and attachments to a password as one OpenPGP message, and what goes out by SMTP is a short notice with a link to /portal/protected/<token>, where the recipient's browser decrypts it. The link is the whole access control. It needs portal_enabled on and a public address for the links (the confidential_base_url chain the messages that expire use), and is offered only over HTTPS. A message lives protected_message_days (30 by default). The ceilings are 25 MB a message and 250 MB an account. It does not promise that the recipient cannot copy what they read. Zero-access mailboxes Since 6.3.4 (schema 6083). A mailbox this server holds and cannot read. An administrator allows it server-wide once (zero_access_allowed, under /api/v1/settings/openpgp, off by default); the person then switches it on for their own account on the webmail's Security page. From then on every message delivered to the account is encrypted to the account's own OpenPGP key - after the spam and virus filters, the rules and the Sieve script have had the text - and stored as PGP/MIME with the headers in the clear. An administrator sees the switch read-only everywhere. What it costs. The webmail refuses to switch until the recovery key has been typed back and the sentence about a lost password has been ticked. Cost What it means The administrator cannot read the mail The message source and the account export answer 403 Neither the administrator nor the server can search it The index and IMAP SEARCH read the headers, never the text. A word in the subject is found; a word in the body is not Nobody can recover it A forgotten password without the recovery key is the mail lost, for ever IMAP and POP see ciphertext Valid PGP/MIME that a client holding the key opens. Until then the account is a webmail account The plaintext was on the disk From acceptance until the seal. The account's copy is overwritten with zeros; whether the bytes are gone is the filesystem's decision Only what is delivered is sealed, and the Sent copy A draft, and anything a client uploads with IMAP APPEND, is stored as it is A journal rule sees the plaintext Journaling runs at acceptance, before delivery The filters have read it A forward goes on in the clear. A rule that files into a public folder files the plaintext there. Quarantined mail is held in the clear Switching off decrypts nothing Mail that arrived while it was on stays encrypted to the key The headers the server kept. From, To, Cc, Date, Subject, Message-ID, the Received trace and the rest stay in the clear, so that the listing, the threading and the spam folder work without the key. The server adds X-hMailServer-Zero-Access: sealed on delivery. The key, in two sealed copies. hm_zeroaccess holds two copies of the secret key, both sealed in the browser: one under a passphrase the page derives from the account password with Argon2id, three passes over 64 MiB in four lanes. It was measured at 1.6 seconds in Node 24 on the development machine; a phone is expected to take several seconds, and it was not measured on one; one under the recovery key: 30 random octets shown once as eight groups of six base32 characters. A key that carries an expiry is refused at the switch-on. When a seal fails, the delivery is refused rather than stored in the clear: the sender is told with 4.3.0 and the error log carries HM7070. Passwords. A change through the webmail carries the key sealed again under the new password, and the server refuses a change without it (409, zero_access_rewrap). A password an administrator sets cannot re-wrap; at the next sign-in the page asks for the previous password or the recovery key. It is not encryption at rest. Mailbox encryption at rest stores mail under a key the server holds while the person is signed in. The two are never on one mailbox: each switch is refused where the other is on. Not built: a local bridge for IMAP or POP clients; search over the decrypted text in the browser; encryption of mail already in the mailbox when the switch is thrown. What the server does with a sealed message A sealed message is one signed or encrypted end to end, which must leave this server byte for byte as it arrived. An opaque one is a sealed message the server cannot read at all. A multipart/signed message is sealed but not opaque: its text is in the clear. Opaque is decided from the body, never from the label, or every check that stands aside for an encrypted message would be one header away from being switched off by anyone sending mail. The body must be S/MIME whose one part begins a CMS object (enveloped-data, authenticated-enveloped-data or signed-data), or PGP/MIME of exactly two parts: Version: 1, then one armoured OpenPGP message. None of what follows is a setting. Part of the server What it does with an opaque message Blocked-attachment list Never rewrites it. One application log line says the list was not applied. In 6.3.3 and earlier, blocking *.asc or *.p7m destroyed the ciphertext without a log line Virus scanner Scans it, and logs that its content COULD NOT BE EXAMINED for viruses. It is not treated as a scanner failure, so AVFailAction=1 does not hold it. If mail the server cannot scan must not be delivered, use a rule or a Sieve script on body :raw Delete attachments action (Remove all attachments and deliver the text since 6.3.7) Never strips an encrypted message. Since 6.3.7 an S/MIME message signed as one part - application/pkcs7-mime holding signed-data, opaque but not encrypted - is replaced by the action's note when the scanner finds something in it, where it was delivered as it arrived (work item 92). Set the action to delete the message if such mail must not be delivered Rules A body criterion does not match, whichever way it is phrased, with one log line per rule per message: under AND the rule does not fire; under OR its other criteria still count. To catch encrypted mail, test Content-Type for multipart/encrypted or application/pkcs7-mime with a custom-header criterion Sieve The evaluation is unchanged, as RFC 5173 requires; a log line is new. Use body :raw to recognise such a message Search and the index Only the header is indexed Content spam scoring Sees nothing in it, so encrypted spam is scored more leniently. SPF, DKIM, DMARC and DNSBL are unaffected A message only labelled encrypted, whose parts are readable, is blocked, stripped, filtered and searched like any other. In SealedMail.md's words: a message that arrives encrypted is not scanned, and your mail client, and you, are the last check. Gateway encryption, briefly Since 6.3.4 (schema 6094). On a remote domain policy, Encrypt mail to this domain at this server encrypts every message to one partner, here, to the partner's own gateway key. It encrypts at the server, as a delivery to a zero-access mailbox does, and the server holds no private key for it. The Subject is not hidden. When a message cannot be encrypted the default is to refuse it with 5.7.10. It does not sign, look keys up, or decrypt replies. Where to read more Operator documents at the v6.3.4 tag: SmimeAuthority.md, OpenPGP.md, ZeroAccess.md, SealedMail.md and RemoteDomains.md. Wiki: Encryption for People and Encryption and Certificates. Public documentation: 36.6 The portal: what a user gets. On this forum: The webmail portal at /portal, The Control Deck, Moving to new hardware before you carry an authority's key to another machine, and the rest of this category.
  • 0 Votes
    1 Posts
    125 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) renames two Control Panel pages this topic names: SSL certificates is TLS certificates and SSL/TLS is TLS versions & ciphers; Ctrl+K takes every old title and route to its page. See the 6.3.7 announcement. On Windows, hMailServer 6.3.8 (2 November 2026) reads each certificate and private key file, dh2048.pem and a port's client-certificate CA file from the file configured, and refuses with HM7536 a name it cannot hand over as it is named (work item 115): a port whose certificate or key such a name gives does not start, a client-certificate CA file verifies no certificate, and dh2048.pem is left out as a missing one is. Before upgrading, give each such file whose path holds characters outside ASCII, and the folders above it, an ASCII name; the Control Panel shows a certificate or key file whose name is refused as Name refused. On Linux every name goes over in UTF-8, so on a server that ran under a LANG that is not UTF-8, rename such a file to its UTF-8 name before upgrading. See the 6.3.8 announcement. This topic is the manual half of TLS on the SMTP, IMAP and POP3 listeners: your own certificate and where the Windows service may read it, then versions, ciphers, key exchange, session resumption, client certificates with SASL EXTERNAL, and certificates for IP addresses. Know this first: a port set to use TLS whose certificate cannot be loaded does not fall back to plain text. It does not start. The application log says HM5113, and the only other symptom is a listener missing. Where each thing is set, and when it applies What Set in Applies Certificates, and which port uses which Control Panel: TLS & certificates → SSL certificates and Connections & protocols → TCP/IP ports; the Deck's certificates and listeners views; /api/v1/certificates and /api/v1/ports; hmctl's certificate and port resources; COM At a reinitialise, or a service restart TLS 1.0 to 1.3, the cipher list for TLS 1.2 and below, the two cipher-order switches Control Panel: TLS & certificates → SSL/TLS; PUT /api/v1/settings; hmctl settings set; COM Settings See the note below TlsCipherSuites13, TlsKeyExchangeGroups and the four session settings The same page; PUT /api/v1/settings/ini/{name}; hmctl ini set; on Linux hmailserver --set-setting At a reinitialise A reinitialise is Apply now on the Control Panel's pending bar or in the Deck's Settings view, POST /api/v1/server/reinitialize, or Application.Reinitialize over COM. Every service stops and starts again in the same process; it ends the Deck's session and any drain in progress. GET /api/v1/server/settings/pending lists the [Settings] keys saved and not yet running, such as TlsKeyExchangeGroups; a port, a certificate, the versions and the cipher list are never in it. The Control Panel and COM are Windows-only; on Linux use the Deck, REST or hmctl. Do not edit hMailServer.ini. Since 6.3.4 the database holds the [Settings] keys, so a line changed in the file is put back (HM5804) or left unused; Settings: when a save takes effect explains the store and the reinitialise. When the versions and the cipher list apply. They and the two order switches are database settings (hm_settings rows), not [Settings] keys, so SettingsApply.md gives them no mark. Roadmap.md says they are applied in the call that builds each listener's TLS context, and SettingsApply.md says those contexts are built when the servers start; the wiki's Control Panel Pages page, written against 6.2.28, says the SSL/TLS page's COM settings apply at once. Press Apply now after changing any of them, and check from outside. Adding your own certificate Two PEM files. The certificate file must hold the leaf and the issuer chain: the server loads it twice, once as the certificate and once as the chain, and reports each failure separately. The private key is the second file. A passphrase-protected key is supported, per certificate. Give it in the add dialog, on the SSL certificates page's Private key passphrase card, in the Deck, or as private_key_password in POST /api/v1/certificates. It is write-only and kept under the machine's own secret store. The built-in backup and restore carries it to a new machine; a copied or shared database does not, so on a warm standby enter it again after a failover, and again on the primary after failing back, or the key does not load there and the TLS ports that use it do not start. An encrypted key with no passphrase is HM6170, and its port does not start. Where the files go. On Windows, a new 6.3.4 installation runs the service as NT SERVICE\hMailServer, which reads only what it has been granted (next section); put the files in Data\Certificates (C:\Program Files\hMailServer\Data\Certificates on a default installation), where a new 6.3.4 installation's self-signed pair is. An upgraded Windows server keeps the account it had; LocalSystem can read a file anywhere. On Linux the service runs as User=hmailserver under a unit with ProtectHome=yes, so nothing under /home is visible to it. The self-signed pair of a new installation is in /var/lib/hmailserver/Certificates, its key mode 0600 owned by the service's user; keep yours beside it, readable by that user. Copy the certificate file and the key into that folder. Add the certificate: on Windows, TLS & certificates → SSL certificates → Add certificate (a name, the PEM certificate file, the PEM key file, an optional passphrase); or the Deck's certificates view; or POST /api/v1/certificates with name, certificate_file and private_key_file. A file that is not there is refused (400). The Control Panel's grid then shows whether the certificate parses, when it expires, and whether the key is encrypted and matches. On Windows under the virtual account, run Grant access (next section), so it reads the new paths. Set the certificate on every TLS port: TCP/IP ports → Edit, the certificate list. Over REST, PUT /api/v1/ports/{id} is the whole record and fields left out take their defaults, so send protocol, address, port, connection security, certificate_id and the two client-certificate fields every time. Reinitialise. Check the application log for the codes below and, on Windows, Monitoring & troubleshooting → Server status for the listeners actually running and TLS & certificates → Transport encryption overview, which judges ports, certificates, versions and ciphers together. From a shell over the REST listener: add the certificate, then, once the ports are set, reinitialise and read the banner over implicit TLS. --from-json with - reads the body from standard input, which keeps a passphrase off the process list; the JSON holds the fields of step 2 and, for an encrypted key, private_key_password. hmctl certificate create --from-json - < certificate.json hmctl api POST /api/v1/server/reinitialize openssl s_client -connect mail.example.com:465 -quiet Code Message Meaning HM5113 Failed to load certificate file / …certificate chain… / …private key file The path, the format or the passphrase is wrong, the file cannot be read, or the key does not match. The port does not start HM5113 Error initializing SSL. Certificate not set. A TLS port has no certificate. The port does not start HM6170 — An encrypted key with no passphrase stored. The port does not start HM5991 The TLS certificate … expired on … / … is not valid until … Reported at startup; the port starts and offers it anyway, and clients refuse it. A future date usually means the server's clock is wrong HM5603 Unable to enable Diffie-Hellman key agreement dh2048.pem is missing: from Bin on Windows, from beside the binary in /usr/bin on Linux Replacing it later. When ACME renews, it logs ACME: Restarting servers to load the new certificate.; after replacing a file by hand, reinitialise likewise. If you publish DANE records, publish the new TLSA record before you rotate the certificate, never after; GET /api/v1/tlsa gives one 3 1 1 record per configured certificate, for _25._tcp only. Since 6.3.4 the alert certificate.expiring is on as shipped (7 days ahead), but nothing is mailed until AlertRecipient and AlertSenderAddress are set; the metrics listener exports hmailserver_tls_certificate_expiry_seconds. Since 6.3.4 a COM script's change to an SSL certificate is not in effect until Save(), and one never saved changes nothing, so a script that relied on an unsaved change must now save; the listeners still load the certificate at a reinitialise. ACME alongside. The built-in client gives its certificate to TLS ports that have none or hold the installation's self-signed one, and leaves alone a port that names a certificate somebody chose. The HTTPS listeners take files, not a certificate record: RestApiCertificateFile, WebServicesCertificateFile and MetricsServerCertificateFile, each with its private-key partner, all waiting for a reinitialise. ManageSieve reuses the certificate configured on an IMAP, POP3 or SMTP port. The service account's folder rules (Windows, since 6.3.4) hMailServer.exe /GrantServiceAccess, run elevated, grants NT SERVICE\hMailServer Modify on the program folder's own Data, Logs, Temp and Database, Read on its other folders, and what an administrator approved. It is the Grant access… button on Maintenance → Server limits & expert settings, card Windows service account, or, from an elevated Command Prompt in Bin: hMailServer.exe --confirm /GrantServiceAccess It checks every certificate and key file the configuration names (the SSL certificates, DKIM keys, and the REST, metrics and web-services files) against those grants. A file in a folder they reach needs nothing; where the file's own permissions keep the folder's grant from it, the grant gives Read on that file alone. A file they do not reach is listed OUTSIDE and is never given Read where it is, because the configuration could name any file on the machine. Move it into Data\Certificates, or approve its folder for Read with --approve-read "<folder>" or Yes at the question --confirm asks; approve only a folder you recognise. Some folders are refused even when approved, a share and a drive's root among them. ServiceAccount.md does not list the client-certificate CA bundle among the files the grant checks, so keep that bundle in Data\Certificates too. The account's rules in full, from what is never granted to the exit codes, are in Installing on Windows. Protocol versions and the legacy switches Version Bit in SslVersions COM REST, in PUT /api/v1/settings TLS 1.0 (legacy) 2 TlsVersion10Enabled tls_version_10_enabled TLS 1.1 (legacy) 4 TlsVersion11Enabled tls_version_11_enabled TLS 1.2 8 TlsVersion12Enabled tls_version_12_enabled TLS 1.3 16 TlsVersion13Enabled tls_version_13_enabled The shipped value is 24, TLS 1.2 and 1.3 only; SSLv2 and SSLv3 cannot be switched on at all. Do not re-enable TLS 1.0 or 1.1 unless a specific old client forces you to, and plan to replace that client. The versions and the cipher list also govern the connections this server makes when it delivers. The REST, web-services HTTPS and metrics listeners add a TLS 1.2 minimum of their own, so TLS 1.0 switched on for an old mail client stays off for the management API, and a server left with only TLS 1.0 and 1.1 leaves those three listeners with no version at all. Every version switched off is HM5990 and falls back to TLS 1.2 and 1.3; turning TLS off is done per port. Cipher suites: SslCipherList, AEAD-ONLY and TlsCipherSuites13 SslCipherList is an OpenSSL cipher string for TLS 1.2 and below only; it does not touch TLS 1.3. The shipped value is the long-standing list, which also admits CBC-mode suites and static-RSA key exchange. A string OpenSSL rejects is HM5511, and the previous list stays. AEAD-ONLY is a named preset, not an OpenSSL string: set SslCipherList to that word and the server applies a vetted list of forward-secret AES-GCM and ChaCha20-Poly1305 suites (ECDHE and DHE), excluding every CBC-mode suite and static-RSA key exchange. The cost: TLS 1.2 clients that speak only CBC cannot connect, and TLS 1.0 and 1.1 are left with no usable suite, so never enable them alongside it; the Transport encryption overview flags that combination. A misspelled preset goes to OpenSSL, which rejects it, and HM5511 names the string. TlsCipherSuites13 takes RFC 8446 names, colon separated, for example TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256. Empty, the shipped value, leaves OpenSSL's defaults. A list rejected outright is HM6120 and the defaults stand; names this build does not know are named in HM6121 and the rest of the list is applied. Prefer server cipher order and Prioritize ChaCha20-Poly1305 (TlsOptionPreferServerCiphersEnabled, TlsOptionPrioritizeChaChaEnabled; over REST tls_prefer_server_ciphers, tls_prioritize_chacha) are both off as shipped. The second works only with the first on and TLS 1.2 or 1.3 enabled. A compliance baseline from a shell, then a reinitialise: hmctl settings set --set ssl_cipher_list=AEAD-ONLY --set tls_prefer_server_ciphers=true hmctl ini set TlsCipherSuites13 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256 hmctl api POST /api/v1/server/reinitialize The ini line takes the administrator password (--password-file or HMAIL_PASSWORD); an API key is refused there with 401. Key exchange: the hybrid post-quantum groups TlsKeyExchangeGroups ships as X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:secp256r1: two ML-KEM hybrids first, then the classical curves, so a modern peer agrees a hybrid and an older one falls back. The list reaches every TLS context in the server: the mail listeners, ManageSieve, the three HTTPS listeners and the outbound client, so mail this server sends uses it too. A list OpenSSL rejects, an empty one, or one that leaves no group, is HM5720 and falls back to secp384r1:x25519:secp256r1, without the two hybrids; if even that is refused, HM5721 and OpenSSL's own defaults. Session caches, tickets and ticket-key rotation All four are on the SSL/TLS page's Session resumption tab and wait for a reinitialise. At their defaults three of them make no OpenSSL call at all. Setting Default A non-default value TlsSessionTicketsEnabled on Off: no tickets on any version. TLS 1.2 clients fall back to the in-process cache; TLS 1.3 clients get no ticket TlsSessionCacheSize 0, OpenSSL's cap of 20480 A positive value replaces the cap; -1 turns the server-side cache off, leaving tickets alone TlsSessionTimeoutSeconds 0, OpenSSL's 300 Bounds how long a leaked resumption secret stays useful TlsTicketKeyRotationSeconds 0, one key for the life of the process A captured ticket can be decrypted for at most two intervals; 86400 is a day Rotation is the one worth turning on for forward secrecy. Without it every ticket is sealed under one key made at startup, and that key, recovered later from a memory dump or a hibernation file, opens every ticket ever recorded. Rotation is process-wide. Each listener keeps its own session-ID context, so a session made on a port that asks for no client certificate is never resumed on one that requires it. Client certificates (mutual TLS) and SASL EXTERNAL The policy is per port, on the TCP/IP port dialog (COM ClientCertificatePolicy and ClientCertificateCAFile; REST client_certificate_policy and client_certificate_ca_file). Policy Behaviour CA bundle cannot be loaded Off (0) Nothing is asked. The default — Request (1) Asked, verified if offered, the outcome logged; the handshake never fails over it HM6140, and the port starts anyway Require (2) The handshake fails unless the client presents a certificate chaining to the port's CA bundle The listener is not started The bundle is the only trust anchor for that port, not the Windows certificate store. A policy on a port without TLS is refused when you save it; Roadmap.md adds a policy with no CA file, and Require on a STARTTLS (optional) port, where a client that never issues STARTTLS is never asked. A hand-edited database that has one anyway is HM6142 (require stops the listener, request only logs). Start with Request to see which clients could survive Require; Require on the port ordinary mail clients use would lock out every one without a certificate. EXTERNAL. A verified client certificate that names an address makes AUTH EXTERNAL (RFC 4422) available on that connection, on SMTP, IMAP and POP3, and to nobody else. It signs in as the mailbox the certificate names: a subjectAltName rfc822Name, the subject's emailAddress, or an address-shaped common name; a certificate for one user is never a credential for another. Since 6.3.4 such a sign-in also meets the domain's and the account's country policy, and an SMTP session signed in with it is no longer refused its second message with 550. Since 6.3.4 a mailbox encrypted at rest refuses EXTERNAL on IMAP and POP3, opening with its own password only. A certificate for an IP address (Linux, since 6.3.4) This is about certificates this server checks when it connects out, not the one it serves. An endpoint given by its address (an object store, a webhook, the update feed, an SMTP route, the syslog sink) presents a certificate with the address in an iPAddress entry, where RFC 5280 puts it. The Linux check read only dNSName entries and the common name, so such a certificate was refused; it is accepted now, and the HTTPS client no longer sends an address as the server name (SNI), as the IMAP migration client already did not. Newly refused on Linux: a wildcard taken to cover an address, and the common name of a certificate whose iPAddress entries name other addresses. Windows always accepted such certificates. What it does not do It does not obtain or renew certificates: that is ACME, in Let's Encrypt certificates are automatic. Connection security per port, and what a mail client is told: Connecting a mail client. MTA-STS, DANE, TLS-RPT and remote domain policies: Requiring TLS. The hardening checklist: Hardening a public mail server. TLS to the database is [Database] TlsMode and TlsCaFile, with no Control Panel or Deck editor: Choosing a database and encrypting the link. Where to read more Wiki: Encryption and Certificates §9.2 to §9.4 and §9.12, Ports Reference §3.4, Settings Reference, Security Hardening §17.3. Documents: ServiceAccount.md, SettingsApply.md, SecureDefaults.md, SecretsAtRest.md, DatabaseTls.md, AlertsAndAuditTrail.md. On the forum, for the certificate-expiry alert and where it is mailed: Alerts, the audit trail, syslog and the journal. The documentation page, 9.2 The manual way.
  • 0 Votes
    1 Posts
    128 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) is a security release that bears on this guide. On Windows, hMailServer.exe /ProtectFolders, which setup runs at every installation and upgrade, now hardens the program folder, Bin and ControlPanel: where hMailServer was installed in a folder that took its permissions from the root of a drive, any account that could sign in to the computer could replace the programs the service runs (CVE-2026-107571); the default folder under Program Files was never affected, and an installation at a drive's root is hardened by the upgrade itself. The serveradmin role given with a list of domains is confined to those domains over COM as over REST, a credential confined to named domains is refused the changes that reached beyond its domains over REST and COM, and a state-changing REST request that a browser reports as cross-site is refused. SMTP, POP3 and ManageSieve refuse a SASL PLAIN sign-in that asks to act as another account, and the stored-secrets key is no longer left in memory freed without being written over. An upgraded Windows server keeps the .NET 10 Desktop Runtime it had, 10.0.12 where 6.3.4 to 6.3.6 installed it, against which Microsoft published advisories on 13 October 2026: bring it to 10.0.13 through Microsoft Update or Microsoft's installer. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) is a security release too: upgrade every server running 6.3.7 or earlier. On Windows setup now makes or hardens the program folder, Bin and ControlPanel before it copies a file or runs a program of the installation (the four narrower cases of work item 78 that 6.3.7's notes left open), and a program folder, Bin or ControlPanel, a folder or a link on the way to them, or a program, library, script or settings file in them, that another account than SYSTEM, the Administrators and TrustedInstaller controls ends the installation with exit code 7, naming the icacls that deals with it and, where it says so, a restart of Windows before setup runs again. The auto-ban saves the IP range it adds under a name of its own, so that it bans the address; the web services listener (CardDAV, CalDAV, Autodiscover) refuses an address an auto-ban or an IP range bans; and a sign-in's name is matched to its account as the per-name lockout and the compromised-account hold count it, so a mail client that signs in with another spelling of the account's address than the stored one, in more than the case of the letters A to Z, or with white space at either end, may no longer sign in: correct the name in the client. See the 6.3.8 announcement. This guide covers what stands between a mail server on the public internet and the people who guess passwords at it. All of it is new in 6.3.4 except auto-ban, account lockout, the administrator's second factor and the IP range option Require TLS for auth, which are older; 6.3.4 sets that option on a new installation and judges it in the checklist. The one thing to know first: the secure defaults are applied only to a database the installation itself created, and an upgraded server is never changed. After an upgrade your listeners, IP ranges and service account are as they were and nothing is sealed until you make a key; the checklist says what is left. What a new installation starts with, and what an upgrade keeps In 6.3.3 and earlier a new installation listened on ports 25, 587, 110 and 143 with no TLS, and neither shipped IP range required TLS for authentication. The create scripts still make that database; the Windows installer, and the Linux packages' first start, now change it before the server first listens. A new installation since 6.3.4 An upgraded server Ports 25 and 587 (SMTP), 110 (POP3), 143 (IMAP) STARTTLS offered, optional as it was IP range Internet (every IPv4 address) TLS required for authentication as it was; the checklist flags it IP range My computer (127.0.0.1) unchanged as it was Certificates Self-signed (made at installation) as they were Windows service account NT SERVICE\hMailServer the account it had (LocalSystem, if it began before 6.3.4) What a client sees. From another machine, AUTH on SMTP is not offered before STARTTLS and is refused with 530 A SSL/TLS-connection is required for authentication. if it is sent; IMAP LOGIN and AUTHENTICATE answer BAD A SSL/TLS-connection is required for authentication.; POP3 USER and AUTH answer -ERR. After STARTTLS each works as before. From the server itself, on 127.0.0.1, nothing has changed, and mail from other servers on port 25 is taken with or without TLS. Why STARTTLS is optional. A port's requirement applies to every address: on port 25 it would refuse every server that cannot start TLS, on the others this computer's own clients. The range's rule protects the password. The certificate. self-signed-certificate.pem and its key self-signed-key.pem are in <data directory>/Certificates: C:\Program Files\hMailServer\Data\Certificates on a default Windows installation, /var/lib/hmailserver/Certificates from the Linux packages, where the key is mode 0600 and owned by the service's user. It is RSA 2048, runs 825 days, names the host and gives the organisation hMailServer, self-signed at installation. It encrypts, but nothing vouches for it, so mail clients warn. Replace it with ACME (set AcmeEnabled, AcmeContactEmail and AcmeDomains; the first certificate goes to every listener that holds the self-signed one) or with your own, set on each port. See Let's Encrypt certificates are automatic. Where the defaults are applied The create step marks its database with the hm_settings row NewInstallDefaultsPending; the verb --apply-new-install-defaults requires the mark and removes it once it has decided. Platform When it runs What to know Windows The installer, after the database step of a new installation, with the service stopped A database DBSetup attached, rather than created, carries no mark: exit 5, logged, nothing changed Linux (.deb, .rpm) The unit's first start after sudo -u hmailserver hmailserver --create-database, which marks the database and leaves a one-line file, .new-installation, in the DataFolder The unit runs ExecStartPre=-/usr/bin/hmailserver --apply-new-install-defaults --if-marked: nothing unless that file is there, then one try at the database, given room by TimeoutStartSec=300 (the leading - forgives a failure, not a timeout). It ignores a mark made more than a day from its clock; a run by hand has no such limit A container (since 6.3.8) The image's entrypoint, before whatever command the container runs; the Compose file and the Helm chart run the same step before the server hmailserver --apply-new-install-defaults --if-marked secures a database the entrypoint's --create-database made a moment before and opens nothing at any other start; a failure (run as root, a database not reached) is said and forgiven, and the server starts all the same By hand A container from an image before 6.3.8, a server started by hand rather than by the unit, or defaults left for later With the service stopped; on Linux as the service's user, because the verb refuses to run as root By hand, on Windows and then on Linux: Stop-Service hMailServer & "C:\Program Files\hMailServer\Bin\hMailServer.exe" --apply-new-install-defaults Start-Service hMailServer sudo systemctl stop hmailserver sudo -u hmailserver hmailserver --apply-new-install-defaults sudo systemctl start hmailserver Exit codes: 0 applied (with --if-marked, also nothing to do); 1 something could not be made or written (HM7510, HM7511 in the error log); 2 no database named, or not readable enough to judge, or on Linux run as root; 4 the Windows service is running; 5 not a database this installation created, or not as the create script left it: nothing changed. On Linux, mind the data directory. A DataFolder outside the unit's ReadWritePaths (/var/lib/hmailserver and /var/log/hmailserver) is read-only to the service, so the first start can neither write the certificate nor store mail there. Add the directory with a drop-in (sudo systemctl edit hmailserver, then ReadWritePaths=/srv/mail under [Service]) before that first start. Not yet proved on a clean machine. SecureDefaults.md's checks that the installer applies the defaults are not yet written, and those for the .deb ran only on GitHub Actions, so nothing runs them today. The setup checklist The checklist judges every server, new or upgraded, in twelve items. Key Done when profile a setting profile has been applied; asked for on a server with fewer than ten accounts domain an active domain exists, and none has a name the server reserves for its own directories account at least one account exists submission every SMTP listener other than port 25 has STARTTLS or implicit TLS, and a certificate other than the self-signed one sign_in_tls every IP range that lets a client connect from another address requires TLS for authentication, or reaches only listeners that start TLS before a password spf, dkim, dmarc every active domain has a TXT record beginning v=spf1, signs with a key whose record is published at <selector>._domainkey.<domain>, and has a TXT record beginning v=DMARC1 at _dmarc.<domain> backup the destination is set and the folder exists second_factor the administrator password is enrolled with a one-time code stored_secrets_key this server holds the key the database records, and an export of it has been recorded service_account it is not LocalSystem on Windows, not root on Linux Each item is done, action_needed, cannot_tell or not_needed. Port 25 is never the submission port, and a plain submission port beside a secured one still needs action. Where it is. GET /api/v1/setup/checklist (reading needs view_configuration; ?dns=0 skips the lookups), the Deck's Checklist view and, on Windows, COM's Utilities.GetSetupChecklist(LookUpDns) and the Control Panel's External setup page and dashboard. Passwords only over TLS, on a server you upgraded "Require TLS for auth" is an option of an IP range, not of a port, and covers SMTP, POP3, IMAP and ManageSieve alike. Read the sign_in_tls item: it names each range, and the listeners where a password from there would cross the network in the clear. If the finding is sign_in_no_tls, a listener has no TLS to start, and requiring TLS would refuse every client on it: give those listeners a certificate and STARTTLS first. This finding carries no fix. Set every mail client that signs in from another machine to STARTTLS first. Once the range requires TLS, a client that signs in without TLS is refused; that is why an upgrade is never changed by itself. When the finding is sign_in_plain, apply its fix: the Deck's button Require TLS for sign-in from this range, or PUT /api/v1/ipranges/{id} with {"require_tls_for_auth": true}, which needs manage_settings. On Windows the Control Panel opens its IP ranges page, where the setting is Require TLS for auth. Replace the self-signed certificate if you have one, so that submission_self_signed clears. Secrets at rest and the stored-secrets key Until 6.3.4 the administrator password (unsalted MD5, from the Windows installer), each account's second factor and the [Settings] secrets were kept as written. The administrator password is now written as PBKDF2. An older MD5 or SHA-256 value is rewritten as PBKDF2 at the first successful administrator sign-in where the service can write the file. Where it cannot (NT SERVICE\hMailServer, or the Linux package's root-owned file) the old value stays, HM7494 says so once, and the verb rewrites it: on Windows hMailServer.exe --set-admin-password from an elevated prompt, with the password in HM_ADMIN_PASSWORD, or on one line of standard input from a pipe or a file for a password in plain ASCII (Windows PowerShell 5.1 turns every other character into ?), never as an argument; on Linux sudo hmailserver --set-admin-password, which reads standard input. Second factors and ten [Settings] secrets (SRSSecret, BATVSecret, OAuth2HmacSecret and the rest) are sealed with AES-256-GCM under the installation's stored-secrets key, in the form HMK1:<key id>:<base64>, once the installation has a key. The key is kept in each server's hMailServer.ini as [Security] StoredSecretsKey, and never in the database or a backup archive. Who gets a key without asking. The Windows installer makes one on a new installation. On Linux, hmailserver --create-database makes one when it creates the schema and can write the configuration: in a container, whose /etc/hmailserver the service owns, it does (no image of 6.3.4 is published); run by the package as the service's user, it cannot. An upgraded installation, and a package installation on Linux, seal nothing until an administrator makes the key: Make the key on one node. Windows, from an elevated prompt beside hMailServer.exe: .\hMailServer.exe --create-stored-secrets-key. Linux: sudo hmailserver --create-stored-secrets-key. Export it at once: .\hMailServer.exe --export-stored-secrets-key D:\Escrow\hmailserver-stored-secrets.key, or sudo hmailserver --export-stored-secrets-key FILE. The export writes a new file, never over one that exists, readable only by SYSTEM and the administrators on Windows and mode 0600 on Linux, and prints the key id, never the key. Restart the service on that node (Restart-Service hMailServer, or sudo systemctl restart hmailserver). Its next start seals what is stored as written; until then nothing is sealed. On every other node that shares the database, --import-stored-secrets-key FILE, then restart each. An import is refused while the node holds another key that opens: a standby or new node whose installer made a key of its own removes its [Security] StoredSecretsKey line first, and until then the shared database refuses it (HM7495). Keep the exported file with the database backups, stored apart from them. Why the export matters. The product's own backup archive carries these secrets opened, and a restore seals them under the restoring server's key, so set a backup passphrase. A database-level backup (a dump, a copy of the .sdf) restored without the key brings back every second factor and [Settings] secret unopenable. A copy of hMailServer.ini is no copy of the key on Windows, since its line is sealed under that machine's DPAPI; never copy that line from one machine to another. When a node lacks the key it says HM7495 at every start and makes no key of its own; until the key is imported, a [Settings] secret sealed under it runs as though unset (HM7492) and a second factor refuses every code (HM7493). A key lost for good is recovered from with --forget-sealed-secrets <key id>, with the service stopped (exit 4 otherwise), which clears only what that key sealed: every second factor is enrolled again, every [Settings] secret entered again. ProtectStoredSecretsWithDPAPI=0 (default 1) has a cost. The next start opens what was sealed and stores it as written, readable by anyone who can read the file or the database, and the machine-bound secrets are written with the legacy Blowfish scheme, whose key is in the source code: the way back, not a setting for a new server. Two things to do after an upgrade. Since 6.3.4 PasswordPepper stays in hMailServer.ini only and is left out of every backup, so keep a copy of that value with the backups. And a password hash stored before 6.3.4 from a password with a character above U+00FF is refused, to its own password too: an administrator sets such passwords again, and the application log names each account as its user tries to sign in. Checking new passwords against published breaches Off by default, because it sends something off the server. When on, a new password found in the published corpus of breached passwords is refused. Setting (REST key) Default What it means breach_check_enabled off The whole feature breach_check_url https://api.pwnedpasswords.com/range/ Where the five characters are sent breach_check_minimum_count 1 Refuse a password seen this many times or more breach_check_unreachable_action allow allow lets a change through unchecked; refuse refuses it What leaves the server is the first five characters of the password's SHA-1 and nothing else. The default endpoint is a third-party service, Have I Been Pwned's Pwned Passwords, and an organisation that cannot send it those five characters, its address and the time runs its own mirror. When it runs. When a password is chosen, by the person or by an administrator, and never at logon. An endpoint that gives no complete answer within eight seconds counts as unusable, and the error log records HM6600 (let through) or HM6601 (refused). Under refuse, an endpoint that is down refuses every password change on the server. Bulk imports make one request per account, so run them against a mirror or with the check off. Where it is set. The four settings are hm_settings rows, not hMailServer.ini lines, and a change applies from the next password chosen. They are on the Control Panel's Administrative access → Password page (the Passwords found in published breaches card), in the Deck's Settings view, over REST and in hmctl settings set: hmctl settings set --set breach_check_enabled=true Compromised-account detection This judges every authenticated submission (SMTP, the webmail's send, a scheduled draft) against the account's own history of the last 30 days, without reading the message. It starts in watch on every installation, new or upgraded: the baseline is built, the account.compromised alert says what enforcing would have done, and nothing else happens. Four signals score: volume (2 points), the last hour's messages reaching compromise_volume_floor (30) and exceeding compromise_volume_factor (4) times the busiest baseline hour; recipients (2), the last hour's distinct recipients reaching compromise_recipient_floor (20), at least 80 per cent of them new to the account; hour (1), an hour of the day (UTC) the account has not submitted at in the baseline; address (1), a client network (the /24 of an IPv4 address, the /64 of an IPv6 one) not seen for this account. compromise_score_to_lock (3) is a takeover. In enforce, the message is held, the account is locked until an administrator unlocks it, and the alert and an account.locked webhook are raised. The client is still answered 250 Queued. Held mail is reviewed on the Deck's Held for review page and under /api/v1/review; a held message nobody has looked at in compromise_held_days days (7) is discarded. Watch it before enforcing: the false positive is a person locked out of their own mail. There is no Control Panel page for it. Roles instead of the administrator password Rather than share the administrator password, give each administrator a role in the domains they look after, on the Deck's Roles view, over REST, or on Windows in the Control Panel or over COM. Nothing changes on the day you upgrade: an account with no assignment holds the authority its admin level has always given it, and a narrow role cannot take over a wider one, so a help desk is refused a domain administrator's password reset. Administrator roles end to end has the permissions, how to give a role, and the two stores only the administrator password opens. The rest of the list Auto-ban is on by default: 3 failures within 30 minutes, a 60-minute ban. MaxInvalidLogonAttempts at 0 disables it, and AutoBanMinutes at 0 disconnects the client but bans nothing. Account lockout (AccountLockoutThreshold) is off until set, and counts by the name being guessed at. IP ranges, auto-ban, lockout and the rest has both in full. The server-enforced administrator second factor refuses the password alone everywhere, the REST API included. On Windows, move a service still on LocalSystem with the installer, the Control Panel's button, or from an elevated Command Prompt in Bin: hMailServer.exe --confirm /GrantServiceAccess, then, only if that ended in 0, hMailServer.exe --service-account virtual /Register, then net stop hMailServer && net start hMailServer. Check first what the virtual account cannot do, listed in Installing on Windows. The event-script flaw fixed in 6.3.4 (CVE-2026-100741; its record was not yet public when this was written) is in every release from 6.0.0 to 6.3.3: upgrade, and until you can, switch event scripting off. See Event scripts and the 6.3.4 security fix and the 6.3.4 security announcement. What it does not do Passkeys, single sign-on and the session policies do not reach IMAP, POP3 or SMTP, which still take the password or an app password. The breach check does not cache, and does not expire a password that enters the corpus later. Compromised-account detection does not drop a session that is already up. The stored-secrets key protects against a copy of the database that leaves without the file, not against a local account on the server. Where to read more Wiki: Security Hardening. Documents: SecureDefaults.md, SetupChecklist.md, SecretsAtRest.md, PasswordBreachCheck.md, CompromisedAccounts.md and Roles.md. The public documentation: chapter 17, Security hardening. On this forum: Upgrading to 6.3.4, The Control Deck and hMailServer on Linux. A suspected vulnerability is reported privately, never in a forum post: 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 (SECURITY.md).
  • 0 Votes
    1 Posts
    111 Views
    ProgressiverobotP
    Written for 6.3.4. hMailServer 6.3.7 (17 October 2026) fixes the ACME HTTP-01 challenge responder, which served one connection at a time, so that idle clients could make a certificate's issuance or renewal fail in every earlier version (work item 97): it now serves up to 64 connections at once and closes one past that, though a client that holds 64 idle connections open through a whole validation can still make it fail. In 6.3.7's Control Panel the SSL certificates page is TLS certificates, and Ctrl+K takes the old title to it. See the 6.3.7 announcement. hMailServer 6.3.8 (2 November 2026) no longer leaves a host added to AcmeDomains waiting for the renewal (work item 98): a certificate that does not name every host on the list is due at once, a save of the list in the running server starts the check (a value hmailserver --set-setting stores is read at the next reinitialise or start), and GET /api/v1/acme, POST /api/v1/acme/run and the Control Deck's Certificates page give the certificate's state and ask for a check, the CA's rate limits kept. Where the list holds a host the certificate lacks, the first check after the upgrade orders a certificate for every host on it, and a host the CA cannot validate fails the whole order, at most three times an hour, and holds the renewal for the other hosts: take such a host off the list before upgrading. A wildcard, an IP address or another name no certificate can carry is left out of the check and the order and named in the application log. An IP range of your own that allows some addresses none of SMTP, POP3 and IMAP now closes the web services listener to them, and with it ACME's http-01 validation where that listener answers it, so check that the certificate authority does not come from one; and on Windows an AcmeCertificateDirectory whose path holds characters outside ASCII may be refused with HM7536, and then the client reads and writes nothing there and orders no certificate: give it, and the folders above it, an ASCII name before upgrading. See the 6.3.8 announcement. hMailServer has an ACME v2 client built in (RFC 8555). It obtains a free certificate, installs it, assigns it to your TLS ports, renews it before expiry and reloads it. No restart, no scheduled task, no external client. Configure it under TLS & certificates → Certificates (ACME), over REST with PUT /api/v1/settings/ini/{name}, or on Linux with sudo hmailserver --set-setting NAME=VALUE. Since 6.3.4 these are database settings, not lines to edit in hMailServer.INI: a line edited there is named in the error log (HM5804) and not used, and is put back where the service may write the file. The values: AcmeEnabled=1 [email protected] AcmeDomains=mail.yourcompany.com,mta-sts.yourcompany.com,autoconfig.yourcompany.com AcmeHttpPort=80 AcmeReuseKey=1 AcmeEnabled ships as 0, and a change to it takes effect at a reinitialise (Apply now), not at the save. AcmeDirectoryUrl defaults to https://acme-v02.api.letsencrypt.org/directory. Two requirements. Port 80 must be reachable from the internet, because that is how the http-01 challenge proves you control the name. Every name in AcmeDomains must already resolve to this server before you switch it on. If you also host MTA-STS or client autoconfiguration, put mta-sts.<domain>, autoconfig.<domain> and autodiscover.<domain> in AcmeDomains so one certificate covers them. After issuance. The server creates or updates a certificate record named ACME (automatic) and assigns it to every TLS-enabled listener that has none of its own, and since 6.3.4 to every listener still holding the self-signed certificate a new installation is given, logging each assignment. A listener the assignment could not be saved for is reported as a High 6100 rather than logged as a success, because a port left without a certificate accepts no TLS at all. That is the error to alert on. Renewal. The renewal window is two thirds of the certificate lifetime, plus ARI. That is why there is nothing left to schedule. Files live in Data\ACME on Windows and /var/lib/hmailserver/ACME/ on Linux: the account key, fullchain.pem and privkey.pem. Back that directory up with the rest of Data\. If a restore misses it the client re-issues, but the new key invalidates any TLSA records you have published. Leave AcmeReuseKey=1. Keeping the same private key across renewals is what keeps published TLSA records valid. Linux. Port 80 works without root. CAP_NET_BIND_SERVICE on the unit is what allows the bind. The default certificate directory is <DataFolder>/ACME, which is inside ReadWritePaths. Point AcmeCertificateDirectory elsewhere and you must add that path to ReadWritePaths with a drop-in, or issuance fails on a read-only filesystem. If it never worked for you. The built-in client had never once succeeded against real Let's Encrypt until 6.2.24 (GitHub issue 34). Boulder pretty-prints its JSON and the challenge locator searched for the compact spelling. 6.2.25 then fixed issuance and renewal ending the process (GitHub issue 93). If you gave up on this on an older build, it is worth another look. Another CA. Leave AcmeEnabled=0, add the PEM certificate and key under TLS & certificates → SSL certificates, then assign it to ports under Connections & protocols → TCP/IP ports. On Linux, an external client that replaces the PEM files in place needs POST /api/v1/server/reinitialize or a restart before the new files are served. Chapter 9, encryption and certificates: https://www.progressiverobot.com/hmailserver-documentation/#9-encryption-and-certificates Report problems: https://gitlab.com/hmailserver/hmailserver, or without a GitLab account by email to the project's Service Desk address, contact-project+hmailserver-hmailserver-86730559-issue-@incoming.gitlab.com