Delay the mails with Postfix

Some days ago I commented in Catalan that I wanted to delay the mails using Postfix. Why? To have a period of time when I could cancel (or “unsend”) some mails. Google has done it in GMail (see Labs option). It can be useful for these moments like “Ops! I sent the mail to the mailing list instead of sending it to a person” or “ops, I sent to A instead of B”. It doesn’t happen often, but can happen.

My setup: mutt is sending the mails to a local Postfix using the sendmail program, Then Postfix sends the all mails (relay) to another SMTP server. Technically I could change both SMTP, but I only wanted to change the local one since it’s “my one” and closer to the MUA.

Of course, searching in Google for “delay mails with Postfix” gives answers to the question “Why my mails are delayed” and not “I want to delay the mails”. I used what in Postfix is called Advanced content filter example.

You can follow the instructions of the Postfix webpage with a new script that I’ll show you later.

In /etc/postfix/master.cf you will need to change the pickup configuration to:
pickup fifo n - - 60 1 pickup
-o content_filter=scan:localhost:10025

So, it adds a content_filter “scan” (does mainly nothing) and sends the mail to the port 10025.
At the end of the /etc/postfix/master.cf you should add something like:
scan unix - - n - 10 smtp
-o smtp_send_xforward_command=yes
-o disable_mime_output_conversion=yes
-o smtp_generic_maps=
localhost:10025 inet n n n - 1 spawn
user=filter argv=/usr/local/bin/mail_delay.sh localhost 10026
localhost:10026 inet n - n - 1 smtpd
-o content_filter=
-o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters
-o smtpd_helo_restrictions=
-o smtpd_client_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks=127.0.0.0/8
-o smtpd_authorized_xforward_hosts=127.0.0.0/8

What it does?:
master process will be listening on port 10025. Whatever will be received to that port it will be sent using the stdin to /usr/local/bin/mail_delay.sh . This program/script will process the mail and send it to the port 10026. 10026 port will receive the mail and carry on the process to send the email.

The script /usr/local/bin/mail_delay.sh is a very simple script (I have some debugging information that I’ve strip it out here):
#!/bin/bash
sleep 60
nc localhost 10026
exit $?

“nc” (Debian package netcat) reads from the stdin and send the output to localhost, port 10026. Of course, before doing that it waits 60 seconds.

Disclaimer:

  • I use Postfix only as a hobby, I’m not a Postfix expert. Apologises for the problems or mistakes of this configuration. However, it works for me.
  • I’ve only tested it on my personal system. If this Postfix needed to handle thousands of mails per minute the above configuration would have problems.
  • If you are curious, you have seen that I’ve limited the simultaneous processes to one. So, my mails are sent one after another, that helps a bit because I’m using Gandi as a final SMTP and I should send only 5 mails/minute. I don’t guarantee that the system will work if you increase the limit (7th column in master.cf). Actually I cannot guarantee that this would work even without changing the configuration.
  • This is tested using Postfix 2.6.5-3 on a Debian system

5 comments to Delay the mails with Postfix

  • […] Actualització: el «¿Cómo se hizo?» ja està fet. […]

  • Pav

    Very good idea, the only downside I can see that it would spawn a lot of sleeping processes on a busy server. I am looking to delay only certain emails till the morning (e.g. ones from debian security mailing list) so they don’t wake me up in the night unnecessarily, so I would need delay of hours not seconds so I would need to check if the recipient is the one I want to delay and then either pass the message or to delay it X hours. Shouldn’t be too bad as sleeping processes amount would be limited. Thanks!

  • The delaying emails postfix was thought for a personal station (I use mutt+postfix locally).

    For the server I think that I would filter the emails to be seen only in the morning in some other IMAP folder (or different mailbox) and have the alerts on the phone with a schedule. I use K9-Mail on the phone and I think that I could setup different priority alerts for different mailboxes (or folders?) with different time schedule, for example.

  • Hi guys, also not an expert. But trying my best. The solution above works, but with a few additions.

    Firstly I needed to add a user ‘filter’ to the system by ‘adduser filter’. Then I edited the entry in /etc/passwd so it reads:

    filter:x:1002:1002:,,,:/nonexistent:/bin/false

    As pr. recommendations here: http://www.postfix.org/FILTER_README.html

    “filter” is a dedicated local user account. The user will never log in, and can be given a “*” password and non-existent shell and home directory. This user handles all potentially dangerous mail content – that is why it should be a separate account.

    Then also I had issues with getting mail rejected, so for the last part that was to be included in master.cf, I edited the following line

    # -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_recipient_restrictions=

  • At the bottom of master.cf, you may want to add this if you have issues with relaying mail on the non-standard port:

    Ref: http://www.postfix.org/postconf.5.html#smtpd_relay_restrictions

    -o mynetworks=127.0.0.0/8,[::ffff:127.0.0.0]/104,[::1]/128,111.111.111.111/32
    -o smtpd_relay_restrictions=permit_mynetworks,permit_sasl_authenticated,defer_unauth_destination

    The 111.111.111.111 ip in this case would be any ip you want to allow relay.

    If you only send e-mail to a single domain, you could also use relay_domains in main.cf as so: relay_domains = whatever.com

    Make sure to restart postfix to have changes take effect.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>