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
|
||||
=====
|
||||
- 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">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>postfwd - postfix firewall daemon</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<link rev="made" href="mailto:feedback@suse.de" />
|
||||
<link rev="made" href="mailto:root@localhost" />
|
||||
</head>
|
||||
|
||||
<body style="background-color: white">
|
||||
|
@ -22,6 +20,7 @@
|
|||
<li><a href="#introduction">INTRODUCTION</a></li>
|
||||
<li><a href="#configuration">CONFIGURATION</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="#macros_acls">MACROS/ACLS</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_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
|
||||
-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>
|
||||
Informational (use only at command-line!):
|
||||
-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>
|
||||
<pre>
|
||||
====================================================================
|
||||
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$/i
|
||||
ITEM=VALUE default behaviour (see ITEMS section)
|
||||
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$/i
|
||||
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>
|
||||
<p>To identify single rules in your log files, you may add an unique identifier for each of it:</p>
|
||||
<pre>
|
||||
|
@ -213,13 +217,19 @@ arguments. Please see the COMMAND LINE section below for more information on thi
|
|||
recipient_domain</pre>
|
||||
<pre>
|
||||
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>
|
||||
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>
|
||||
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>
|
||||
version - postfwd version, contains "postfwd n.nn"
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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
|
||||
|
@ -370,16 +455,25 @@ rule containing only an action statement:</p>
|
|||
# size limit 1.5mb per hour per client
|
||||
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>
|
||||
<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>
|
||||
ask (<addr>:<port>[:<ignore>])
|
||||
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
|
||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||
# 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'
|
||||
# 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>
|
||||
wait (<delay>)
|
||||
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
|
||||
(which means their access times changed since last read) this might
|
||||
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>These arguments are for command line usage only. Never ever use them with postfix spawn!</p>
|
||||
<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_mx_lookups max names to look up with sender_mx_addrs
|
||||
-I, --instantcfg re-reads rulefiles for every new request
|
||||
--config_timeout <i> parser timeout in seconds
|
||||
|
||||
Informational (use only at command-line!):
|
||||
-C, --showconfig shows ruleset summary, -v for verbose
|
||||
|
@ -115,11 +116,15 @@ DESCRIPTION
|
|||
in the following way:
|
||||
|
||||
====================================================================
|
||||
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$/i
|
||||
ITEM=VALUE default behaviour (see ITEMS section)
|
||||
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$/i
|
||||
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
|
||||
|
@ -187,12 +192,18 @@ DESCRIPTION
|
|||
|
||||
helo_address - postfwd tries to look up the helo_name. use
|
||||
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_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_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"
|
||||
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
|
||||
|
||||
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 '!!(...)':
|
||||
|
||||
|
@ -299,6 +314,97 @@ DESCRIPTION
|
|||
be performed as case insensitive exact match. Use the '-vv' option to
|
||||
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
|
||||
*General*
|
||||
|
||||
|
@ -371,15 +477,24 @@ DESCRIPTION
|
|||
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)
|
||||
|
||||
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>])
|
||||
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
|
||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||
# 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'
|
||||
# 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>)
|
||||
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
|
||||
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*
|
||||
|
||||
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:
|
||||
.\" ========================================================================
|
||||
|
@ -128,8 +128,8 @@
|
|||
.rm #[ #] #H #V #F C
|
||||
.\" ========================================================================
|
||||
.\"
|
||||
.IX Title "MANUAL1 8"
|
||||
.TH MANUAL1 8 "2009-06-27" "perl v5.8.8" "User Contributed Perl Documentation"
|
||||
.IX Title "POSTFWD 1"
|
||||
.TH POSTFWD 1 "2009-09-03" "perl v5.8.5" "User Contributed Perl Documentation"
|
||||
.SH "NAME"
|
||||
postfwd \- postfix firewall daemon
|
||||
.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
|
||||
.Ve
|
||||
.PP
|
||||
.Vb 16
|
||||
.Vb 17
|
||||
\& Optional:
|
||||
\& -t, --test testing, always returns "dunno"
|
||||
\& -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_mx_lookups max names to look up with sender_mx_addrs
|
||||
\& -I, --instantcfg re-reads rulefiles for every new request
|
||||
\& --config_timeout <i> parser timeout in seconds
|
||||
.Ve
|
||||
.PP
|
||||
.Vb 7
|
||||
|
@ -261,13 +262,17 @@ is not important. So the following would lead to the same result as the previous
|
|||
.PP
|
||||
The way how request items are compared to the ruleset can be influenced in the following way:
|
||||
.PP
|
||||
.Vb 7
|
||||
.Vb 11
|
||||
\& ====================================================================
|
||||
\& 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$/i
|
||||
\& ITEM=VALUE default behaviour (see ITEMS section)
|
||||
\& 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$/i
|
||||
\& 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
|
||||
.PP
|
||||
|
@ -352,19 +357,25 @@ Rules can span multiple lines by adding a trailing backslash \*(L"\e\*(R" charac
|
|||
\& recipient_domain
|
||||
.Ve
|
||||
.PP
|
||||
.Vb 2
|
||||
.Vb 4
|
||||
\& helo_address - postfwd tries to look up the helo_name. use
|
||||
\& 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
|
||||
.PP
|
||||
.Vb 2
|
||||
.Vb 4
|
||||
\& sender_ns_names, - postfwd tries to look up the names/ip addresses
|
||||
\& 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
|
||||
.PP
|
||||
.Vb 2
|
||||
.Vb 4
|
||||
\& sender_mx_names, - postfwd tries to look up the names/ip addresses
|
||||
\& 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
|
||||
.PP
|
||||
.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
|
||||
.Ve
|
||||
.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 '!!(...)':
|
||||
.PP
|
||||
.Vb 1
|
||||
|
@ -480,6 +497,122 @@ Request attributes can be compared by preceeding '$$' characters, e.g.:
|
|||
.PP
|
||||
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.
|
||||
.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"
|
||||
.IX Subsection "ACTIONS"
|
||||
\&\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)
|
||||
.Ve
|
||||
.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
|
||||
\& ask (<addr>:<port>[:<ignore>])
|
||||
\& 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
|
||||
\& specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||
\& # 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'
|
||||
\& # 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
|
||||
.PP
|
||||
.Vb 3
|
||||
|
@ -962,6 +1106,12 @@ These parameters influence the way postfwd is working. Any of them can be combin
|
|||
\& significantly increase system load.
|
||||
.Ve
|
||||
.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
|
||||
.PP
|
||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
||||
|
|
400
sbin/postfwd
400
sbin/postfwd
|
@ -25,7 +25,7 @@ use vars qw(@ISA);
|
|||
|
||||
# Program constants
|
||||
our($NAME) = 'postfwd';
|
||||
our($VERSION) = '1.14';
|
||||
our($VERSION) = '1.16';
|
||||
|
||||
# Networking options (use -i, -p and -R to change)
|
||||
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_retries) = "3";
|
||||
our($def_dns_timeout) = "14";
|
||||
our($def_config_timeout) = "3";
|
||||
our($reply_maxlen) = "512";
|
||||
|
||||
# change this, to match your POD requirements
|
||||
|
@ -49,7 +50,7 @@ our($cmd_pager) = "more";
|
|||
|
||||
# default action, do not change
|
||||
# unless you really know why
|
||||
our($default_action) = "dunno";
|
||||
our($default_action) = "DUNNO";
|
||||
|
||||
# default maximum values for the score() command
|
||||
# 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_RCLIENT) = "rhsbl_reverse_client";
|
||||
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
|
||||
our($COMP_DATE) = "date";
|
||||
our($COMP_TIME) = "time";
|
||||
|
@ -136,7 +145,7 @@ our($COMP_VAR) = "[\$][\$]";
|
|||
# date calculations
|
||||
our($COMP_DATECALC) = "($COMP_DATE|$COMP_TIME|$COMP_DAYS|$COMP_MONTHS)";
|
||||
# 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
|
||||
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_maxlen) = 0;
|
||||
our($syslog_safe) = 0;
|
||||
our($syslog_unsafe_charset) = qr/[^\x20-\x7E]/;
|
||||
if ( defined $Sys::Syslog::VERSION and $Sys::Syslog::VERSION ge '0.15' ) {
|
||||
# use 'native' when Sys::Syslog >= 0.15
|
||||
$syslog_socktype = 'native';
|
||||
|
@ -203,7 +213,7 @@ use vars qw(
|
|||
$opt_norulelog $opt_summary $net_interface $net_port
|
||||
$net_user $net_group $net_chroot $net_pid $net_proto
|
||||
$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_showconfig $opt_stdoutlog $opt_shortlog $dns_async_txt
|
||||
$DNS $Reload_Conf $dns_queuesize $dns_retries $dns_timeout
|
||||
|
@ -239,7 +249,7 @@ sub mylog {
|
|||
sub mylogs {
|
||||
my($prio) = shift(@_);
|
||||
my($msg) = shift(@_);
|
||||
$msg =~ s/\%/%%/g;
|
||||
$msg =~ s/\%/%%/g; $msg =~ s/$syslog_unsafe_charset/?/g;
|
||||
mylog $prio, $msg;
|
||||
};
|
||||
#
|
||||
|
@ -486,27 +496,100 @@ sub devar_item {
|
|||
# preparses configuration line for ACL syntax
|
||||
#
|
||||
sub acl_parser {
|
||||
my($myline) = @_;
|
||||
my($file,$num,$myline) = @_;
|
||||
if ( $myline =~ /^\s*($COMP_ACL[\-\w]+)\s*{\s*(.*?)\s*;\s*}[\s;]*$/ ) {
|
||||
$ACLs{$1} = $2; $myline = "";
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
};
|
||||
#
|
||||
# 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
|
||||
#
|
||||
sub parse_config_line {
|
||||
my($mynum, $myindex, $myline) = @_;
|
||||
my($forced_reload, $myfile, $mynum, $myindex, $myline) = @_;
|
||||
my(%myrule) = ();
|
||||
my($mykey, $myvalue, $mycomp, $neg);
|
||||
|
||||
if ( $myline = acl_parser ($myline) ) {
|
||||
my($mykey, $myvalue, $mycomp);
|
||||
eval {
|
||||
local $SIG{'__DIE__'};
|
||||
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 };
|
||||
my $prevalert = alarm($config_timeout) if $config_timeout;
|
||||
if ( $myline = acl_parser ($myfile, $mynum, $myline) ) {
|
||||
unless ( $myline =~ /^\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+(;\s*[^=\s]+\s*$COMP_SEPARATOR\s*([^;\s]+\s*)+)*[;\s]*$/ ) {
|
||||
warn "warning: ignoring invalid line ".$mynum.": \"".$myline."\"";
|
||||
mylogs 'warning', "ignoring invalid $myfile line ".$mynum.": \"".$myline."\"";
|
||||
} else {
|
||||
# separate items
|
||||
foreach (split ";", $myline) {
|
||||
|
@ -514,68 +597,59 @@ sub parse_config_line {
|
|||
s/^\s*(.*?)\s*($COMP_SEPARATOR)\s*(.*?)\s*$/$1$2$3/;
|
||||
( ($mycomp = $2) =~ /^([\<\>\~])=$/ ) and $mycomp = "=$1";
|
||||
($mykey, $myvalue) = split /$COMP_SEPARATOR/, $_, 2;
|
||||
if ($mykey =~ /^$COMP_CSV$/) {
|
||||
$myvalue =~ s/\s*-\s*/-/g if ($mykey =~ /^$COMP_DATECALC$/);
|
||||
$myvalue =~ s/\s*,\s*/,/g;
|
||||
map { push ( @{$myrule{$mykey}}, $mycomp.";".$_ ) } ( split ",", $myvalue );
|
||||
} elsif ($mykey =~ /^$COMP_SINGLE$/) {
|
||||
mylogs "notice", "warning: Rule $myindex (line $mynum):"
|
||||
if ($mykey =~ /^$COMP_SINGLE$/) {
|
||||
mylogs 'notice', "notice: Rule $myindex ($myfile line $mynum):"
|
||||
." overriding $mykey=\"".$myrule{$mykey}."\""
|
||||
." with $mykey=\"$myvalue\""
|
||||
if (defined $myrule{$mykey});
|
||||
$myrule{$mykey} = $myvalue;
|
||||
} elsif ($mykey =~ /^$COMP_CSV$/) {
|
||||
$myvalue =~ s/\s*,\s*/,/g;
|
||||
map { push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $_) } ( split /\s*,\s*/, $myvalue );
|
||||
} else {
|
||||
if ( $mycomp eq '=~' or $mycomp eq '!~') {
|
||||
# temporarily remove negation
|
||||
$myvalue = $neg if ($neg = deneg_item($myvalue));
|
||||
# allow // regex
|
||||
$myvalue =~ s/^\/?(.*?)\/?$/$1/;
|
||||
# tested, slower
|
||||
#$myvalue = qr/$myvalue/i;
|
||||
# re-enable negation
|
||||
$myvalue = "!!($myvalue)" if $neg;
|
||||
};
|
||||
push ( @{$myrule{$mykey}}, $mycomp.";".$myvalue );
|
||||
push @{$myrule{$mykey}}, prepare_item ($forced_reload, $mycomp, $myvalue);
|
||||
};
|
||||
};
|
||||
unless (exists($myrule{$COMP_ACTION})) {
|
||||
$myrule{$COMP_ACTION} = "WARN rule found but no action was defined";
|
||||
mylogs "notice", "warning: Rule ".$myindex." (line ".$mynum."): contains no action - default will be used";
|
||||
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 (line $mynum): contains no rule identifier - will use \"$myrule{id}\"" if $opt_verbose;
|
||||
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 (line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
|
||||
mylogs $syslog_priority, "loaded: Rule $myindex ($myfile line $mynum): id->\"$myrule{id}\" action->\"$myrule{action}\"" if $opt_verbose;
|
||||
};
|
||||
};
|
||||
alarm($prevalert) if $config_timeout;
|
||||
};
|
||||
return %myrule;
|
||||
}
|
||||
};
|
||||
#
|
||||
# parses configuration file
|
||||
#
|
||||
sub read_config_file {
|
||||
my($myindex, $myfile) = @_;
|
||||
my($forced_reload, $myindex, $myfile) = @_;
|
||||
my(%myrule, @myruleset) = ();
|
||||
my($mybuffer) = "";
|
||||
my($mybuffer) = ""; undef my $fh;
|
||||
|
||||
unless (-e $myfile) {
|
||||
warn "error: file ".$myfile." not found - file will be ignored";
|
||||
} else {
|
||||
unless (open (IN, "<$myfile")) {
|
||||
warn "error: could not open ".$myfile." - file will be ignored";
|
||||
unless (open ($fh, "<$myfile")) {
|
||||
warn "error: could not open ".$myfile." - $! - file will be ignored";
|
||||
} else {
|
||||
mylogs $syslog_priority, "reading file $myfile" if $opt_verbose;
|
||||
while (<IN>) {
|
||||
while (<$fh>) {
|
||||
chomp;
|
||||
s/(\"|#.*)//g;
|
||||
next if /^\s*$/;
|
||||
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);
|
||||
$mybuffer = "";
|
||||
};
|
||||
close (IN);
|
||||
close ($fh);
|
||||
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
|
||||
#
|
||||
sub read_config {
|
||||
my($forced_reload) = shift;
|
||||
my(%myrule, @myruleset) = ();
|
||||
my($mytype,$myitem,$config);
|
||||
|
||||
|
@ -595,17 +670,17 @@ sub read_config {
|
|||
for $config (@Configs) {
|
||||
($mytype,$myitem) = split '::', $config;
|
||||
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);
|
||||
} 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,
|
||||
"file \"$myitem\" unchanged - using cached ruleset (mtime: ".(stat $myitem)[9].",
|
||||
cache: $Config_Cache{$myitem}{lastread})"
|
||||
if $opt_verbose;
|
||||
push ( @Rules, @{$Config_Cache{$myitem}{ruleset}} );
|
||||
} else {
|
||||
@myruleset = read_config_file (($#Rules+1), $myitem);
|
||||
@myruleset = read_config_file ($forced_reload, ($#Rules+1), $myitem);
|
||||
if (@myruleset) {
|
||||
push ( @Rules, @myruleset );
|
||||
$Config_Cache{$myitem}{lastread} = time;
|
||||
|
@ -614,8 +689,12 @@ sub read_config {
|
|||
};
|
||||
};
|
||||
};
|
||||
if ($#Rules < 0) {
|
||||
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
|
||||
|
@ -931,7 +1010,7 @@ sub postfwd_items {
|
|||
my($myresult) = undef;
|
||||
mylogs $syslog_priority, "type numeric : \"$myitem\" \"$cmp\" \"$val\"" if ($opt_verbose > 1);
|
||||
$myitem ||= "0"; $val ||= "0";
|
||||
if ( ($cmp eq '==') or ($cmp eq '=') ) {
|
||||
if ($cmp eq '==') {
|
||||
$myresult = ($myitem == $val);
|
||||
} elsif ($cmp eq '=<') {
|
||||
$myresult = ($myitem <= $val);
|
||||
|
@ -968,7 +1047,7 @@ sub postfwd_items {
|
|||
my($cmp,$val,$myitem,%request) = @_;
|
||||
my($myresult) = undef;
|
||||
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;
|
||||
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $months{$rmax}) : (($val =~ /-/) ? $imon : $rmin);
|
||||
mylogs $syslog_priority, "type months : \"$imon\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
||||
|
@ -981,7 +1060,7 @@ sub postfwd_items {
|
|||
my($cmp,$val,$myitem,%request) = @_;
|
||||
my($myresult) = undef;
|
||||
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;
|
||||
$rmax = ($rmax) ? (($rmax =~ /^\d$/) ? $rmax : $weekdays{$rmax}) : (($val =~ /-/) ? $iday : $rmin);
|
||||
mylogs $syslog_priority, "type days : \"$iday\" \"$cmp\" \"$rmin\"-\"$rmax\""
|
||||
|
@ -994,7 +1073,7 @@ sub postfwd_items {
|
|||
my($cmp,$val,$myitem,%request) = @_;
|
||||
my($myresult) = undef;
|
||||
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);
|
||||
$rmin = ($rmin) ? join ('', reverse split ('\.', $rmin)) : $idat;
|
||||
$rmax = ($rmax) ? join ('', reverse split ('\.', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
||||
|
@ -1008,7 +1087,7 @@ sub postfwd_items {
|
|||
my($cmp,$val,$myitem,%request) = @_;
|
||||
my($myresult) = undef;
|
||||
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);
|
||||
$rmin = ($rmin) ? join ('', split ('\:', $rmin)) : $idat;
|
||||
$rmax = ($rmax) ? join ('', split ('\:', $rmax)) : (($val =~ /-/) ? $idat : $rmin);
|
||||
|
@ -1229,7 +1308,7 @@ sub postfwd_items {
|
|||
type => $mycmd,
|
||||
maxcount => $ratecount,
|
||||
ttl => $ratetime,
|
||||
count => ( ($mycmd eq 'size') ? $request{size} : 1 ),
|
||||
count => ( ($mycmd eq 'size') ? $request{size} : (($mycmd eq 'rcpt') ? $request{recipient_count} : 1 ) ),
|
||||
time => $now,
|
||||
rule => $Rules[$index]{$COMP_ID},
|
||||
action => $ratecmd,
|
||||
|
@ -1246,6 +1325,8 @@ sub postfwd_items {
|
|||
},
|
||||
# size() command
|
||||
"size" => sub { return &{$postfwd_actions{rate}}(@_); },
|
||||
# rcpt() command
|
||||
"rcpt" => sub { return &{$postfwd_actions{rate}}(@_); },
|
||||
# wait() command
|
||||
"wait" => sub {
|
||||
my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
||||
|
@ -1309,7 +1390,7 @@ sub postfwd_items {
|
|||
mylogs ('notice', "rule: $index got invalid answer '$sendstr' from $myarg");
|
||||
};
|
||||
} else {
|
||||
mylogs ('notice', "Could not open socket to '$myarg'");
|
||||
mylogs ('notice', "Could not open socket to '$myarg' - $!");
|
||||
};
|
||||
return ($stop,$index,$myaction,$myline,%request);
|
||||
},
|
||||
|
@ -1356,19 +1437,24 @@ sub get_plugins {
|
|||
# use: compare_item ( $TYPE, $RULEITEM, $MINIMUMHITS, $REQUESTITEM, %REQUEST, %REQUESTINFO );
|
||||
#
|
||||
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($rcount) = 0;
|
||||
$mymin ||= 1;
|
||||
|
||||
#
|
||||
# determine the right compare function
|
||||
$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
|
||||
ITEM: foreach (@{$mymask}) {
|
||||
ITEM: foreach (@items) {
|
||||
($cmp, $val) = split ";";
|
||||
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);
|
||||
$val = $neg if ($neg = deneg_item($val));
|
||||
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 ) {
|
||||
undef $Reload_Conf;
|
||||
show_stats;
|
||||
read_config;
|
||||
read_config(1);
|
||||
};
|
||||
|
||||
# clear dnsbl timeout counters
|
||||
|
@ -1724,7 +1810,8 @@ sub smtpd_access_policy {
|
|||
next RATES unless ( $request{$checkreq} and (defined $Rates{$request{$checkreq}}) );
|
||||
if ( ($now - $Rates{$request{$checkreq}}{"time"}) > $Rates{$request{$checkreq}}{ttl} ) {
|
||||
# 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;
|
||||
mylogs $syslog_priority, "[RATE] renewing rate object ".$request{$checkreq}
|
||||
." [type: ".$Rates{$request{$checkreq}}{type}
|
||||
|
@ -1733,7 +1820,8 @@ sub smtpd_access_policy {
|
|||
if ($opt_verbose > 1);
|
||||
} else {
|
||||
# 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}
|
||||
." to ".$Rates{$request{$checkreq}}{count}
|
||||
." [type: ".$Rates{$request{$checkreq}}{type}
|
||||
|
@ -1829,10 +1917,10 @@ sub smtpd_access_policy {
|
|||
} else {
|
||||
|
||||
# refresh config if '-I' was set
|
||||
read_config if $opt_instantconfig;
|
||||
read_config(0) if $opt_instantconfig;
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -1918,6 +2006,33 @@ sub smtpd_access_policy {
|
|||
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 ####
|
||||
|
||||
|
@ -1970,6 +2085,7 @@ GetOptions ( "term|kill|stop|k" => \$opt_kill,
|
|||
'noidlestats' => \$opt_noidlestats,
|
||||
'no-idlestats' => \$opt_noidlestats,
|
||||
's|scores=s' => \%opt_scores,
|
||||
'config_timeout=i' => \$config_timeout,
|
||||
'f|file=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
||||
'r|rule=s' => sub{ my($opt,$value) = @_; push (@Configs, $opt.'::'.$value) },
|
||||
'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);
|
||||
|
||||
$opt_verbose = 0 unless $opt_verbose;
|
||||
$opt_stdoutlog = 1 if ($opt_kill or $opt_hup or $opt_showconfig);
|
||||
|
||||
# terminate at -k or --kill
|
||||
if ($opt_kill) {
|
||||
|
@ -2002,7 +2119,7 @@ openlog $syslog_name, $syslog_options, $syslog_facility;
|
|||
mylogs "notice", $NAME." ".$VERSION." starting" if $opt_daemon;
|
||||
|
||||
# read configuration
|
||||
read_config;
|
||||
read_config(1);
|
||||
if ($opt_showconfig) {
|
||||
show_config;
|
||||
exit 1;
|
||||
|
@ -2041,6 +2158,7 @@ $net_pid ||= $def_net_pid;
|
|||
$dns_queuesize ||= $def_dns_queuesize;
|
||||
$dns_retries ||= $def_dns_retries;
|
||||
$dns_timeout ||= $def_dns_timeout;
|
||||
$config_timeout ||= $def_config_timeout;
|
||||
$syslog_name ||= $NAME;
|
||||
$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;
|
||||
|
@ -2053,6 +2171,7 @@ $dns_queuesize = ( $dns_queuesize =~ /^(\d+)$/ ) ? $1 : $dns_queuesize;
|
|||
$dns_retries = ( $dns_retries =~ /^(\d+)$/ ) ? $1 : $dns_retries;
|
||||
$dns_timeout = ( $dns_timeout =~ /^(\d+)$/ ) ? $1 : $dns_timeout;
|
||||
$syslog_name = ( $syslog_name =~ /^(.+)$/ ) ? $1 : $NAME;
|
||||
$config_timeout = ( $config_timeout =~ /^(\d+)$/ ) ? $1 : $def_config_timeout;
|
||||
|
||||
# Unbuffer standard output.
|
||||
select((select(STDOUT), $| = 1)[0]);
|
||||
|
@ -2144,26 +2263,7 @@ if ($opt_daemon) {
|
|||
# check request line and print output
|
||||
next unless defined $1;
|
||||
$request = $1;
|
||||
if ($request =~ /([^=]+)=(.*)/) {
|
||||
$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."\"";
|
||||
};
|
||||
process_input ($client, $request, \%myattr);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -2178,26 +2278,7 @@ if ($opt_daemon) {
|
|||
s/^([^\r\n]*)\r?\n//;
|
||||
next unless defined $1;
|
||||
$request = $1;
|
||||
if ($request =~ /([^=]+)=(.*)/) {
|
||||
$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."\"";
|
||||
};
|
||||
process_input (undef, $request, \%myattr);
|
||||
};
|
||||
|
||||
# finishing
|
||||
|
@ -2264,6 +2345,7 @@ postfwd [OPTIONS] [SOURCE1, SOURCE2, ...]
|
|||
--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
|
||||
-I, --instantcfg re-reads rulefiles for every new request
|
||||
--config_timeout <i> parser timeout in seconds
|
||||
|
||||
Informational (use only at command-line!):
|
||||
-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=!!(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_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_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"
|
||||
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
|
||||
|
||||
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 '!!(...)':
|
||||
|
||||
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.
|
||||
|
||||
|
||||
=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
|
||||
|
||||
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); \
|
||||
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>])
|
||||
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
|
||||
specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
||||
# 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'
|
||||
# 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>)
|
||||
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
|
||||
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>
|
||||
|
||||
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
||||
|
|
Loading…
Reference in a new issue