828 lines
34 KiB
HTML
828 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 SMTP Access Policy Delegation </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 SMTP Access Policy Delegation </h1>
|
|
|
|
<hr>
|
|
|
|
<h2>Purpose of Postfix SMTP access policy delegation</h2>
|
|
|
|
<p> The Postfix SMTP server has a number of built-in mechanisms to
|
|
block or accept mail at specific SMTP protocol stages. In addition,
|
|
the Postfix SMTP server can delegate decisions to an external policy
|
|
server (Postfix 2.1 and later). </p>
|
|
|
|
<p> With this policy delegation mechanism, a simple
|
|
<a href="#greylist">greylist</a> policy can be implemented with only a dozen lines of
|
|
Perl, as is shown at the end of this document. A complete example
|
|
can be found in the Postfix source code, in the directory
|
|
examples/smtpd-policy. </p>
|
|
|
|
<p> Another example of policy delegation is the SPF policy server
|
|
at <a href="http://www.open-spf.org/Software/">http://www.open-spf.org/Software/</a> </p>
|
|
|
|
<p> Policy delegation is now the preferred method for adding policies
|
|
to Postfix. It's much easier to develop a new feature in few lines
|
|
of Perl, Python, Ruby, or TCL, than trying to do the same in C code.
|
|
The difference in
|
|
performance will be unnoticeable except in the most demanding
|
|
environments. On active systems a policy daemon process is used
|
|
multiple times, for up to $<a href="postconf.5.html#max_use">max_use</a> incoming SMTP connections. </p>
|
|
|
|
<p> This document covers the following topics: </p>
|
|
|
|
<ul>
|
|
|
|
<li><a href="#protocol">Policy protocol description</a>
|
|
|
|
<li><a href="#client_config">Simple policy client/server configuration</a>
|
|
|
|
<li><a href="#advanced">Advanced policy client configuration</a>
|
|
|
|
<li><a href="#greylist">Example: greylist policy server</a>
|
|
|
|
<li><a href="#frequent">Greylisting mail from frequently forged domains</a>
|
|
|
|
<li><a href="#all_mail">Greylisting all your mail</a>
|
|
|
|
<li><a href="#maintenance">Routine greylist maintenance</a>
|
|
|
|
<li><a href="#greylist_code">Example Perl greylist server</a>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="protocol">Protocol description</a></h2>
|
|
|
|
<p> The Postfix policy delegation protocol is really simple. The client
|
|
sends a request and the server sends a response. Unless there was an
|
|
error, the server must not close the connection, so that the same
|
|
connection can be used multiple times. </p>
|
|
|
|
<p> The client request is a sequence of name=value attributes separated
|
|
by newline, and is terminated by an empty line. The server reply is one
|
|
name=value attribute and it, too, is terminated by an empty line. </p>
|
|
|
|
<p> Here is an example of all the attributes that the Postfix SMTP
|
|
server sends in a delegated SMTPD access policy request: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
<b>Postfix version 2.1 and later:</b>
|
|
request=smtpd_access_policy
|
|
protocol_state=RCPT
|
|
protocol_name=SMTP
|
|
helo_name=some.domain.tld
|
|
queue_id=8045F2AB23
|
|
sender=foo@bar.tld
|
|
recipient=bar@foo.tld
|
|
recipient_count=0
|
|
client_address=1.2.3.4
|
|
client_name=another.domain.tld
|
|
reverse_client_name=another.domain.tld
|
|
instance=123.456.7
|
|
<b>Postfix version 2.2 and later:</b>
|
|
sasl_method=plain
|
|
sasl_username=you
|
|
sasl_sender=
|
|
size=12345
|
|
ccert_subject=solaris9.porcupine.org
|
|
ccert_issuer=Wietse+20Venema
|
|
ccert_fingerprint=C2:9D:F4:87:71:73:73:D9:18:E7:C2:F3:C1:DA:6E:04
|
|
<b>Postfix version 2.3 and later:</b>
|
|
encryption_protocol=TLSv1/SSLv3
|
|
encryption_cipher=DHE-RSA-AES256-SHA
|
|
encryption_keysize=256
|
|
etrn_domain=
|
|
<b>Postfix version 2.5 and later:</b>
|
|
stress=
|
|
<b>Postfix version 2.9 and later:</b>
|
|
ccert_pubkey_fingerprint=68:B3:29:DA:98:93:E3:40:99:C7:D8:AD:5C:B9:C9:40
|
|
<b>Postfix version 3.0 and later:</b>
|
|
client_port=1234
|
|
<b>Postfix version 3.1 and later:</b>
|
|
policy_context=submission
|
|
<b>Postfix version 3.2 and later:</b>
|
|
server_address=10.3.2.1
|
|
server_port=54321
|
|
<b>Postfix version 3.8 and later:</b>
|
|
<a href="postconf.5.html#compatibility_level">compatibility_level</a>=<i>major</i>.<i>minor</i>.<i>patch</i>
|
|
<a href="postconf.5.html#mail_version">mail_version</a>=3.8.0
|
|
[empty line]
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Notes: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> The "request" attribute is required. In this example
|
|
the request type is "smtpd_access_policy". </p>
|
|
|
|
<li> <p> The order of the attributes does not matter. The policy
|
|
server should ignore any attributes that it does not care about.
|
|
</p>
|
|
|
|
<li> <p> When the same attribute name is sent more than once,
|
|
the server may keep the first value or the last attribute value.
|
|
</p>
|
|
|
|
<li> <p> When an attribute value is unavailable, the client
|
|
either does not send the attribute, sends the attribute with
|
|
an empty value ("name="), or sends a zero value ("name=0") in
|
|
the case of a numerical attribute. </p>
|
|
|
|
<li> <p> The "recipient" attribute is available in the "RCPT
|
|
TO" stage. It is also available in the "DATA" and "END-OF-MESSAGE"
|
|
stages if Postfix accepted only one recipient for the current
|
|
message.
|
|
The DATA protocol state also applies to email that is received
|
|
with BDAT commands (Postfix 3.4 and later). </p>
|
|
|
|
<li> <p> The "recipient_count" attribute (Postfix 2.3 and later)
|
|
is non-zero only in the "DATA" and "END-OF-MESSAGE" stages. It
|
|
specifies the number of recipients that Postfix accepted for
|
|
the current message.
|
|
The DATA protocol state also applies to email that is received
|
|
with BDAT commands (Postfix 3.4 and later). </p>
|
|
|
|
<li> <p> The remote client or local server IP address is an
|
|
IPv4 dotted quad in the form 1.2.3.4 or it is an IPv6 address
|
|
in the form 1:2:3::4:5:6. </p>
|
|
|
|
<li> <p> The remote client or local server port is a decimal
|
|
number in the range 0-65535. </p>
|
|
|
|
<li> <p> For a discussion of the differences between reverse
|
|
and verified client_name information, see the
|
|
<a href="postconf.5.html#reject_unknown_client_hostname">reject_unknown_client_hostname</a> discussion in the <a href="postconf.5.html">postconf(5)</a>
|
|
document. </p>
|
|
|
|
<li> <p> An attribute name must not contain "=", null or newline,
|
|
and an attribute value must not contain null or newline. </p>
|
|
|
|
<li> <p> The "instance" attribute value can be used to correlate
|
|
different requests regarding the same message delivery. These
|
|
requests are sent over the same policy connection (unless the
|
|
policy daemon terminates the connection). Once Postfix sends
|
|
a query with a different instance attribute over that same
|
|
policy connection, the previous message delivery is either
|
|
completed or aborted. </p>
|
|
|
|
<li> <p> The "size" attribute value specifies the message size
|
|
that the client specified in the MAIL FROM command (zero if
|
|
none was specified). With Postfix 2.2 and later, it specifies
|
|
the actual message size after the client sends the END-OF-MESSAGE.
|
|
</p>
|
|
|
|
<li> <p> The "sasl_*" attributes (Postfix 2.2 and later) specify
|
|
information about how the client was authenticated via SASL.
|
|
These attributes are empty in case of no SASL authentication.
|
|
</p>
|
|
|
|
<li> <p> The "ccert_*" attributes (Postfix 2.2 and later) specify
|
|
information about how the client was authenticated via TLS.
|
|
These attributes are empty in case of no certificate authentication.
|
|
As of Postfix 2.2.11 these attribute values are encoded as
|
|
xtext: some characters are represented by +XX, where XX is the
|
|
two-digit hexadecimal representation of the character value. With
|
|
Postfix 2.6 and later, the decoded string is an UTF-8 string
|
|
without non-printable ASCII characters. </p>
|
|
|
|
<li> <p> The "encryption_*" attributes (Postfix 2.3 and later)
|
|
specify information about how the connection is encrypted. With
|
|
plaintext connections the protocol and cipher attributes are
|
|
empty and the keysize is zero. </p>
|
|
|
|
<li> <p> The "etrn_domain" attribute is defined only in the
|
|
context of the ETRN command, and specifies the ETRN command
|
|
parameter. </p>
|
|
|
|
<li> <p> The "stress" attribute is either empty or "yes". See
|
|
the <a href="STRESS_README.html">STRESS_README</a> document for further information. </p>
|
|
|
|
<li> <p> The "policy_context" attribute provides a way to pass
|
|
information that is not available via other attributes (Postfix
|
|
version 3.1 and later). </p>
|
|
|
|
<li> <p> The "<a href="postconf.5.html#compatibility_level">compatibility_level</a>" attribute corresponds to the
|
|
<a href="postconf.5.html#compatibility_level">compatibility_level</a> parameter value. It has the form
|
|
<i>major</i>.<i>minor</i>.<i>patch</i> where <i>minor</i> and
|
|
<i>patch</i> may be absent. </p>
|
|
|
|
<li> <p> The "<a href="postconf.5.html#mail_version">mail_version</a>" attribute corresponds to the
|
|
<a href="postconf.5.html#mail_version">mail_version</a> parameter value. It has the form
|
|
<i>major</i>.<i>minor</i>.<i>patch</i> for stable releases, and
|
|
<i>major</i>.<i>minor</i>-<i>yyyymmdd</i> for unstable releases.
|
|
</p>
|
|
|
|
</ul>
|
|
|
|
<p> The following is specific to SMTPD delegated policy requests:
|
|
</p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Protocol names are ESMTP or SMTP. </p>
|
|
|
|
<li> <p> Protocol states are CONNECT, EHLO, HELO, MAIL, RCPT,
|
|
DATA, END-OF-MESSAGE, VRFY or ETRN; these are the SMTP protocol
|
|
states where
|
|
the Postfix SMTP server makes an OK/REJECT/HOLD/etc. decision.
|
|
The DATA protocol state also applies to email that is received
|
|
with BDAT commands (Postfix 3.4 and later).
|
|
</p>
|
|
|
|
</ul>
|
|
|
|
<p> The policy server replies with any action that is allowed in a
|
|
Postfix SMTPD <a href="access.5.html">access(5)</a> table. Example: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
action=<a href="postconf.5.html#defer_if_permit">defer_if_permit</a> Service temporarily unavailable
|
|
[empty line]
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> This causes the Postfix SMTP server to reject the request with
|
|
a 450 temporary error code and with text "Service temporarily
|
|
unavailable", if the Postfix SMTP server finds no reason to reject
|
|
the request permanently. </p>
|
|
|
|
<p> In case of trouble the policy server must not send a reply.
|
|
Instead the server must log a warning and disconnect. Postfix will
|
|
retry the request at some later time. </p>
|
|
|
|
<h2><a name="client_config">Simple policy client/server configuration</a></h2>
|
|
|
|
<p> The Postfix delegated policy client can connect to a TCP socket
|
|
or to a UNIX-domain socket. Examples: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
inet:127.0.0.1:9998
|
|
unix:/some/where/policy
|
|
unix:private/policy
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The first example specifies that the policy server listens on
|
|
a TCP socket at 127.0.0.1 port 9998. The second example specifies
|
|
an absolute pathname of a UNIX-domain socket. The third example
|
|
specifies a pathname relative to the Postfix queue directory; use
|
|
this for policy servers that are spawned by the Postfix master
|
|
daemon. On many systems, "local" is a synonym for "unix".</p>
|
|
|
|
<p> To create a policy service that listens on a UNIX-domain socket
|
|
called "policy", and that runs under control of the Postfix <a href="spawn.8.html">spawn(8)</a>
|
|
daemon, you would use something like this: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="master.5.html">master.cf</a>:
|
|
2 policy unix - n n - 0 spawn
|
|
3 user=nobody argv=/some/where/policy-server
|
|
4
|
|
5 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
6 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
7 ...
|
|
8 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
9 <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/policy
|
|
10 ...
|
|
11 <a href="postconf.5.html#transport_time_limit">policy_time_limit</a> = 3600
|
|
12 # <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> = 1
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> NOTES: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Lines 2-3: this creates the service called "policy" that
|
|
listens on a UNIX-domain socket. The service is implemented by the
|
|
Postfix <a href="spawn.8.html">spawn(8)</a> daemon, which executes the policy server program
|
|
that is specified with the <b>argv</b> attribute, using the privileges
|
|
specified with the <b>user</b> attribute. </p>
|
|
|
|
<li> <p> Line 2: specify a "0" process limit instead of the default
|
|
"-", to avoid "connection refused" and other problems when you
|
|
increase the smtpd process limit. </p>
|
|
|
|
<li> <p> Line 8: <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> is not needed here if
|
|
the mail relay policy is specified with <a href="postconf.5.html#smtpd_relay_restrictions">smtpd_relay_restrictions</a>
|
|
(available with Postfix 2.10 and later). </p>
|
|
|
|
<li> <p> Lines 8, 9: always specify "<a href="postconf.5.html#check_policy_service">check_policy_service</a>" AFTER
|
|
"<a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>" or else your system could become an
|
|
open relay. </p>
|
|
|
|
<li> <p> Line 11: this increases the time that a policy server
|
|
process may run to 3600 seconds. The default time limit of 1000
|
|
seconds is too short; the policy daemon needs to run as long as the
|
|
SMTP server process that talks to it.
|
|
See the <a href="spawn.8.html">spawn(8)</a> manpage for more information about the
|
|
<a href="postconf.5.html#transport_time_limit"><i>transport</i>_time_limit</a> parameter. </p>
|
|
|
|
<blockquote> <p> Note: the "<a href="postconf.5.html#transport_time_limit">policy_time_limit</a>" parameter will not
|
|
show up in "postconf" command output before Postfix version 2.9.
|
|
This limitation applies to many parameters whose name is a combination
|
|
of a <a href="master.5.html">master.cf</a> service name (in the above example, "policy") and a
|
|
built-in suffix (in the above example: "_time_limit"). </p>
|
|
</blockquote>
|
|
|
|
<li> <p> Line 12: specify <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> to
|
|
avoid error-recovery delays with policy servers that cannot
|
|
maintain a persistent connection. </p>
|
|
|
|
<li> <p> With Solaris < 9, or Postfix < 2.10 on any Solaris
|
|
version, use TCP sockets instead of UNIX-domain sockets: </p>
|
|
|
|
</ul>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="master.5.html">master.cf</a>:
|
|
2 127.0.0.1:9998 inet n n n - 0 spawn
|
|
3 user=nobody argv=/some/where/policy-server
|
|
4
|
|
5 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
6 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
7 ...
|
|
8 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
9 <a href="postconf.5.html#check_policy_service">check_policy_service</a> inet:127.0.0.1:9998
|
|
10 ...
|
|
11 127.0.0.1:9998_time_limit = 3600
|
|
12 # <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> = 1
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Configuration parameters that control the client side of the
|
|
policy delegation protocol: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_default_action">smtpd_policy_service_default_action</a> (default: 451 4.3.5
|
|
Server configuration problem): The default action when an SMTPD
|
|
policy service request fails. Available with Postfix 3.0 and
|
|
later. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_max_idle">smtpd_policy_service_max_idle</a> (default: 300s): The amount
|
|
of time before the Postfix SMTP server closes an unused policy
|
|
client connection. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_max_ttl">smtpd_policy_service_max_ttl</a> (default: 1000s): The amount
|
|
of time before the Postfix SMTP server closes an active policy
|
|
client connection. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> (default: 0): The maximal
|
|
number of requests per policy connection, or zero (no limit).
|
|
Available with Postfix 3.0 and later. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_timeout">smtpd_policy_service_timeout</a> (default: 100s): The time
|
|
limit to connect to, send to or receive from a policy server. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_try_limit">smtpd_policy_service_try_limit</a> (default: 2): The maximal
|
|
number of attempts to send an SMTPD policy service request before
|
|
giving up. Available with Postfix 3.0 and later. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_retry_delay">smtpd_policy_service_retry_delay</a> (default: 1s): The delay
|
|
between attempts to resend a failed SMTPD policy service request.
|
|
Available with Postfix 3.0 and later. </p>
|
|
|
|
<li> <p> <a href="postconf.5.html#smtpd_policy_service_policy_context">smtpd_policy_service_policy_context</a> (default: empty):
|
|
Optional information that is passed in the "policy_context" attribute
|
|
of an SMTPD policy service request (originally, to share the same
|
|
SMTPD service endpoint among multiple <a href="postconf.5.html#check_policy_service">check_policy_service</a> clients).
|
|
Available with Postfix 3.1 and later. </p>
|
|
|
|
</ul>
|
|
|
|
<p> Configuration parameters that control the server side of the
|
|
policy delegation protocol: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> <a href="postconf.5.html#transport_time_limit"><i>transport</i>_time_limit</a> ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>): The
|
|
maximal amount of time the policy daemon is allowed to run before
|
|
it is terminated. The <i>transport</i> is the service name of the
|
|
<a href="master.5.html">master.cf</a> entry for the policy daemon service. In the above
|
|
examples, the service name is "policy" or "127.0.0.1:9998". </p>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="advanced">Advanced policy client configuration</a></h2>
|
|
|
|
<p> The previous section lists a number of Postfix <a href="postconf.5.html">main.cf</a> parameters
|
|
that control time limits and other settings for all policy clients.
|
|
This is sufficient for simple configurations. With more complex
|
|
configurations it becomes desirable to have different settings per
|
|
policy client. This is supported with Postfix 3.0 and later. </p>
|
|
|
|
<p> The following example shows a "non-critical" policy service
|
|
with a short timeout, and with "DUNNO" as default action when the
|
|
service is unvailable. The "DUNNO" action causes Postfix to ignore
|
|
the result. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
2 mua_recipient_restrictions =
|
|
3 ...
|
|
4 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
5 <a href="postconf.5.html#check_policy_service">check_policy_service</a> { inet:host:port,
|
|
6 timeout=10s, default_action=DUNNO
|
|
7 policy_context=submission }
|
|
8 ...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Instead of a server endpoint, we now have a list enclosed in {}. </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Line 5: 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 6-7: The remainder of the list contains per-client
|
|
settings. These settings override global <a href="postconf.5.html">main.cf</a> parameters,
|
|
and have the same name as those parameters, without the
|
|
"smtpd_policy_service_" prefix. </p>
|
|
|
|
</ul>
|
|
|
|
<p> Inside the list, syntax is similar to what we already know from
|
|
<a href="postconf.5.html">main.cf</a>: 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>. This comes in handy when different policy servers require
|
|
different default actions with different SMTP status codes or text:
|
|
</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
2 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
3 ...
|
|
4 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
5 <a href="postconf.5.html#check_policy_service">check_policy_service</a> {
|
|
6 inet:host:port1,
|
|
7 { default_action = 451 4.3.5 See <a href="https://www.example.com/support1">https://www.example.com/support1</a> }
|
|
8 }
|
|
9 ...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<h2><a name="greylist">Example: greylist policy server</a></h2>
|
|
|
|
<p> Greylisting is a defense against junk email that is described at
|
|
<a href="https://www.greylisting.org/">https://www.greylisting.org/</a>. The idea was discussed on the
|
|
postfix-users mailing list <a
|
|
href="https://marc.info/?l=postfix-users&m=101594914212564&w=2">
|
|
one year before it was popularized</a> (<a
|
|
href="http://archives.neohapsis.com/archives/postfix/2002-03/0846.html">alternative
|
|
version</a>). </p>
|
|
|
|
<p> The file examples/smtpd-policy/greylist.pl in the Postfix source
|
|
tree implements a simplified greylist policy server. This server
|
|
stores a time stamp for every (client, sender, recipient) triple.
|
|
By default, mail is not accepted until a time stamp is more than
|
|
60 seconds old. This stops junk mail with randomly selected sender
|
|
addresses, and mail that is sent through randomly selected open
|
|
proxies. It also stops junk mail from spammers that change their
|
|
IP address frequently. </p>
|
|
|
|
<p> Copy examples/smtpd-policy/greylist.pl to /usr/libexec/postfix
|
|
or whatever location is appropriate for your system. </p>
|
|
|
|
<p> In the greylist.pl Perl script you need to specify the
|
|
location of the greylist database file, and how long mail will
|
|
be delayed before it is accepted. The default settings are:
|
|
</p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
$database_name="/var/mta/greylist.db";
|
|
$greylist_delay=60;
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> The /var/mta directory (or whatever you choose) should be
|
|
writable by "nobody", or by whatever username you configure below
|
|
in <a href="master.5.html">master.cf</a> for the policy service. </p>
|
|
|
|
<p> Example: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
# mkdir /var/mta
|
|
# chown nobody /var/mta
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Note: DO NOT create the greylist database in a world-writable
|
|
directory such as /tmp or /var/tmp, and DO NOT create the greylist
|
|
database in a file system that may run out of space. Postfix can
|
|
survive "out of space" conditions with the mail queue and with the
|
|
mailbox store, but it cannot survive a corrupted greylist database.
|
|
If the file becomes corrupted you may not be able to receive mail
|
|
at all until you delete the file by hand. </p>
|
|
|
|
<p> The greylist.pl Perl script can be run under control by
|
|
the Postfix master daemon. For example, to run the script as user
|
|
"nobody", using a UNIX-domain socket that is accessible by Postfix
|
|
processes only: </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="master.5.html">master.cf</a>:
|
|
2 greylist unix - n n - 0 spawn
|
|
3 user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl
|
|
4
|
|
5 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
6 <a href="postconf.5.html#transport_time_limit">greylist_time_limit</a> = 3600
|
|
7 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
8 ...
|
|
9 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
10 <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/greylist
|
|
11 ...
|
|
12 # <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> = 1
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> Notes: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Lines 2-3: this creates the service called "greylist" that
|
|
listens on a UNIX-domain socket. The service is implemented by the
|
|
Postfix <a href="spawn.8.html">spawn(8)</a> daemon, which executes the greylist.pl script that
|
|
is specified with the <b>argv</b> attribute, using the privileges
|
|
specified with the <b>user</b> attribute. </p>
|
|
|
|
<li> <p> Line 2: specify a "0" process limit instead of the default
|
|
"-", to avoid "connection refused" and other problems when you
|
|
increase the smtpd process limit. </p>
|
|
|
|
<li> <p> Line 3: Specify "greylist.pl -v" for verbose logging of
|
|
each request and reply. </p>
|
|
|
|
<li> <p> Line 6: this increases the time that a greylist server
|
|
process may run to 3600 seconds. The default time limit of 1000
|
|
seconds is too short; the greylist daemon needs to run as long as the
|
|
SMTP server process that talks to it.
|
|
See the <a href="spawn.8.html">spawn(8)</a> manpage for more information about the
|
|
<a href="postconf.5.html#transport_time_limit"><i>transport</i>_time_limit</a> parameter. </p>
|
|
|
|
<li> <p> Line 9: <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> is not needed here if
|
|
the mail relay policy is specified with <a href="postconf.5.html#smtpd_relay_restrictions">smtpd_relay_restrictions</a>
|
|
(available with Postfix 2.10 and later). </p>
|
|
|
|
<blockquote> <p> Note: the "<a href="postconf.5.html#transport_time_limit">greylist_time_limit</a>" parameter will not
|
|
show up in "postconf" command output before Postfix version 2.9.
|
|
This limitation applies to many parameters whose name is a combination
|
|
of a <a href="master.5.html">master.cf</a> service name (in the above example, "greylist") and
|
|
a built-in suffix (in the above example: "_time_limit"). </p>
|
|
</blockquote>
|
|
|
|
<li> <p> Line 12: specify <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> to
|
|
avoid error-recovery delays with policy servers that cannot
|
|
maintain a persistent connection. </p>
|
|
|
|
</ul>
|
|
|
|
<p> With Solaris < 9, or Postfix < 2.10 on any Solaris
|
|
version, use inet: style sockets instead of unix:
|
|
style, as detailed in the "<a href="#client_config">Policy
|
|
client/server configuration</a>" section above. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="master.5.html">master.cf</a>:
|
|
2 127.0.0.1:9998 inet n n n - 0 spawn
|
|
3 user=nobody argv=/usr/bin/perl /usr/libexec/postfix/greylist.pl
|
|
4
|
|
5 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
6 127.0.0.1:9998_time_limit = 3600
|
|
7 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
8 ...
|
|
9 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
10 <a href="postconf.5.html#check_policy_service">check_policy_service</a> inet:127.0.0.1:9998
|
|
11 ...
|
|
12 # <a href="postconf.5.html#smtpd_policy_service_request_limit">smtpd_policy_service_request_limit</a> = 1
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<h2><a name="frequent">Greylisting mail from frequently forged domains</a></h2>
|
|
|
|
<p> It is relatively safe to turn on greylisting for specific
|
|
domains that often appear in forged email. At some point
|
|
in cyberspace/time a list of frequently
|
|
forged MAIL FROM domains could be found at
|
|
<a href="https://web.archive.org/web/20080526153208/http://www.monkeys.com/anti-spam/filtering/sender-domain-validate.in">https://web.archive.org/web/20080526153208/http://www.monkeys.com/anti-spam/filtering/sender-domain-validate.in</a>.
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
2 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
3 <a href="postconf.5.html#reject_unlisted_recipient">reject_unlisted_recipient</a>
|
|
4 ...
|
|
5 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
6 <a href="postconf.5.html#check_sender_access">check_sender_access</a> <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/sender_access
|
|
7 ...
|
|
8 <a href="postconf.5.html#smtpd_restriction_classes">smtpd_restriction_classes</a> = greylist
|
|
9 greylist = <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/greylist
|
|
10
|
|
11 /etc/postfix/sender_access:
|
|
12 aol.com greylist
|
|
13 hotmail.com greylist
|
|
14 bigfoot.com greylist
|
|
15 ... <i>etcetera</i> ...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> NOTES: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Line 9: On Solaris < 9, or Postfix < 2.10 on any
|
|
Solaris version, use inet: style sockets
|
|
instead of unix: style, as detailed in the "<a href="#greylist">Example:
|
|
greylist policy server</a>" section above. </p>
|
|
|
|
<li> <p> Line 5: <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> is not needed here if
|
|
the mail relay policy is specified with <a href="postconf.5.html#smtpd_relay_restrictions">smtpd_relay_restrictions</a>
|
|
(available with Postfix 2.10 and later). </p>
|
|
|
|
<li> <p> Line 6: Be sure to specify "<a href="postconf.5.html#check_sender_access">check_sender_access</a>" AFTER
|
|
"<a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>" or else your system could become an
|
|
open mail relay. </p>
|
|
|
|
<li> <p> Line 3: With Postfix 2.0 snapshot releases,
|
|
"<a href="postconf.5.html#reject_unlisted_recipient">reject_unlisted_recipient</a>" is called "check_recipient_maps".
|
|
Postfix 2.1 understands both forms. </p>
|
|
|
|
<li> <p> Line 3: The greylist database gets polluted quickly with
|
|
bogus addresses. It helps if you protect greylist lookups with
|
|
other restrictions that reject unknown senders and/or recipients.
|
|
</p>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="all_mail">Greylisting all your mail</a></h2>
|
|
|
|
<p> If you turn on greylisting for all mail you may want to make
|
|
exceptions for mailing lists that use one-time sender addresses,
|
|
because each message will be delayed due to greylisting, and the
|
|
one-time sender addresses can pollute your greylist database
|
|
relatively quickly. Instead of making exceptions, you can automatically
|
|
allowlist clients that survive greylisting repeatedly; this avoids
|
|
most of the delays and most of the database pollution problem. </p>
|
|
|
|
<blockquote>
|
|
<pre>
|
|
1 /etc/postfix/<a href="postconf.5.html">main.cf</a>:
|
|
2 <a href="postconf.5.html#smtpd_recipient_restrictions">smtpd_recipient_restrictions</a> =
|
|
3 <a href="postconf.5.html#reject_unlisted_recipient">reject_unlisted_recipient</a>
|
|
4 ...
|
|
5 <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a>
|
|
6 <a href="postconf.5.html#check_sender_access">check_sender_access</a> <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/sender_access
|
|
7 <a href="postconf.5.html#check_policy_service">check_policy_service</a> unix:private/policy
|
|
8 ...
|
|
9
|
|
10 /etc/postfix/sender_access:
|
|
11 securityfocus.com OK
|
|
12 ...
|
|
</pre>
|
|
</blockquote>
|
|
|
|
<p> NOTES: </p>
|
|
|
|
<ul>
|
|
|
|
<li> <p> Line 7: On Solaris < 9, or Postfix < 2.10 on any
|
|
Solaris version, use inet: style sockets
|
|
instead of unix: style, as detailed in the "<a href="#greylist">Example:
|
|
greylist policy server</a>" section above. </p>
|
|
|
|
<li> <p> Line 5: <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> is not needed here if
|
|
the mail relay policy is specified with <a href="postconf.5.html#smtpd_relay_restrictions">smtpd_relay_restrictions</a>
|
|
(available with Postfix 2.10 and later). </p>
|
|
|
|
<li> <p> Lines 6-7: Be sure to specify <a href="postconf.5.html#check_sender_access">check_sender_access</a> and
|
|
<a href="postconf.5.html#check_policy_service">check_policy_service</a> AFTER <a href="postconf.5.html#reject_unauth_destination">reject_unauth_destination</a> or else your
|
|
system could become an open mail relay. </p>
|
|
|
|
<li> <p> Line 3: The greylist database gets polluted quickly with
|
|
bogus addresses. It helps if you precede greylist lookups with
|
|
restrictions that reject unknown senders and/or recipients. </p>
|
|
|
|
</ul>
|
|
|
|
<h2><a name="maintenance">Routine greylist maintenance</a></h2>
|
|
|
|
<p> The greylist database grows over time, because the greylist server
|
|
never removes database entries. If left unattended, the greylist
|
|
database will eventually run your file system out of space. </p>
|
|
|
|
<p> When the status file size exceeds some threshold you can simply
|
|
rename or remove the file without adverse effects; Postfix
|
|
automatically creates a new file. In the worst case, new mail will
|
|
be delayed by an hour or so. To lessen the impact, rename or remove
|
|
the file in the middle of the night at the beginning of a weekend.
|
|
</p>
|
|
|
|
<h2><a name="greylist_code">Example Perl greylist server</a></h2>
|
|
|
|
<p> This is the Perl subroutine that implements the example greylist
|
|
policy. It is part of a general purpose sample policy server that
|
|
is distributed with the Postfix source as
|
|
examples/smtpd-policy/greylist.pl. </p>
|
|
|
|
<pre>
|
|
#
|
|
# greylist status database and greylist time interval. DO NOT create the
|
|
# greylist status database in a world-writable directory such as /tmp
|
|
# or /var/tmp. DO NOT create the greylist database in a file system
|
|
# that can run out of space.
|
|
#
|
|
$database_name="/var/mta/greylist.db";
|
|
$greylist_delay=60;
|
|
|
|
#
|
|
# Auto-allowlist threshold. Specify 0 to disable, or the number of
|
|
# successful "come backs" after which a client is no longer subject
|
|
# to greylisting.
|
|
#
|
|
$auto_allowlist_threshold = 10;
|
|
|
|
#
|
|
# Demo SMTPD access policy routine. The result is an action just like
|
|
# it would be specified on the right-hand side of a Postfix access
|
|
# table. Request attributes are available via the %attr hash.
|
|
#
|
|
sub smtpd_access_policy {
|
|
my($key, $time_stamp, $now);
|
|
|
|
# Open the database on the fly.
|
|
open_database() unless $database_obj;
|
|
|
|
# Search the auto-allowlist.
|
|
if ($auto_allowlist_threshold > 0) {
|
|
$count = read_database($attr{"client_address"});
|
|
if ($count > $auto_allowlist_threshold) {
|
|
return "dunno";
|
|
}
|
|
}
|
|
|
|
# Lookup the time stamp for this client/sender/recipient.
|
|
$key =
|
|
lc $attr{"client_address"}."/".$attr{"sender"}."/".$attr{"recipient"};
|
|
$time_stamp = read_database($key);
|
|
$now = time();
|
|
|
|
# If new request, add this client/sender/recipient to the database.
|
|
if ($time_stamp == 0) {
|
|
$time_stamp = $now;
|
|
update_database($key, $time_stamp);
|
|
}
|
|
|
|
# The result can be any action that is allowed in a Postfix <a href="access.5.html">access(5)</a> map.
|
|
#
|
|
# To label the mail, return ``PREPEND headername: headertext''
|
|
#
|
|
# In case of success, return ``DUNNO'' instead of ``OK'', so that the
|
|
# <a href="postconf.5.html#check_policy_service">check_policy_service</a> restriction can be followed by other restrictions.
|
|
#
|
|
# In case of failure, return ``DEFER_IF_PERMIT optional text...'',
|
|
# so that mail can still be blocked by other access restrictions.
|
|
#
|
|
syslog $syslog_priority, "request age %d", $now - $time_stamp if $verbose;
|
|
if ($now - $time_stamp > $greylist_delay) {
|
|
# Update the auto-allowlist.
|
|
if ($auto_allowlist_threshold > 0) {
|
|
update_database($attr{"client_address"}, $count + 1);
|
|
}
|
|
return "dunno";
|
|
} else {
|
|
return "<a href="postconf.5.html#defer_if_permit">defer_if_permit</a> Service temporarily unavailable";
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
</body>
|
|
|
|
</html>
|