Monthly Archives: November 2020

A little gotcha with Postfix’s header_checks

I run my own mail on all of my personal domains using Postfix and Dovecot, doing almost all of my day-to-day interaction with it using Thunderbird. Many years ago, I realized I would rather not be leaking my home IP address in the initial Received: header in the mail I send, so, guided by various online postings that I can no longer identify with certainty, I initially did the following.

First, I set up a custom cleanup service to be used by the message submission service by adding this entry to

# Scrubs client IP address from things arriving over the submission port.
# DON'T FORGET "-o cleanup_service_name=subcleanup" IN THE SERVICE'S ENTRY!
subcleanup unix n       -       -       -       0       cleanup
  -o syslog_name=postfix/submission
  -o header_checks=pcre:/etc/postfix/submission-header-checks
# (Don't actually deploy the snippet above - keep reading.)

And I put the following in /etc/postfix/submission-header-checks to define the actual substitution to be performed:

/^(Received:\s+)from\s.*?\b(by\s.*)$/mi REPLACE $1$2

And finally, I tacked this line onto the submission entry in to choose the new custom cleanup service:

  -o cleanup_service_name=subcleanup

(Don’t forget the indentation.)

This worked well for quite a while, but has a subtle shortcoming that I didn’t hit until a few days ago when I found myself needing to provide someone with a copy of some pieces of spam I had received, which I decided to forward as attachments – something I had never done before – to ensure the headers would be preserved.

That’s when I found out that header_checks, by default, apply to MIME headers and MIME subparts too – including the Received: headers in the attached spam, stripping away the very information I was trying to preserve by forwarding the messages as attachments.

(I noticed immediately because I was watching Postfix’s logs as I sent the message, having temporarily reconfigured Postfix not to use my usual outbound relay service since… well… it kind of might look like I was trying to send spam through them… and I wanted to see that Postfix really was delivering the message directly. It’s nice that header_checks rules log every change they make.)

A quick look at cleanup(8) revealed that mime_header_checks and nested_header_checks default to the value of header_checks, so I updated the entry to set those to nothing:

# Scrubs client IP address from things arriving over submissions port.
# DON'T FORGET "-o cleanup_service_name=subcleanup" IN THE SERVICE'S ENTRY!
subcleanup unix n       -       -       -       0       cleanup
  -o syslog_name=postfix/submissions
  -o header_checks=pcre:/etc/postfix/submission-header-checks
  -o mime_header_checks=
  -o nested_header_checks=

…and resent the message (after testing with a resend to myself, of course), saw that only the one actual intended hit of the Received:-modifying rule occurred this time, re-enabled use of the outbound relay service, and went about my day.

(That block now refers to submissions – note the extra “s” – because between the time I first added Received: header filtering to my configuration and the time of this incident, I moved the submission service from port 587 with STARTTLS to port 465 with TLS from the start, in accordance with RFC 8314 not just undeprecating that port but calling for it to become new best practice.)

Looking back at the documentation, I now feel that setting disable_mime_input_processing = yes (disclaimer: haven’t actually tried it) might have been a better thing to do to fix this, but I don’t quite feel up to messing more with something that works for the moment.

If this article happens to directly help you get endpoint IP scrubbing working properly (i.e. without also removing information from messages forwarded as attachments) in your Postfix installation, I’d love to know in the comments.