Bind outbound SMTP to a specific network
Advice not extensively tested
This configuration advice is a community contribution.
If your Docker host has multiple public IPv4 or IPv6 IP addresses, it may be beneficial to bind outgoing SMTP connections to a specific IP.
When the IP address for DMS is inconsistent, it may fail to pass common security checks when interacting with other mail servers:
- When a mail is sent outbound from DMS, it connects to the external MTA and greets it with an EHLO (the DMS FQDN) which the MTA might verify that the EHLO address resolves to the same IP that DMS is connecting to the MTA from, and that a
PTRrecord for that same IP resolves back to that same IP (thePTRrecord address may not need to match the DMS FQDN). - SPF receivers evaluate whether the connecting source IP is authorized by the SPF
TXTrecord for the envelope sender domain. If that policy uses DNS-based mechanisms such asmxora, the correspondingMX,A, orAAAArecords must resolve consistently with the selected sending IP. - There may be other security checks handled by an MTA, like Postfix supports with
reject_unknown_sender.
IP addresses for documentation
IP addresses shown in the below examples (198.51.100.42 + 2001:DB8::42) are placeholders, they are IP addresses reserved for documentation by IANA (RFC-5737 (IPv4) and RFC-3849 (IPv6)). Replace them with the IP addresses you want DMS to send mail through.
Directly binding
This approach is for when the desired source IP is available inside the DMS container's network namespace, such as with network_mode: host or a macvlan network with the desired address assigned to the container. A macvlan container does not inherit addresses assigned only to the host.
This can be configured by overriding the default Postfix configurations DMS provides. Create postfix-master.cf and postfix-main.cf files for your config volume (docker-data/dms/config).
In postfix-main.cf you'll have to set the smtp_bind_address and smtp_bind_address6 to the respective IP-address on the server you want to use.
Example
smtp_bind_address = 198.51.100.42
smtp_bind_address6 = 2001:DB8::42
Inheriting the bind from main.cf can misconfigure services
One problem when setting smtp_bind_address in main.cf is that it will be inherited by any services in master.cf that extend the smtp transport.
One of these is smtp-amavis, which is explicitly configured to listen / connect via loopback (localhost / 127.0.0.1).
A postfix-master.cf override can workaround that issue by ensuring smtp-amavis binds to the expected internal IP:
smtp-amavis/unix/smtp_bind_address=127.0.0.1
smtp-amavis/unix/smtp_bind_address6=::1
A potentially better solution might be to instead explicitly set the smtp_bind_address override on the smtp transport service:
smtp/inet/smtp_bind_address = 198.51.100.42
smtp/inet/smtp_bind_address6 = 2001:DB8::42
If that avoids the concern with smtp-amavis, you may still need to additionally override for the relay transport as well if you have configured DMS to send mail through a relay service.
Bridged Networks
When your DMS container is using a bridge network, you'll instead need to restrict which IP address inbound and outbound traffic is routed through via the bridged interface.
Info
For inbound traffic, you may configure this at whatever scope is most appropriate for you:
- Daemon: Change the default bind address configured in
/etc/docker/daemon.json(default0.0.0.0). - Network: Assign the
host_binding_ipv4bridge driver option as shown in the belowcompose.yamlsnippet. - Container: Provide an explicit host IP address when publishing a port.
For outbound traffic, the bridge network will use the default route. You can change this by either:
- Manually routing networks on the host.
- Use the
host_ipv4driver option for Docker networks to force the SNAT (source IP) that the bridged network will route outbound traffic through.- This IP address must belong to a network interface to be routed through it.
- IPv6 support via
host_ipv6requires at least Docker v25.
Example
Here is a compose.yaml snippet that applies the inbound + outbound settings to the default bridge network Docker Compose creates (if it already exists, you will need to ensure it's re-created to apply the updated settings):
networks:
default:
driver_opts:
# Inbound IP (sets the host IP that published ports receive traffic from):
com.docker.network.bridge.host_binding_ipv4: 198.51.100.42
# Outbound IP (sets the host IP that external hosts will receive connections from):
com.docker.network.host_ipv4: 198.51.100.42