Repository navigation
Conversation
… SMTP client Controls which IP address family is used for outgoing SMTP connections when a remote host publishes both AAAA and A records. smtp_client.address_preference accepts ipv6 (default), ipv4 (preferred first, other family used as a fallback), ipv6_only or ipv4_only (only that family is used). This allows avoiding IPv6 for outgoing mail where the sending IP has no matching PTR record (e.g. Google's FCrDNS check) without disabling IPv6 on the host.
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implement address preference for IP address family selection in SMTP client
Problem
Outgoing mail is delivered over IPv6 whenever a recipient's host publishes an AAAA
record. For senders whose IPv6 address has no matching PTR record, this fails
reverse-DNS / FCrDNS checks — e.g. Google rejects the connection because the IPv6
address does not resolve back to the sending hostname.
Disabling IPv6 on the host (or disabling it "only in the email service") does not
help: the SMTP client resolves AAAA records itself and connects to the literal IP,
so the OS-level setting is bypassed. Disabling IPv6 everywhere is also invasive and
slow to roll out.
Solution
Let the SMTP client decide which address family to use when a remote host has both
AAAA and A records, via a new setting:
smtp_client.address_preference.SMTPClient::Server#endpointsnow orders/selects the resolved endpoints accordingto that preference. Because
Net::SMTPconnects to a literal IP, the endpointright order fully determines which family is used — no OS changes required.
Configuration
address_preferenceipv6(default)ipv4ipv6_onlyipv4_onlyipv6, so existing installations are unaffected.ipv6and log a warning once, rather than breakingdeliveries.
Set via environment variable:
...or in
postal.yml:Changes
app/lib/smtp_client/address_preferences.rb(new) —SMTPClient::AddressPreferencesexposes
current,prefer_ipv4?andfallback?for the two models (prefer / only)across both families.
app/lib/smtp_client/server.rb—#endpointsorders the resolved endpoints bythe preferred family and, in strict modes, drops the other family. Adds a private
ordered_endpointshelper.lib/postal/config_schema.rb— addsstring :address_preferenceto thesmtp_clientgroup.doc/config/yaml.yml,doc/config/environment-variables.md— regeneratedconfig documentation.
spec/lib/smtp_client/address_preferences_spec.rb(new) — covers.current(all four values, case/whitespace, invalid),
.prefer_ipv4?and.fallback?.spec/lib/smtp_client/server_spec.rb— covers endpoint order/selection for allfour modes, the invalid-value fallback, and the strict-mode edge where the preferred
family has no records (returns
[]).Testing
CI runs the suite against the
ciimage via Docker Compose, so it was reproducedlocally the same way — Docker only, no host Ruby.
Full suite
Result: 831 examples, 2 failures. Both failures are in
spec/lib/dns_resolver_spec.rband depend on the runner's external DNS behaviour:DNSResolver#a ... raises an error when the timeout is exceeded— expects aresolver timeout that did not fire in this environment.
DNSResolver when using a resolver for a domain will not return domains that are not hosted on that server— expectsexample.comto resolve to nothing, but thesandbox resolved it to live Cloudflare addresses (
104.20.23.154,172.66.147.243).Neither file is touched by this change: both depend on external DNS behaviour and are
unrelated to the SMTP client, so they fail independently of the address-preference
work.
Targeted specs (same image)
Result: 65 examples, 0 failures.
Lint
RuboCop pinned to the lockfile version (1.48.1):
No offences in the new/changed code. (The two existing offences in
lib/postal/config_schema.rbare present onmaintoo.)Behaviour
All four modes were exercised against a dual-stack hostname:
ipv6ipv4ipv6_onlyipv4_onlybananaStrict mode with the preferred family absent (
ipv4_only+ AAAA-only, andipv6_only+ A-only) returns no endpoints, which makesSMTPSender#startsoft-failas intended.
Existing behaviour is preserved: the default remains IPv6-first, and
spec/senders/smtp_sender_spec.rbis unchanged.Backward compatibility
smtp_client.address_preferenceis configured.use the default.
Checklist
doc/config/yaml.yml,doc/config/environment-variables.md)