Email Delivery: the Authenticated SMTP Relay
Email verification, password recovery and MFA enrolment all depend on one thing: security-critical mail that actually arrives. Public cloud providers commonly block outbound port 25, so a Keycloak or application running in the cloud cannot deliver mail directly. As an interim solution, the platform runs its own hardened outbound relay on infrastructure that has unrestricted outbound mail, and every application submits to it over authenticated TLS.
Interim by design
This relay is a deliberate stop-gap. Applications only know "an SMTP submission endpoint with credentials", so it can later be swapped for a transactional email provider by changing configuration — no application code changes.
Architecture
flowchart LR
subgraph Cloud["Cloud environments (port 25 blocked)"]
KC[Keycloak]
APP[Applications]
end
subgraph Relay["Relay host (unrestricted outbound)"]
SUB["Postfix<br/>submission :587<br/>TLS required + SASL"]
end
MX[Recipient mail servers]
KC -- "STARTTLS + SASL login" --> SUB
APP -- "STARTTLS + SASL login" --> SUB
SUB -- "direct delivery via recipient MX" --> MX
The relay is one Postfix instance with Cyrus SASL, provisioned by an Ansible role that is disabled unless a deployment opts in. Nothing in the role names a customer: every caller is just one more entry in a list.
Security properties
| Property | How it is enforced |
|---|---|
| Not an open relay | smtpd_relay_restrictions = permit_sasl_authenticated, reject — there is no permit_mynetworks and no open fallback. Even a loopback connection must log in |
| Not an inbound mail server | The plain smtp (port 25) listener is removed from master.cf; the host only exposes the submission service on 587 |
| Encryption is mandatory | smtpd_tls_security_level = encrypt — credentials are never accepted on a plaintext session; SSLv2/3 and TLS 1.0/1.1 are disabled |
| Anonymous access is impossible | smtpd_sasl_security_options = noanonymous |
| Per-caller identity | One SASL account per caller. Every session is attributable, and a leaked credential is revoked by deleting that single account |
| Trusted certificate | Let's Encrypt certificate issued and renewed automatically (HTTP-01), with a reload hook that restarts Postfix on renewal |
| Secrets stay out of logs | Account passwords are handled with no_log in the automation and never stored in the repository |
| Safe to rotate | Account creation is create-if-missing; a rotation is remove → apply → re-add, never a silent in-place overwrite |
Onboarding a caller
A caller (for example, an identity provider) needs four values: the relay hostname, port 587, STARTTLS, and its own username/password. In Keycloak these go under the realm's Email settings:
| Keycloak field | Value |
|---|---|
| Host | the relay's public hostname |
| Port | 587 |
| Enable StartTLS | on |
| Authentication | on, with the caller's own account |
| From | an address on a domain you control |
Adding a caller is a one-line change to the relay's user list followed by an apply:
[smtp_relay] Deploy main.cf / master.cf ... changed
[smtp_relay] Create missing SASL accounts ... changed
[smtp_relay] Issue Let's Encrypt cert for the relay hostname ... ok
[smtp_relay] Enable and start postfix ... ok
Verifying the relay
Check the security posture from the outside, then send a real message.
Verification: OK
swaks --server mail-relay.example.com --port 587 --tls \ --to [email protected] --from [email protected]<~ 530 5.7.0 Authentication required# unauthenticated submission is refusedswaks --server mail-relay.example.com --port 587 --tls \ --auth LOGIN --auth-user "$SMTP_USER" --auth-password "$SMTP_PASS" \ --to [email protected] --from [email protected]<~ 250 2.0.0 Ok: queued as 4F1C0A2B3D
Deliverability checklist
A relay only helps if receivers trust its mail. Keep the sender domain aligned with:
- SPF — authorise the relay's address in the domain's SPF record.
- DKIM — sign outgoing mail for the sender domain.
- DMARC — publish a policy so receivers can verify alignment.
- Reverse DNS — the relay's IP should resolve back to its hostname.
Security mail is a target
Verification and reset emails are exactly what phishers imitate. Keep the From domain fixed, keep links pointing at your Keycloak host only, and never include a secret in the message body — the links are signed and expire by design (see Email Verification & MFA).