70.1.13. pepsi-stage-dot-forward

process per-user ~/.forward files

Manual section:

1

70.1.13.1.1. Name

pepsi-stage-dot-forward - the ~/.forward processing stage of the Pepsi pipeline.

70.1.13.1.2. Synopsis

pepsi-stage-dot-forward [GLOBAL-OPTIONS] worker

70.1.13.1.3. Description

pepsi-stage-dot-forward is a stage program run by pepsi-dispatch(1). For each envelope recipient that resolves to a local account it runs that user’s ~/.forward file — the classic sendmail/Postfix mechanism by which an account owner redirects their own mail. Locality is decided exactly as in pepsi-stage-relay-to-maildir(1): the recipient’s domain must be in LOCAL_DOMAINS, its mailbox name (local-part, sub-address after RECIPIENT_DELIMITER removed, lower-cased) must resolve to a passwd entry, and that uid must be permitted by TARGETS. A recipient that is not local is left untouched and advances to NEXT_STAGE.

The ~/.forward itself is read and executed by the privileged helper pepsi-helper-dot-forward(1), which drops to the target user first. The helper’s exit status tells the stage what became of that recipient:

  • no ~/.forward — the recipient is a passthrough: it stays on the message, which advances to NEXT_STAGE (typically local delivery, then a smarthost).

  • forwarded — the recipient is replaced by the addresses the ~/.forward named. Those restart the pipeline at RESTART_STAGE (default init) on a new pending sibling row, so the rewritten recipients are re-authenticated and re-routed like fresh mail. An empty address list (the message was consumed entirely by |pipe//file directives) simply drops the recipient.

  • failed — running the ~/.forward failed (a pipe command exited non-zero, a file could not be written, or a disabled directive was requested); the recipient is routed to BOUNCE_STAGE so a failure DSN is generated (honouring the sender’s NOTIFY). A message that is itself a bounce (null sender) is never re-bounced.

A single message may have a mix of these outcomes across its recipients; the forwarded addresses, the per-recipient bounces and the kept (passthrough) recipients are reconciled atomically in one database round-trip.

70.1.13.1.4. Forwarding loops

Because a forwarded message restarts the whole pipeline, two users forwarding to each other (~bobalice, ~alicebob) would loop. To prevent this the stage records every login whose ~/.forward it has run in the message’s state["dot-forwarders"] array (see pepsi.state(7)). A recipient whose user is already listed there is dropped without calling the helper (a warning is logged), so the chain terminates: the offending copy is discarded, and a message left with no recipients is deleted.

70.1.13.1.5. Arguments and sub-commands

worker

Run as a persistent pepsi-dispatch(1) worker, reading message ids on standard input and writing one status line each. This is how the dispatcher runs the stage in production.

70.1.13.1.6. Configuration

Read from the message’s [stage-<stage>] section:

PROGRAM

Must be pepsi-stage-dot-forward.

RESTART_STAGE

The stage a successfully forwarded message restarts at. Defaults to init (re-running the full inbound pipeline, including authentication). Set it to a later stage (e.g. srs) to skip re-authenticating an internally forwarded message. Must name an existing stage.

ALLOW_PIPE

Whether |command directives in a ~/.forward are honoured (the message is piped to the command, run as the user). Defaults to yes.

ALLOW_FILE

Whether /path directives (append the message to a file) are honoured. Defaults to yes. When both ALLOW_PIPE and ALLOW_FILE are no the stage does not pipe the message body to the helper at all (the helper only needs to read forwarding addresses).

TARGETS, LOCAL_DOMAINS, RECIPIENT_DELIMITER

The local-account selection, identical in meaning to pepsi-stage-relay-to-maildir(1). TARGETS defaults to the regular (non-system) uid range from /etc/login.defs; LOCAL_DOMAINS defaults to [pepsi-ingress] ACCEPTED_DOMAINS; RECIPIENT_DELIMITER defaults to + (none disables sub-address stripping).

HELPER

The privileged ~/.forward helper to run. Defaults to pepsi-helper-dot-forward (resolved on $PATH); set an absolute path to override.

NEXT_STAGE

Where passthrough recipients (no ~/.forward) advance. Required, since most recipients have no ~/.forward. The stage is normally placed just before local delivery, so NEXT_STAGE is the local-delivery stage.

BOUNCE_STAGE

The DSN-generation stage a recipient is routed to when its ~/.forward execution fails.

70.1.13.1.7. A ~/.forward file

The helper interprets each non-empty, non-comment (#) line of the user’s ~/.forward as one of:

  • an e-mail address — collected and used as a new recipient (a leading \ is stripped);

  • |command — when ALLOW_PIPE is on, the message is piped to /bin/sh -c command run as the user;

  • /absolute/path — when ALLOW_FILE is on, the message is appended to that file (mbox style) as the user.

Forwarding addresses should be fully qualified (user@domain); a bare local name is returned verbatim and may not re-resolve as local.

70.1.13.1.8. Installation and privileges

To reach the setuid-root helper, pepsi-stage-dot-forward must itself be installed set-group-id to the pepsi-forward group (mode 2755, owner root:pepsi-forward). pepsi-dispatch(1) runs the stage as the unprivileged pepsi service user; the setgid bit gives the worker an effective gid of pepsi-forward, which is exactly what lets it execute the group-restricted pepsi-helper-dot-forward(1) — and nothing else on the host gains that ability. make install sets this up (its install-forward-stage step), provided it is run as root and the pepsi-forward group exists; otherwise it prints the exact commands to run by hand.

70.1.13.1.9. See Also

pepsi-helper-dot-forward(1), pepsi-stage-relay-to-maildir(1), pepsi-stage-aliases(1), pepsi-stage-bounce(1), pepsi-dispatch(1), pepsi.conf(5), pepsi.state(7)

70.1.13.1.10. Bugs

Report bugs to the Pepsi issue tracker.