Merge tag 'upstream/1.16'
Upstream version 1.16
This commit is contained in:
commit
af9371f56e
|
@ -1,3 +1,28 @@
|
||||||
|
1.16
|
||||||
|
=====
|
||||||
|
- bugfix: this is a bugfix release for 1.15. anyone affected is encouraged to upgrade.
|
||||||
|
detail: the default behavior for the '=' operator with numeric items
|
||||||
|
(size, recipient_count, ...) changed with version 1.15 to '==' (equals to).
|
||||||
|
now these items are compared '>=' (greater than) again.
|
||||||
|
note: if you are using 1.15 and you are not able upgrade for some reason,
|
||||||
|
please change '=' to '>=' in your ruleset where you mean 'greater than'.
|
||||||
|
|
||||||
|
1.15
|
||||||
|
=====
|
||||||
|
- feature: items may now be retrieved from files using "item=file:/some/where"
|
||||||
|
more information in the postfwd manual (FILES section)
|
||||||
|
- feature: helo_address, and sender_(ns|mx)_addrs can now be csv items
|
||||||
|
- feature: new rcpt() command counts recipients for rate limits (thanks to Sahil Tandon)
|
||||||
|
- code: redirect syslog to stdout for --kill, --reload and --showconfig
|
||||||
|
- code: option --reload (HUP signal) now reloads config, if the file is unchanged
|
||||||
|
- code: configuration parser improvements:
|
||||||
|
* rules without defined action will be skipped at configuration stage
|
||||||
|
* undefined ACLs will now be detected and skipped at configuration stage
|
||||||
|
* parser timeout skips loading a rule after 4s, to prevent problems with
|
||||||
|
large files or loops. use --config_timeout to override
|
||||||
|
- bugfix: documentation fixed (missing "action=" in ask() examples)
|
||||||
|
|
||||||
|
|
||||||
1.14
|
1.14
|
||||||
=====
|
=====
|
||||||
- feature: new compare operators *
|
- feature: new compare operators *
|
||||||
|
|
126
doc/postfwd.html
126
doc/postfwd.html
|
@ -1,10 +1,8 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>postfwd - postfix firewall daemon</title>
|
<title>postfwd - postfix firewall daemon</title>
|
||||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
<link rev="made" href="mailto:root@localhost" />
|
||||||
<link rev="made" href="mailto:feedback@suse.de" />
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="background-color: white">
|
<body style="background-color: white">
|
||||||
|
@ -22,6 +20,7 @@
|
||||||
<li><a href="#introduction">INTRODUCTION</a></li>
|
<li><a href="#introduction">INTRODUCTION</a></li>
|
||||||
<li><a href="#configuration">CONFIGURATION</a></li>
|
<li><a href="#configuration">CONFIGURATION</a></li>
|
||||||
<li><a href="#items">ITEMS</a></li>
|
<li><a href="#items">ITEMS</a></li>
|
||||||
|
<li><a href="#files">FILES</a></li>
|
||||||
<li><a href="#actions">ACTIONS</a></li>
|
<li><a href="#actions">ACTIONS</a></li>
|
||||||
<li><a href="#macros_acls">MACROS/ACLS</a></li>
|
<li><a href="#macros_acls">MACROS/ACLS</a></li>
|
||||||
<li><a href="#plugins">PLUGINS</a></li>
|
<li><a href="#plugins">PLUGINS</a></li>
|
||||||
|
@ -97,7 +96,8 @@
|
||||||
--dns_timeout_interval interval in seconds for dns timeout maximum counter
|
--dns_timeout_interval interval in seconds for dns timeout maximum counter
|
||||||
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
||||||
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
||||||
-I, --instantcfg re-reads rulefiles for every new request</pre>
|
-I, --instantcfg re-reads rulefiles for every new request
|
||||||
|
--config_timeout <i> parser timeout in seconds</pre>
|
||||||
<pre>
|
<pre>
|
||||||
Informational (use only at command-line!):
|
Informational (use only at command-line!):
|
||||||
-C, --showconfig shows ruleset summary, -v for verbose
|
-C, --showconfig shows ruleset summary, -v for verbose
|
||||||
|
@ -148,11 +148,15 @@ is not important. So the following would lead to the same result as the previous
|
||||||
<p>The way how request items are compared to the ruleset can be influenced in the following way:</p>
|
<p>The way how request items are compared to the ruleset can be influenced in the following way:</p>
|
||||||
<pre>
|
<pre>
|
||||||
====================================================================
|
====================================================================
|
||||||
ITEM==VALUE true if ITEM equals VALUE
|
ITEM == VALUE true if ITEM equals VALUE
|
||||||
ITEM>=VALUE true if ITEM >= VALUE
|
ITEM => VALUE true if ITEM >= VALUE
|
||||||
ITEM<=VALUE true if ITEM <= VALUE
|
ITEM =< VALUE true if ITEM <= VALUE
|
||||||
ITEM~=VALUE true if ITEM ~= /^VALUE$/i
|
ITEM =~ VALUE true if ITEM ~= /^VALUE$/i
|
||||||
ITEM=VALUE default behaviour (see ITEMS section)
|
ITEM != VALUE false if ITEM equals VALUE
|
||||||
|
ITEM !> VALUE false if ITEM >= VALUE
|
||||||
|
ITEM !< VALUE false if ITEM <= VALUE
|
||||||
|
ITEM !~ VALUE false if ITEM ~= /^VALUE$/i
|
||||||
|
ITEM = VALUE default behaviour (see ITEMS section)
|
||||||
====================================================================</pre>
|
====================================================================</pre>
|
||||||
<p>To identify single rules in your log files, you may add an unique identifier for each of it:</p>
|
<p>To identify single rules in your log files, you may add an unique identifier for each of it:</p>
|
||||||
<pre>
|
<pre>
|
||||||
|
@ -213,13 +217,19 @@ arguments. Please see the COMMAND LINE section below for more information on thi
|
||||||
recipient_domain</pre>
|
recipient_domain</pre>
|
||||||
<pre>
|
<pre>
|
||||||
helo_address - postfwd tries to look up the helo_name. use
|
helo_address - postfwd tries to look up the helo_name. use
|
||||||
helo_address=!!(0.0.0.0/0) to check for unknown.</pre>
|
helo_address=!!(0.0.0.0/0) to check for unknown.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.</pre>
|
||||||
<pre>
|
<pre>
|
||||||
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_ns_addrs of the nameservers for the sender domain part.</pre>
|
sender_ns_addrs of the nameservers for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.</pre>
|
||||||
<pre>
|
<pre>
|
||||||
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_mx_addrs of the mx records for the sender domain part.</pre>
|
sender_mx_addrs of the mx records for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.</pre>
|
||||||
<pre>
|
<pre>
|
||||||
version - postfwd version, contains "postfwd n.nn"
|
version - postfwd version, contains "postfwd n.nn"
|
||||||
this enables version based checks in your rulesets
|
this enables version based checks in your rulesets
|
||||||
|
@ -298,6 +308,9 @@ Pattern matching is performed case insensitive.</p>
|
||||||
<p>Any item can be negated by preceeding '!!' to it, e.g.:</p>
|
<p>Any item can be negated by preceeding '!!' to it, e.g.:</p>
|
||||||
<pre>
|
<pre>
|
||||||
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please</pre>
|
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please</pre>
|
||||||
|
<p>or using the right compare operator:</p>
|
||||||
|
<pre>
|
||||||
|
id=USER01 ; sasl_username !~ /^(bob|alice)$/ ; action=REJECT who is that?</pre>
|
||||||
<p>To avoid confusion with regexps or simply for better visibility you can use '!!(...)':</p>
|
<p>To avoid confusion with regexps or simply for better visibility you can use '!!(...)':</p>
|
||||||
<pre>
|
<pre>
|
||||||
id=USER01 ; sasl_username=!!( (bob|alice) ) ; action=REJECT who is that?</pre>
|
id=USER01 ; sasl_username=!!( (bob|alice) ) ; action=REJECT who is that?</pre>
|
||||||
|
@ -310,6 +323,78 @@ Pattern matching is performed case insensitive.</p>
|
||||||
Use the '-vv' option to debug.</p>
|
Use the '-vv' option to debug.</p>
|
||||||
<p>
|
<p>
|
||||||
</p>
|
</p>
|
||||||
|
<h2><a name="files">FILES</a></h2>
|
||||||
|
<p>Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:</p>
|
||||||
|
<pre>
|
||||||
|
id=R001 ; ccert_fingerprint==<a href="file:/etc/postfwd/wl_ccerts">file:/etc/postfwd/wl_ccerts</a> ; action=DUNNO</pre>
|
||||||
|
<p>postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:</p>
|
||||||
|
<pre>
|
||||||
|
# client1
|
||||||
|
11:22:33:44:55:66:77:88:99
|
||||||
|
# client2
|
||||||
|
22:33:44:55:66:77:88:99:00
|
||||||
|
# client3
|
||||||
|
33:44:55:66:77:88:99:00:11</pre>
|
||||||
|
<p>To use existing tables in key=value format, you can use:</p>
|
||||||
|
<pre>
|
||||||
|
id=R001 ; ccert_fingerprint==table:/etc/postfwd/wl_ccerts ; action=DUNNO</pre>
|
||||||
|
<p>This will ignore the right-hand value. Items can be mixed:</p>
|
||||||
|
<pre>
|
||||||
|
id=R002 ; action=REJECT \
|
||||||
|
client_name==unknown; \
|
||||||
|
client_name==<a href="file:/etc/postfwd/blacklisted">file:/etc/postfwd/blacklisted</a></pre>
|
||||||
|
<p>and for non pcre (comma separated) items:</p>
|
||||||
|
<pre>
|
||||||
|
id=R003 ; action=REJECT \
|
||||||
|
client_address==10.1.1.1, <a href="file:/etc/postfwd/blacklisted">file:/etc/postfwd/blacklisted</a></pre>
|
||||||
|
<pre>
|
||||||
|
id=R004 ; action=REJECT \
|
||||||
|
rbl=myrbl.home.local, zen.spamhaus.org, <a href="file:/etc/postfwd/rbls_changing">file:/etc/postfwd/rbls_changing</a></pre>
|
||||||
|
<p>You can check your configuration with the --show_config option at the command line:</p>
|
||||||
|
<pre>
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, <a href="file:/etc/postfwd/wl_clients">file:/etc/postfwd/wl_clients</a>, 192.168.2.1'</pre>
|
||||||
|
<p>should give something like:</p>
|
||||||
|
<pre>
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"</pre>
|
||||||
|
<p>If a file can not be read, it will be ignored:</p>
|
||||||
|
<pre>
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, <a href="file:/etc/postfwd/wl_clients">file:/etc/postfwd/wl_clients</a>, 192.168.2.1'
|
||||||
|
[LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"</pre>
|
||||||
|
<p>File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.</p>
|
||||||
|
<p>If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:</p>
|
||||||
|
<pre>
|
||||||
|
id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno</pre>
|
||||||
|
<p>This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
|
||||||
|
necessary. Of course this might increase the system load, so please use it with care.</p>
|
||||||
|
<p>The --showconfig option illustrates the difference:</p>
|
||||||
|
<pre>
|
||||||
|
## evaluated at configuration stage
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"</pre>
|
||||||
|
<pre>
|
||||||
|
## evaluated for any rulehit
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"</pre>
|
||||||
|
<p>Files can refer to other files. The following is valid.</p>
|
||||||
|
<pre>
|
||||||
|
-- FILE /etc/postfwd/rules.cf --
|
||||||
|
id=R001; client_address=<a href="file:/etc/postfwd/clients_master.cf">file:/etc/postfwd/clients_master.cf</a>; action=DUNNO</pre>
|
||||||
|
<pre>
|
||||||
|
-- FILE /etc/postfwd/clients_master.cf --
|
||||||
|
192.168.1.0/24
|
||||||
|
<a href="file:/etc/postfwd/clients_east.cf">file:/etc/postfwd/clients_east.cf</a>
|
||||||
|
<a href="file:/etc/postfwd/clients_west.cf">file:/etc/postfwd/clients_west.cf</a></pre>
|
||||||
|
<pre>
|
||||||
|
-- FILE /etc/postfwd/clients_east.cf --
|
||||||
|
192.168.2.0/24</pre>
|
||||||
|
<pre>
|
||||||
|
-- FILE /etc/postfwd/clients_west.cf --
|
||||||
|
192.168.3.0/24</pre>
|
||||||
|
<p>Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
|
||||||
|
with postfwd1 v1.15 and postfwd2 v0.18 and higher.</p>
|
||||||
|
<p>
|
||||||
|
</p>
|
||||||
<h2><a name="actions">ACTIONS</a></h2>
|
<h2><a name="actions">ACTIONS</a></h2>
|
||||||
<p><em>General</em></p>
|
<p><em>General</em></p>
|
||||||
<p>Actions will be executed, when all rule items have matched a request (or at least one of any item list). You can refer to
|
<p>Actions will be executed, when all rule items have matched a request (or at least one of any item list). You can refer to
|
||||||
|
@ -370,16 +455,25 @@ rule containing only an action statement:</p>
|
||||||
# size limit 1.5mb per hour per client
|
# size limit 1.5mb per hour per client
|
||||||
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)</pre>
|
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)</pre>
|
||||||
|
<pre>
|
||||||
|
rcpt (<item>/<max>/<time>/<action>)
|
||||||
|
this command works similar to the rate() command with the difference, that the rate counter is
|
||||||
|
increased by the request's recipient_count attribute. to do this reliably you should call postfwd
|
||||||
|
from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
|
||||||
|
check it within the ruleset:
|
||||||
|
# recipient count limit 3 per hour per client
|
||||||
|
id=RCPT01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
|
action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)</pre>
|
||||||
<pre>
|
<pre>
|
||||||
ask (<addr>:<port>[:<ignore>])
|
ask (<addr>:<port>[:<ignore>])
|
||||||
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
||||||
and the second argument (address and port) are mandatory. a third optional argument may be
|
and the second argument (address and port) are mandatory. a third optional argument may be
|
||||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||||
# example1: query postgrey and return it's answer to postfix
|
# example1: query postgrey and return it's answer to postfix
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
|
||||||
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
||||||
# and continue parsing postfwd's ruleset
|
# and continue parsing postfwd's ruleset
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)</pre>
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)</pre>
|
||||||
<pre>
|
<pre>
|
||||||
wait (<delay>)
|
wait (<delay>)
|
||||||
pauses the program execution for <delay> seconds. use this for
|
pauses the program execution for <delay> seconds. use this for
|
||||||
|
@ -650,6 +744,10 @@ The following arguments will control it's behaviour in this case.</p>
|
||||||
without restarting. Though files will be read only if necessary
|
without restarting. Though files will be read only if necessary
|
||||||
(which means their access times changed since last read) this might
|
(which means their access times changed since last read) this might
|
||||||
significantly increase system load.</pre>
|
significantly increase system load.</pre>
|
||||||
|
<pre>
|
||||||
|
--config_timeout (default=3)
|
||||||
|
timeout in seconds to parse a single configuration line. if exceeded, the rule will
|
||||||
|
be skipped. this is used to prevent problems due to large files or loops.</pre>
|
||||||
<p><em>Informational arguments</em></p>
|
<p><em>Informational arguments</em></p>
|
||||||
<p>These arguments are for command line usage only. Never ever use them with postfix spawn!</p>
|
<p>These arguments are for command line usage only. Never ever use them with postfix spawn!</p>
|
||||||
<pre>
|
<pre>
|
||||||
|
|
133
doc/postfwd.txt
133
doc/postfwd.txt
|
@ -51,6 +51,7 @@ SYNOPSIS
|
||||||
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
||||||
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
||||||
-I, --instantcfg re-reads rulefiles for every new request
|
-I, --instantcfg re-reads rulefiles for every new request
|
||||||
|
--config_timeout <i> parser timeout in seconds
|
||||||
|
|
||||||
Informational (use only at command-line!):
|
Informational (use only at command-line!):
|
||||||
-C, --showconfig shows ruleset summary, -v for verbose
|
-C, --showconfig shows ruleset summary, -v for verbose
|
||||||
|
@ -115,11 +116,15 @@ DESCRIPTION
|
||||||
in the following way:
|
in the following way:
|
||||||
|
|
||||||
====================================================================
|
====================================================================
|
||||||
ITEM==VALUE true if ITEM equals VALUE
|
ITEM == VALUE true if ITEM equals VALUE
|
||||||
ITEM>=VALUE true if ITEM >= VALUE
|
ITEM => VALUE true if ITEM >= VALUE
|
||||||
ITEM<=VALUE true if ITEM <= VALUE
|
ITEM =< VALUE true if ITEM <= VALUE
|
||||||
ITEM~=VALUE true if ITEM ~= /^VALUE$/i
|
ITEM =~ VALUE true if ITEM ~= /^VALUE$/i
|
||||||
ITEM=VALUE default behaviour (see ITEMS section)
|
ITEM != VALUE false if ITEM equals VALUE
|
||||||
|
ITEM !> VALUE false if ITEM >= VALUE
|
||||||
|
ITEM !< VALUE false if ITEM <= VALUE
|
||||||
|
ITEM !~ VALUE false if ITEM ~= /^VALUE$/i
|
||||||
|
ITEM = VALUE default behaviour (see ITEMS section)
|
||||||
====================================================================
|
====================================================================
|
||||||
|
|
||||||
To identify single rules in your log files, you may add an unique
|
To identify single rules in your log files, you may add an unique
|
||||||
|
@ -187,12 +192,18 @@ DESCRIPTION
|
||||||
|
|
||||||
helo_address - postfwd tries to look up the helo_name. use
|
helo_address - postfwd tries to look up the helo_name. use
|
||||||
helo_address=!!(0.0.0.0/0) to check for unknown.
|
helo_address=!!(0.0.0.0/0) to check for unknown.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_ns_addrs of the nameservers for the sender domain part.
|
sender_ns_addrs of the nameservers for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_mx_addrs of the mx records for the sender domain part.
|
sender_mx_addrs of the mx records for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
version - postfwd version, contains "postfwd n.nn"
|
version - postfwd version, contains "postfwd n.nn"
|
||||||
this enables version based checks in your rulesets
|
this enables version based checks in your rulesets
|
||||||
|
@ -284,6 +295,10 @@ DESCRIPTION
|
||||||
|
|
||||||
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please
|
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please
|
||||||
|
|
||||||
|
or using the right compare operator:
|
||||||
|
|
||||||
|
id=USER01 ; sasl_username !~ /^(bob|alice)$/ ; action=REJECT who is that?
|
||||||
|
|
||||||
To avoid confusion with regexps or simply for better visibility you can
|
To avoid confusion with regexps or simply for better visibility you can
|
||||||
use '!!(...)':
|
use '!!(...)':
|
||||||
|
|
||||||
|
@ -299,6 +314,97 @@ DESCRIPTION
|
||||||
be performed as case insensitive exact match. Use the '-vv' option to
|
be performed as case insensitive exact match. Use the '-vv' option to
|
||||||
debug.
|
debug.
|
||||||
|
|
||||||
|
FILES
|
||||||
|
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in
|
||||||
|
separate files:
|
||||||
|
|
||||||
|
id=R001 ; ccert_fingerprint==file:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
|
||||||
|
postfwd will read a list of items (one item per line) from
|
||||||
|
/etc/postfwd/wl_ccerts. comments are allowed:
|
||||||
|
|
||||||
|
# client1
|
||||||
|
11:22:33:44:55:66:77:88:99
|
||||||
|
# client2
|
||||||
|
22:33:44:55:66:77:88:99:00
|
||||||
|
# client3
|
||||||
|
33:44:55:66:77:88:99:00:11
|
||||||
|
|
||||||
|
To use existing tables in key=value format, you can use:
|
||||||
|
|
||||||
|
id=R001 ; ccert_fingerprint==table:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
|
||||||
|
This will ignore the right-hand value. Items can be mixed:
|
||||||
|
|
||||||
|
id=R002 ; action=REJECT \
|
||||||
|
client_name==unknown; \
|
||||||
|
client_name==file:/etc/postfwd/blacklisted
|
||||||
|
|
||||||
|
and for non pcre (comma separated) items:
|
||||||
|
|
||||||
|
id=R003 ; action=REJECT \
|
||||||
|
client_address==10.1.1.1, file:/etc/postfwd/blacklisted
|
||||||
|
|
||||||
|
id=R004 ; action=REJECT \
|
||||||
|
rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
|
||||||
|
|
||||||
|
You can check your configuration with the --show_config option at the
|
||||||
|
command line:
|
||||||
|
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
|
||||||
|
should give something like:
|
||||||
|
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
|
||||||
|
|
||||||
|
If a file can not be read, it will be ignored:
|
||||||
|
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
[LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
|
||||||
|
|
||||||
|
File items are evaluated at configuration stage. Therefore postfwd needs
|
||||||
|
to be reloaded if a file has changed.
|
||||||
|
|
||||||
|
If you want to specify a file, that will be reloaded for each request,
|
||||||
|
you can use lfile: and ltable:
|
||||||
|
|
||||||
|
id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
|
||||||
|
|
||||||
|
This will check the modification time of /etc/postfwd/client_whitelist
|
||||||
|
every time the rule is evaluated and reload it as necessary. Of course
|
||||||
|
this might increase the system load, so please use it with care.
|
||||||
|
|
||||||
|
The --showconfig option illustrates the difference:
|
||||||
|
|
||||||
|
## evaluated at configuration stage
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
|
||||||
|
|
||||||
|
## evaluated for any rulehit
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
|
||||||
|
|
||||||
|
Files can refer to other files. The following is valid.
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/rules.cf --
|
||||||
|
id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_master.cf --
|
||||||
|
192.168.1.0/24
|
||||||
|
file:/etc/postfwd/clients_east.cf
|
||||||
|
file:/etc/postfwd/clients_west.cf
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_east.cf --
|
||||||
|
192.168.2.0/24
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_west.cf --
|
||||||
|
192.168.3.0/24
|
||||||
|
|
||||||
|
Remind that there is currently no loop detection (/a/file calls /a/file)
|
||||||
|
and that this feature is only available with postfwd1 v1.15 and postfwd2
|
||||||
|
v0.18 and higher.
|
||||||
|
|
||||||
ACTIONS
|
ACTIONS
|
||||||
*General*
|
*General*
|
||||||
|
|
||||||
|
@ -371,15 +477,24 @@ DESCRIPTION
|
||||||
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
||||||
|
|
||||||
|
rcpt (<item>/<max>/<time>/<action>)
|
||||||
|
this command works similar to the rate() command with the difference, that the rate counter is
|
||||||
|
increased by the request's recipient_count attribute. to do this reliably you should call postfwd
|
||||||
|
from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
|
||||||
|
check it within the ruleset:
|
||||||
|
# recipient count limit 3 per hour per client
|
||||||
|
id=RCPT01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
|
action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
|
||||||
|
|
||||||
ask (<addr>:<port>[:<ignore>])
|
ask (<addr>:<port>[:<ignore>])
|
||||||
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
||||||
and the second argument (address and port) are mandatory. a third optional argument may be
|
and the second argument (address and port) are mandatory. a third optional argument may be
|
||||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||||
# example1: query postgrey and return it's answer to postfix
|
# example1: query postgrey and return it's answer to postfix
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
|
||||||
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
||||||
# and continue parsing postfwd's ruleset
|
# and continue parsing postfwd's ruleset
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
|
||||||
|
|
||||||
wait (<delay>)
|
wait (<delay>)
|
||||||
pauses the program execution for <delay> seconds. use this for
|
pauses the program execution for <delay> seconds. use this for
|
||||||
|
@ -671,6 +786,10 @@ DESCRIPTION
|
||||||
(which means their access times changed since last read) this might
|
(which means their access times changed since last read) this might
|
||||||
significantly increase system load.
|
significantly increase system load.
|
||||||
|
|
||||||
|
--config_timeout (default=3)
|
||||||
|
timeout in seconds to parse a single configuration line. if exceeded, the rule will
|
||||||
|
be skipped. this is used to prevent problems due to large files or loops.
|
||||||
|
|
||||||
*Informational arguments*
|
*Informational arguments*
|
||||||
|
|
||||||
These arguments are for command line usage only. Never ever use them
|
These arguments are for command line usage only. Never ever use them
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32
|
.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
|
||||||
.\"
|
.\"
|
||||||
.\" Standard preamble:
|
.\" Standard preamble:
|
||||||
.\" ========================================================================
|
.\" ========================================================================
|
||||||
|
@ -128,8 +128,8 @@
|
||||||
.rm #[ #] #H #V #F C
|
.rm #[ #] #H #V #F C
|
||||||
.\" ========================================================================
|
.\" ========================================================================
|
||||||
.\"
|
.\"
|
||||||
.IX Title "MANUAL1 8"
|
.IX Title "POSTFWD 1"
|
||||||
.TH MANUAL1 8 "2009-06-27" "perl v5.8.8" "User Contributed Perl Documentation"
|
.TH POSTFWD 1 "2009-09-03" "perl v5.8.5" "User Contributed Perl Documentation"
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
postfwd \- postfix firewall daemon
|
postfwd \- postfix firewall daemon
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
|
@ -175,7 +175,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
|
||||||
\& --cleanup-rates cleanup interval in seconds for rate cache
|
\& --cleanup-rates cleanup interval in seconds for rate cache
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 16
|
.Vb 17
|
||||||
\& Optional:
|
\& Optional:
|
||||||
\& -t, --test testing, always returns "dunno"
|
\& -t, --test testing, always returns "dunno"
|
||||||
\& -v, --verbose verbose logging, use twice (-vv) to increase level
|
\& -v, --verbose verbose logging, use twice (-vv) to increase level
|
||||||
|
@ -192,6 +192,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
|
||||||
\& --dns_max_ns_lookups max names to look up with sender_ns_addrs
|
\& --dns_max_ns_lookups max names to look up with sender_ns_addrs
|
||||||
\& --dns_max_mx_lookups max names to look up with sender_mx_addrs
|
\& --dns_max_mx_lookups max names to look up with sender_mx_addrs
|
||||||
\& -I, --instantcfg re-reads rulefiles for every new request
|
\& -I, --instantcfg re-reads rulefiles for every new request
|
||||||
|
\& --config_timeout <i> parser timeout in seconds
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 7
|
.Vb 7
|
||||||
|
@ -261,13 +262,17 @@ is not important. So the following would lead to the same result as the previous
|
||||||
.PP
|
.PP
|
||||||
The way how request items are compared to the ruleset can be influenced in the following way:
|
The way how request items are compared to the ruleset can be influenced in the following way:
|
||||||
.PP
|
.PP
|
||||||
.Vb 7
|
.Vb 11
|
||||||
\& ====================================================================
|
\& ====================================================================
|
||||||
\& ITEM==VALUE true if ITEM equals VALUE
|
\& ITEM == VALUE true if ITEM equals VALUE
|
||||||
\& ITEM>=VALUE true if ITEM >= VALUE
|
\& ITEM => VALUE true if ITEM >= VALUE
|
||||||
\& ITEM<=VALUE true if ITEM <= VALUE
|
\& ITEM =< VALUE true if ITEM <= VALUE
|
||||||
\& ITEM~=VALUE true if ITEM ~= /^VALUE$/i
|
\& ITEM =~ VALUE true if ITEM ~= /^VALUE$/i
|
||||||
\& ITEM=VALUE default behaviour (see ITEMS section)
|
\& ITEM != VALUE false if ITEM equals VALUE
|
||||||
|
\& ITEM !> VALUE false if ITEM >= VALUE
|
||||||
|
\& ITEM !< VALUE false if ITEM <= VALUE
|
||||||
|
\& ITEM !~ VALUE false if ITEM ~= /^VALUE$/i
|
||||||
|
\& ITEM = VALUE default behaviour (see ITEMS section)
|
||||||
\& ====================================================================
|
\& ====================================================================
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
|
@ -352,19 +357,25 @@ Rules can span multiple lines by adding a trailing backslash \*(L"\e\*(R" charac
|
||||||
\& recipient_domain
|
\& recipient_domain
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 2
|
.Vb 4
|
||||||
\& helo_address - postfwd tries to look up the helo_name. use
|
\& helo_address - postfwd tries to look up the helo_name. use
|
||||||
\& helo_address=!!(0.0.0.0/0) to check for unknown.
|
\& helo_address=!!(0.0.0.0/0) to check for unknown.
|
||||||
|
\& Please do not use this for positive access control
|
||||||
|
\& (whitelisting), as it might be forged.
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 2
|
.Vb 4
|
||||||
\& sender_ns_names, - postfwd tries to look up the names/ip addresses
|
\& sender_ns_names, - postfwd tries to look up the names/ip addresses
|
||||||
\& sender_ns_addrs of the nameservers for the sender domain part.
|
\& sender_ns_addrs of the nameservers for the sender domain part.
|
||||||
|
\& Please do not use this for positive access control
|
||||||
|
\& (whitelisting), as it might be forged.
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 2
|
.Vb 4
|
||||||
\& sender_mx_names, - postfwd tries to look up the names/ip addresses
|
\& sender_mx_names, - postfwd tries to look up the names/ip addresses
|
||||||
\& sender_mx_addrs of the mx records for the sender domain part.
|
\& sender_mx_addrs of the mx records for the sender domain part.
|
||||||
|
\& Please do not use this for positive access control
|
||||||
|
\& (whitelisting), as it might be forged.
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 6
|
.Vb 6
|
||||||
|
@ -464,6 +475,12 @@ Any item can be negated by preceeding '!!' to it, e.g.:
|
||||||
\& id=TLS001 ; hostname=!!^secure\e.trust\e.local$ ; action=REJECT only secure.trust.local please
|
\& id=TLS001 ; hostname=!!^secure\e.trust\e.local$ ; action=REJECT only secure.trust.local please
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
|
or using the right compare operator:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& id=USER01 ; sasl_username !~ /^(bob|alice)$/ ; action=REJECT who is that?
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
|
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
|
||||||
.PP
|
.PP
|
||||||
.Vb 1
|
.Vb 1
|
||||||
|
@ -480,6 +497,122 @@ Request attributes can be compared by preceeding '$$' characters, e.g.:
|
||||||
.PP
|
.PP
|
||||||
This is only valid for \s-1PCRE\s0 values (see list above). The comparison will be performed as case insensitive exact match.
|
This is only valid for \s-1PCRE\s0 values (see list above). The comparison will be performed as case insensitive exact match.
|
||||||
Use the '\-vv' option to debug.
|
Use the '\-vv' option to debug.
|
||||||
|
.Sh "\s-1FILES\s0"
|
||||||
|
.IX Subsection "FILES"
|
||||||
|
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& id=R001 ; ccert_fingerprint==file:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:
|
||||||
|
.PP
|
||||||
|
.Vb 6
|
||||||
|
\& # client1
|
||||||
|
\& 11:22:33:44:55:66:77:88:99
|
||||||
|
\& # client2
|
||||||
|
\& 22:33:44:55:66:77:88:99:00
|
||||||
|
\& # client3
|
||||||
|
\& 33:44:55:66:77:88:99:00:11
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
To use existing tables in key=value format, you can use:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& id=R001 ; ccert_fingerprint==table:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
This will ignore the right-hand value. Items can be mixed:
|
||||||
|
.PP
|
||||||
|
.Vb 3
|
||||||
|
\& id=R002 ; action=REJECT \e
|
||||||
|
\& client_name==unknown; \e
|
||||||
|
\& client_name==file:/etc/postfwd/blacklisted
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
and for non pcre (comma separated) items:
|
||||||
|
.PP
|
||||||
|
.Vb 2
|
||||||
|
\& id=R003 ; action=REJECT \e
|
||||||
|
\& client_address==10.1.1.1, file:/etc/postfwd/blacklisted
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
.Vb 2
|
||||||
|
\& id=R004 ; action=REJECT \e
|
||||||
|
\& rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
You can check your configuration with the \-\-show_config option at the command line:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
should give something like:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
If a file can not be read, it will be ignored:
|
||||||
|
.PP
|
||||||
|
.Vb 3
|
||||||
|
\& # postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
\& [LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
|
||||||
|
\& Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.
|
||||||
|
.PP
|
||||||
|
If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:
|
||||||
|
.PP
|
||||||
|
.Vb 1
|
||||||
|
\& id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
|
||||||
|
necessary. Of course this might increase the system load, so please use it with care.
|
||||||
|
.PP
|
||||||
|
The \-\-showconfig option illustrates the difference:
|
||||||
|
.PP
|
||||||
|
.Vb 3
|
||||||
|
\& ## evaluated at configuration stage
|
||||||
|
\& # postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
\& Rule 0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
.Vb 3
|
||||||
|
\& ## evaluated for any rulehit
|
||||||
|
\& # postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
\& Rule 0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
Files can refer to other files. The following is valid.
|
||||||
|
.PP
|
||||||
|
.Vb 2
|
||||||
|
\& -- FILE /etc/postfwd/rules.cf --
|
||||||
|
\& id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
.Vb 4
|
||||||
|
\& -- FILE /etc/postfwd/clients_master.cf --
|
||||||
|
\& 192.168.1.0/24
|
||||||
|
\& file:/etc/postfwd/clients_east.cf
|
||||||
|
\& file:/etc/postfwd/clients_west.cf
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
.Vb 2
|
||||||
|
\& -- FILE /etc/postfwd/clients_east.cf --
|
||||||
|
\& 192.168.2.0/24
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
.Vb 2
|
||||||
|
\& -- FILE /etc/postfwd/clients_west.cf --
|
||||||
|
\& 192.168.3.0/24
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
|
Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
|
||||||
|
with postfwd1 v1.15 and postfwd2 v0.18 and higher.
|
||||||
.Sh "\s-1ACTIONS\s0"
|
.Sh "\s-1ACTIONS\s0"
|
||||||
.IX Subsection "ACTIONS"
|
.IX Subsection "ACTIONS"
|
||||||
\&\fIGeneral\fR
|
\&\fIGeneral\fR
|
||||||
|
@ -562,16 +695,27 @@ postfwd actions control the behaviour of the program. Currently you can specify
|
||||||
\& action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
\& action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
|
.Vb 8
|
||||||
|
\& rcpt (<item>/<max>/<time>/<action>)
|
||||||
|
\& this command works similar to the rate() command with the difference, that the rate counter is
|
||||||
|
\& increased by the request's recipient_count attribute. to do this reliably you should call postfwd
|
||||||
|
\& from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
|
||||||
|
\& check it within the ruleset:
|
||||||
|
\& # recipient count limit 3 per hour per client
|
||||||
|
\& id=RCPT01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \e
|
||||||
|
\& action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
.Vb 9
|
.Vb 9
|
||||||
\& ask (<addr>:<port>[:<ignore>])
|
\& ask (<addr>:<port>[:<ignore>])
|
||||||
\& allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
\& allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
||||||
\& and the second argument (address and port) are mandatory. a third optional argument may be
|
\& and the second argument (address and port) are mandatory. a third optional argument may be
|
||||||
\& specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
\& specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||||
\& # example1: query postgrey and return it's answer to postfix
|
\& # example1: query postgrey and return it's answer to postfix
|
||||||
\& id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
|
\& id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
|
||||||
\& # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
\& # example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
||||||
\& # and continue parsing postfwd's ruleset
|
\& # and continue parsing postfwd's ruleset
|
||||||
\& id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
|
\& id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
.Vb 3
|
.Vb 3
|
||||||
|
@ -962,6 +1106,12 @@ These parameters influence the way postfwd is working. Any of them can be combin
|
||||||
\& significantly increase system load.
|
\& significantly increase system load.
|
||||||
.Ve
|
.Ve
|
||||||
.PP
|
.PP
|
||||||
|
.Vb 3
|
||||||
|
\& --config_timeout (default=3)
|
||||||
|
\& timeout in seconds to parse a single configuration line. if exceeded, the rule will
|
||||||
|
\& be skipped. this is used to prevent problems due to large files or loops.
|
||||||
|
.Ve
|
||||||
|
.PP
|
||||||
\&\fIInformational arguments\fR
|
\&\fIInformational arguments\fR
|
||||||
.PP
|
.PP
|
||||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
||||||
|
|
456
sbin/postfwd
456
sbin/postfwd
|
@ -25,7 +25,7 @@ use vars qw(@ISA);
|
||||||
|
|
||||||
# Program constants
|
# Program constants
|
||||||
our($NAME) = 'postfwd';
|
our($NAME) = 'postfwd';
|
||||||
our($VERSION) = '1.14';
|
our($VERSION) = '1.16';
|
||||||
|
|
||||||
# Networking options (use -i, -p and -R to change)
|
# Networking options (use -i, -p and -R to change)
|
||||||
our($def_net_pid) = "/var/run/".$NAME.".pid";
|
our($def_net_pid) = "/var/run/".$NAME.".pid";
|
||||||
|
@ -38,6 +38,7 @@ our($def_net_group) = "nobody";
|
||||||
our($def_dns_queuesize) = "300";
|
our($def_dns_queuesize) = "300";
|
||||||
our($def_dns_retries) = "3";
|
our($def_dns_retries) = "3";
|
||||||
our($def_dns_timeout) = "14";
|
our($def_dns_timeout) = "14";
|
||||||
|
our($def_config_timeout) = "3";
|
||||||
our($reply_maxlen) = "512";
|
our($reply_maxlen) = "512";
|
||||||
|
|
||||||
# change this, to match your POD requirements
|
# change this, to match your POD requirements
|
||||||
|
@ -49,7 +50,7 @@ our($cmd_pager) = "more";
|
||||||
|
|
||||||
# default action, do not change
|
# default action, do not change
|
||||||
# unless you really know why
|
# unless you really know why
|
||||||
our($default_action) = "dunno";
|
our($default_action) = "DUNNO";
|
||||||
|
|
||||||
# default maximum values for the score() command
|
# default maximum values for the score() command
|
||||||
# if exceeded, the specified action will be returned
|
# if exceeded, the specified action will be returned
|
||||||
|
@ -113,6 +114,14 @@ our($COMP_RHSBL_KEY_CLIENT) = "rhsbl_client";
|
||||||
our($COMP_RHSBL_KEY_SENDER) = "rhsbl_sender";
|
our($COMP_RHSBL_KEY_SENDER) = "rhsbl_sender";
|
||||||
our($COMP_RHSBL_KEY_RCLIENT) = "rhsbl_reverse_client";
|
our($COMP_RHSBL_KEY_RCLIENT) = "rhsbl_reverse_client";
|
||||||
our($COMP_RHSBL_KEY_HELO) = "rhsbl_helo";
|
our($COMP_RHSBL_KEY_HELO) = "rhsbl_helo";
|
||||||
|
# file items
|
||||||
|
our($COMP_CONF_FILE) = 'cfile|file';
|
||||||
|
our($COMP_CONF_TABLE) = 'ctable|table';
|
||||||
|
our($COMP_LIVE_FILE) = 'lfile';
|
||||||
|
our($COMP_LIVE_TABLE) = 'ltable';
|
||||||
|
our($COMP_TABLES) = qr/^($COMP_CONF_TABLE|$COMP_LIVE_TABLE)$/i;
|
||||||
|
our($COMP_CONF_FILE_TABLE) = qr/^($COMP_CONF_FILE|$COMP_CONF_TABLE):(.+)$/i;
|
||||||
|
our($COMP_LIVE_FILE_TABLE) = qr/^($COMP_LIVE_FILE|$COMP_LIVE_TABLE):(.+)$/i;
|
||||||
# date checks
|
# date checks
|
||||||
our($COMP_DATE) = "date";
|
our($COMP_DATE) = "date";
|
||||||
our($COMP_TIME) = "time";
|
our($COMP_TIME) = "time";
|
||||||
|
@ -136,7 +145,7 @@ our($COMP_VAR) = "[\$][\$]";
|
||||||
# date calculations
|
# date calculations
|
||||||
our($COMP_DATECALC) = "($COMP_DATE|$COMP_TIME|$COMP_DAYS|$COMP_MONTHS)";
|
our($COMP_DATECALC) = "($COMP_DATE|$COMP_TIME|$COMP_DAYS|$COMP_MONTHS)";
|
||||||
# these items allow whitespace-or-comma-separated values
|
# these items allow whitespace-or-comma-separated values
|
||||||
our($COMP_CSV) = "($COMP_NETWORK_CIDRS|$COMP_RBL_KEY|$COMP_RHSBL_KEY|$COMP_RHSBL_KEY_CLIENT|$COMP_RHSBL_KEY_HELO|$COMP_RHSBL_KEY_SENDER|$COMP_RHSBL_KEY_RCLIENT|$COMP_DATECALC)";
|
our($COMP_CSV) = "($COMP_NETWORK_CIDRS|$COMP_RBL_KEY|$COMP_RHSBL_KEY|$COMP_RHSBL_KEY_CLIENT|$COMP_RHSBL_KEY_HELO|$COMP_RHSBL_KEY_SENDER|$COMP_RHSBL_KEY_RCLIENT|$COMP_DATECALC|$COMP_HELO_ADDR|$COMP_NS_ADDR|$COMP_MX_ADDR)";
|
||||||
# dont treat these as lists
|
# dont treat these as lists
|
||||||
our($COMP_SINGLE) = "($COMP_ID|$COMP_ACTION|$COMP_SCORES|$COMP_RBL_CNT|$COMP_RHSBL_CNT)";
|
our($COMP_SINGLE) = "($COMP_ID|$COMP_ACTION|$COMP_SCORES|$COMP_RBL_CNT|$COMP_RHSBL_CNT)";
|
||||||
|
|
||||||
|
@ -148,6 +157,7 @@ our($syslog_options) = "pid";
|
||||||
our($syslog_socktype) = 'unix';
|
our($syslog_socktype) = 'unix';
|
||||||
our($syslog_maxlen) = 0;
|
our($syslog_maxlen) = 0;
|
||||||
our($syslog_safe) = 0;
|
our($syslog_safe) = 0;
|
||||||
|
our($syslog_unsafe_charset) = qr/[^\x20-\x7E]/;
|
||||||
if ( defined $Sys::Syslog::VERSION and $Sys::Syslog::VERSION ge '0.15' ) {
|
if ( defined $Sys::Syslog::VERSION and $Sys::Syslog::VERSION ge '0.15' ) {
|
||||||
# use 'native' when Sys::Syslog >= 0.15
|
# use 'native' when Sys::Syslog >= 0.15
|
||||||
$syslog_socktype = 'native';
|
$syslog_socktype = 'native';
|
||||||
|
@ -203,7 +213,7 @@ use vars qw(
|
||||||
$opt_norulelog $opt_summary $net_interface $net_port
|
$opt_norulelog $opt_summary $net_interface $net_port
|
||||||
$net_user $net_group $net_chroot $net_pid $net_proto
|
$net_user $net_group $net_chroot $net_pid $net_proto
|
||||||
$opt_perfmon $opt_test $opt_verbose $opt_noidlestats
|
$opt_perfmon $opt_test $opt_verbose $opt_noidlestats
|
||||||
$opt_cache_rdomain_only $opt_cache_no_size
|
$opt_cache_rdomain_only $opt_cache_no_size $config_timeout
|
||||||
$opt_cache_no_sender $opt_no_rulestats $opt_kill $opt_hup
|
$opt_cache_no_sender $opt_no_rulestats $opt_kill $opt_hup
|
||||||
$opt_showconfig $opt_stdoutlog $opt_shortlog $dns_async_txt
|
$opt_showconfig $opt_stdoutlog $opt_shortlog $dns_async_txt
|
||||||
$DNS $Reload_Conf $dns_queuesize $dns_retries $dns_timeout
|
$DNS $Reload_Conf $dns_queuesize $dns_retries $dns_timeout
|
||||||
|
@ -239,7 +249,7 @@ sub mylog {
|
||||||
sub mylogs {
|
sub mylogs {
|
||||||
my($prio) = shift(@_);
|
my($prio) = shift(@_);
|
||||||
my($msg) = shift(@_);
|
my($msg) = shift(@_);
|
||||||
$msg =~ s/\%/%%/g;
|
$msg =~ s/\%/%%/g; $msg =~ s/$syslog_unsafe_charset/?/g;
|
||||||
mylog $prio, $msg;
|
mylog $prio, $msg;
|
||||||
};
|
};
|
||||||
#
|
#
|
||||||
|
@ -486,96 +496,160 @@ sub devar_item {
|
||||||
# preparses configuration line for ACL syntax
|
# preparses configuration line for ACL syntax
|
||||||
#
|
#
|
||||||
sub acl_parser {
|
sub acl_parser {
|
||||||
my($myline) = @_;
|
my($file,$num,$myline) = @_;
|
||||||
if ( $myline =~ /^\s*($COMP_ACL[\-\w]+)\s*{\s*(.*?)\s*;\s*}[\s;]*$/ ) {
|
if ( $myline =~ /^\s*($COMP_ACL[\-\w]+)\s*{\s*(.*?)\s*;\s*}[\s;]*$/ ) {
|
||||||
$ACLs{$1} = $2; $myline = "";
|
$ACLs{$1} = $2; $myline = "";
|
||||||
} else {
|
} else {
|
||||||
while ( $myline =~ /($COMP_ACL[\-\w]+)/) {
|
while ( $myline =~ /($COMP_ACL[\-\w]+)/) {
|
||||||
my($acl) = $1; $myline =~ s/\s*$acl\s*/$ACLs{$acl}/g if exists($ACLs{$acl});
|
my($acl) = $1;
|
||||||
|
if ( $acl and defined $ACLs{$acl} ) {
|
||||||
|
$myline =~ s/\s*$acl\s*/$ACLs{$acl}/g;
|
||||||
|
} else {
|
||||||
|
#return "action=note(undefined macro '$acl')";
|
||||||
|
mylogs 'warning', "file $file, ignoring line $num: undefined macro '$acl'";
|
||||||
|
return "";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
return $myline;
|
||||||
return $myline;
|
};
|
||||||
}
|
#
|
||||||
|
# prepares pcre item
|
||||||
|
#
|
||||||
|
sub prepare_pcre {
|
||||||
|
my($item) = shift; undef my $neg;
|
||||||
|
# temporarily remove negation
|
||||||
|
$item = $neg if ($neg = deneg_item($item));
|
||||||
|
# allow // regex
|
||||||
|
$item =~ s/^\/?(.*?)\/?$/$1/;
|
||||||
|
# tested slow
|
||||||
|
#$item = qr/$item/i;
|
||||||
|
# re-enable negation
|
||||||
|
$item = "!!($item)" if $neg;
|
||||||
|
return $item;
|
||||||
|
};
|
||||||
|
#
|
||||||
|
# prepares file item
|
||||||
|
#
|
||||||
|
sub prepare_file {
|
||||||
|
my($forced_reload,$type,$cmp,$file) = @_; my(@result) = (); undef my $fh;
|
||||||
|
my($is_table) = ($type =~ /^$COMP_TABLES$/);
|
||||||
|
unless (-e $file) {
|
||||||
|
mylogs 'warning', "error: $type:$file not found - will be ignored";
|
||||||
|
return @result;
|
||||||
|
};
|
||||||
|
if ( not($forced_reload) and (defined $Config_Cache{$file}{lastread}) and ($Config_Cache{$file}{lastread} > (stat $file)[9]) ) {
|
||||||
|
mylogs $syslog_priority, "$type:$file unchanged - using cached content (mtime: "
|
||||||
|
.(stat $file)[9].", cache: $Config_Cache{$file}{lastread})"
|
||||||
|
if ($opt_verbose > 1);
|
||||||
|
return @{$Config_Cache{$file}{content}};
|
||||||
|
};
|
||||||
|
unless (open ($fh, "<$file")) {
|
||||||
|
mylogs 'warning', "error: could not open $type:$file - $! - will be ignored";
|
||||||
|
return @result;
|
||||||
|
};
|
||||||
|
mylogs $syslog_priority, "reading $type:$file" if ($opt_verbose > 1);
|
||||||
|
while (<$fh>) {
|
||||||
|
chomp;
|
||||||
|
s/#.*//g;
|
||||||
|
next if /^\s*$/;
|
||||||
|
s/\s+[^\s]+$// if $is_table;
|
||||||
|
s/^\s+//; s/\s+$//;
|
||||||
|
push @result, prepare_item($forced_reload, $cmp, $_);
|
||||||
|
}; close ($fh);
|
||||||
|
# update Config_Cache
|
||||||
|
$Config_Cache{$file}{lastread} = time;
|
||||||
|
@{$Config_Cache{$file}{content}} = @result;
|
||||||
|
mylogs $syslog_priority, "read ".($#result + 1)." items from $type:$file" if ($opt_verbose > 1);
|
||||||
|
return @result;
|
||||||
|
};
|
||||||
|
#
|
||||||
|
# prepares ruleset item
|
||||||
|
#
|
||||||
|
sub prepare_item {
|
||||||
|
my($forced_reload,$cmp,$item) = @_; my(@result) = (); undef my $type;
|
||||||
|
if ($item =~ /$COMP_CONF_FILE_TABLE/) {
|
||||||
|
return prepare_file ($forced_reload, $1, $cmp, $2);
|
||||||
|
} elsif ($cmp eq '=~' or $cmp eq '!~') {
|
||||||
|
return $cmp.";".prepare_pcre($item);
|
||||||
|
} else {
|
||||||
|
return $cmp.";".$item;
|
||||||
|
};
|
||||||
|
};
|
||||||
#
|
#
|
||||||
# parses configuration line
|
# parses configuration line
|
||||||
#
|
#
|
||||||
sub parse_config_line {
|
sub parse_config_line {
|
||||||
my($mynum, $myindex, $myline) = @_;
|
my($forced_reload, $myfile, $mynum, $myindex, $myline) = @_;
|
||||||
my(%myrule) = ();
|
my(%myrule) = ();
|
||||||
my($mykey, $myvalue, $mycomp, $neg);
|
my($mykey, $myvalue, $mycomp);
|
||||||
|
eval {
|
||||||
if ( $myline = acl_parser ($myline) ) {
|
local $SIG{'__DIE__'};
|
||||||
unless ( $myline =~ /^\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+(;\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+)*[;\s]*$/ ) {
|
local $SIG{'ALRM'} = sub { $myline =~ s/[ \t][ \t]*/ /g; mylogs 'warning', "timeout after ".$config_timeout."s at parsing Rule $myindex ($myfile line $mynum): \"$myline\""; %myrule = (); die };
|
||||||
warn "warning: ignoring invalid line ".$mynum.": \"".$myline."\"";
|
my $prevalert = alarm($config_timeout) if $config_timeout;
|
||||||
} else {
|
if ( $myline = acl_parser ($myfile, $mynum, $myline) ) {
|
||||||
# separate items
|
unless ( $myline =~ /^\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+(;\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+)*[;\s]*$/ ) {
|
||||||
foreach (split ";", $myline) {
|
mylogs 'warning', "ignoring invalid $myfile line ".$mynum.": \"".$myline."\"";
|
||||||
# remove whitespaces around
|
} else {
|
||||||
s/^\s*(.*?)\s*($COMP_SEPARATOR)\s*(.*?)\s*$/$1$2$3/;
|
# separate items
|
||||||
( ($mycomp = $2) =~ /^([\<\>\~])=$/ ) and $mycomp = "=$1";
|
foreach (split ";", $myline) {
|
||||||
($mykey, $myvalue) = split /$COMP_SEPARATOR/, $_, 2;
|
# remove whitespaces around
|
||||||
if ($mykey =~ /^$COMP_CSV$/) {
|
s/^\s*(.*?)\s*($COMP_SEPARATOR)\s*(.*?)\s*$/$1$2$3/;
|
||||||
$myvalue =~ s/\s*-\s*/-/g if ($mykey =~ /^$COMP_DATECALC$/);
|
( ($mycomp = $2) =~ /^([\<\>\~])=$/ ) and $mycomp = "=$1";
|
||||||
$myvalue =~ s/\s*,\s*/,/g;
|
($mykey, $myvalue) = split /$COMP_SEPARATOR/, $_, 2;
|
||||||
map { push ( @{$myrule{$mykey}}, $mycomp.";".$_ ) } ( split ",", $myvalue );
|
if ($mykey =~ /^$COMP_SINGLE$/) {
|
||||||
} elsif ($mykey =~ /^$COMP_SINGLE$/) {
|
mylogs 'notice', "notice: Rule $myindex ($myfile line $mynum):"
|
||||||
mylogs "notice", "warning: Rule $myindex (line $mynum):"
|
." overriding $mykey=\"".$myrule{$mykey}."\""
|
||||||
." overriding $mykey=\"".$myrule{$mykey}."\""
|
." with $mykey=\"$myvalue\""
|
||||||
." with $mykey=\"$myvalue\""
|
if (defined $myrule{$mykey});
|
||||||
if (defined $myrule{$mykey});
|
$myrule{$mykey} = $myvalue;
|
||||||
$myrule{$mykey} = $myvalue;
|
} elsif ($mykey =~ /^$COMP_CSV$/) {
|
||||||
} else {
|
$myvalue =~ s/\s*,\s*/,/g;
|
||||||
if ( $mycomp eq '=~' or $mycomp eq '!~') {
|
map { push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $_) } ( split /\s*,\s*/, $myvalue );
|
||||||
# temporarily remove negation
|
} else {
|
||||||
$myvalue = $neg if ($neg = deneg_item($myvalue));
|
push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $myvalue);
|
||||||
# allow // regex
|
|
||||||
$myvalue =~ s/^\/?(.*?)\/?$/$1/;
|
|
||||||
# tested, slower
|
|
||||||
#$myvalue = qr/$myvalue/i;
|
|
||||||
# re-enable negation
|
|
||||||
$myvalue = "!!($myvalue)" if $neg;
|
|
||||||
};
|
};
|
||||||
push ( @{$myrule{$mykey}}, $mycomp.";".$myvalue );
|
|
||||||
};
|
};
|
||||||
|
unless (exists($myrule{$COMP_ACTION})) {
|
||||||
|
mylogs 'warning', "Rule ".$myindex." ($myfile line ".$mynum."): contains no action and will be ignored";
|
||||||
|
return (%myrule = ());
|
||||||
|
};
|
||||||
|
unless (exists($myrule{$COMP_ID})) {
|
||||||
|
$myrule{$COMP_ID} = "R-".$myindex;
|
||||||
|
mylogs 'notice', "notice: Rule $myindex ($myfile line $mynum): contains no rule identifier - will use \"$myrule{id}\"" if $opt_verbose;
|
||||||
|
};
|
||||||
|
mylogs $syslog_priority, "loaded: Rule $myindex ($myfile line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
|
||||||
};
|
};
|
||||||
unless (exists($myrule{$COMP_ACTION})) {
|
};
|
||||||
$myrule{$COMP_ACTION} = "WARN rule found but no action was defined";
|
alarm($prevalert) if $config_timeout;
|
||||||
mylogs "notice", "warning: Rule ".$myindex." (line ".$mynum."): contains no action - default will be used";
|
|
||||||
};
|
|
||||||
unless (exists($myrule{$COMP_ID})) {
|
|
||||||
$myrule{$COMP_ID} = "R-".$myindex;
|
|
||||||
mylogs "notice", "notice: Rule $myindex (line $mynum): contains no rule identifier - will use \"$myrule{id}\"" if $opt_verbose;
|
|
||||||
};
|
|
||||||
mylogs $syslog_priority, "loaded: Rule $myindex (line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
|
|
||||||
};
|
};
|
||||||
};
|
return %myrule;
|
||||||
return %myrule;
|
};
|
||||||
}
|
|
||||||
#
|
#
|
||||||
# parses configuration file
|
# parses configuration file
|
||||||
#
|
#
|
||||||
sub read_config_file {
|
sub read_config_file {
|
||||||
my($myindex, $myfile) = @_;
|
my($forced_reload, $myindex, $myfile) = @_;
|
||||||
my(%myrule, @myruleset) = ();
|
my(%myrule, @myruleset) = ();
|
||||||
my($mybuffer) = "";
|
my($mybuffer) = ""; undef my $fh;
|
||||||
|
|
||||||
unless (-e $myfile) {
|
unless (-e $myfile) {
|
||||||
warn "error: file ".$myfile." not found - file will be ignored";
|
warn "error: file ".$myfile." not found - file will be ignored";
|
||||||
} else {
|
} else {
|
||||||
unless (open (IN, "<$myfile")) {
|
unless (open ($fh, "<$myfile")) {
|
||||||
warn "error: could not open ".$myfile." - file will be ignored";
|
warn "error: could not open ".$myfile." - $! - file will be ignored";
|
||||||
} else {
|
} else {
|
||||||
mylogs $syslog_priority, "reading file $myfile" if $opt_verbose;
|
mylogs $syslog_priority, "reading file $myfile" if $opt_verbose;
|
||||||
while (<IN>) {
|
while (<$fh>) {
|
||||||
chomp;
|
chomp;
|
||||||
s/(\"|#.*)//g;
|
s/(\"|#.*)//g;
|
||||||
next if /^\s*$/;
|
next if /^\s*$/;
|
||||||
if (/(.*)\\\s*$/) { $mybuffer = $mybuffer.$1; next; };
|
if (/(.*)\\\s*$/) { $mybuffer = $mybuffer.$1; next; };
|
||||||
%myrule = parse_config_line ($., ($#myruleset+$myindex+1), $mybuffer.$_);
|
%myrule = parse_config_line ($forced_reload, $myfile, $., ($#myruleset+$myindex+1), $mybuffer.$_);
|
||||||
push ( @myruleset, { %myrule } ) if (%myrule);
|
push ( @myruleset, { %myrule } ) if (%myrule);
|
||||||
$mybuffer = "";
|
$mybuffer = "";
|
||||||
};
|
};
|
||||||
close (IN);
|
close ($fh);
|
||||||
mylogs $syslog_priority, "loaded: Rules $myindex - ".($myindex + $#myruleset)." from file \"$myfile\"" if $opt_verbose;
|
mylogs $syslog_priority, "loaded: Rules $myindex - ".($myindex + $#myruleset)." from file \"$myfile\"" if $opt_verbose;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -585,6 +659,7 @@ sub read_config_file {
|
||||||
# reads all configuration items
|
# reads all configuration items
|
||||||
#
|
#
|
||||||
sub read_config {
|
sub read_config {
|
||||||
|
my($forced_reload) = shift;
|
||||||
my(%myrule, @myruleset) = ();
|
my(%myrule, @myruleset) = ();
|
||||||
my($mytype,$myitem,$config);
|
my($mytype,$myitem,$config);
|
||||||
|
|
||||||
|
@ -595,17 +670,17 @@ sub read_config {
|
||||||
for $config (@Configs) {
|
for $config (@Configs) {
|
||||||
($mytype,$myitem) = split '::', $config;
|
($mytype,$myitem) = split '::', $config;
|
||||||
if ($mytype eq "r" or $mytype eq "rule") {
|
if ($mytype eq "r" or $mytype eq "rule") {
|
||||||
%myrule = parse_config_line (0, ($#Rules + 1), $myitem);
|
%myrule = parse_config_line ($forced_reload, 'RULE', 0, ($#Rules + 1), $myitem);
|
||||||
push ( @Rules, { %myrule } ) if (%myrule);
|
push ( @Rules, { %myrule } ) if (%myrule);
|
||||||
} elsif ($mytype eq "f" or $mytype eq "file") {
|
} elsif ($mytype eq "f" or $mytype eq "file") {
|
||||||
if ( (defined $Config_Cache{$myitem}{lastread}) and ($Config_Cache{$myitem}{lastread} > (stat $myitem)[9]) ) {
|
if ( not($forced_reload) and (defined $Config_Cache{$myitem}{lastread}) and ($Config_Cache{$myitem}{lastread} > (stat $myitem)[9]) ) {
|
||||||
mylogs $syslog_priority,
|
mylogs $syslog_priority,
|
||||||
"file \"$myitem\" unchanged - using cached ruleset (mtime: ".(stat $myitem)[9].",
|
"file \"$myitem\" unchanged - using cached ruleset (mtime: ".(stat $myitem)[9].",
|
||||||
cache: $Config_Cache{$myitem}{lastread})"
|
cache: $Config_Cache{$myitem}{lastread})"
|
||||||
if $opt_verbose;
|
if $opt_verbose;
|
||||||
push ( @Rules, @{$Config_Cache{$myitem}{ruleset}} );
|
push ( @Rules, @{$Config_Cache{$myitem}{ruleset}} );
|
||||||
} else {
|
} else {
|
||||||
@myruleset = read_config_file (($#Rules+1), $myitem);
|
@myruleset = read_config_file ($forced_reload, ($#Rules+1), $myitem);
|
||||||
if (@myruleset) {
|
if (@myruleset) {
|
||||||
push ( @Rules, @myruleset );
|
push ( @Rules, @myruleset );
|
||||||
$Config_Cache{$myitem}{lastread} = time;
|
$Config_Cache{$myitem}{lastread} = time;
|
||||||
|
@ -614,8 +689,12 @@ sub read_config {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
# update Rule by ID hash
|
if ($#Rules < 0) {
|
||||||
map { $Rule_by_ID{$Rules[$_]{$COMP_ID}} = $_ } (0 .. $#Rules);
|
mylogs 'warning', "critical: no rules found - i feel useless (have you set -f or -r?)";
|
||||||
|
} else {
|
||||||
|
# update Rule by ID hash
|
||||||
|
map { $Rule_by_ID{$Rules[$_]{$COMP_ID}} = $_ } (0 .. $#Rules);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#
|
#
|
||||||
# displays configuration
|
# displays configuration
|
||||||
|
@ -931,7 +1010,7 @@ sub postfwd_items {
|
||||||
my($myresult) = undef;
|
my($myresult) = undef;
|
||||||
mylogs $syslog_priority, "type numeric : \"$myitem\" \"$cmp\" \"$val\"" if ($opt_verbose > 1);
|
mylogs $syslog_priority, "type numeric : \"$myitem\" \"$cmp\" \"$val\"" if ($opt_verbose > 1);
|
||||||
$myitem ||= "0"; $val ||= "0";
|
$myitem ||= "0"; $val ||= "0";
|
||||||
if ( ($cmp eq '==') or ($cmp eq '=') ) {
|
if ($cmp eq '==') {
|
||||||
$myresult = ($myitem == $val);
|
$myresult = ($myitem == $val);
|
||||||
} elsif ($cmp eq '=<') {
|
} elsif ($cmp eq '=<') {
|
||||||
$myresult = ($myitem <= $val);
|
$myresult = ($myitem <= $val);
|
||||||
|
@ -968,7 +1047,7 @@ sub postfwd_items {
|
||||||
my($cmp,$val,$myitem,%request) = @_;
|
my($cmp,$val,$myitem,%request) = @_;
|
||||||
my($myresult) = undef;
|
my($myresult) = undef;
|
||||||
my($imon) = (split (',', $myitem))[4]; $imon ||= 0;
|
my($imon) = (split (',', $myitem))[4]; $imon ||= 0;
|
||||||
my($rmin,$rmax) = split ('-', $val);
|
my($rmin,$rmax) = split (/\s*-\s*/, $val);
|
||||||
$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $months{$rmin}) : $imon;
|
$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $months{$rmin}) : $imon;
|
||||||
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $months{$rmax}) : (($val =~ /-/) ? $imon : $rmin);
|
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $months{$rmax}) : (($val =~ /-/) ? $imon : $rmin);
|
||||||
mylogs $syslog_priority, "type months : \"$imon\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
mylogs $syslog_priority, "type months : \"$imon\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
||||||
|
@ -981,7 +1060,7 @@ sub postfwd_items {
|
||||||
my($cmp,$val,$myitem,%request) = @_;
|
my($cmp,$val,$myitem,%request) = @_;
|
||||||
my($myresult) = undef;
|
my($myresult) = undef;
|
||||||
my($iday) = (split (',', $myitem))[6]; $iday ||= 0;
|
my($iday) = (split (',', $myitem))[6]; $iday ||= 0;
|
||||||
my($rmin,$rmax) = split ('-', $val);
|
my($rmin,$rmax) = split (/\s*-\s*/, $val);
|
||||||
$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $weekdays{$rmin}) : $iday;
|
$rmin = ($rmin) ? (($rmin =~ /^\d$/) ? $rmin : $weekdays{$rmin}) : $iday;
|
||||||
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $weekdays{$rmax}) : (($val =~ /-/) ? $iday : $rmin);
|
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $weekdays{$rmax}) : (($val =~ /-/) ? $iday : $rmin);
|
||||||
mylogs $syslog_priority, "type days : \"$iday\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
mylogs $syslog_priority, "type days : \"$iday\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
||||||
|
@ -994,7 +1073,7 @@ sub postfwd_items {
|
||||||
my($cmp,$val,$myitem,%request) = @_;
|
my($cmp,$val,$myitem,%request) = @_;
|
||||||
my($myresult) = undef;
|
my($myresult) = undef;
|
||||||
my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
|
my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
|
||||||
my($rmin,$rmax) = split ('-', $val);
|
my($rmin,$rmax) = split (/\s*-\s*/, $val);
|
||||||
my($idat) = ($iyear + 1900) . ((($imon+1) < 10) ? '0'.($imon+1) : ($imon+1)) . (($iday < 10) ? '0'.$iday : $iday);
|
my($idat) = ($iyear + 1900) . ((($imon+1) < 10) ? '0'.($imon+1) : ($imon+1)) . (($iday < 10) ? '0'.$iday : $iday);
|
||||||
$rmin = ($rmin) ? join ('', reverse split ('\.', $rmin)) : $idat;
|
$rmin = ($rmin) ? join ('', reverse split ('\.', $rmin)) : $idat;
|
||||||
$rmax = ($rmax) ? join ('', reverse split ('\.', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
$rmax = ($rmax) ? join ('', reverse split ('\.', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
||||||
|
@ -1008,7 +1087,7 @@ sub postfwd_items {
|
||||||
my($cmp,$val,$myitem,%request) = @_;
|
my($cmp,$val,$myitem,%request) = @_;
|
||||||
my($myresult) = undef;
|
my($myresult) = undef;
|
||||||
my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
|
my($isec,$imin,$ihour,$iday,$imon,$iyear) = split (',', $myitem);
|
||||||
my($rmin,$rmax) = split ('-', $val);
|
my($rmin,$rmax) = split (/\s*-\s*/, $val);
|
||||||
my($idat) = (($ihour < 10) ? '0'.$ihour : $ihour) . (($imin < 10) ? '0'.$imin : $imin) . (($isec < 10) ? '0'.$isec : $isec);
|
my($idat) = (($ihour < 10) ? '0'.$ihour : $ihour) . (($imin < 10) ? '0'.$imin : $imin) . (($isec < 10) ? '0'.$isec : $isec);
|
||||||
$rmin = ($rmin) ? join ('', split ('\:', $rmin)) : $idat;
|
$rmin = ($rmin) ? join ('', split ('\:', $rmin)) : $idat;
|
||||||
$rmax = ($rmax) ? join ('', split ('\:', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
$rmax = ($rmax) ? join ('', split ('\:', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
||||||
|
@ -1229,7 +1308,7 @@ sub postfwd_items {
|
||||||
type => $mycmd,
|
type => $mycmd,
|
||||||
maxcount => $ratecount,
|
maxcount => $ratecount,
|
||||||
ttl => $ratetime,
|
ttl => $ratetime,
|
||||||
count => ( ($mycmd eq 'size') ? $request{size} : 1 ),
|
count => ( ($mycmd eq 'size') ? $request{size} : (($mycmd eq 'rcpt') ? $request{recipient_count} : 1 ) ),
|
||||||
time => $now,
|
time => $now,
|
||||||
rule => $Rules[$index]{$COMP_ID},
|
rule => $Rules[$index]{$COMP_ID},
|
||||||
action => $ratecmd,
|
action => $ratecmd,
|
||||||
|
@ -1246,6 +1325,8 @@ sub postfwd_items {
|
||||||
},
|
},
|
||||||
# size() command
|
# size() command
|
||||||
"size" => sub { return &{$postfwd_actions{rate}}(@_); },
|
"size" => sub { return &{$postfwd_actions{rate}}(@_); },
|
||||||
|
# rcpt() command
|
||||||
|
"rcpt" => sub { return &{$postfwd_actions{rate}}(@_); },
|
||||||
# wait() command
|
# wait() command
|
||||||
"wait" => sub {
|
"wait" => sub {
|
||||||
my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
||||||
|
@ -1309,7 +1390,7 @@ sub postfwd_items {
|
||||||
mylogs ('notice', "rule: $index got invalid answer '$sendstr' from $myarg");
|
mylogs ('notice', "rule: $index got invalid answer '$sendstr' from $myarg");
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mylogs ('notice', "Could not open socket to '$myarg'");
|
mylogs ('notice', "Could not open socket to '$myarg' - $!");
|
||||||
};
|
};
|
||||||
return ($stop,$index,$myaction,$myline,%request);
|
return ($stop,$index,$myaction,$myline,%request);
|
||||||
},
|
},
|
||||||
|
@ -1356,19 +1437,24 @@ sub get_plugins {
|
||||||
# use: compare_item ( $TYPE, $RULEITEM, $MINIMUMHITS, $REQUESTITEM, %REQUEST, %REQUESTINFO );
|
# use: compare_item ( $TYPE, $RULEITEM, $MINIMUMHITS, $REQUESTITEM, %REQUEST, %REQUESTINFO );
|
||||||
#
|
#
|
||||||
sub compare_item {
|
sub compare_item {
|
||||||
my($mykey,$mymask,$mymin,$myitem, %request) = @_;
|
my($mykey,$mymask,$mymin,$myitem,%request) = @_;
|
||||||
my($val,$var,$cmp,$neg,$myresult,$postfwd_compare_proc);
|
my($val,$var,$cmp,$neg,$myresult,$postfwd_compare_proc);
|
||||||
my($rcount) = 0;
|
my($rcount) = 0;
|
||||||
$mymin ||= 1;
|
$mymin ||= 1;
|
||||||
|
|
||||||
#
|
|
||||||
# determine the right compare function
|
# determine the right compare function
|
||||||
$postfwd_compare_proc = (defined $postfwd_compare{$mykey}) ? $mykey : "default";
|
$postfwd_compare_proc = (defined $postfwd_compare{$mykey}) ? $mykey : "default";
|
||||||
#
|
# save list due to possible modification
|
||||||
|
my @items = @{$mymask};
|
||||||
# now compare request to every single item
|
# now compare request to every single item
|
||||||
ITEM: foreach (@{$mymask}) {
|
ITEM: foreach (@items) {
|
||||||
($cmp, $val) = split ";";
|
($cmp, $val) = split ";";
|
||||||
next ITEM unless ($cmp and $val and $mykey);
|
next ITEM unless ($cmp and $val and $mykey);
|
||||||
|
# prepare_file
|
||||||
|
if ($val =~ /$COMP_LIVE_FILE_TABLE/) {
|
||||||
|
push @items, prepare_file (0, $1, $cmp, $2);
|
||||||
|
next ITEM;
|
||||||
|
};
|
||||||
mylogs $syslog_priority, "compare $mykey: \"$myitem\" \"$cmp\" \"$val\"" if ($opt_verbose > 1);
|
mylogs $syslog_priority, "compare $mykey: \"$myitem\" \"$cmp\" \"$val\"" if ($opt_verbose > 1);
|
||||||
$val = $neg if ($neg = deneg_item($val));
|
$val = $neg if ($neg = deneg_item($val));
|
||||||
mylogs $syslog_priority, "deneg $mykey: \"$myitem\" \"$cmp\" \"$val\"" if ($neg and ($opt_verbose > 1));
|
mylogs $syslog_priority, "deneg $mykey: \"$myitem\" \"$cmp\" \"$val\"" if ($neg and ($opt_verbose > 1));
|
||||||
|
@ -1695,7 +1781,7 @@ sub smtpd_access_policy {
|
||||||
if ( $Reload_Conf ) {
|
if ( $Reload_Conf ) {
|
||||||
undef $Reload_Conf;
|
undef $Reload_Conf;
|
||||||
show_stats;
|
show_stats;
|
||||||
read_config;
|
read_config(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
# clear dnsbl timeout counters
|
# clear dnsbl timeout counters
|
||||||
|
@ -1724,7 +1810,8 @@ sub smtpd_access_policy {
|
||||||
next RATES unless ( $request{$checkreq} and (defined $Rates{$request{$checkreq}}) );
|
next RATES unless ( $request{$checkreq} and (defined $Rates{$request{$checkreq}}) );
|
||||||
if ( ($now - $Rates{$request{$checkreq}}{"time"}) > $Rates{$request{$checkreq}}{ttl} ) {
|
if ( ($now - $Rates{$request{$checkreq}}{"time"}) > $Rates{$request{$checkreq}}{ttl} ) {
|
||||||
# renew rate
|
# renew rate
|
||||||
$Rates{$request{$checkreq}}{count} = ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} : 1 );
|
$Rates{$request{$checkreq}}{count} = ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} :
|
||||||
|
(($Rates{$request{$checkreq}}{type} eq 'rcpt') ? $request{recipient_count} : 1 ) );
|
||||||
$Rates{$request{$checkreq}}{"time"} = $now;
|
$Rates{$request{$checkreq}}{"time"} = $now;
|
||||||
mylogs $syslog_priority, "[RATE] renewing rate object ".$request{$checkreq}
|
mylogs $syslog_priority, "[RATE] renewing rate object ".$request{$checkreq}
|
||||||
." [type: ".$Rates{$request{$checkreq}}{type}
|
." [type: ".$Rates{$request{$checkreq}}{type}
|
||||||
|
@ -1733,7 +1820,8 @@ sub smtpd_access_policy {
|
||||||
if ($opt_verbose > 1);
|
if ($opt_verbose > 1);
|
||||||
} else {
|
} else {
|
||||||
# increase rate
|
# increase rate
|
||||||
$Rates{$request{$checkreq}}{count} += ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} : 1 );
|
$Rates{$request{$checkreq}}{count} += ( ($Rates{$request{$checkreq}}{type} eq 'size') ? $request{size} :
|
||||||
|
(($Rates{$request{$checkreq}}{type} eq 'rcpt') ? $request{recipient_count} : 1 ) );
|
||||||
mylogs $syslog_priority, "[RATE] increasing rate object ".$request{$checkreq}
|
mylogs $syslog_priority, "[RATE] increasing rate object ".$request{$checkreq}
|
||||||
." to ".$Rates{$request{$checkreq}}{count}
|
." to ".$Rates{$request{$checkreq}}{count}
|
||||||
." [type: ".$Rates{$request{$checkreq}}{type}
|
." [type: ".$Rates{$request{$checkreq}}{type}
|
||||||
|
@ -1829,10 +1917,10 @@ sub smtpd_access_policy {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
# refresh config if '-I' was set
|
# refresh config if '-I' was set
|
||||||
read_config if $opt_instantconfig;
|
read_config(0) if $opt_instantconfig;
|
||||||
|
|
||||||
if ($#Rules < 0) {
|
if ($#Rules < 0) {
|
||||||
warn "critical: no rules found - i feel useless (have you set -f or -r?)";
|
mylogs 'warning', "critical: no rules found - i feel useless (have you set -f or -r?)";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -1918,6 +2006,33 @@ sub smtpd_access_policy {
|
||||||
return $myaction;
|
return $myaction;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# process delegation protocol input
|
||||||
|
sub process_input {
|
||||||
|
my($client,$msg,$attr) = @_;
|
||||||
|
# remember argument=value
|
||||||
|
if ( $msg =~ /^([^=]{1,512})=(.{0,512})/ ) {
|
||||||
|
$$attr{$1} = $2;
|
||||||
|
# evaluate request
|
||||||
|
} elsif ( $msg eq '' ) {
|
||||||
|
map { mylogs $syslog_priority, "Attribute: $_=$$attr{$_}" } (keys %$attr) if ($opt_verbose > 1);
|
||||||
|
unless ( (defined $$attr{request}) and ($$attr{request} eq "smtpd_access_policy") ) {
|
||||||
|
mylogs 'warning', "Ignoring unrecognized request type: '".((defined $$attr{request}) ? substr($$attr{request},0,100) : '')."'";
|
||||||
|
} else {
|
||||||
|
my $action = smtpd_access_policy(%$attr) || $default_action;
|
||||||
|
mylogs $syslog_priority, "Action: $action" if ($opt_verbose > 1);
|
||||||
|
if ($client) {
|
||||||
|
print $client ("action=$action\n\n");
|
||||||
|
} else {
|
||||||
|
print STDOUT ("action=$action\n\n");
|
||||||
|
};
|
||||||
|
%$attr = ();
|
||||||
|
};
|
||||||
|
# unknown command
|
||||||
|
} else {
|
||||||
|
mylogs 'warning', "Ignoring garbage '".substr($msg, 0, 100)."'";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#### MAIN ####
|
#### MAIN ####
|
||||||
|
|
||||||
|
@ -1970,6 +2085,7 @@ GetOptions ( "term|kill|stop|k" => \$opt_kill,
|
||||||
'noidlestats' => \$opt_noidlestats,
|
'noidlestats' => \$opt_noidlestats,
|
||||||
'no-idlestats' => \$opt_noidlestats,
|
'no-idlestats' => \$opt_noidlestats,
|
||||||
's|scores=s' => \%opt_scores,
|
's|scores=s' => \%opt_scores,
|
||||||
|
'config_timeout=i' => \$config_timeout,
|
||||||
'f|file=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
'f|file=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
||||||
'r|rule=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
'r|rule=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
||||||
'plugins=s' => \@Plugins,
|
'plugins=s' => \@Plugins,
|
||||||
|
@ -1983,6 +2099,7 @@ GetOptions ( "term|kill|stop|k" => \$opt_kill,
|
||||||
) or pod2usage (-msg => "\nPlease see \"".$NAME." -m\" for detailed instructions.\n", -verbose => 1);
|
) or pod2usage (-msg => "\nPlease see \"".$NAME." -m\" for detailed instructions.\n", -verbose => 1);
|
||||||
|
|
||||||
$opt_verbose = 0 unless $opt_verbose;
|
$opt_verbose = 0 unless $opt_verbose;
|
||||||
|
$opt_stdoutlog = 1 if ($opt_kill or $opt_hup or $opt_showconfig);
|
||||||
|
|
||||||
# terminate at -k or --kill
|
# terminate at -k or --kill
|
||||||
if ($opt_kill) {
|
if ($opt_kill) {
|
||||||
|
@ -2002,7 +2119,7 @@ openlog $syslog_name, $syslog_options, $syslog_facility;
|
||||||
mylogs "notice", $NAME." ".$VERSION." starting" if $opt_daemon;
|
mylogs "notice", $NAME." ".$VERSION." starting" if $opt_daemon;
|
||||||
|
|
||||||
# read configuration
|
# read configuration
|
||||||
read_config;
|
read_config(1);
|
||||||
if ($opt_showconfig) {
|
if ($opt_showconfig) {
|
||||||
show_config;
|
show_config;
|
||||||
exit 1;
|
exit 1;
|
||||||
|
@ -2041,6 +2158,7 @@ $net_pid ||= $def_net_pid;
|
||||||
$dns_queuesize ||= $def_dns_queuesize;
|
$dns_queuesize ||= $def_dns_queuesize;
|
||||||
$dns_retries ||= $def_dns_retries;
|
$dns_retries ||= $def_dns_retries;
|
||||||
$dns_timeout ||= $def_dns_timeout;
|
$dns_timeout ||= $def_dns_timeout;
|
||||||
|
$config_timeout ||= $def_config_timeout;
|
||||||
$syslog_name ||= $NAME;
|
$syslog_name ||= $NAME;
|
||||||
$net_interface = ( $net_interface =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ) ? $1 : $def_net_interface;
|
$net_interface = ( $net_interface =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/ ) ? $1 : $def_net_interface;
|
||||||
$net_port = ( $net_port =~ /^(\d+|[-\|\@\/\w. ]+)$/ ) ? $1 : $def_net_port;
|
$net_port = ( $net_port =~ /^(\d+|[-\|\@\/\w. ]+)$/ ) ? $1 : $def_net_port;
|
||||||
|
@ -2053,6 +2171,7 @@ $dns_queuesize = ( $dns_queuesize =~ /^(\d+)$/ ) ? $1 : $dns_queuesize;
|
||||||
$dns_retries = ( $dns_retries =~ /^(\d+)$/ ) ? $1 : $dns_retries;
|
$dns_retries = ( $dns_retries =~ /^(\d+)$/ ) ? $1 : $dns_retries;
|
||||||
$dns_timeout = ( $dns_timeout =~ /^(\d+)$/ ) ? $1 : $dns_timeout;
|
$dns_timeout = ( $dns_timeout =~ /^(\d+)$/ ) ? $1 : $dns_timeout;
|
||||||
$syslog_name = ( $syslog_name =~ /^(.+)$/ ) ? $1 : $NAME;
|
$syslog_name = ( $syslog_name =~ /^(.+)$/ ) ? $1 : $NAME;
|
||||||
|
$config_timeout = ( $config_timeout =~ /^(\d+)$/ ) ? $1 : $def_config_timeout;
|
||||||
|
|
||||||
# Unbuffer standard output.
|
# Unbuffer standard output.
|
||||||
select((select(STDOUT), $| = 1)[0]);
|
select((select(STDOUT), $| = 1)[0]);
|
||||||
|
@ -2144,26 +2263,7 @@ if ($opt_daemon) {
|
||||||
# check request line and print output
|
# check request line and print output
|
||||||
next unless defined $1;
|
next unless defined $1;
|
||||||
$request = $1;
|
$request = $1;
|
||||||
if ($request =~ /([^=]+)=(.*)/) {
|
process_input ($client, $request, \%myattr);
|
||||||
$myattr{substr($1, 0, 512)} = substr($2, 0, 512);
|
|
||||||
} elsif ($request eq '') {
|
|
||||||
if ($opt_verbose > 1) {
|
|
||||||
for (keys %myattr) {
|
|
||||||
mylogs $syslog_priority, "Client: $client Attribute: $_=$myattr{$_}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
unless ( (defined $myattr{request}) and ($myattr{request} eq "smtpd_access_policy") ) {
|
|
||||||
warn "ignoring unrecognized request type: '".($myattr{request} || '')."'";
|
|
||||||
} else {
|
|
||||||
my($action) = substr ( smtpd_access_policy(%myattr), 0, $reply_maxlen ) if $reply_maxlen;
|
|
||||||
mylogs $syslog_priority, "Client: $client Action: $action" if $opt_verbose;
|
|
||||||
print $client "action=$action\n\n";
|
|
||||||
$Counter_Requests++; $Counter_Interval++;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
chop $request if $request;
|
|
||||||
warn "error: ignoring garbage".( ($opt_verbose) ? " from $client" : "")." \"".$request."\"";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2178,26 +2278,7 @@ if ($opt_daemon) {
|
||||||
s/^([^\r\n]*)\r?\n//;
|
s/^([^\r\n]*)\r?\n//;
|
||||||
next unless defined $1;
|
next unless defined $1;
|
||||||
$request = $1;
|
$request = $1;
|
||||||
if ($request =~ /([^=]+)=(.*)/) {
|
process_input (undef, $request, \%myattr);
|
||||||
$myattr{substr($1, 0, 512)} = substr($2, 0, 512);
|
|
||||||
} elsif ($request eq '') {
|
|
||||||
if ($opt_verbose > 1) {
|
|
||||||
for (keys %myattr) {
|
|
||||||
mylogs $syslog_priority, "Attribute: $_=$myattr{$_}";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
unless ( (defined $myattr{request}) and ($myattr{request} eq "smtpd_access_policy") ) {
|
|
||||||
warn "ignoring unrecognized request type: '".($myattr{request} || '')."'";
|
|
||||||
} else {
|
|
||||||
my($action) = substr ( smtpd_access_policy(%myattr), 0, $reply_maxlen ) if $reply_maxlen;
|
|
||||||
mylogs $syslog_priority, "Action: $action" if $opt_verbose;
|
|
||||||
myprint "action=$action\n\n";
|
|
||||||
$Counter_Requests++; $Counter_Interval++;
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
chop $request if $request;
|
|
||||||
warn "error: ignoring garbage \"".$request."\"";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# finishing
|
# finishing
|
||||||
|
@ -2264,6 +2345,7 @@ postfwd [OPTIONS] [SOURCE1, SOURCE2, ...]
|
||||||
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
--dns_max_ns_lookups max names to look up with sender_ns_addrs
|
||||||
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
--dns_max_mx_lookups max names to look up with sender_mx_addrs
|
||||||
-I, --instantcfg re-reads rulefiles for every new request
|
-I, --instantcfg re-reads rulefiles for every new request
|
||||||
|
--config_timeout <i> parser timeout in seconds
|
||||||
|
|
||||||
Informational (use only at command-line!):
|
Informational (use only at command-line!):
|
||||||
-C, --showconfig shows ruleset summary, -v for verbose
|
-C, --showconfig shows ruleset summary, -v for verbose
|
||||||
|
@ -2402,12 +2484,18 @@ Rules can span multiple lines by adding a trailing backslash "\" character:
|
||||||
|
|
||||||
helo_address - postfwd tries to look up the helo_name. use
|
helo_address - postfwd tries to look up the helo_name. use
|
||||||
helo_address=!!(0.0.0.0/0) to check for unknown.
|
helo_address=!!(0.0.0.0/0) to check for unknown.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
sender_ns_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_ns_addrs of the nameservers for the sender domain part.
|
sender_ns_addrs of the nameservers for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
sender_mx_names, - postfwd tries to look up the names/ip addresses
|
||||||
sender_mx_addrs of the mx records for the sender domain part.
|
sender_mx_addrs of the mx records for the sender domain part.
|
||||||
|
Please do not use this for positive access control
|
||||||
|
(whitelisting), as it might be forged.
|
||||||
|
|
||||||
version - postfwd version, contains "postfwd n.nn"
|
version - postfwd version, contains "postfwd n.nn"
|
||||||
this enables version based checks in your rulesets
|
this enables version based checks in your rulesets
|
||||||
|
@ -2494,6 +2582,10 @@ Any item can be negated by preceeding '!!' to it, e.g.:
|
||||||
|
|
||||||
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please
|
id=TLS001 ; hostname=!!^secure\.trust\.local$ ; action=REJECT only secure.trust.local please
|
||||||
|
|
||||||
|
or using the right compare operator:
|
||||||
|
|
||||||
|
id=USER01 ; sasl_username !~ /^(bob|alice)$/ ; action=REJECT who is that?
|
||||||
|
|
||||||
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
|
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
|
||||||
|
|
||||||
id=USER01 ; sasl_username=!!( (bob|alice) ) ; action=REJECT who is that?
|
id=USER01 ; sasl_username=!!( (bob|alice) ) ; action=REJECT who is that?
|
||||||
|
@ -2508,6 +2600,92 @@ This is only valid for PCRE values (see list above). The comparison will be perf
|
||||||
Use the '-vv' option to debug.
|
Use the '-vv' option to debug.
|
||||||
|
|
||||||
|
|
||||||
|
=head2 FILES
|
||||||
|
|
||||||
|
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:
|
||||||
|
|
||||||
|
id=R001 ; ccert_fingerprint==file:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
|
||||||
|
postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:
|
||||||
|
|
||||||
|
# client1
|
||||||
|
11:22:33:44:55:66:77:88:99
|
||||||
|
# client2
|
||||||
|
22:33:44:55:66:77:88:99:00
|
||||||
|
# client3
|
||||||
|
33:44:55:66:77:88:99:00:11
|
||||||
|
|
||||||
|
To use existing tables in key=value format, you can use:
|
||||||
|
|
||||||
|
id=R001 ; ccert_fingerprint==table:/etc/postfwd/wl_ccerts ; action=DUNNO
|
||||||
|
|
||||||
|
This will ignore the right-hand value. Items can be mixed:
|
||||||
|
|
||||||
|
id=R002 ; action=REJECT \
|
||||||
|
client_name==unknown; \
|
||||||
|
client_name==file:/etc/postfwd/blacklisted
|
||||||
|
|
||||||
|
and for non pcre (comma separated) items:
|
||||||
|
|
||||||
|
id=R003 ; action=REJECT \
|
||||||
|
client_address==10.1.1.1, file:/etc/postfwd/blacklisted
|
||||||
|
|
||||||
|
id=R004 ; action=REJECT \
|
||||||
|
rbl=myrbl.home.local, zen.spamhaus.org, file:/etc/postfwd/rbls_changing
|
||||||
|
|
||||||
|
You can check your configuration with the --show_config option at the command line:
|
||||||
|
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
|
||||||
|
should give something like:
|
||||||
|
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
|
||||||
|
|
||||||
|
If a file can not be read, it will be ignored:
|
||||||
|
|
||||||
|
# postfwd --showconfig --rule='action=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1'
|
||||||
|
[LOG warning]: error: file /etc/postfwd/wl_clients not found - file will be ignored ?
|
||||||
|
Rule 0: id->"R-0"; action->"DUNNO"; client_address->"=;10.1.0.0/16, =;192.168.2.1"
|
||||||
|
|
||||||
|
File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.
|
||||||
|
|
||||||
|
If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:
|
||||||
|
|
||||||
|
id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
|
||||||
|
|
||||||
|
This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
|
||||||
|
necessary. Of course this might increase the system load, so please use it with care.
|
||||||
|
|
||||||
|
The --showconfig option illustrates the difference:
|
||||||
|
|
||||||
|
## evaluated at configuration stage
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=table:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;1.1.1.1, =;1.1.1.2, =;1.1.1.3"
|
||||||
|
|
||||||
|
## evaluated for any rulehit
|
||||||
|
# postfwd2 --nodaemon -L --rule='client_address=ltable:/etc/postfwd/clients; action=dunno' -C
|
||||||
|
Rule 0: id->"R-0"; action->"dunno"; client_address->"=;ltable:/etc/postfwd/clients"
|
||||||
|
|
||||||
|
Files can refer to other files. The following is valid.
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/rules.cf --
|
||||||
|
id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_master.cf --
|
||||||
|
192.168.1.0/24
|
||||||
|
file:/etc/postfwd/clients_east.cf
|
||||||
|
file:/etc/postfwd/clients_west.cf
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_east.cf --
|
||||||
|
192.168.2.0/24
|
||||||
|
|
||||||
|
-- FILE /etc/postfwd/clients_west.cf --
|
||||||
|
192.168.3.0/24
|
||||||
|
|
||||||
|
Remind that there is currently no loop detection (/a/file calls /a/file) and that this feature is only available
|
||||||
|
with postfwd1 v1.15 and postfwd2 v0.18 and higher.
|
||||||
|
|
||||||
|
|
||||||
=head2 ACTIONS
|
=head2 ACTIONS
|
||||||
|
|
||||||
I<General>
|
I<General>
|
||||||
|
@ -2576,15 +2754,24 @@ postfwd actions control the behaviour of the program. Currently you can specify
|
||||||
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
action==size($$client_address/1572864/3600/450 4.7.1 sorry, max 1.5mb per hour)
|
||||||
|
|
||||||
|
rcpt (<item>/<max>/<time>/<action>)
|
||||||
|
this command works similar to the rate() command with the difference, that the rate counter is
|
||||||
|
increased by the request's recipient_count attribute. to do this reliably you should call postfwd
|
||||||
|
from smtpd_data_restrictions or smtpd_end_of_data_restrictions. if you want to be sure, you could
|
||||||
|
check it within the ruleset:
|
||||||
|
# recipient count limit 3 per hour per client
|
||||||
|
id=RCPT01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \
|
||||||
|
action==rcpt($$client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
|
||||||
|
|
||||||
ask (<addr>:<port>[:<ignore>])
|
ask (<addr>:<port>[:<ignore>])
|
||||||
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
||||||
and the second argument (address and port) are mandatory. a third optional argument may be
|
and the second argument (address and port) are mandatory. a third optional argument may be
|
||||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||||
# example1: query postgrey and return it's answer to postfix
|
# example1: query postgrey and return it's answer to postfix
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031)
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
|
||||||
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
# example2: query postgrey but ignore it's answer, if it matches 'DUNNO'
|
||||||
# and continue parsing postfwd's ruleset
|
# and continue parsing postfwd's ruleset
|
||||||
id=GREY; client_address==10.1.1.1; ask(127.0.0.1:10031:^dunno$)
|
id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
|
||||||
|
|
||||||
wait (<delay>)
|
wait (<delay>)
|
||||||
pauses the program execution for <delay> seconds. use this for
|
pauses the program execution for <delay> seconds. use this for
|
||||||
|
@ -2876,6 +3063,11 @@ These parameters influence the way postfwd is working. Any of them can be combin
|
||||||
(which means their access times changed since last read) this might
|
(which means their access times changed since last read) this might
|
||||||
significantly increase system load.
|
significantly increase system load.
|
||||||
|
|
||||||
|
--config_timeout (default=3)
|
||||||
|
timeout in seconds to parse a single configuration line. if exceeded, the rule will
|
||||||
|
be skipped. this is used to prevent problems due to large files or loops.
|
||||||
|
|
||||||
|
|
||||||
I<Informational arguments>
|
I<Informational arguments>
|
||||||
|
|
||||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
||||||
|
|
Loading…
Reference in a new issue