Score:0

Postfix should tempfail mail back to queue when local delivery fails

in flag
PoC

I'm running postfix on Debian 11, together with procmail as lda. Some users have a ~/.procmailrc which calls an external program, parsing the mail and doing things accordingly. This works as expected, as long as nothing goes wrong.

Goal definition

I found out that if the program being run within a procmail recipe exits with an error, the mail is put into the user's local mailbox and no further action is done. This is not my intended outcome. I want the mail to stay in the postfix queue, so the next queue run can try delivery again. Just akin to a temporary failure when mail can't be delivered through SMTP to another host for a temporary error condition on the remote host's side.

Postfix + procmail

I'm using this configuration parameter in /etc/postfix/main.cf:

mailbox_command = procmail -t -a "$EXTENSION"

According to the procmail man page -t should force a soft-fail — as intended —, but this apparently doesn't work with postfix? Unfortunately, I found no documentation about how postfix is expected to handle rc!=0 with external mailbox_commands.

I found a source explaining how to write a ~/procmailrc-recipe to force procmail to exit with the error of a called program. This is the snippet I was using for testing purposes:

:0
* ^Subject: failme$
{
  :0wc
  |/usr/bin/false

  :0
  { EXITCODE=$? }
}

This works basically: Postfix recognizes the lda exited with an error, and immediately bounces the mail.

So I guess there might be a setting in Postfix I'm missing, despite having browsed postconf(5) for keywords like error and defer, and read local(8).

Summary

How to configure Postfix so that mails are kept in the queue when the lda fails?

Score:1
ar flag

The crux is that Procmail doesn't fail if you don't explicitly make it fail. Its default behavior, like you noted, is to fail the current recipe, and then eventually deliver to $DEFAULT as a fallback if no delivering recipe was successfully executed.

I don't think this is obviously fixable, but I can come up with a hacky workaround; configure it so that $DEFAULT points to a location where mail cannot be delivered; then you should get an error when it falls off the end of the user's .procmailrc

Actually, if you override $DEFAULT with an unwritable location, Procmail will repeatedly try to deliver, fail, and retry. Eventually it seems to fall back to $ORGMAIL (the "default $DEFAULT") in my quick and dirty experiment.

I suppose you could also think about wrapping it somehow, maybe explicitly includerc=$HOME/.procmailrc in /etc/procmailrc and then fail if it returns without having delivered.

The following /etc/procmailrc is really hacky.

It actually ends up running the user's .procmailrc twice; it assumes that if the first one didn't deliver anything, the second invocation won't, either. I could not find a way to prevent it from executing the user's .procmailrc again; but maybe there is a way.

# ... your regular /etc/procmailrc contents here

# Drop root privileges
DROPPRIVS=yes
# Run user's recipes
INCLUDERC=$HOME/.procmailrc

# Don't deliver to DEFAULT any longer
DEFAULT=/dev/null
# Set exit code to an error code (check your local manual for what makes sense for your MTA)
EXITCODE=77
# Force quit
HOST=

Sendmail (and I think Postfix) use exit codes from sysexits.h; Qmail (if somebody is still using that) allegedly has its own conventions.

By the by, the c flag on your testing recipe seems misdirected (:0wc); the c flag says to clone the message, and effectively generate two of them. Since your recipe will always fail, I guess you are then immediately discarding the one you just created; but anyway, the w flag should be sufficient here.

PoC avatar
in flag
PoC
Thanks. Anyway, I've worked around the procmail-issue as already stated, because my testing shows that postfix bounces the test mail. My mentioning of procmail was mainly for completeness, providing additional context, and probably help others when facing a similar issue. However, the central question is how to tell postfix to not immediately fail and bounce the test mail, but leave it in the queue, to be picked up and tried again at the next queue run.
tripleee avatar
ar flag
I will repeat the observation that the LDA does not fail in this scenario. It successfully delivers the message, just not where you wanted it.
PoC avatar
in flag
PoC
How comes, Postfix bounces a test mail with <[email protected]> (expanded from <poc>): Command died with status 1: "procmail -t -a "$EXTENSION"" To me this means, a) postfix is aware that something went wrong and b) apparently the lda failed, contrary to your assertion.
tripleee avatar
ar flag
My best guess is that the account doesn't exist, and fails for that reason. But your question doesn't really contain enough details to know for sure.
PoC avatar
in flag
PoC
The account exists, and runs the ~/.procmail-snippet I've posted in my original question. Which details do you miss?
tripleee avatar
ar flag
I was unable to see how the pieces fit together, but I guess you have the answer then. If your user's `.procmailrc` ends with `EXITCODE=77` (or another non-zero error) it works like you hoped. This answer discusses how to remove the requirement for every user to separately have their `.procmailrc` end with `EXITCODE=77` (and ideally also `DEFAULT=/dev/null`)
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.