14

I run a Postfix/Dovecot mail server for personal use. There's only a handful of actual mailboxes, essentially just [email protected] and [email protected].

I frequently use the virtual file to create virtual mailboxes forwarded to [email protected]. I have a number of these for throwaway accounts like one-time purchases from online stores, online games I want to try without worrying about ongoing spam, etc. To do so I SSH and run the following commands:

sudo vim /etc/postfix/virtual
# add a line that looks like:
# # [email protected]      [email protected]
sudo postmap virtual
sudo service postfix restart

I do this with enough frequency that I'd like to automate the process to some degree. I considered simply writing a shell script that took the virtual mailbox and real mailbox as arguments and made the changes itself, but am hoping for something even more hands off.

I would like to be able to send an email from [email protected] to some other mailbox on the server with the virtual mailbox name as the body of the message. The issue would be the sudo calls but I can create a new user whose sole responsibility is to handle this which should handle that.

Mostly the question is this: how would I create an event that would be triggered by an email? Is there a service somewhere that does this? Can I configure either Postfix or Dovecot to listen for that email and run commands on that event?

9
  • Have you checked this link?
    – kirill-a
    Commented Jan 12, 2015 at 9:54
  • @kirill-a no I hadn't. It looks like that could be purposed to solve this problem, and now I finally have a good reason to learn PHP
    – Adam Smith
    Commented Jan 12, 2015 at 18:48
  • You do not need to learn PHP: just substitute your own shell script to the PHP script, and you are done. Commented Jan 15, 2015 at 14:59
  • 1
    @DoktorJ I wrote an answer in case if the link goes down
    – kirill-a
    Commented Oct 23, 2015 at 8:13
  • 1
    Which it did, web.archive.org/web/20160614054458/http://… is the archived version.
    – leggewie
    Commented Jun 27, 2021 at 20:52

4 Answers 4

11

The correct procedure to execute a script (I use a shell script) upon receipt of a mail message is the following. It involves modifying postfix's configuration file, master.cf (which, in my Debian, is located in /etc/postfix) by adding the following line:

 my_shell_script unix - n n - - pipe flags=F user=MY_USERNAME argv=/path/to/my/shell/script ${sender} ${size} ${recipient}

which instructs postfix to run the script (you need to make it executable) when some event occurs.

To specify when to execute the script, you do as follows: suppose you want it to be executed when [email protected] receives a message. Place the following line

 [email protected] FILTER my_shell_script:dummy

inside the file /etc/postfix/address.txt; you will need to create a proper database for postfix to use this file, which you accomplish by means of

  postmap /etc/postfix/address.txt

which produces as output a file called /etc/postfix/address.db. Now go back to the /etc/postfix/main.cf file and add the following line:

 smtpd_recipient_restrictions = check_recipient_access hash:/etc/postfix/access, permit_mynetworks, reject_unauth_destination

Now restart postfix,

  postfix reload

and you should be good to go.

6
  • 3
    Don't you mean 'check_recipient_access hash:/etc/postfix/address' here? You used "access" where I think you meant "address", based on your instructions.
    – Lee Fuller
    Commented Jun 22, 2016 at 6:06
  • 3
    Is this the same if you are running virtual_mailboxes for various virtual domains? I've followed these instructions verbatim - and if I do not declare a virtual_alias I get a bounce. If I do, it delivers the message to the mailbox but does not trigger the script.
    – Lee Fuller
    Commented Jun 22, 2016 at 6:12
  • I didn't get this to work. This answer worked: serverfault.com/questions/322657/…
    – jlh
    Commented Mar 19, 2019 at 8:01
  • From what I understand this solution also has the issue that it will only trigger for messages received through SMTP. sendmail direct on the server will not trigger this. Commented Jun 16, 2019 at 20:56
  • @PhilipCouling See this answer: unix.stackexchange.com/a/179407. You may use the pickup option.
    – Stephan
    Commented Aug 27, 2019 at 5:19
4
+25

I can see two obvious solutions.

If you are using procmail as the MDA on your server, or are willing to convert to using it, then you can run any arbitrary script on any arbitrary email through procmail's | (pipe) recipe action. It hopefully won't be started as root, but it should be easy to make a script that performs the required magic and which can be invoked passwordless as root by a relevant user. The output of the script could even be fed back into the same email and the email later delivered by making the recipe a filter action.

