fix(email): announce a configurable SMTP EHLO name #508
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-507-configurable-smtp-ehlo-name"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Both SMTP paths let lettre pick the EHLO name, so with the default
hostnamefeatureClientId::default()resolved to the OS hostname, which inside a container is the short container id (EHLO 16c98fb0ffe1on the Stalwart relay). A container id is not a FQDN: relays that enforcereject-non-fqdn, that match EHLO against forward/reverse DNS, or that score it for spam reject or penalise the mail, and the name changes on every container recreate so it has no diagnostic value either.EmailConfiggainssmtp_ehlo_name(from a newSMTP_EHLO_NAME, trimmed, empty treated as unset) andEmailConfig::ehlo_name(), which resolvesSMTP_EHLO_NAME-> host ofbase_url(APP_URL) -> domain offrom_email-> lettre's default. The fallback chain makes this a no-config fix:APP_URLis already a real FQDN in every environment, so existing deployments stop announcing the container id on upgrade. An IP source goes on the wire as an address literal ([10.1.2.3]) per RFC 5321. The EHLO identity is deployment/network configuration rather than a per-tenant email setting, so it stays env-derived likebase_urland needs noemail_configcolumn or migration; it is not a secret, so noSECRET_MAPentry.Both
build_transportarms now call.hello_name(...),test_connectionuses the same resolved name forconnect_tokio1andconn.starttls(...), and the resolved name is logged once atinfowhen the transport is built (so it also prints on every admin reload).Completeness sweep, every SMTP client-id / connection construction site in the workspace:
services/email.rs:87-105build_transport, both TLS arms.hello_name(config.ehlo_name())services/email.rs:161test_connectionhelloconfig.ehlo_name()services/email.rs:172,185AsyncSmtpConnection::connect_tokio1helloservices/email.rs:197conn.starttls(tls_params, &hello)helloservices/email.rs:1426,1446mock-relay test connectionsconfig_with_smtp(..).ehlo_name()services/email.rs:1477new on-the-wire EHLO testEHLO mail.example.comconfig.rs:371ClientId::default()bunyip-api,bunyip-web,bunyip-oci,bunyip-oidc)bunyip-domain-only dependency, no other crate opens an SMTP sessionservices::email::tests::no_smtp_session_falls_back_to_the_container_hostnamefails the build ifClientId::default()reappears inemail.rsor if a relay builder stops setting the hello name, so the removal is enforced mechanically.#BUNYIP-507
Both SMTP paths let lettre pick the EHLO name, so with the default `hostname` feature `ClientId::default()` resolved to the OS hostname, which inside a container is the short container id (`EHLO 16c98fb0ffe1` on the Stalwart relay). A container id is not a FQDN: relays that enforce `reject-non-fqdn`, that match EHLO against forward/reverse DNS, or that score it for spam reject or penalise the mail, and the name changes on every container recreate so it has no diagnostic value either. `EmailConfig` gains `smtp_ehlo_name` (from a new `SMTP_EHLO_NAME`, trimmed, empty treated as unset) and `EmailConfig::ehlo_name()`, which resolves `SMTP_EHLO_NAME` -> host of `base_url` (`APP_URL`) -> domain of `from_email` -> lettre's default. The fallback chain makes this a no-config fix: `APP_URL` is already a real FQDN in every environment, so existing deployments stop announcing the container id on upgrade. An IP source goes on the wire as an address literal (`[10.1.2.3]`) per RFC 5321. The EHLO identity is deployment/network configuration rather than a per-tenant email setting, so it stays env-derived like `base_url` and needs no `email_config` column or migration; it is not a secret, so no `SECRET_MAP` entry. Both `build_transport` arms now call `.hello_name(...)`, `test_connection` uses the same resolved name for `connect_tokio1` and `conn.starttls(...)`, and the resolved name is logged once at `info` when the transport is built (so it also prints on every admin reload). Completeness sweep, every SMTP client-id / connection construction site in the workspace: | Site | Classification | | --- | --- | | `services/email.rs:87-105` `build_transport`, both TLS arms | compliant: `.hello_name(config.ehlo_name())` | | `services/email.rs:161` `test_connection` hello | compliant: `config.ehlo_name()` | | `services/email.rs:172,185` `AsyncSmtpConnection::connect_tokio1` | compliant: both take that `hello` | | `services/email.rs:197` `conn.starttls(tls_params, &hello)` | compliant: same `hello` | | `services/email.rs:1426,1446` mock-relay test connections | compliant: `config_with_smtp(..).ehlo_name()` | | `services/email.rs:1477` new on-the-wire EHLO test | compliant: asserts the relay saw `EHLO mail.example.com` | | `config.rs:371` `ClientId::default()` | compliant: the resolver's last fallback, the one place it may appear | | rest of the workspace (`bunyip-api`, `bunyip-web`, `bunyip-oci`, `bunyip-oidc`) | N/A: lettre is a `bunyip-domain`-only dependency, no other crate opens an SMTP session | `services:📧:tests::no_smtp_session_falls_back_to_the_container_hostname` fails the build if `ClientId::default()` reappears in `email.rs` or if a relay builder stops setting the hello name, so the removal is enforced mechanically. #BUNYIP-507