1023 lines
34 KiB
HTML
1023 lines
34 KiB
HTML
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
|
|
"https://www.w3.org/TR/html4/loose.dtd">
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Postfix before-queue Milter support </title>
|
|
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
|
|
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix before-queue Milter support </h1>
|
|
|
|
<hr>
|
|
|
|
<h2>Introduction</h2>
|
|
|
|
<p> Postfix implements support for the Sendmail version 8 Milter
|
|
(mail filter) protocol. This protocol is used by applications that
|
|
run outside the MTA to inspect SMTP events (CONNECT, DISCONNECT),
|
|
SMTP commands (HELO, MAIL FROM, etc.) as well as mail content
|
|
(headers and body). All this happens before mail is queued. </p>
|
|
|
|
<p> The reason for adding Milter support to Postfix is that there
|
|
exists a large collection of applications, not only to block unwanted
|
|
mail, but also to verify authenticity (examples: <a
|
|
href="http://www.opendkim.org/">OpenDKIM</a> and <a
|
|
href="http://www.trusteddomain.org/opendmarc/">DMARC </a>)
|
|
or to digitally sign mail (example: <a
|
|
href="http://www.opendkim.org/">OpenDKIM</a>).
|
|
Having yet another Postfix-specific version of all that software
|
|
is a poor use of human and system resources. </p>
|
|
|
|
<p> The Milter protocol has evolved over time, and different Postfix
|
|
versions implement different feature sets. See the <a
|
|
href="#workarounds">workarounds</a> and <a
|
|
href="#limitations">limitations</a> sections at the end of this
|
|
document for differences between Postfix and Sendmail implementations.
|
|
</p>
|
|
|
|
<p> This document provides information on the following topics: </p>
|
|
|
|
<ul>
|
|
|
|
<li><a href="#plumbing">How Milter applications plug into Postfix </a>
|
|
|
|
<li><a href="#when-inspect">When Postfix and Milters inspect an
|
|
SMTP session </a>
|
|
|
|
<li><a href="#building">Building Milter applications</a>
|
|
|
|
<li><a href="#running">Running Milter applications</a>
|
|
|
|
<li><a href="#config">Configuring Postfix</a>
|
|
|
|
<li><a href="#workarounds">Workarounds</a>
|
|
|
|
<li><a href="#limitations">Limitations</a>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="plumbing">How Milter applications plug into Postfix </a> </h2>
|
|
|
|
<p> The Postfix Milter implementation uses two different lists of
|
|
mail filters: one list of filters for SMTP mail only,
|
|
and one list of filters for non-SMTP mail. The two
|
|
lists have different capabilities, which is unfortunate. Avoiding
|
|
this would require major restructuring of Postfix. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> The SMTP-only filters handle mail that arrives via the
|
|
Postfix smtpd(8) server. They are typically used to filter unwanted
|
|
mail and to sign mail from authorized SMTP clients. You specify
|
|
SMTP-only Milter applications with the smtpd_milters parameter as
|
|
described in a later section. Mail that arrives via the Postfix
|
|
smtpd(8) server is not filtered by the non-SMTP filters that are
|
|
described next. </p>
|
|
|
|
<li> <p> The non-SMTP filters handle mail that arrives via the
|
|
Postfix sendmail(1) command-line or via the Postfix qmqpd(8) server.
|
|
They are typically used to digitally sign mail only. Although
|
|
non-SMTP filters can be used to filter unwanted mail, they have
|
|
limitations compared to the SMTP-only filters. You specify non-SMTP
|
|
Milter applications with the non_smtpd_milters parameter as described
|
|
in a later section. </p>
|
|
|
|
</ul>
|
|
|
|
<p> For those who are familiar with the Postfix architecture, the
|
|
figure below shows how Milter applications plug into Postfix. Names
|
|
followed by a number are Postfix commands or server programs, while
|
|
unnumbered names inside shaded areas represent Postfix queues. To
|
|
avoid clutter, the path for local submission is simplified (the
|
|
OVERVIEW document has a more complete description of the Postfix
|
|
architecture). </p>
|
|
|
|
<blockquote>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td colspan="2"> </td>
|
|
|
|
<td align="center"> SMTP-only <br> filters </td>
|
|
|
|
<td> </td>
|
|
|
|
<td align="center"> non-SMTP <br> filters </td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="2"> </td>
|
|
|
|
<td align="center"> <table> <tr> <td align="center">
|
|
^<br> <tt> | </tt> </td> <td align="center"> <tt> |<br> v </tt>
|
|
</td> </tr> </table> </td>
|
|
|
|
<td rowspan="2"> </td>
|
|
|
|
<td rowspan="3" align="center"> <table> <tr> <td align="center">
|
|
^<br> <tt> |<br> |<br> | </tt> </td> <td align="center"> <tt> |<br>
|
|
|<br> |<br> v </tt> </td> </tr> </table> </td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td> Network </td> <td> <tt> -> </tt> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> smtpd(8)
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="3"> </td> <td> <tt> \ </tt> </td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td> Network </td> <td> <tt> -> </tt> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> qmqpd(8)
|
|
</td>
|
|
|
|
<td> <tt> -> </tt> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> cleanup(8)
|
|
</td>
|
|
|
|
<td> <tt> -> </tt> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> <a
|
|
href="QSHAPE_README.html#incoming_queue"> incoming </a> </td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="3"> </td> <td> <tt> / </tt> </td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td colspan="2"> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> pickup(8)
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr> <td colspan="2"> </td> <td align="center"> : </td> </tr>
|
|
|
|
<tr>
|
|
|
|
<td> Local </td> <td> <tt> -> </tt> </td>
|
|
|
|
<td bgcolor="#f0f0ff" align="center" valign="middle"> sendmail(1)
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
</blockquote>
|
|
|
|
<h2><a name="when-inspect">When Postfix and Milters inspect an SMTP
|
|
session </a></h2>
|
|
|
|
<p> Generally, Postfix inspects information first, then the first
|
|
configured Milter, the second configured Milter, and so on. </p>
|
|
|
|
<ul>
|
|
|
|
<li><p> With most SMTP commands: Postfix reviews one SMTP command,
|
|
and if Postfix does not reject it, Postfix passes the command to
|
|
the first configured Milter. If the first Milter does not reject
|
|
the command, Postfix passes it to the second configured Milter, and
|
|
so on. This includes commands with an envelope sender (MAIL FROM)
|
|
or envelope recipient (RCPT TO). Postfix stores the same envelope
|
|
records in a queue file as when no Milters are configured, including
|
|
rewritten envelope addresses, expanded virtual aliases, BCC addresses
|
|
from sender/recipient_bcc_maps, and so on. </p>
|
|
|
|
<li><p> With header/body content: Postfix may rewrite or reject
|
|
header/body content before it stores that content in the queue file;
|
|
Postfix stores the same header/body content as when no Milters are
|
|
configured. If Postfix does not reject the header/body content,
|
|
Postfix passes it to the first configured Milter which may modify
|
|
or reject that content or may modify the stored envelope. If the
|
|
first Milter does not reject the header/body content, Postfix passes
|
|
it to the second configured Milter, and so on. </p>
|
|
|
|
</ul>
|
|
|
|
<p> Details: </p>
|
|
|
|
<ul>
|
|
|
|
<li><p> Postfix hides its own Postfix-prepended Received: header, for
|
|
compatibility with Sendmail. Postfix does not hide other headers that
|
|
Postfix or Milters added or modified. </p>
|
|
|
|
|
|
<li><p> When the Postfix SMTP server receives a sequence of one or
|
|
more valid BDAT commands, it generates one DATA command for the
|
|
Milters. </p>
|
|
|
|
<li><p> The Milter API does not support inspection of SMTP commands
|
|
such as QUIT, NOOP, or VRFY; the API supports only commands that are
|
|
needed for email delivery. <p>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="building">Building Milter applications</a></h2>
|
|
|
|
<p> Milter applications have been written in C, Haskell, Java, Perl,
|
|
Python, Rust, and more, but
|
|
this document covers C applications only. For these, you need
|
|
an object library that implements the Sendmail 8 Milter protocol.
|
|
Postfix currently does not provide such a library, but Sendmail
|
|
does. </p>
|
|
|
|
<p> Some
|
|
systems install the Sendmail libmilter library by default. With
|
|
other systems, libmilter may be provided by a package (called
|
|
"sendmail-devel" on some Linux systems). </p>
|
|
|
|
<p> Once libmilter is installed, applications such as <a
|
|
href="http://www.opendkim.org/">OpenDKIM</a> and
|
|
<a href="http://www.trusteddomain.org/opendmarc/">OpenDMARC</a>
|
|
build out of the box without requiring any tinkering:</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
$ <b>gzcat opendkim-<i>x.y.z</i>.tar.gz | tar xf -</b>
|
|
$ <b>cd opendkim-<i>x.y.z</i></b>
|
|
$ <b>./configure ...<i>options</i>...</b>
|
|
$ <b>make</b>
|
|
[...<i>lots of output omitted</i>...]
|
|
$ <b>make install</b>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<h2><a name="running">Running Milter applications</a></h2>
|
|
|
|
<p> To run a Milter application, see the documentation of the filter
|
|
for options. A typical command looks like this:</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
# <b>/some/where/opendkim -l -u <i>userid</i> -p inet:<i>portnumber</i>@localhost ...<i>other options</i>...</b>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Please specify a <i>userid</i> value that isn't used for other
|
|
applications (not "postfix", not "www", etc.). </p>
|
|
|
|
<h2><a name="config">Configuring Postfix</a></h2>
|
|
|
|
<p> Like Sendmail, Postfix has a lot of configuration options that
|
|
control how it talks to Milter applications. Besides global options
|
|
that apply to all Milter applications, Postfix 3.0 and later
|
|
support per-Milter timeouts, per-Milter error handling, etc. </p>
|
|
|
|
<p> Information in this section: </p>
|
|
|
|
<ul>
|
|
|
|
<li><a href="#smtp-only-milters">SMTP-Only Milter applications </a>
|
|
|
|
<li><a href="#non-smtp-milters">Non-SMTP Milter applications </a>
|
|
|
|
<li><a href="#errors">Milter error handling </a>
|
|
|
|
<li><a href="#version">Milter protocol version</a>
|
|
|
|
<li><a href="#timeouts">Milter protocol timeouts</a>
|
|
|
|
<li><a href="#per-milter">Different settings for different Milter
|
|
applications </a>
|
|
|
|
<li><a href="#per-client">Different settings for different SMTP
|
|
clients </a>
|
|
|
|
<li><a href="#macros">Sendmail macro emulation</a>
|
|
|
|
<li><a href="#send-macros">What macros will Postfix send to Milters?</a>
|
|
|
|
</ul>
|
|
|
|
<h3><a name="smtp-only-milters">SMTP-Only Milter applications</a></h3>
|
|
|
|
<p> The SMTP-only Milter applications handle mail that arrives via
|
|
the Postfix smtpd(8) server. They are typically used to filter
|
|
unwanted mail, and to sign mail from authorized SMTP clients. Mail
|
|
that arrives via the Postfix smtpd(8) server is not filtered by the
|
|
non-SMTP filters that are described in the next section. </p>
|
|
|
|
<blockquote> NOTE for Postfix versions that have a mail_release_date
|
|
before 20141018: do not use the header_checks(5) IGNORE action to remove
|
|
Postfix's own Received: message header. This causes problems with
|
|
mail signing filters. Instead, keep Postfix's own Received: message
|
|
header and use the header_checks(5) REPLACE action to sanitize
|
|
information. </blockquote>
|
|
|
|
<p> You specify SMTP-only Milter applications (there can be more
|
|
than one) with the smtpd_milters parameter. Each Milter application
|
|
is identified by the name of its listening socket; other Milter
|
|
configuration options will be discussed in later sections. Postfix
|
|
sends commands to each Milter application in the order as configured
|
|
with smtpd_milters. When a Milter application rejects a command,
|
|
that will override responses from other Milter applications. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
/etc/postfix/main.cf:
|
|
# Milters for mail that arrives via the smtpd(8) server.
|
|
# See below for socket address syntax.
|
|
smtpd_milters = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The general syntax for listening sockets is as follows: </p>
|
|
|
|
<blockquote>
|
|
|
|
<dl>
|
|
|
|
<dt> <b>unix:</b><i>pathname</i> </dt> <dd><p>Connect to the local
|
|
UNIX-domain server that is bound to the specified pathname. If the
|
|
smtpd(8) or cleanup(8) process runs chrooted, an absolute pathname
|
|
is interpreted relative to the Postfix queue directory. On many
|
|
systems, <b>local</b> is a synonym for <b>unix</b></p> </dd>
|
|
|
|
<dt> <b> inet:</b><i>host</i><b>:</b><i>port</i> </dt> <dd> <p>
|
|
Connect to the specified TCP port on the specified local or remote
|
|
host. The host and port can be specified in numeric or symbolic
|
|
form.</p>
|
|
|
|
<p> NOTE: Postfix syntax differs from Milter syntax which has the
|
|
form <b>inet:</b><i>port</i><b>@</b><i>host</i>. </p> </dd>
|
|
|
|
</dl>
|
|
|
|
</blockquote>
|
|
|
|
<p> For advanced configuration see "<a href="#per-client">Different
|
|
settings for different SMTP clients</a>" and "<a
|
|
href="#per-milter">Different settings for different Milter
|
|
applications</a>". </p>
|
|
|
|
<h3> <a name="non-smtp-milters">Non-SMTP Milter applications </a> </h3>
|
|
|
|
<p> The non-SMTP Milter applications handle mail that arrives via
|
|
the Postfix sendmail(1) command-line or via the Postfix qmqpd(8)
|
|
server. They are typically used to digitally sign mail. Although
|
|
non-SMTP filters can be used to filter unwanted mail, there are
|
|
limitations as discussed later in this section. Mail that arrives
|
|
via the Postfix smtpd(8) server is not filtered by the non-SMTP
|
|
filters. </p>
|
|
|
|
<p> NOTE: Do not use the header_checks(5) IGNORE action to remove
|
|
Postfix's own Received: message header. This causes problems with
|
|
mail signing filters. Instead, keep Postfix's own Received: message
|
|
header and use the header_checks(5) REPLACE action to sanitize
|
|
information. </p>
|
|
|
|
<p> You specify non-SMTP Milter applications with the non_smtpd_milters
|
|
parameter. This parameter uses the same syntax as the smtpd_milters
|
|
parameter in the previous section. As with the SMTP-only filters,
|
|
you can specify more than one Milter application. Postfix sends
|
|
commands to each Milter application in the order as configured with
|
|
non_smtpd_milters. When a Milter application rejects a command,
|
|
that will override responses from other Milter applications. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
/etc/postfix/main.cf:
|
|
# Milters for non-SMTP mail.
|
|
# See below for socket address syntax.
|
|
non_smtpd_milters = inet:localhost:<i>portnumber</i> ...<i>other filters</i>...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> There's one small complication when using Milter applications
|
|
for non-SMTP mail: there is no SMTP session. To keep Milter
|
|
applications happy, the Postfix cleanup(8) server actually has to
|
|
simulate the SMTP client CONNECT and DISCONNECT events, and the
|
|
SMTP client EHLO, MAIL FROM, RCPT TO and DATA commands. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> When new mail arrives via the sendmail(1) command line,
|
|
the Postfix cleanup(8) server pretends that the mail arrives with
|
|
ESMTP from "localhost" with IP address "127.0.0.1". The result is
|
|
very similar to what happens with command line submissions in
|
|
Sendmail version 8.12 and later, although Sendmail uses a different
|
|
mechanism to achieve this result. </p>
|
|
|
|
<li> <p> When new mail arrives via the qmqpd(8) server, the Postfix
|
|
cleanup(8) server pretends that the mail arrives with ESMTP, and
|
|
uses the QMQPD client hostname and IP address. </p>
|
|
|
|
<li> <p> When old mail is re-injected into the queue with "postsuper
|
|
-r", the Postfix cleanup(8) server uses the same client information
|
|
that was used when the mail arrived as new mail. </p>
|
|
|
|
</ul>
|
|
|
|
<p> This generally works as expected, with only one exception:
|
|
non-SMTP filters must not REJECT or TEMPFAIL simulated RCPT TO
|
|
commands. When a non_smtpd_milters application REJECTs or TEMPFAILs
|
|
a recipient, Postfix will report a configuration error, and mail
|
|
will stay in the queue. </p>
|
|
|
|
<h4> Signing internally-generated bounce messages </h4>
|
|
|
|
<p> Postfix normally does not apply content filters to mail
|
|
that is generated internally such as bounces or Postmaster
|
|
notifications. Filtering internally-generated bounces would result
|
|
in loss of mail when a filter rejects a message, as the resulting
|
|
double-bounce message would almost certainly also be blocked. </p>
|
|
|
|
<p> To sign Postfix's own bounce messages, enable filtering of
|
|
internally-generated bounces (line 2 below), and don't reject any
|
|
internally-generated bounces with non_smtpd_milters, header_checks
|
|
or body_checks (lines 3-5 below). </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/main.cf:
|
|
2 internal_mail_filter_classes = bounce
|
|
3 non_smtpd_milters = <i>don't reject internally-generated bounces</i>
|
|
4 header_checks = <i>don't reject internally-generated bounces</i>
|
|
5 body_checks = <i>don't reject internally-generated bounces</i>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<h3><a name="errors">Milter error handling</a></h3>
|
|
|
|
<p> The milter_default_action parameter specifies how Postfix handles
|
|
Milter application errors. The default action is to respond with a
|
|
temporary error status, so that the client will try again later.
|
|
Specify "accept" if you want to receive mail as if the filter does
|
|
not exist, and "reject" to reject mail with a permanent status.
|
|
The "quarantine" action is like "accept" but freezes the message
|
|
in the "hold" queue, and is available with Postfix 2.6 or later.
|
|
</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
/etc/postfix/main.cf:
|
|
# What to do in case of errors? Specify accept, reject, tempfail,
|
|
# or quarantine (Postfix 2.6 or later).
|
|
milter_default_action = tempfail
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> See "<a href="#per-milter">Different settings for different
|
|
Milter applications</a>" for advanced configuration options. </p>
|
|
|
|
<h3><a name="version">Milter protocol version</a></h3>
|
|
|
|
<p> As Postfix is not built with the Sendmail libmilter library,
|
|
you may need to configure the Milter protocol version that Postfix
|
|
should use. The default version is 6 (before Postfix 2.6 the default
|
|
version is 2). </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
/etc/postfix/main.cf:
|
|
# Postfix ≥ 2.6
|
|
milter_protocol = 6
|
|
# 2.3 ≤ Postfix ≤ 2.5
|
|
milter_protocol = 2
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> If the Postfix milter_protocol setting specifies a too low
|
|
version, the libmilter library will log an error message like this:
|
|
</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
<i>application name</i>: st_optionneg[<i>xxxxx</i>]: 0x<i>yy</i> does not fulfill action requirements 0x<i>zz</i>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The remedy is to increase the Postfix milter_protocol version
|
|
number. See, however, the <a href="#limitations">limitations</a>
|
|
section below for features that aren't supported by Postfix. </p>
|
|
|
|
<p> With Postfix 2.7 and earlier, if the Postfix milter_protocol
|
|
setting specifies a too high
|
|
version, the libmilter library simply hangs up without logging a
|
|
warning, and you see a Postfix warning message like one of the
|
|
following: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Unknown error : 0
|
|
warning: milter inet:<i>host</i>:<i>port</i>: can't read packet header: Success
|
|
warning: milter inet:<i>host</i>:<i>port</i>: can't read SMFIC_DATA reply packet header: No such file or directory
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The remedy is to lower the Postfix milter_protocol version
|
|
number. Postfix 2.8 and later will automatically turn off protocol
|
|
features that the application's libmilter library does not expect.
|
|
</p>
|
|
|
|
<p> See "<a href="#per-milter">Different settings for different
|
|
Milter applications</a>" for advanced configuration options. </p>
|
|
|
|
<h3><a name="timeouts">Milter protocol timeouts</a></h3>
|
|
|
|
<p> Postfix uses different time limits at different Milter protocol
|
|
stages. The table shows the timeout settings and the corresponding
|
|
protocol stages
|
|
(EOH = end of headers; EOM = end of message). </p>
|
|
|
|
<blockquote>
|
|
|
|
<table border="1">
|
|
|
|
<tr> <th> Postfix parameter </th> <th> Time limit </th> <th> Milter
|
|
protocol stage</th> </tr>
|
|
|
|
<tr> <td> milter_connect_timeout </td> <td> 30s </td> <td> CONNECT
|
|
</td> </tr>
|
|
|
|
<tr> <td> milter_command_timeout </td> <td> 30s </td> <td> HELO,
|
|
MAIL, RCPT, DATA, UNKNOWN </td> </tr>
|
|
|
|
<tr> <td> milter_content_timeout </td> <td> 300s </td> <td> HEADER,
|
|
EOH, BODY, EOM </td> </tr>
|
|
|
|
</table>
|
|
|
|
</blockquote>
|
|
|
|
<p> Beware: 30s may be too short for Milter applications that do
|
|
lots of DNS lookups. However, if you increase the above timeouts
|
|
too much, remote SMTP clients may hang up and mail may be delivered
|
|
multiple times. This is an inherent problem with before-queue
|
|
filtering. </p>
|
|
|
|
<p> See "<a href="#per-milter">Different settings for different
|
|
Milter applications</a>" for advanced configuration options. </p>
|
|
|
|
<h3><a name="per-milter">Different settings for different Milter
|
|
applications </a></h3>
|
|
|
|
<p> The previous sections list a number of Postfix main.cf parameters
|
|
that control time limits and other settings for all Postfix Milter
|
|
clients. This is sufficient for simple configurations. With more
|
|
complex configurations it becomes desirable to have different
|
|
settings for different Milter clients. This is supported with Postfix
|
|
3.0 and later. </p>
|
|
|
|
<p> The following example shows a "non-critical" Milter client with
|
|
a short connect timeout, and with "accept" as default action when
|
|
the service is unvailable. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/main.cf:
|
|
2 smtpd_milters = { inet:host:port,
|
|
3 connect_timeout=10s, default_action=accept }
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Instead of a server endpoint, we now have a list enclosed in {}. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Line 2: The first item in the list is the server endpoint.
|
|
This supports the exact same "inet" and "unix" syntax as described
|
|
earlier. </p>
|
|
|
|
<li> <p> Line 3: The remainder of the list contains per-Milter
|
|
settings. These settings override global main.cf parameters, and
|
|
have the same name as those parameters, without the "milter_" prefix.
|
|
The per-Milter settings that are supported as of Postfix 3.0 are
|
|
command_timeout, connect_timeout, content_timeout, default_action,
|
|
and protocol. </p>
|
|
|
|
</ul>
|
|
|
|
<p> Inside the list, syntax is similar to what we already know from
|
|
main.cf: items separated by space or comma. There is one difference:
|
|
<b>you must enclose a setting in parentheses, as in "{ name = value
|
|
}", if you want to have space or comma within a value or around
|
|
"="</b>. </p>
|
|
|
|
<h3><a name="per-client">Different settings for different SMTP
|
|
clients </a></h3>
|
|
|
|
<p> The smtpd_milter_maps feature supports different Milter settings
|
|
for different client IP addresses. Lookup results override the
|
|
global smtpd_milters setting, and have the same syntax. For example,
|
|
to disable Milter settings for local address ranges: </p>
|
|
|
|
<pre>
|
|
/etc/postfix/main.cf:
|
|
smtpd_milter_maps = cidr:/etc/postfix/smtpd_milter_map
|
|
smtpd_milters = inet:host:port, { inet:host:port, ... }, ...
|
|
|
|
/etc/postfix/smtpd_milter_map:
|
|
# Disable Milters for local clients.
|
|
127.0.0.0/8 DISABLE
|
|
192.168.0.0/16 DISABLE
|
|
::/64 DISABLE
|
|
2001:db8::/32 DISABLE
|
|
</pre>
|
|
|
|
<p> This feature is available with Postfix 3.2 and later. </p>
|
|
|
|
<h3><a name="macros">Sendmail macro emulation</a></h3>
|
|
|
|
<p> Postfix emulates a limited number of Sendmail macros, as shown
|
|
in the table. Some macro values depend on whether a recipient is
|
|
rejected (rejected recipients are available on request by the Milter
|
|
application). Different macros are available at different Milter
|
|
protocol stages (EOH = end-of-header, EOM = end-of-message); their
|
|
availability is not
|
|
always the same as in Sendmail. See the <a
|
|
href="#workarounds">workarounds</a> section below for solutions.
|
|
</p>
|
|
|
|
<blockquote>
|
|
|
|
<table border="1">
|
|
|
|
<tr> <th> Sendmail macro </th> <th> Milter protocol stage </th>
|
|
<th> Description </th> </tr>
|
|
|
|
<tr> <td> i </td> <td> DATA, EOH, EOM </td> <td> Queue ID, also
|
|
Postfix queue file name </td> </tr>
|
|
|
|
<tr> <td> j </td> <td> Always </td> <td> Value of myhostname </td>
|
|
</tr>
|
|
|
|
<tr> <td> _ </td> <td> Always </td> <td> The validated client name
|
|
and address </td> </tr>
|
|
|
|
<tr> <td> {auth_authen} </td> <td> MAIL, DATA, EOH, EOM </td> <td> SASL
|
|
login name </td> </tr>
|
|
|
|
<tr> <td> {auth_author} </td> <td> MAIL, DATA, EOH, EOM </td> <td> SASL
|
|
sender </td> </tr>
|
|
|
|
<tr> <td> {auth_type} </td> <td> MAIL, DATA, EOH, EOM </td> <td> SASL
|
|
login method </td> </tr>
|
|
|
|
<tr> <td> {client_addr} </td> <td> Always </td> <td> Remote client
|
|
IP address </td> </tr>
|
|
|
|
<tr> <td> {client_connections} </td> <td> CONNECT </td> <td>
|
|
Connection concurrency for this client (zero if the client is
|
|
excluded from all smtpd_client_* limits). </td> </tr>
|
|
|
|
<tr> <td> {client_name} </td> <td> Always </td> <td> Remote client
|
|
hostname <br> When address → name lookup or name → address
|
|
verification fails: "unknown" </td> </tr>
|
|
|
|
<tr> <td> {client_port} </td> <td> Always (Postfix ≥2.5) </td>
|
|
<td> Remote client TCP port </td> </tr>
|
|
|
|
<tr> <td> {client_ptr} </td> <td> CONNECT, HELO, MAIL, DATA </td>
|
|
<td> Client name from address → name lookup <br> When address
|
|
→ name lookup fails: "unknown" </td> </tr>
|
|
|
|
<tr> <td> {cert_issuer} </td> <td> HELO, MAIL, DATA, EOH, EOM </td> <td>
|
|
TLS client certificate issuer </td> </tr>
|
|
|
|
<tr> <td> {cert_subject} </td> <td> HELO, MAIL, DATA, EOH, EOM </td>
|
|
<td> TLS client certificate subject </td> </tr>
|
|
|
|
<tr> <td> {cipher_bits} </td> <td> HELO, MAIL, DATA, EOH, EOM </td> <td>
|
|
TLS session key size </td> </tr>
|
|
|
|
<tr> <td> {cipher} </td> <td> HELO, MAIL, DATA, EOH, EOM </td> <td> TLS
|
|
cipher </td> </tr>
|
|
|
|
<tr> <td> {daemon_addr} </td> <td> Always (Postfix ≥3.2) </td>
|
|
<td> Local server IP address </td> </tr>
|
|
|
|
<tr> <td> {daemon_name} </td> <td> Always </td> <td> value of
|
|
milter_macro_daemon_name </td> </tr>
|
|
|
|
<tr> <td> {daemon_port} </td> <td> Always (Postfix ≥3.2) </td>
|
|
<td> Local server TCP port </td> </tr>
|
|
|
|
<tr> <td> {mail_addr} </td> <td> MAIL </td> <td> Sender address
|
|
</td> </tr>
|
|
|
|
<tr> <td> {mail_host} </td> <td> MAIL (Postfix ≥ 2.6, only with
|
|
smtpd_milters) </td> <td> Sender next-hop destination </td> </tr>
|
|
|
|
<tr> <td> {mail_mailer} </td> <td> MAIL (Postfix ≥ 2.6, only with
|
|
smtpd_milters) </td> <td> Sender mail delivery transport </td> </tr>
|
|
|
|
<tr> <td> {rcpt_addr} </td> <td> RCPT </td> <td> Recipient address
|
|
<br> With rejected recipient: descriptive text </td> </tr>
|
|
|
|
<tr> <td> {rcpt_host} </td> <td> RCPT (Postfix ≥ 2.6, only with
|
|
smtpd_milters) </td> <td> Recipient next-hop destination <br> With
|
|
rejected recipient: enhanced status code </td> </tr>
|
|
|
|
<tr> <td> {rcpt_mailer} </td> <td> RCPT (Postfix ≥ 2.6, only with
|
|
smtpd_milters) </td> <td> Recipient mail delivery transport <br>
|
|
With rejected recipient: "error" </td> </tr>
|
|
|
|
<tr> <td> {tls_version} </td> <td> HELO, MAIL, DATA, EOH, EOM </td>
|
|
<td> TLS protocol version </td> </tr>
|
|
|
|
<tr> <td> v </td> <td> Always </td> <td> value of milter_macro_v
|
|
</td> </tr>
|
|
|
|
</table>
|
|
|
|
</blockquote>
|
|
|
|
<h3><a name="send-macros">What macros will Postfix send to Milters?</a></h3>
|
|
|
|
<p> Postfix sends specific sets of macros at different Milter protocol
|
|
stages. The names of these macros are configured with the parameters
|
|
shown in the table below (EOH = end of headers; EOM = end of message).
|
|
Some lists require a minimum Milter protocol version. </p>
|
|
|
|
<p> As of Sendmail 8.14.0, Milter applications can specify what
|
|
macros they want to receive at different Milter protocol stages.
|
|
An application-specified list takes precedence over a Postfix-specified
|
|
list. </p>
|
|
|
|
<blockquote>
|
|
|
|
<table border="1">
|
|
|
|
<tr> <th> Postfix parameter </th> <th> Milter protocol version </th>
|
|
<th> Milter protocol stage </th> </tr>
|
|
|
|
<tr> <td> milter_connect_macros </td> <td> 2 or higher </td> <td>
|
|
CONNECT </td> </tr>
|
|
|
|
<tr> <td> milter_helo_macros </td> <td> 2 or higher </td> <td>
|
|
HELO/EHLO </td> </tr>
|
|
|
|
<tr> <td> milter_mail_macros </td> <td> 2 or higher </td> <td> MAIL
|
|
FROM </td> </tr>
|
|
|
|
<tr> <td> milter_rcpt_macros </td> <td> 2 or higher </td> <td> RCPT
|
|
TO </td> </tr>
|
|
|
|
<tr> <td> milter_data_macros </td> <td> 4 or higher </td> <td> DATA
|
|
</td> </tr>
|
|
|
|
<tr> <td> milter_end_of_header_macros </td> <td> 6 or higher </td>
|
|
<td> EOH </td> </tr>
|
|
|
|
<tr> <td> milter_end_of_data_macros </td> <td> 2 or higher </td>
|
|
<td> EOM </td> </tr>
|
|
|
|
<tr> <td> milter_unknown_command_macros </td> <td> 3 or higher </td>
|
|
<td> unknown command </td> </tr>
|
|
|
|
</table>
|
|
|
|
</blockquote>
|
|
|
|
<p> By default, Postfix will send only macros whose values have been
|
|
updated with information from main.cf or master.cf, from an SMTP session
|
|
(for example; SASL login, or TLS certificates) or from a Mail delivery
|
|
transaction (for example; queue ID, sender, or recipient). </p>
|
|
|
|
<p> To force a macro to be sent even when its value has not been updated,
|
|
you may specify macro default values with the milter_macro_defaults
|
|
parameter. Specify zero or more <i>name=value</i> pairs separated by
|
|
comma or whitespace; you may even specify macro names that Postfix does
|
|
not know about! </p>
|
|
|
|
<h2><a name="workarounds">Workarounds</a></h2>
|
|
|
|
<ul>
|
|
|
|
<li> <p> To avoid breaking DKIM etc. signatures with an SMTP-based
|
|
content filter, update the before-filter SMTP client in master.cf,
|
|
and add a line with "-o disable_mime_output_conversion=yes" (note:
|
|
no spaces around the "="). For details, see the <a
|
|
href="FILTER_README.html#advanced_filter">advanced content filter</a>
|
|
example. </p>
|
|
|
|
<pre>
|
|
/etc/postfix/master.cf:
|
|
# =============================================================
|
|
# service type private unpriv chroot wakeup maxproc command
|
|
# (yes) (yes) (yes) (never) (100)
|
|
# =============================================================
|
|
scan unix - - n - 10 smtp
|
|
-o smtp_send_xforward_command=yes
|
|
-o disable_mime_output_conversion=yes
|
|
-o smtp_generic_maps=
|
|
</pre>
|
|
|
|
<li> <p> Some Milter applications use the "<tt>{if_addr}</tt>" macro
|
|
to recognize local mail; this macro does not exist in Postfix.
|
|
Workaround: use the "<tt>{daemon_addr}</tt>" (Postfix ≥ 3.2) or
|
|
"<tt>{client_addr}</tt>" macro instead. </p>
|
|
|
|
<li> <p> Some Milter applications log a warning that looks like
|
|
this: </p>
|
|
|
|
<blockquote> <pre>
|
|
sid-filter[36540]: WARNING: sendmail symbol 'i' not available
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> And they may insert an ugly message header with "unknown-msgid"
|
|
like this: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com <unknown-msgid>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The problem is that Milter applications expect that the queue
|
|
ID is known <i>before</i> the MTA accepts the MAIL FROM (sender)
|
|
command. Postfix does not choose a queue ID, which is used as the
|
|
queue file name, until <i>after</i> it accepts the first valid RCPT
|
|
TO (recipient) command. </p>
|
|
|
|
<p> If you experience the ugly header problem, see if a recent
|
|
version of the Milter application fixes it. For example, current
|
|
versions of dkim-filter and dk-filter already have code that looks
|
|
up the Postfix queue ID at a later protocol stage, and sid-filter
|
|
version 1.0.0 no longer includes the queue ID in the message header.
|
|
</p>
|
|
|
|
<p> To fix the ugly message header, you will need to add code that
|
|
looks up the Postfix queue ID at some later point in time. The
|
|
example below adds the lookup after the end-of-message. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Edit the filter source file (typically named
|
|
<tt>xxx-filter/xxx-filter.c</tt> or similar). </p>
|
|
|
|
<li> <p> Look up the <tt>mlfi_eom()</tt> function and add code near
|
|
the top shown as <b>bold</b> text below: </p>
|
|
|
|
</ul>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
dfc = cc->cctx_msg;
|
|
assert(dfc != NULL);
|
|
<b>
|
|
/* Determine the job ID for logging. */
|
|
if (dfc->mctx_jobid == 0 || strcmp(dfc->mctx_jobid, JOBIDUNKNOWN) == 0) {
|
|
char *jobid = smfi_getsymval(ctx, "i");
|
|
if (jobid != 0)
|
|
dfc->mctx_jobid = jobid;
|
|
}</b>
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> NOTES: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Different mail filters use slightly different names for
|
|
variables. If the above code does not compile, look elsewhere in
|
|
the mail filter source file for code that looks up the "i" macro
|
|
value, and copy that code. </p>
|
|
|
|
<li> <p> This change fixes only the ugly message header, but not
|
|
the WARNING message. Fortunately, many Milters log that message
|
|
only once. </p>
|
|
|
|
</ul>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="limitations">Limitations</a></h2>
|
|
|
|
<p> This section lists limitations of the Postfix Milter implementation.
|
|
Some limitations will be removed as the implementation is extended
|
|
over time. Of course the usual limitations of before-queue filtering
|
|
will always apply. See the CONTENT_INSPECTION_README document for
|
|
a discussion. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> The Milter protocol has evolved over time. Therefore,
|
|
different Postfix versions implement different feature sets. </p>
|
|
|
|
<table border="1">
|
|
|
|
<tr> <th> Postfix </th> <th> Supported Milter requests </th>
|
|
</tr>
|
|
|
|
<tr> <td align="center"> 2.6 </td> <td> All Milter requests of
|
|
Sendmail 8.14.0 (see notes below). </td> </tr>
|
|
|
|
<tr> <td align="center"> 2.5 </td> <td> All Milter requests of
|
|
Sendmail 8.14.0, except: <br> SMFIP_RCPT_REJ (report rejected
|
|
recipients to the mail filter), <br> SMFIR_CHGFROM (replace sender,
|
|
with optional ESMTP parameters), <br> SMFIR_ADDRCPT_PAR (add
|
|
recipient, with optional ESMTP parameters). </td> </tr>
|
|
|
|
<tr> <td align="center"> 2.4 </td> <td> All Milter requests of
|
|
Sendmail 8.13.0. </td> </tr>
|
|
|
|
<tr> <td align="center"> 2.3 </td> <td> All Milter requests of
|
|
Sendmail 8.13.0, except: <br> SMFIR_REPLBODY (replace message body).
|
|
|
|
</table>
|
|
|
|
<li> <p> For Milter applications that are written in C, you need
|
|
to use the Sendmail libmilter library. </p>
|
|
|
|
<li> <p> Postfix has TWO sets of mail filters: filters that are used
|
|
for SMTP mail only (specified with the smtpd_milters parameter),
|
|
and filters for non-SMTP mail (specified with the non_smtpd_milters
|
|
parameter). The non-SMTP filters are primarily for local submissions.
|
|
</p>
|
|
|
|
<p> When mail is filtered by non_smtpd_milters, the Postfix cleanup(8)
|
|
server has to simulate SMTP client requests. This works as expected,
|
|
with only one exception: non_smtpd_milters must not REJECT or
|
|
TEMPFAIL simulated RCPT TO commands. When this rule is violated,
|
|
Postfix will report a configuration error, and mail will stay in
|
|
the queue. </p>
|
|
|
|
<li> <p> When you use the before-queue content filter for incoming
|
|
SMTP mail (see SMTPD_PROXY_README), Milter applications have access
|
|
only to the SMTP command information; they have no access to the
|
|
message header or body, and cannot make modifications to the message
|
|
or to the envelope. </p>
|
|
|
|
<li> <p> Postfix 3.3 and later support the ESMTP parameters RET and
|
|
ENVID in requests to replace the envelope sender (SMFIR_CHGFROM).
|
|
Postfix logs a warning message when a Milter application supplies
|
|
other ESMTP parameters: </p>
|
|
|
|
<pre>
|
|
warning: <i>queue-id</i>: cleanup_chg_from: ignoring bad ESMTP
|
|
parameter "<i>whatever</i>" in SMFI_CHGFROM request
|
|
</pre>
|
|
|
|
<li> <p> Postfix 3.0 and later support the ESMTP parameters NOTIFY
|
|
and ORCPT in requests to add an envelope recipient. Postfix logs a
|
|
warning message when a Milter application supplies other ESMTP
|
|
parameters: </p>
|
|
|
|
<pre>
|
|
warning: <i>queue-id</i>: cleanup_add_rcpt: ignoring ESMTP argument
|
|
from Milter or header/body_checks: "<i>whatever</i>"
|
|
</pre>
|
|
|
|
<li> <p> Postfix 2.6 and later ignore optional ESMTP parameters in
|
|
requests to replace the sender (SMFIR_CHGFROM) or to append a
|
|
recipient (SMFIR_ADDRCPT_PAR). Postfix logs a warning message when
|
|
a Milter application supplies such ESMTP parameters: </p>
|
|
|
|
<pre>
|
|
warning: <i>queue-id</i>: cleanup_chg_from: ignoring ESMTP arguments "<i>whatever</i>"
|
|
warning: <i>queue-id</i>: cleanup_add_rcpt: ignoring ESMTP arguments "<i>whatever</i>"
|
|
</pre>
|
|
|
|
<li> <p> Postfix 2.3 does not implement requests to replace the
|
|
message body. Milter applications log a warning message when they
|
|
need this unsupported operation: </p>
|
|
|
|
<pre>
|
|
st_optionneg[134563840]: 0x3d does not fulfill action requirements 0x1e
|
|
</pre>
|
|
|
|
<p> The solution is to use Postfix version 2.4 or later. </p>
|
|
|
|
<li> <p> Postfix versions before 3.0 did not support per-Milter
|
|
timeouts, per-Milter error handling, etc. </p>
|
|
|
|
</ul>
|
|
|
|
</body>
|
|
|
|
</html>
|