This page is deliberately specific, including about what is not protected. A security control you have misunderstood is worse than one you know the limits of, and anyone evaluating this properly will find the gaps anyway.
Encryption at rest
Message storage uses Fernet: AES-128 in CBC mode with PKCS7 padding, a random 128-bit IV per message, and an HMAC-SHA256 authentication tag over the whole token. Authenticated encryption, so tampering is detected rather than decrypted into something plausible.
One master key, generated at install, held at /etc/securemail/secret.key with mode 0400 owned by the service account. It never leaves the host and is never transmitted.
What the master key protects
| Data | Scope |
|---|---|
| Secure messages | The entire raw message — all headers, all bodies, every attachment — as one encrypted blob, file mode 0600. |
| Quarantined messages | Same treatment. Held mail is not sitting in plaintext. |
| eDiscovery archive | Same treatment, in a separate tree with its own retention. |
| Stored credentials | Relay and submission passwords, AI API keys, Safe Browsing key, LDAP bind password, Entra client secrets, backup passphrase. |
| Administrator MFA secrets | TOTP seeds are encrypted, not merely hashed — they have to be recoverable to verify a code. |
| API relay passwords | Per-key SMTP credentials for application submission. |
Credential decryption fails closed to empty: if a stored secret cannot be decrypted, the appliance uses nothing rather than passing ciphertext to a remote service as a password.
What is not encrypted
Envelope metadata is stored in the clear in PostgreSQL: sender, recipients, subject, client IP, HELO, size and message ID, in the mail log and event stream. Message content is always encrypted; the fact that a message existed, between whom, and with what subject, is not.
This is what makes searching, reporting and the dashboard possible. If subjects are sensitive in your environment, treat database access as equivalent to metadata access, and note that retention settings control how long that metadata persists.
Administrator passwords are stored as salted one-way hashes and are not recoverable. One-time access codes are stored as salted hashes, never in plaintext.
There is no key escrow and no key rotation. Back up /etc/securemail/secret.key off-host and treat it as the crown jewels. A database backup without it is unreadable. This is the deliberate trade for the key never existing anywhere you do not control.
Encryption in transit
- Web: TLS 1.2 and 1.3 only, HSTS at one year, plus frame-deny, no-sniff and no-referrer headers.
- SMTP: STARTTLS on inbound and outbound, reusing the same certificate by default. Policy levels from opportunistic through to DANE and full verification.
- Per-domain enforcement: require TLS for named domains, refusing to deliver in the clear if the far end will not negotiate.
- MTA-STS and TLS-RPT: publish a policy so senders refuse to downgrade, and collect failure reports. Start in testing mode.
Backups
Encrypted backups use a separate key derived from your passphrase with scrypt, not the master key — so a backup can be restored to new hardware without transporting the master key, and possessing a backup is not possessing the live key.
The archive format defends against more than eavesdropping:
- Streamed in 4 MiB chunks, so a large store does not have to fit in memory.
- Each chunk carries its index, so reordered chunks are detected.
- A zero-length terminator marks the end, so a truncated backup is detected rather than restoring partially.
- A self-test runs before every encrypted backup, so failures surface at backup time, not at restore time.
Set a backup passphrase. Without one the backup is data-only: it excludes the master key, the environment file and the DKIM keys, so it cannot rebuild a working appliance on its own. With a passphrase set, the backup is complete.
Restore is deliberately constrained: it refuses any file outside the backup directory, rejects archive entries using path traversal or symlinks, and refuses a database dump containing shell-escape or COPY … FROM PROGRAM constructs — because restore runs as root.
Administrative access
Roles
| Role | Can do |
|---|---|
| superadmin | Everything, including the setup wizard and administrator management. |
| operator | Day-to-day operation: quarantine, lists, reports, mail flow. |
| auditor | Read-only, enforced at the request level — every state-changing method is refused, not merely hidden. |
Authentication
- MFA: standard TOTP with a 160-bit secret, 30-second period, one step of clock tolerance, and constant-time comparison. Enrol via QR code.
- Microsoft Entra SSO: OIDC authorization-code flow with a confidential client. Hardened in ways worth checking against your own requirements: the multi-tenant
commonendpoint is rejected so only your tenant can sign in, a nonce is mandatory, state and nonce are single-use, and TOTP is still required after SSO if the account has it enrolled. Auto-provisioned accounts get operator, never superadmin. - CSRF tokens on every form.
- Network restriction: an admin allowlist can limit the console to named networks, and it gates the login and first-run pages too — not just authenticated ones.
Brute-force protection
An application-layer firewall counts failed password, MFA, SSO and one-time-code attempts and blocks the source IP — five failures in fifteen minutes yields a sixty-minute block by default. It trusts a forwarded client address only from loopback and only the rightmost entry, so the header cannot be spoofed to evade or to frame another address.
Locked yourself out? On the host:
sudo /opt/securemail/venv/bin/python /opt/securemail/scripts/fw_unblock.py --alleDiscovery: separated from administration
This is the strongest access control in the product and the one most worth knowing about.
- The ordinary admin message viewer never decrypts. An administrator investigating quarantine sees metadata and the filter verdict, not the message.
- Reading archived content requires a separate per-administrator permission that is off for everyone by default, including superadmins. It is not implied by any role.
- Even with it, the administrator must re-authenticate, and the grant lasts ten minutes.
- Legal hold exempts archived messages from retention so they cannot be pruned while under hold.
The practical consequence: “the mail administrator can read anyone’s mail” is not true here by default, and making it true is an explicit, auditable act.
Audit logging
Every successful state-changing request is audited automatically — not a curated list of interesting actions, which is how audit trails end up with gaps. Each entry records the administrator, the action and the submitted values.
- Secrets are masked before storage: any field whose name looks like a password, key, secret or token is replaced. Enabling an integration does not write its API key into the audit log.
- Retained 90 days by default, alongside the mail log, event stream, DMARC results and firewall events.
- Exportable as CSV, with spreadsheet formula injection neutralised — so opening an export cannot execute content an attacker placed in a subject line.
- The console’s log viewer reads a fixed allowlist of service logs, with no shell involved and hard output caps.
Recipient access model
External recipients have no accounts and no passwords. Access needs both the message link and a one-time code delivered to the address the message was addressed to. Forwarding a link does not transfer access, because the code goes to the original recipient.
Message HTML is sanitised before rendering — inline styles and data URIs stripped — and displayed in a sandboxed frame. The quarantine self-service portal returns identical responses whether or not mail is held for an address, so it cannot be used to enumerate valid addresses.
Other hardening worth knowing
- Never an open relay: the unauthorised-destination check is unconditional and cannot be configured away.
- Never an open redirector: click-time URL protection refuses to redirect without a valid signature, and fails closed to a warning page when reputation cannot be checked.
- Regex safety: administrator-supplied patterns run under a guard with bounded input, so a pathological pattern cannot stall the mail pipeline.
- Send API keys are stored only as hashes, are pinned to a single sending domain, and have their From header rewritten from the key rather than trusted from the request.
- Service isolation: the portal and API run with no-new-privileges and a read-only system view; each web application runs unprivileged.
- Configuration rollback: a Postfix change that fails validation is rolled back automatically, so a bad edit cannot leave mail broken.
DLP
Outbound content inspection across financial, identity, health and secrets categories, with checksum validation rather than pattern matching alone — card numbers are Luhn checked, national identifiers validated by their own algorithms — which is what keeps false positives survivable. It also scans a normalised copy with HTML entities decoded and zero-width characters stripped, so padding a card number to evade detection does not work. It applies whenever any recipient is external, so adding an internal recipient does not suppress it.