85.1.5. pepsi-stage-srs

rewrite the envelope sender with the Sender Rewriting Scheme

Manual section:

1

85.1.5.1.1. Name

pepsi-stage-srs - SRS envelope-sender rewriting stage of the Pepsi pipeline.

85.1.5.1.2. Synopsis

pepsi-stage-srs [GLOBAL-OPTIONS] worker

pepsi-stage-srs [GLOBAL-OPTIONS] forward ADDRESS

pepsi-stage-srs [GLOBAL-OPTIONS] reverse ADDRESS

85.1.5.1.3. Description

This stage defaults to FUSION = yes: when stage fusion is enabled ([pepsi] ALLOW_FUSION, the default) and this stage is folded into the unified pepsi binary, a predecessor may run it in its own worker process instead of dispatching it separately. See pepsi-dispatch(1) and pepsi.conf(5).

pepsi-stage-srs is a stage program run by pepsi-dispatch(1) as a persistent worker reading message ids on standard input. It implements the Sender Rewriting Scheme (SRS).

When Pepsi forwards a message it keeps the body intact but relays it from its own IP addresses. The next hop’s SPF check then sees Pepsi sending mail whose envelope sender is still the original sender’s domain, which does not authorise Pepsi’s IPs, and the check fails. SRS solves this by rewriting the envelope sender (MAIL FROM) into a local address of a Pepsi-controlled domain (SRS_DOMAIN) that encodes — and HMAC-signs — the original sender:

SRS0=HHHH=TT=origin.example=alice@srs.example.org

SPF at the next hop now checks srs.example.org, which legitimately sends from Pepsi’s IPs. The body and its DKIM signatures are untouched. The stage then advances the message to its NEXT_STAGE (the delivery stage). It changes only the envelope sender, leaving the row’s state — including any RFC 3461 state.dsn parameters — untouched for later stages to honour.

The null sender (<>, i.e. a returning bounce) is never rewritten, and a sender already in SRS_DOMAIN is left unchanged (rewriting is idempotent). A sender that is already an SRS address — the message reached Pepsi through another forwarder — is re-signed in the compact SRS1 form rather than being nested, so a bounce hops back one forwarder at a time.

85.1.5.1.3.1. Reverse direction

The other half of SRS — recognising a bounce returned to an SRS address, verifying its signature and timestamp, decoding the original sender and relaying the bounce there — is performed by pepsi-ingress(1) at RCPT time, not by this program. A recipient in SRS_DOMAIN whose local-part is a valid SRS token is accepted and rewritten back to the original sender (even though that sender’s domain is not one Pepsi serves — the valid signature authorises the relay); a forged or expired token is rejected with 550. Both directions read the same shared [pepsi-srs] configuration, so the secret and domain match.

This half is what makes SRS_DOMAIN a domain that must receive mail, not merely one this host sends as. Rewriting the envelope sender makes Pepsi the bounce destination for a message it did not write; if the domain publishes no MX and no address record, the returning bounce is discarded by the MTA that tried to send it and the original author is never told. pepsi-setup(1) checks for this on every run, under its DNS cross-checks, because the domain’s SPF and DMARC records look complete without it.

85.1.5.1.4. Configuration

The stage needs only a NEXT_STAGE in its own [stage-<name>] section (PROGRAM = pepsi-stage-srs); the SRS parameters (SRS_DOMAIN, SECRET/SECRET_FILE, MAX_AGE_DAYS) live in the shared [pepsi-srs] section it shares with pepsi-ingress(1). All are documented in pepsi.conf(5). Omitting [pepsi-srs] disables SRS: a worker of this stage then refuses to start (as it does when the secret cannot be read), so the dispatcher holds the messages queued for it until the configuration is fixed rather than failing them, and pepsi-ingress(1) treats SRS-looking recipients as ordinary addresses. The secret is re-read for every message, so a rotated secret needs no restart; if it becomes unreadable while a worker runs, the message is retried with back-off (see pepsi-dispatch(1)).

85.1.5.1.5. State

Inputs: none — the stage operates on the mail_from column, not on state.

Outputs: state.srs.original, the envelope sender the rewrite replaced. Nothing else in state is touched — state.dsn and the rest are preserved unchanged. The state layout is described in pepsi.state(7).

That one key exists for a DSN Pepsi originates after this stage has run, which is every DSN on a relaying path: this stage necessarily precedes the delivery stage, so pepsi-stage-bounce(1) would otherwise address the report to our own SRS alias and make it take a round trip out to the next hop and back in through our MX to be decoded. The bounce stage reads this instead of reversing the alias, which keeps the SRS secret out of a stage that only composes mail. It is written only when absent, so a message rewritten twice keeps the address that names a real correspondent rather than the intermediate alias.

Transitions: always advances to NEXT_STAGE, whether the envelope sender was SRS-rewritten or passed through unchanged (a null sender, or one already in SRS_DOMAIN). There is no branch; the stage never pauses, fails, reroutes or finishes.

85.1.5.1.6. Commands

Run as a worker, the program rewrites each message’s envelope sender and advances it. The subcommands are operator tools:

forward ADDRESS

Print the SRS rewrite of ADDRESS (the forward direction) and exit. Useful for inspecting what a given sender becomes.

reverse ADDRESS

Decode and verify ADDRESS (an SRS address or bare local-part) and print the original sender, or fail if it is not a valid SRS address.

85.1.5.1.7. Global Options

-c FILE, –config FILE

Read the configuration from FILE instead of the default search path.

-L LOGLEVEL, –log LOGLEVEL

Set the logging verbosity (error, warn, info, debug, trace; default info).

-h, –help

Print a usage summary and exit.

-V, –version

Print the version and exit.

85.1.5.1.8. Exit Status

The outcome of each message is reported to pepsi-dispatch(1) on the worker’s status line, not as the exit status.

0

The worker ran until its standard input was closed, or the forward / reverse subcommand succeeded.

1

A fatal error occurred (unreadable configuration, the database could not be opened, standard input/output failed, or reverse was given an address that is not a valid SRS address). The reason is written to the log.

85.1.5.1.9. Examples

Show what a sender is rewritten to:

pepsi-stage-srs -c /etc/pepsi/pepsi.conf forward alice@origin.example

Decode a returned bounce address:

pepsi-stage-srs -c /etc/pepsi/pepsi.conf reverse 'SRS0=HHHH=TT=origin.example=alice@srs.example.org'

85.1.5.1.10. See Also

pepsi-config(1), pepsi-stage-relay-to-smarthost(1), pepsi-stage-relay-to-internet(1), pepsi-stage-bounce(1), pepsi-stage-dkim-sign(1), pepsi-dispatch(1), pepsi-ingress(1), pepsi.conf(5), pepsi.state(7), pepsi-setup(1)

85.1.5.1.11. Bugs

Report bugs to the Pepsi issue tracker.