Knowledge base

External addresses on one machine, nodes inside

critical

outbound proxy deployment
Why set up a proxy at all

The problem the mechanism solves is single and very specific: you have many sending addresses and many nodes, and distributing the former across the latter is painful. Every address move means editing a node’s configuration, reloading it and a drift between what is configured and what actually is.

The solution is to separate two things. Nodes live on internal addresses and deal with mail. All external sending addresses are gathered on one machine — the proxy. When delivering a message, a node connects to the proxy and tells it a pair: “go out from this address to that one.”

Name resolution stays with the node. It looks up the recipient’s MX itself and decides where to go; the proxy only carries it out and knows nothing about mail.

🔴 The proxy is transparent to accounting. The sending address field records the proxy’s external address, the destination field the receiving server’s address. Regular accounting cannot tell you that a message went through a proxy — for that there are separate dlvProxy* fields that have to be added to the set explicitly.

🔎 This section of the manual is out of date. It ends with the promise “we will add protocol support for inbound traffic in future releases” — but it already exists: use-inbound-proxy on the listener and on the Transmissions API entry point. This paragraph should not be read as a description of capabilities.

The main trap is assuming the node will check the proxy’s configuration. It checks only the form of the address entry. Whether it matches what is configured on the proxy the node does not and cannot know — and a mismatch means messages leaving from the wrong address with a configuration that looks fine.
What the Hub does. It uses it — the Hub’s proxy gate is built exactly this way: the engine goes over PROXY protocol version two to the gate on the node itself (hubgate), and the gate goes out through a rented proxy — over SOCKS5 or HTTP CONNECT.

The three conclusions of the analysis are built into it: first — the proxy list is a fallback, not a balancer, so the line form was chosen (smtp-proxy-source-host, a sending address of its own for each proxy), and traffic is spread with set pools; second — the gate gets the queue domain through protocol version two with the proxy-include-tlv domain line; third — the Hub itself keeps the “sending address ↔ proxy” mapping: the tag in the engine’s line and the gate table /etc/hubgate/upstreams.tsv are written together, from one registry.

Recommended values

The mechanism has no values of its own — it is assembled from a block and three lines:

What is needed How it is set
describe the proxy a <proxy> block with server and client
assign it use-proxy on the node or sending address
pass the queue domain proxy-protocol 2 and proxy-include-tlv
see failures in accounting enable-tq-record-for-proxy-failure plus the tq type
The problem statement, the proxy’s transparency in accounting, the dlvProxy fields and name resolution staying with the node come from the manual (§ 11.3). We noticed the outdated caveat about inbound traffic ourselves: § 3.3.17.16 refutes it.
Example configuration
# the node on an internal address, the external address lives on the proxy
<proxy mta-proxy-a>    server 10.1.2.3:5000
    client 16.70.55.233:0  client.hostname.com
</proxy>

<virtual-mta vmta>
    smtp-source-host 10.8.9.10 vmta.hostname.com
    use-proxy mta-proxy-a
</virtual-mta>
The node’s sending address is internal — the node goes from it to the proxy. What goes out is the address from the client line, and it is the one the receiving server will see.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends3 mistakes▶

Expecting to see the proxy in regular accounting

§ 11.3

What it costsIt is transparent: the sending address recorded is the proxy’s external address, and the destination is the receiving server. Delivery through a proxy cannot be told apart from direct delivery by these fields.
How to spot itIn accounting everything looks like regular delivery.
How to do it rightAdd the dlvProxy* fields to record-fields for the d, b, t and tq types.

Reading the section as a description of current capabilities

§ 11.3

What it costsIt claims the protocol is supported only for outbound traffic. That is outdated: receiving the header on inbound exists both on the listener and on the Transmissions API entry point.
How to spot itPeople give up on a load balancer in front of the node, thinking the engine will not understand it.
How to do it rightSee use-inbound-proxy — the inbound side exists.

Relying on the node to check the address against the proxy

our experience

What it costsThe node checks only the form of the entry. A typo means messages from someone else’s address and warm-up counted against the wrong one.
How to spot itThe message headers show an address different from the one in the configuration.
How to do it rightCompare the list of addresses on the proxy with the client lines and check with a message to a seed mailbox.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
Inheritance
The mechanism has no inheritance — the directives that enable it are inherited. Two of them have three levels: the proxy block, the sending address and the node, and the most precise one wins.
How to check on the node
The main check is not on the node but on the receiver’s side: which address the message came from:
dig +short -x 16.70.55.233
The external address’s reverse record must match the name in the client line. The real check is a message to a seed mailbox: its headers show the address it actually came from.
In the Hub
Cost of a mistake
reversible: harms only while it is set
Scenarios
Capacity planning · Sending through proxies
Priority
critical