An alternative (which should require less hands-on maintenance) is to create a single mailbox and configure Postfix's $recipient_delimiter to be something that is not normally used for that purpose; for example, . or -. Note that recipient_delimeter is a server-wide setting. Let's say you set up the mailbox [email protected] and set $recipient_delimeter = .. This will then allow dummy.<anything>@example.com which will deliver to the local mailbox corresponding to [email protected]. To disable one of these, add dummy.<whatever>@example.com to an appropriate recipient table with a reject action. The downside is that it will be a catch-all mailbox for the prefix, so you would want to make the prefix hard to stumble across by accident or dictionary attacks. The upside is that in the normal case (the sender respects your wishes and does not resell your email address) this approach requires zero maintenance, and you can always change your prefix later and explicitly list those combinations that you want to keep on receiving mail for.

5
  • I'm not tied to Postfix for any reason other than it's what I have currently. I'd prefer to find a solution that would work with my current MDA/MTA, however. As for delimiter's: I hardly trust that the sender will respect my wishes and not resell my email address, or I'd be giving them the permanent address :)
    – Adam Smith
    Commented Jan 12, 2015 at 16:43
  • @AdamSmith The difference is, with something like my second alternative, you can disable the address you gave a particular entity without causing problems anywhere else, and the addresses look sensible even to dumb automated verifiers. As for keeping the MDA/MTA, procmail can be used as a drop-in replacement for the Postfix MDA; no need to replace Postfix as your MTA.
    – user
    Commented Jan 12, 2015 at 20:15
  • procmail is not maintained any more: see lwn.net/Articles/416901, an article with title: Reports of procmail's death are not terribly exaggerated. Better play it safe, and follow @kirill-a's suggestion. Commented Jan 15, 2015 at 15:01
  • procmail is still plenty used, and (IMHO) remains the best solution for ad hoc filtering. @MariusMatutiae's note of it being effectively dead applies solely to its development. I can't think of any procmail bugs that I've ever experienced, and I've done ridiculous things like implement full-blown mailing lists in it. Maybe it hasn't received further development because the only direction to take it would be beyond its scope (e.g. anti-spam, better served by SpamAssassin or e.g. milter-greylist).
    – Adam Katz
    Commented Jan 17, 2015 at 22:37
  • And you are saying this to me because....? Commented Jan 18, 2015 at 7:01
3

In case if link goes down, here's summary.

First, go to master.cf and register your script "myhook" by adding the following line:

myhook unix - n n - - pipe flags=F user=www-data argv=/path/to/script.sh ${sender} ${size} ${recipient}

Also, edit smtp line to tell Postfix to run the filter for any mail arriving via the SMTP delivery:

smtp inet n - - - - smtpd -o content_filter=myhook:dummy

Please note that if you are sending mails using the "sendmail" command, the filter will not trigger. In this case, add the option after the "pickup" delivery method:

pickup fifo n - - 60 1 pickup -o content_filter=myhook:dummy

Restart postfix: postfix reload

Make your script readable and executable by anybody: chmod +rx script.sh

Note that script is always triggered for any mail arriving. To specify exact address see @MariusMatutiae answer.

1
  • that got me "unknown mail transport error" ... Commented Oct 4, 2017 at 1:16
3

I found the configuring of Dovecot to be a more elegant solution (as opposed to configuring Postfix). The steps are as follows:

  1. create your script (ie. mail_processor.py) in the /usr/lib/dovecot/sieve-execute/ directory:

    #!/usr/bin/python3
    from sys import stdin
    with open('/var/log/mail_processor.log', 'a') as logfile:
        for line in stdin:
            print(line.rstrip(), file=logfile)
    
    • ensure your script and target files have correct permissions:

      $ chmod +rx /usr/lib/dovecot/sieve-execute/mail_processor.py
      $ chmod 0777 /var/log/mail_processor.log
      
  2. enable the sieve_extprograms plugin:

    • modify \etc\dovecot\conf.d\90-sieve.conf's plugin section with the following:

      sieve_extensions = +vnd.dovecot.execute
      sieve_plugins = sieve_extprograms
      sieve_execute_bin_dir = /usr/lib/dovecot/sieve-execute
      
    • reload dovecot:

      $ service dovecot restart
      
  3. create sieve filter (ie. in Roundcube goto settings -> filters -> actions -> edit filter set):

    require ["vnd.dovecot.execute"];
    # rule:[mail processing]
    if true
    {
        execute :pipe "mail_processor.py";
    }
    

Now all mail delivered to this mailbox with the sieve filter will be piped through mail_processor.py for action.

Pigeonhole Sieve: Extprograms Plugin documentation for reference

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .