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:
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
-
Copy them into
/opt/msh-zr/scripts. -
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 -
If you changed
SCRIPTS_DIRorSCRIPTS_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:
| Variable | Value |
|---|---|
MSH_SENDER | Envelope sender |
MSH_RECIPIENTS | All recipients (To, Cc and Bcc), comma-separated |
MSH_SUBJECT | Subject |
MSH_MESSAGE_ID | Message-ID header |
MSH_MESSAGE_SIZE | Message size in bytes |
MSH_QUEUE_ID | Postfix queue ID |
MSH_MESSAGE_FILE | Path 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=/xis 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