Skip to main content
Version: 1.3

Prepare scripts

The External script condition and the Run executable action run programs on the mail server. Both run them only from the scripts directory, under the same rules. This page shows how to set up the directory, add your own scripts and write them safely.

The scripts directory​

Scripts run only from the scripts directory of the server, never from a path given in a rule, so an account that can edit rules cannot run arbitrary programs. The directory is SCRIPTS_DIR in the configuration; the default is scripts in the installation, /opt/msh-zr/scripts, so scripts work out of the box. A relative path is resolved against the install directory. Set SCRIPTS_DIR empty to keep conditions and actions from running anything:

/opt/msh-zr/etc/config.cf
SCRIPTS_DIR=

SCRIPTS_MAX_CONCURRENT caps how many scripts and programs run at the same time (5 by default). An External script that finds no free slot within its timeout does not run.

The directory ships with two sample scripts and a README.txt. Both samples get the sender, the recipients and the subject as arguments and always end the same way, whatever the message holds: accept.sh exits with 0 and reject.sh with 1. Use them to try the External script condition before writing your own. An update of the installation replaces the samples and the README; scripts under other names are kept.

Add your own scripts​

  1. Copy them into /opt/msh-zr/scripts.

  2. Run the permissions script. It makes the directory and its scripts owned by root and readable and executable by the application user, which cannot change them:

    sudo sh /opt/msh-zr/lib/fixperms.sh
  3. If you changed SCRIPTS_DIR or SCRIPTS_MAX_CONCURRENT, restart the services so the milter and the administrator panel pick up the new values:

    sudo systemctl restart msh-zr-milter.service msh-zr-web.service

Which scripts may run​

A script is run only when its real path - after resolving .. and symbolic links - is inside the scripts directory, it is a regular executable file, and neither its group nor other users can write to it. Anything else is refused, and the reason is written to the processing trace and to the server log.

Environment variables​

A script runs without a shell and with a cleaned environment that holds only these variables:

VariableValue
MSH_SENDEREnvelope sender
MSH_RECIPIENTSAll recipients (To, Cc and Bcc), comma-separated
MSH_SUBJECTSubject
MSH_MESSAGE_IDMessage-ID header
MSH_MESSAGE_SIZEMessage size in bytes
MSH_QUEUE_IDPostfix queue ID
MSH_MESSAGE_FILEPath of the message as an .eml file, when an External script passes the message as a file or an argument uses {message_file}

Write scripts safely​

Because a script runs without a shell, values from the message cannot be expanded by a shell. Still, every argument and variable resolved from the message - a subject, an address, a header - is chosen by the sender:

  • Prefer the MSH_* variables to arguments and quote them in the script ("$MSH_SUBJECT").
  • When a value has to be an argument, put -- before it so a subject such as --config=/x is not read as an option.
  • Never pass a value through a shell (eval, sh -c).

Control characters in the values are replaced by spaces before the script starts, and an argument that resolves to nothing is passed as an empty string so the arguments after it keep their positions.

Additional resources​

  • External script - run a script as a condition and match on its exit code or output
  • Run executable - run a program as an action, after a rule matched
  • Variables - variables that can be used in the arguments