Chroot the process to the specified path.
Test this before using - you might need some libs there.
@@ -847,9 +854,10 @@ DESCRIPTION
# 1. 30MB for systems in *.customer1.tld
# 2. 20MB for SASL user joejob
# 3. 10MB default
- id=SZ001; state==END-OF-MESSAGE; action=REJECT message too large; size=30000000 ; client_name=\.customer1.tld$
- id=SZ002; state==END-OF-MESSAGE; action=REJECT message too large; size=20000000 ; sasl_username==joejob
- id=SZ003; state==END-OF-MESSAGE; action=REJECT message too large; size=10000000
+ id=SZ001; state==END-OF-MESSAGE; action=DUNNO; size<=30000000 ; client_name=\.customer1.tld$
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=20000000 ; sasl_username==joejob
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=10000000
+ id=SZ100; state==END-OF-MESSAGE; action=REJECT message too large
## Selective Greylisting
# 1. if listed on zen.spamhaus.org with results 127.0.0.10 or .11, dns cache timeout 1200s
@@ -898,9 +906,9 @@ DESCRIPTION
# 1. exceeded 30 requests per hour or
# 2. tried to send more than 1.5mb within 10 minutes
id=RATE01 ; client_name==unknown ; state==RCPT ; \
- action==rate($$client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
+ action==rate(client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
id=SIZE01 ; client_name==unknown ; state==END_OF_DATA ; \
- action==size($$client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
+ action==size(client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
## Macros
# definition
diff --git a/doc/postfwd2.html b/doc/postfwd2.html
new file mode 100644
index 0000000..a860ba9
--- /dev/null
+++ b/doc/postfwd2.html
@@ -0,0 +1,1182 @@
+
+
+
+postfwd2 - postfix firewall daemon
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+postfwd2 - postfix firewall daemon
+
+
+
+
+postfwd2 [OPTIONS] [SOURCE1, SOURCE2, ...]
+
+ Ruleset: (at least one, multiple use is allowed):
+ -f, --file <file> reads rules from <file>
+ -r, --rule <rule> adds <rule> to config
+ -s, --scores <v>=<r> returns <r> when score exceeds <v>
+
+ Server:
+ -i, --interface <dev> listen on interface <dev>
+ -p, --port <port> listen on port <port>
+ --proto <proto> socket type (tcp or unix)
+ --server_socket <sock> e.g. tcp:127.0.0.1:10045
+ -u, --user <name> set uid to user <name>
+ -g, --group <name> set gid to group <name>
+ --umask <mask> umask for master filepermissions
+ --server_umask <mask> umask for server filepermissions
+ --pidfile <path> create pidfile under <path>
+ --min_servers <i> spawn at least <i> children
+ --max_servers <i> do not spawn more than <i> children
+ --min_spare_servers <i> minimum idle children
+ --max_spare_servers <i> maximum idle children
+
+ Cache:
+ -c, --cache <int> sets the request-cache timeout to <int> seconds
+ --cleanup-requests <int> cleanup interval in seconds for request cache
+ --cache_interface <dev> listen on interface <dev>
+ --cache_port <port> listen on port <port>
+ --cache_proto <proto> socket type (tcp or unix)
+ --cache_socket <sock> e.g. tcp:127.0.0.1:10043
+ --cache_umask <mask> umask for cache filepermissions
+ --cacheid <list> list of request items for cache-id
+ --cache-rdomain-only skip recipient localpart for cache-id
+ --cache-no-sender skip sender address for cache-id
+ --cache-no-size skip size for cache-id
+ --no_parent_request_cache disable parent request cache
+ --no_parent_rate_cache disable parent rate cache
+ --no_parent_dns_cache disable parent dns cache
+ --no_parent_cache disable all parent caches
+
+ Rates:
+ --cleanup-rates <int> cleanup interval in seconds for rate cache
+
+ Control:
+ -k, --kill, --stop terminate postfwd2
+ --reload, --hup reload postfwd2
+ --watchdog <w> watchdog timer in seconds
+ --respawn <r> respawn delay in seconds
+ --failures <f> max respawn failure counter
+ --daemons <list> list of daemons to start
+ --dumpcache show cache contents
+ --dumpstats show statistics
+
+ DNS:
+ -n, --nodns skip any dns based test
+ --dns_timeout <i> dns query timeout in seconds
+ --dns_timeout_max <i> disable dnsbl after <i> timeouts
+ --dns_timeout_interval <i> reenable dnsbl after <i> seconds
+ --cache-rbl-timeout <i> default dns ttl if not specified in ruleset
+ --cache-rbl-default <s> default dns pattern if not specified in ruleset
+ --cleanup-rbls <i> cleanup old dns cache items every <i> seconds
+ --dns_async_txt perform dnsbl A and TXT lookups simultaneously
+ --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
+
+ Optional:
+ -t, --test testing, always returns "dunno"
+ -S, --summary <i> show stats every <i> seconds
+ --noidlestats disables statistics when idle
+ --norulestats disables per rule statistics
+ -I, --instantcfg reloads ruleset on every new request
+ --config_timeout <i> parser timeout in seconds
+
+ Logging:
+ -l, --logname <label> label for syslog messages
+ --facility <s> use syslog facility <s>
+ --nodnslog do not log dns results
+ --anydnslog log any dns (even cached) results
+ --norulelog do not log rule actions
+ --nolog|--perfmon no logging at all
+ -v, --verbose verbose logging, use twice to increase
+ --debug <s> list of debugging classes
+
+ Information (use only at command-line!):
+ -h, --help display this help and exit
+ -m, --manual shows program manual
+ -V, --version output version information and exit
+ -D, --defaults show postfwd2 settings and exit
+ -C, --showconfig show postfwd2 ruleset and exit (-v allowed)
+ -L, --stdout redirect syslog messages to stdout
+ -q, --quiet no syslogging, no stdout (-P works for compatibility)
+
+ Obsolete (only for compatibility with postfwd v1):
+ -d|--daemon, --shortlog, --dns_queuesize, --dns_retries
+
+
+
+
+
+
+
+postfwd2 is written to combine complex postfix restrictions in a ruleset similar to those of the most firewalls.
+The program uses the postfix policy delegation protocol to control access to the mail system before a message
+has been accepted (please visit http://www.postfix.org/SMTPD_POLICY_README.html for more information).
+postfwd2 allows you to choose an action (e.g. reject, dunno) for a combination of several smtp parameters
+(like sender and recipient address, size or the client's TLS fingerprint). Also it offers simple macros/acls
+which should allow straightforward and easy-to-read configurations.
+Features:
+* Complex combinations of smtp parameters
+* Combined RBL/RHSBL lookups with arbitrary actions depending on results
+* Scoring system
+* Date/time based rules
+* Macros/ACLs, Groups, Negation
+* Compare request attributes (e.g. client_name and helo_name)
+* Internal caching for requests and dns lookups
+* Built in statistics for rule efficiency analysis
+
+
+
+A configuration line consists of optional item=value pairs, separated by semicolons
+(`;`) and the appropriate desired action:
+
+ [ <item1>[=><~]=<value>; <item2>[=><~]=<value>; ... ] action=<result>
+Example:
+
+ client_address=192.168.1.1 ; sender==no@bad.local ; action=REJECT
+This will deny all mail from 192.168.1.1 with envelope sender no@bad.local. The order of the elements
+is not important. So the following would lead to the same result as the previous example:
+
+ action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
+The way how request items are compared to the ruleset can be influenced 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 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 identifier for each of it:
+
+ id=R_001 ; action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
+You may use these identifiers as target for the `jump()` command (see ACTIONS section below). Leading
+or trailing whitespace characters will be ignored. Use '#' to comment your configuration. Others will
+appreciate.
+A ruleset consists of one or multiple rules, which can be loaded from files or passed as command line
+arguments. Please see the COMMAND LINE section below for more information on this topic.
+Rules can span multiple lines by adding a trailing backslash ``\'' character:
+
+ id=R_001 ; client_address=192.168.1.0/24; sender==no@bad.local; \
+ action=REJECT please use your relay from there
+
+
+
+
+ id - a unique rule id, which can be used for log analysis
+ ids also serve as targets for the "jump" command.
+
+ date, time - a time or date range within the specified rule shall hit
+ # FORMAT:
+ # Feb, 29th
+ date=29.02.2008
+ # Dec, 24th - 26th
+ date=24.12.2008-26.12.2008
+ # from today until Nov, 23rd
+ date=-23.09.2008
+ # from April, 1st until today
+ date=01.04.2008-
+
+ days, months - a range of weekdays (Sun-Sat) or months (Jan-Dec)
+ within the specified rule shall hit
+
+ score - when the specified score is hit (see ACTIONS section)
+ the specified action will be returned to postfix
+ scores are set global until redefined!
+
+ request_score - this value allows to access a request's score. it
+ may be used as variable ($$request_score).
+
+ rbl, rhsbl, - query the specified RBLs/RHSBLs, possible values are:
+ rhsbl_client, <name>[/<reply>/<maxcache>, <name>/<reply>/<maxcache>]
+ rhsbl_sender, (defaults: reply=^127\.0\.0\.\d+$ maxcache=3600)
+ rhsbl_reverse_client the results of all rhsbl_* queries will be combined
+ in rhsbl_count (see below).
+
+ rblcount, rhsblcount - minimum RBL/RHSBL hitcounts to match. if not specified
+ a single RBL/RHSBL hit will match the rbl/rhsbl items.
+ you may specify 'all' to evaluate all items, and use
+ it as variable in an action (see ACTIONS section)
+ (default: 1)
+
+ sender_localpart, - the local-/domainpart of the sender address
+ sender_domain
+
+ recipient_localpart, - the local-/domainpart of the recipient address
+ recipient_domain
+
+ helo_address - postfwd2 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, - postfwd2 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, - postfwd2 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 - postfwd2 version, contains "postfwd2 n.nn"
+ this enables version based checks in your rulesets
+ (e.g. for migration). works with old versions too,
+ because a non-existing item always returns false:
+ id=R01; version~=1.10; sender_domain==some.org \
+ ; action=REJECT sorry no access
+Besides these you can specify any attribute of the postfix policy delegation protocol.
+Feel free to combine them the way you need it (have a look at the EXAMPLES section below).
+Most values can be specified as regular expressions (PCRE). Please see the table below
+for details:
+
+ # ==========================================================
+ # ITEM=VALUE TYPE
+ # ==========================================================
+ id=something mask = string
+ date=01.04.2007-22.04.2007 mask = date (DD.MM.YYYY-DD.MM.YYYY)
+ time=08:30:00-17:00:00 mask = time (HH:MM:SS-HH:MM:SS)
+ days=Mon-Wed mask = weekdays (Mon-Wed) or numeric (1-3)
+ months=Feb-Apr mask = months (Feb-Apr) or numeric (1-3)
+ score=5.0 mask = maximum floating point value
+ rbl=zen.spamhaus.org mask = <name>/<reply>/<maxcache>[,...]
+ rblcount=2 mask = numeric, will match if rbl hits >= 2
+ helo_address=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
+ sender_ns_names=some.domain.tld mask = PCRE
+ sender_mx_names=some.domain.tld mask = PCRE
+ sender_ns_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
+ sender_mx_addrs=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
+ # ------------------------------
+ # Postfix version 2.1 and later:
+ # ------------------------------
+ client_address=<a.b.c.d/nn> mask = CIDR[,CIDR,...]
+ client_name=another.domain.tld mask = PCRE
+ reverse_client_name=another.domain.tld mask = PCRE
+ helo_name=some.domain.tld mask = PCRE
+ sender=foo@bar.tld mask = PCRE
+ recipient=bar@foo.tld mask = PCRE
+ recipient_count=5 mask = numeric, will match if recipients >= 5
+ # ------------------------------
+ # Postfix version 2.2 and later:
+ # ------------------------------
+ sasl_method=plain mask = PCRE
+ sasl_username=you mask = PCRE
+ sasl_sender= mask = PCRE
+ size=12345 mask = numeric, will match if size >= 12345
+ ccert_subject=blackhole.nowhere.local mask = PCRE (only if tls verified)
+ ccert_issuer=John+20Doe mask = PCRE (only if tls verified)
+ ccert_fingerprint=AA:BB:CC:DD:EE:... mask = PCRE (do NOT use "..." here)
+ # ------------------------------
+ # Postfix version 2.3 and later:
+ # ------------------------------
+ encryption_protocol=TLSv1/SSLv3 mask = PCRE
+ encryption_cipher=DHE-RSA-AES256-SHA mask = PCRE
+ encryption_keysize=256 mask = numeric, will match if keysize >= 256
+ ...
+the current list can be found at http://www.postfix.org/SMTPD_POLICY_README.html . Please read carefully about which
+attribute can be used at which level of the smtp transaction (e.g. size will only work reliably at END_OF_DATA level).
+Pattern matching is performed case insensitive.
+Multiple use of the same item is allowed and will compared as logical OR, which means that this will work as expected:
+
+ id=TRUST001; action=OK; encryption_keysize=64; \
+ ccert_fingerprint=11:22:33:44:55:66:77:88:99; \
+ ccert_fingerprint=22:33:44:55:66:77:88:99:00; \
+ ccert_fingerprint=33:44:55:66:77:88:99:00:11; \
+ sender=@domain\.local$
+client_address, rbl and rhsbl items may also be specified as whitespace-or-comma-separated values:
+
+ id=SKIP01; action=dunno; \
+ client_address=192.168.1.0/24, 172.16.254.23
+ id=SKIP02; action=dunno; \
+ client_address= 10.10.3.32 \
+ 10.216.222.0/27
+The following items must be unique:
+
+ id, minimum and maximum values, rblcount and rhsblcount
+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?
+Request attributes can be compared by preceeding '$$' characters, e.g.:
+
+ id=R-003 ; client_name = !! $$helo_name ; action=WARN helo does not match DNS
+ # or
+ id=R-003 ; client_name = !!($$(helo_name)) ; action=WARN helo does not match DNS
+This is only valid for PCRE values (see list above). The comparison will be performed as case insensitive exact match.
+Use the '-vv' option to debug.
+
+
+
+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
+postfwd2 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:
+
+ # postfwd2 --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:
+
+ # postfwd2 --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 postfwd2 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=R01; 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.
+
+
+
+General
+Actions will be executed, when all rule items have matched a request (or at least one of any item list). You can refer to
+request attributes by preceeding $$ characters, like:
+
+ id=R-003; client_name = !!$$helo_name; action=WARN helo '$$helo_name' does not match DNS '$$client_name'
+ # or
+ id=R-003; client_name = !!$$helo_name; action=WARN helo '$$(helo_name)' does not match DNS '$$(client_name)'
+postfix actions
+Actions will be replied to postfix as result to policy delegation requests. Any action that postfix understands is allowed - see
+``man 5 access'' or http://www.postfix.org/access.5.html for a description. If no action is specified, the postfix WARN action
+which simply logs the event will be used for the corresponding rule.
+postfwd2 will return dunno if it has reached the end of the ruleset and no rule has matched. This can be changed by placing a last
+rule containing only an action statement:
+
+ ...
+ action=dunno ; sender=@domain.local # sender is ok
+ action=reject # default deny
+postfwd2 actions
+postfwd2 actions control the behaviour of the program. Currently you can specify the following:
+
+ jump (<id>)
+ jumps to rule with id <id>, use this to skip certain rules.
+ you can jump backwards - but remember that there is no loop
+ detection at the moment! jumps to non-existing ids will be skipped.
+
+ score (<score>)
+ the request's score will be modified by the specified <score>,
+ which must be a floating point value. the modificator can be either
+ +n.nn adds n.nn to current score
+ -n.nn sustracts n.nn from the current score
+ *n.nn multiplies the current score by n.nn
+ /n.nn divides the current score through n.nn
+ =n.nn sets the current score to n.nn
+ if the score exceeds the maximum set by `--scores` option (see
+ COMMAND LINE) or the score item (see ITEMS section), the action
+ defined for this case will be returned (default: 5.0=>"REJECT postfwd2 score exceeded").
+
+ set (<item>=<value>,<item>=<value>,...)
+ this command allows you to insert or override request attributes, which then may be
+ compared to your further ruleset. use this to speed up repeated comparisons to large item lists.
+ please see the EXAMPLES section for more information. you may separate multiple key=value pairs
+ by "," characters.
+
+ rate (<item>/<max>/<time>/<action>)
+ this command creates a counter for the given <item>, which will be increased any time a request
+ containing it arrives. if it exceeds <max> within <time> seconds it will return <action> to postfix.
+ rate counters are very fast as they are executed before the ruleset is parsed.
+ please note that <action> is currently limited to postfix actions (no postfwd actions)!
+ # no more than 3 requests per 5 minutes
+ # from the same "unknown" client
+ id=RATE01 ; client_name==unknown ; \
+ action==rate(client_address/3/300/450 4.7.1 sorry, max 3 requests per 5 minutes)
+
+ size (<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 size attribute. to do this reliably you should call postfwd2 from
+ smtpd_end_of_data_restrictions. if you want to be sure, you could check it within the ruleset:
+ # 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)
+
+ 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 postfwd2 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; 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; action=ask(127.0.0.1:10031:^dunno$)
+
+ wait (<delay>)
+ pauses the program execution for <delay> seconds. use this for
+ delaying or throtteling connections.
+
+ note (<string>)
+ just logs the given string and continues parsing the ruleset.
+ if the string is empty, nothing will be logged.
+
+ quit (<code>)
+ terminates the program with the given exit-code. postfix doesn`t
+ like that too much, so use it with care.
+You can reference to request attributes, like
+
+ id=R-HELO ; helo_name=^[^\.]+$ ; action=REJECT invalid helo '$$helo_name'
+These special attributes will be reset for any new rule:
+
+ rblcount - contains the number of RBL answers
+ rhsblcount - contains the number of RHSBL answers
+ matches - contains the number of matched items
+ dnsbltext - contains the dns TXT part of all RBL and RHSBL replies in the form
+ rbltype:rblname:<txt>; rbltype:rblname:<txt>; ...
+These special attributes will be changed for any matching rule:
+
+ request_hits - contains ids of all matching rules
+This means that it might be necessary to save them, if you plan to use these values in later rules:
+
+ # set vals
+ id=RBL01 ; rhsblcount=all ; rblcount=all ; \
+ rbl=list.dsbl.org, bl.spamcop.net, dnsbl.sorbs.net, zen.spamhaus.org ; \
+ rhsbl_client=rddn.dnsbl.net.au, rhsbl.ahbl.org, rhsbl.sorbs.net ; \
+ rhsbl_sender=rddn.dnsbl.net.au, rhsbl.ahbl.org, rhsbl.sorbs.net ; \
+ action=set(HIT_rhls=$$rhsblcount,HIT_rbls=$$rblcount,HIT_txt=$$dnsbltext)
+
+ # compare
+ id=RBL02 ; HIT_rhls>=1 ; HIT_rbls>=1 ; action=554 5.7.1 blocked using $$HIT_rhls RHSBLs and $$HIT_rbls RBLs [INFO: $$HIT_txt]
+ id=RBL03 ; HIT_rhls>=2 ; action=554 5.7.1 blocked using $$HIT_rhls RHSBLs [INFO: $$HIT_txt]
+ id=RBL04 ; HIT_rbls>=2 ; action=554 5.7.1 blocked using $$HIT_rbls RBLs [INFO: $$HIT_txt]
+
+
+
+Multiple use of long items or combinations of them may be abbreviated by macros. Those must be prefixed by '&&' (two '&' characters).
+First the macros have to be defined as follows:
+
+ &&RBLS { rbl=zen.spamhaus.org,list.dsbl.org,bl.spamcop.net,dnsbl.sorbs.net,ix.dnsbl.manitu.net; };
+Then these may be used in your rules, like:
+
+ &&RBLS ; client_name=^unknown$ ; action=REJECT
+ &&RBLS ; client_name=(\d+[\.-_]){4} ; action=REJECT
+ &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_] ; action=REJECT
+Macros can contain actions, too:
+
+ # definition
+ &&GONOW { action=REJECT your request caused our spam detection policy to reject this message. More info at http://www.domain.local ; };
+ # rules
+ &&GONOW ; &&RBLS ; client_name=^unknown$
+ &&GONOW ; &&RBLS ; client_name=(\d+[\.-_]){4}
+ &&GONOW ; &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_]
+Macros can contain macros, too:
+
+ # definition (note the trailing "\" characters)
+ &&RBLS { \
+ rbl=zen.spamhaus.org ; \
+ rbl=list.dsbl.org ; \
+ rbl=bl.spamcop.net ; \
+ rbl=dnsbl.sorbs.net ; \
+ rbl=ix.dnsbl.manitu.net ; \
+ };
+ &&DYNAMIC { \
+ client_name=^unknown$ ; \
+ client_name=(\d+[\.-_]){4} ; \
+ client_name=[\.-_](adsl|dynamic|ppp|)[\.-_] ; \
+ };
+ &&GOAWAY { &&RBLS; &&DYNAMIC; };
+ # rules
+ &&GOAWAY ; action=REJECT dynamic client and listed on RBL
+Basically macros are simple text substitutions - see the PARSER section for more information.
+
+
+
+Please visit http://www.postfwd.org/postfwd.plugins
+
+
+
+Ruleset
+The following arguments are used to specify the source of the postfwd2 ruleset. This means
+that at least one of the following is required for postfwd2 to work.
+
+ -f, --file <file>
+ Reads rules from <file>. Please see the CONFIGURATION section
+ below for more information.
+
+ -r, --rule <rule>
+ Adds <rule> to ruleset. Remember that you might have to quote
+ strings that contain whitespaces or shell characters.
+Plugins
+
+ --plugins
+ A file containing plugin routines for postfwd. Please see the
+ PLUGINS section for more information.
+Scoring
+
+ -s, --scores <val>=<action>
+ Returns <action> to postfix, when the request's score exceeds <val>
+Multiple usage is allowed. Just chain your arguments, like:
+
+ postfwd2 -r "<item>=<value>;action=<result>" -f <file> -f <file> --plugins <file> ...
+ or
+ postfwd2 --scores 4.5="WARN high score" --scores 5.0="REJECT postfwd2 score too high" ...
+In case of multiple scores, the highest match will count. The order of the arguments will be
+reflected in the postfwd2 ruleset.
+Networking
+postfwd2 can be run as daemon so that it listens on the network for incoming requests.
+The following arguments will control it's behaviour in this case.
+
+ -d, --daemon
+ postfwd2 will run as daemon and listen on the network for incoming
+ queries (default 127.0.0.1:10040).
+
+ -i, --interface <dev>
+ Bind postfwd2 to the specified interface (default 127.0.0.1).
+
+ -p, --port <port>
+ postfwd2 listens on the specified port (default tcp/10040).
+
+ --proto <type>
+ The protocol type for postfwd's socket. Currently you may use 'tcp' or 'unix' here.
+ To use postfwd2 with a unix domain socket, run it as follows:
+ postfwd2 --proto=unix --port=/somewhere/postfwd.socket
+
+ -u, --user <name>
+ Changes real and effective user to <name>.
+
+ -g, --group <name>
+ Changes real and effective group to <name>.
+
+ --umask <mask>
+ Changes the umask for filepermissions of the master process (pidfile).
+ Attention: This is umask, not chmod - you have to specify the bits that
+ should NOT apply. E.g.: umask 077 equals to chmod 700.
+
+ --cache_umask <mask>
+ Changes the umask for filepermissions of the cache process (unix domain socket).
+
+ --server_umask <mask>
+ Changes the umask for filepermissions of the server process (unix domain socket).
+
+ -R, --chroot <path>
+ Chroot the process to the specified path.
+ Test this before using - you might need some libs there.
+
+ --pidfile <path>
+ The process id will be saved in the specified file.
+
+ -l, --logname <label>
+ Labels the syslog messages. Useful when running multiple
+ instances of postfwd.
+
+ --loglen <int>
+ Truncates any syslog message after <int> characters.
+Optional arguments
+These parameters influence the way postfwd2 is working. Any of them can be combined.
+
+ -v, --verbose
+ Verbose logging displays a lot of useful information but can cause
+ your logfiles to grow noticeably. So use it with caution. Set the option
+ twice (-vv) to get more information (logs all request attributes).
+
+ -c, --cache <int> (default=600)
+ Timeout for request cache, results for identical requests will be
+ cached until config is reloaded or this time (in seconds) expired.
+ A setting of 0 disables this feature.
+
+ --cache-no-size
+ Ignores size attribute for cache comparisons which will lead to better
+ cache-hit rates. You should set this option, if you don't use the size
+ item in your ruleset.
+
+ --cache-no-sender
+ Ignores sender address for cache comparisons which will lead to better
+ cache-hit rates. You should set this option, if you don't use the sender
+ item in your ruleset.
+
+ --cache-rdomain-only
+ This will strip the localpart of the recipient's address before filling the
+ cache. This may considerably increase cache-hit rates.
+
+ --cache-rbl-timeout <timeout> (default=3600)
+ This default value will be used as timeout in seconds for rbl cache items,
+ if not specified in the ruleset.
+
+ --cache-rbl-default <pattern> (default=^127\.0\.0\.\d+$)
+ Matches <pattern> to rbl/rhsbl answers (regexp) if not specified in the ruleset.
+
+ --cacheid <item>, <item>, ...
+ This csv-separated list of request attributes will be used to construct
+ the request cache identifier. Use this only, if you know exactly what you
+ are doing. If you, for example, use postfwd2 only for RBL/RHSBL control,
+ you may set this to
+ postfwd2 --cache=3600 --cacheid=client_name,client_address
+ This increases efficiency of caching and improves postfwd's performance.
+ Warning: You should list all items here, which are used in your ruleset!
+
+ --cleanup-requests <interval> (default=600)
+ The request cache will be searched for timed out items after this <interval> in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ --cleanup-rbls <interval> (default=600)
+ The rbl cache will be searched for timed out items after this <interval> in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ --cleanup-rates <interval> (default=600)
+ The rate cache will be searched for timed out items after this <interval> in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ -S, --summary <int> (default=600)
+ Shows some usage statistics (program uptime, request counter, matching rules)
+ every <int> seconds. This option is included by the -v switch.
+ This feature uses the alarm signal, so you can force postfwd2 to dump the stats
+ using `kill -ALRM <pid>` (where <pid> is the process id of postfwd).
+
+ Example:
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Counters: 213000 seconds uptime, 39 rules
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Requests: 71643 overall, 49 last interval, 62.88% cache hits
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Averages: 20.18 overall, 4.90 last interval, 557.30 top
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Contents: 44 cached requests, 239 cached dnsbl results
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-001 matched: 2704 times
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-002 matched: 9351 times
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-003 matched: 3116 times
+ ...
+
+ --no-rulestats
+ Disables per rule statistics. Keeps your log clean, if you do not use them.
+ This option has no effect without --summary or --verbose set.
+
+ -L, --stdout
+ Redirects all syslog messages to stdout for debugging. Never use this with postfix!
+
+ -t, --test
+ In test mode postfwd2 always returns "dunno", but logs according
+ to it`s ruleset. -v will be set automatically with this option.
+
+ -n, --nodns
+ Disables all DNS based checks like RBL checks. Rules containing
+ such elements will be ignored.
+
+ -n, --nodnslog
+ Disables logging of dns events.
+
+ --dns_timeout (default: 14)
+ Sets the timeout for asynchonous dns queries in seconds. This value will apply to
+ all dns items in a rule.
+
+ --dns_timeout_max (default: 10)
+ Sets the maximum timeout counter for dnsbl lookups. If the timeouts exceed this value
+ the corresponding dnsbl will be deactivated for a while (see --dns_timeout_interval).
+
+ --dns_timeout_interval (default=1200)
+ The dnsbl timeout counter will be cleaned after this interval in seconds. Use this
+ in conjunction with the --dns_timeout_max parameter.
+
+ --dns_async_txt
+ Perform dnsbl A and TXT lookups simultaneously (otherwise only for listings with at
+ least one A record). This needs more network bandwidth due to increased queries but
+ might increase throughput because the lookups can be parallelized.
+
+ --dns_max_ns_lookups (default=0)
+ maximum ns names to lookup up with sender_ns_addrs item. use 0 for no maximum.
+
+ --dns_max_mx_lookups (default=0)
+ maximum mx names to lookup up with sender_mx_addrs item. use 0 for no maximum.
+
+ -I, --instantcfg
+ The config files, specified by -f will be re-read for every request
+ postfwd2 receives. This enables on-the-fly configuration changes
+ 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.
+
+ --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!
+
+ -C, --showconfig
+ Displays the current ruleset. Use -v for verbose output.
+
+ -V, --version
+ Displays the program version.
+
+ -h, --help
+ Shows program usage.
+
+ -m, --manual
+ Displays the program manual.
+
+ -D, --defaults
+ displays complete postfwd2 settings.
+
+ -P, --perfmon
+ This option turns of any syslogging and output. It is included
+ for performance testing.
+
+
+
+In daemon mode postfwd2 reloads it's ruleset after receiving a HUP signal. Please see the description of
+the '-I' switch to have your configuration refreshed for every request postfwd2 receives.
+
+
+
+
+ ## whitelisting
+ # 1. networks 192.168.1.0/24, 192.168.2.4
+ # 2. client_names *.gmx.net and *.gmx.de
+ # 3. sender *@someshop.tld from 11.22.33.44
+ id=WL001; action=dunno ; client_address=192.168.1.0/24, 192.168.2.4
+ id=WL002; action=dunno ; client_name=\.gmx\.(net|de)$
+ id=WL003; action=dunno ; sender=@someshop\.tld$ ; client_address=11.22.33.44
+
+ ## TLS control
+ # 1. *@authority.tld only with correct TLS fingerprint
+ # 2. *@secret.tld only with keysizes >=64
+ id=TL001; action=dunno ; sender=@authority\.tld$ ; ccert_fingerprint=AA:BB:CC..
+ id=TL002; action=REJECT wrong TLS fingerprint ; sender=@authority\.tld$
+ id=TL003; action=REJECT tls keylength < 64 ; sender=@secret\.tld$ ; encryption_keysize=64
+
+ ## Combined RBL checks
+ # This will reject mail if
+ # 1. listed on ix.dnsbl.manitu.net
+ # 2. listed on zen.spamhaus.org (sbl and xbl, dns cache timeout 1200s instead of 3600s)
+ # 3. listed on min 2 of bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ # 4. listed on bl.spamcop.net and one of rhsbl.ahbl.org, rhsbl.sorbs.net
+ id=RBL01 ; action=REJECT listed on ix.dnsbl.manitu.net ; rbl=ix.dnsbl.manitu.net
+ id=RBL02 ; action=REJECT listed on zen.spamhaus.org ; rbl=zen.spamhaus.org/127.0.0.[2-8]/1200
+ id=RBL03 ; action=REJECT listed on too many RBLs ; rblcount=2 ; rbl=bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ id=RBL04 ; action=REJECT combined RBL+RHSBL check ; rbl=bl.spamcop.net ; rhsbl=rhsbl.ahbl.org, rhsbl.sorbs.net
+
+ ## Message size (requires message_size_limit to be set to 30000000)
+ # 1. 30MB for systems in *.customer1.tld
+ # 2. 20MB for SASL user joejob
+ # 3. 10MB default
+ id=SZ001; state==END-OF-MESSAGE; action=DUNNO; size<=30000000 ; client_name=\.customer1.tld$
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=20000000 ; sasl_username==joejob
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=10000000
+ id=SZ100; state==END-OF-MESSAGE; action=REJECT message too large
+
+ ## Selective Greylisting
+ # 1. if listed on zen.spamhaus.org with results 127.0.0.10 or .11, dns cache timeout 1200s
+ # 2. Client has no rDNS
+ # 3. Client comes from several dialin domains
+ id=GR001; action=greylisting ; rbl=dul.dnsbl.sorbs.net, zen.spamhaus.org/127.0.0.1[01]/1200
+ id=GR002; action=greylisting ; client_name=^unknown$
+ id=GR003; action=greylisting ; client_name=\.(t-ipconnect|alicedsl|ish)\.de$
+
+ ## Date Time
+ date=24.12.2007-26.12.2007 ; action=450 4.7.1 office closed during christmas
+ time=04:00:00-05:00:00 ; action=450 4.7.1 maintenance ongoing, try again later
+ time=-07:00:00 ; sasl_username=jim ; action=450 4.7.1 to early for you, jim
+ time=22:00:00- ; sasl_username=jim ; action=450 4.7.1 to late now, jim
+ months=-Apr ; action=450 4.7.1 see you in may
+ days=!!Mon-Fri ; action=greylist
+
+ ## Usage of jump
+ # The following allows a message size of 30MB for different
+ # users/clients while others will only have 10MB.
+ id=R001 ; action=jump(R100) ; sasl_username=^(Alice|Bob|Jane)$
+ id=R002 ; action=jump(R100) ; client_address=192.168.1.0/24
+ id=R003 ; action=jump(R100) ; ccert_fingerprint=AA:BB:CC:DD:...
+ id=R004 ; action=jump(R100) ; ccert_fingerprint=AF:BE:CD:DC:...
+ id=R005 ; action=jump(R100) ; ccert_fingerprint=DD:CC:BB:DD:...
+ id=R099 ; state==END-OF-MESSAGE; action=REJECT message too big (max. 10MB); size=10000000
+ id=R100 ; state==END-OF-MESSAGE; action=REJECT message too big (max. 30MB); size=30000000
+
+ ## Usage of score
+ # The following rejects a mail, if the client
+ # - is listed on 1 RBL and 1 RHSBL
+ # - is listed in 1 RBL or 1 RHSBL and has no correct rDNS
+ # - other clients without correct rDNS will be greylist-checked
+ # - some whitelists are used to lower the score
+ id=S01 ; score=2.6 ; action=greylisting
+ id=S02 ; score=5.0 ; action=REJECT postfwd2 score too high
+ id=R00 ; action=score(-1.0) ; rbl=exemptions.ahbl.org,list.dnswl.org,query.bondedsender.org,spf.trusted-forwarder.org
+ id=R01 ; action=score(2.5) ; rbl=bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ id=R02 ; action=score(2.5) ; rhsbl=rhsbl.ahbl.org, rhsbl.sorbs.net
+ id=N01 ; action=score(-0.2) ; client_name==$$helo_name
+ id=N02 ; action=score(2.7) ; client_name=^unknown$
+ ...
+
+ ## Usage of rate and size
+ # The following temporary rejects requests from "unknown" clients, if they
+ # 1. exceeded 30 requests per hour or
+ # 2. tried to send more than 1.5mb within 10 minutes
+ id=RATE01 ; client_name==unknown ; state==RCPT ; \
+ action==rate(client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
+ id=SIZE01 ; client_name==unknown ; state==END_OF_DATA ; \
+ action==size(client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
+
+ ## Macros
+ # definition
+ &&RBLS { rbl=zen.spamhaus.org,list.dsbl.org,bl.spamcop.net,dnsbl.sorbs.net,ix.dnsbl.manitu.net; };
+ &&GONOW { action=REJECT your request caused our spam detection policy to reject this message. More info at http://www.domain.local ; };
+ # rules
+ &&GONOW ; &&RBLS ; client_name=^unknown$
+ &&GONOW ; &&RBLS ; client_name=(\d+[\.-_]){4}
+ &&GONOW ; &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_]
+
+ ## Groups
+ # definition
+ &&RBLS { \
+ rbl=zen.spamhaus.org ; \
+ rbl=list.dsbl.org ; \
+ rbl=bl.spamcop.net ; \
+ rbl=dnsbl.sorbs.net ; \
+ rbl=ix.dnsbl.manitu.net ; \
+ };
+ &&RHSBLS { \
+ ...
+ };
+ &&DYNAMIC { \
+ client_name==unknown ; \
+ client_name~=(\d+[\.-_]){4} ; \
+ client_name~=[\.-_](adsl|dynamic|ppp|)[\.-_] ; \
+ ...
+ };
+ &&BAD_HELO { \
+ helo_name==my.name.tld; \
+ helo_name~=^([^\.]+)$; \
+ helo_name~=\.(local|lan)$; \
+ ...
+ };
+ &&MAINTENANCE { \
+ date=15.01.2007 ; \
+ date=15.04.2007 ; \
+ date=15.07.2007 ; \
+ date=15.10.2007 ; \
+ time=03:00:00 - 04:00:00 ; \
+ };
+ # rules
+ id=COMBINED ; &&RBLS ; &&DYNAMIC ; action=REJECT dynamic client and listed on RBL
+ id=MAINTENANCE ; &&MAINTENANCE ; action=DEFER maintenance time - please try again later
+
+ # now with the set() command, note that long item
+ # lists don't have to be compared twice
+ id=RBL01 ; &&RBLS ; action=set(HIT_rbls=1)
+ id=HELO01 ; &&BAD_HELO ; action=set(HIT_helo=1)
+ id=DYNA01 ; &&DYNAMIC ; action=set(HIT_dyna=1)
+ id=REJECT01 ; HIT_rbls==1 ; HIT_helo==1 ; action=REJECT please see http://some.org/info?reject=01 for more info
+ id=REJECT02 ; HIT_rbls==1 ; HIT_dyna==1 ; action=REJECT please see http://some.org/info?reject=02 for more info
+ id=REJECT03 ; HIT_helo==1 ; HIT_dyna==1 ; action=REJECT please see http://some.org/info?reject=03 for more info
+
+ ## combined with enhanced rbl features
+ #
+ id=RBL01 ; rhsblcount=all ; rblcount=all ; &&RBLS ; &&RHSBLS ; \
+ action=set(HIT_dnsbls=$$rhsblcount,HIT_dnsbls+=$$rblcount,HIT_dnstxt=$$dnsbltext)
+ id=RBL02 ; HIT_dnsbls>=2 ; action=554 5.7.1 blocked using $$HIT_dnsbls DNSBLs [INFO: $$HIT_dnstxt]
+
+
+
+Configuration
+The postfwd2 ruleset can be specified at the commandline (-r option) or be read from files (-f). The order of your arguments will be kept. You should
+check the parser with the -C | --showconfig switch at the command line before applying a new config. The following call:
+
+ postfwd2 --showconfig \
+ -r "id=TEST; recipient_count=100; action=WARN mail with 100+ recipients" \
+ -f /etc/postfwd.cf \
+ -r "id=DEFAULT; action=dunno";
+will produce the following output:
+
+ Rule 0: id->"TEST" action->"WARN mail with 100+ recipients"; recipient_count->"100"
+ ...
+ ... <content of /etc/postfwd.cf> ...
+ ...
+ Rule <n>: id->"DEFAULT" action->"dunno"
+Multiple items of the same type will be added to lists (see the ITEMS section for more info):
+
+ postfwd2 --showconfig \
+ -r "client_address=192.168.1.0/24; client_address=172.16.26.32; action=dunno"
+will result in:
+
+ Rule 0: id->"R-0"; action->"dunno"; client_address->"192.168.1.0/24, 172.16.26.32"
+Macros are evaluated at configuration stage, which means that
+
+ postfwd2 --showconfig \
+ -r "&&RBLS { rbl=bl.spamcop.net; client_name=^unknown$; };" \
+ -r "id=RBL001; &&RBLS; action=REJECT listed on spamcop and bad rdns";
+will result in:
+
+ Rule 0: id->"RBL001"; action->"REJECT listed on spamcop and bad rdns"; rbl->"bl.spamcop.net"; client_name->"^unknown$"
+Request processing
+When a policy delegation request arrives it will be compared against postfwd`s ruleset. To inspect the processing in detail you should increase
+verbority using use the ``-v'' or ``-vv'' switch. ``-L'' redirects log messages to stdout.
+Keeping the order of the ruleset in general, items will be compared in random order, which basically means that
+
+ id=R001; action=dunno; client_address=192.168.1.1; sender=bob@alice.local
+equals to
+
+ id=R001; sender=bob@alice.local; client_address=192.168.1.1; action=dunno
+Lists will be evaluated in the specified order. This allows to place faster expressions at first:
+
+ postfwd2 --nodaemon -vv -L -r "id=RBL001; rbl=localrbl.local zen.spamhaus.org; action=REJECT" /some/where/request.sample
+produces the following
+
+ [LOGS info]: compare rbl: "remotehost.remote.net[68.10.1.7]" -> "localrbl.local"
+ [LOGS info]: count1 rbl: "2" -> "0"
+ [LOGS info]: query rbl: localrbl.local 7.1.10.68 (7.1.10.68.localrbl.local)
+ [LOGS info]: count2 rbl: "2" -> "0"
+ [LOGS info]: match rbl: FALSE
+ [LOGS info]: compare rbl: "remotehost.remote.net[68.10.1.7]" -> "zen.spamhaus.org"
+ [LOGS info]: count1 rbl: "2" -> "0"
+ [LOGS info]: query rbl: zen.spamhaus.org 7.1.10.68 (7.1.10.68.zen.spamhaus.org)
+ [LOGS info]: count2 rbl: "2" -> "0"
+ [LOGS info]: match rbl: FALSE
+ [LOGS info]: Action: dunno
+The negation operator !!(<value>) has the highest priority and therefore will be evaluated first. Then variable substitutions are performed:
+
+ postfwd2 --nodaemon -vv -L -r "id=TEST; action=REJECT; client_name=!!($$heloname)" /some/where/request.sample
+will give
+
+ [LOGS info]: compare client_name: "unknown" -> "!!($$helo_name)"
+ [LOGS info]: negate client_name: "unknown" -> "$$helo_name"
+ [LOGS info]: substitute client_name: "unknown" -> "english-breakfast.cloud8.net"
+ [LOGS info]: match client_name: TRUE
+ [LOGS info]: Action: REJECT
+Ruleset evaluation
+A rule hits when all items (or at least one element of a list for each item) have matched. As soon as one item (or all elements of a list) fails
+to compare against the request attribute the parser will jump to the next rule in the postfwd2 ruleset.
+If a rule matches, there are two options:
+* Rule returns postfix action (dunno, reject, ...)
+The parser stops rule processing and returns the action to postfix. Other rules will not be evaluated.
+* Rule returns postfwd2 action (jump(), note(), ...)
+The parser evaluates the given action and continues with the next rule (except for the jump()
or quit()
actions - please see the ACTIONS section
+for more information). Nothing will be sent to postfix.
+If no rule has matched and the end of the ruleset is reached postfwd2 will return dunno without logging anything unless in verbose mode. You may
+place a last catch-all rule to change that behaviour:
+
+ ... <your rules> ...
+ id=DEFAULT ; action=dunno
+will log any request that passes the ruleset without having hit a prior rule.
+
+
+
+To debug special steps of the parser the '--debug' switch takes a list of debug classes. Currently the following classes are defined:
+
+ all cache config debugdns devel dns getcache getdns
+ getdnspacket rates request setcache setdns
+ parent_cache parent_dns_cache parent_rate_cache parent_request_cache
+ child_cache child_dns_cache child_rate_cache child_request_cache
+
+
+
+Integration via daemon mode
+The common way to use postfwd2 is to start it as daemon, listening at a specified tcp port.
+postfwd2 will spawn multiple child processes which communicate with a parent cache. This is
+the prefered way to use postfwd2 in high volume environments. Start postfwd2 with the following parameters:
+
+ postfwd2 -d -f /etc/postfwd.cf -i 127.0.0.1 -p 10040 -u nobody -g nobody -S
+For efficient caching you should check if you can use the options --cacheid, --cache-rdomain-only,
+--cache-no-sender and --cache-no-size.
+Now check your syslogs (default facility ``mail'') for a line like:
+
+ Aug 9 23:00:24 mail postfwd[5158]: postfwd2 n.nn ready for input
+and use `netstat -an|grep 10040` to check for something like
+
+ tcp 0 0 127.0.0.1:10040 0.0.0.0:* LISTEN
+If everything works, open your postfix main.cf and insert the following
+
+ 127.0.0.1:10040_time_limit = 3600 <--- integration
+ smtpd_recipient_restrictions = permit_mynetworks <--- recommended
+ reject_unauth_destination <--- recommended
+ check_policy_service inet:127.0.0.1:10040 <--- integration
+Reload your configuration with `postfix reload` and watch your logs. In it works you should see
+lines like the following in your mail log:
+
+ Aug 9 23:01:24 mail postfwd[5158]: rule=22, id=ML_POSTFIX, client=english-breakfast.cloud9.net[168.100.1.7], sender=owner-postfix-users@postfix.tld, recipient=someone@domain.local, helo=english-breakfast.cloud9.net, proto=ESMTP, state=RCPT, action=dunno
+If you want to check for size or rcpt_count items you must integrate postfwd2 in smtp_data_restrictions or
+smtpd_end_of_data_restrictions. Of course you can also specify a restriction class and use it in your access
+tables. First create a file /etc/postfix/policy containing:
+
+ domain1.local postfwdcheck
+ domain2.local postfwdcheck
+ ...
+Then postmap that file (`postmap hash:/etc/postfix/policy`), open your main.cf and enter
+
+ # Restriction Classes
+ smtpd_restriction_classes = postfwdcheck, <some more>... <--- integration
+ postfwdcheck = check_policy_service inet:127.0.0.1:10040 <--- integration
+
+ 127.0.0.1:10040_time_limit = 3600 <--- integration
+ smtpd_recipient_restrictions = permit_mynetworks, <--- recommended
+ reject_unauth_destination, <--- recommended
+ ... <--- optional
+ check_recipient_access hash:/etc/postfix/policy, <--- integration
+ ... <--- optional
+Reload postfix and watch your logs.
+
+
+
+First you have to create a ruleset (see Configuration section). Check it with
+
+ postfwd2 -f /etc/postfwd.cf -C
+There is an example policy request distributed with postfwd, called 'request.sample'.
+Simply change it to meet your requirements and use
+
+ postfwd2 -f /etc/postfwd.cf <request.sample
+You should get an answer like
+
+ action=<whateveryouconfigured>
+For network tests I use netcat:
+
+ nc 127.0.0.1 10040 <request.sample
+to send a request to postfwd. If you receive nothing, make sure that postfwd2 is running and
+listening on the specified network settings.
+
+
+
+Some of these proposals might not match your environment. Please check your requirements and test new options carefully!
+
+ - use caching options
+ - use the correct match operator ==, <=, >=
+ - use ^ and/or $ in regular expressions
+ - use item lists (faster than single rules)
+ - use set() action on repeated item lists
+ - use jumps and rate limits
+ - use a pre-lookup rule for rbl/rhsbls with empty note() action
+
+
+
+See http://www.postfix.org/SMTPD_POLICY_README.html for a description
+of how Postfix policy servers work.
+
+
+
+
+postfwd2 is free software and released under BSD license, which basically means
+that you can do what you want as long as you keep the copyright notice:
+Copyright (c) 2009, Jan Peter Kessler
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the authors nor the names of his contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY ME ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+Jan Peter Kessler <info (AT) postfwd (DOT) org>. Let me know, if you have any suggestions.
+
+
+
+
diff --git a/doc/postfwd2.txt b/doc/postfwd2.txt
new file mode 100644
index 0000000..90e4b8d
--- /dev/null
+++ b/doc/postfwd2.txt
@@ -0,0 +1,1270 @@
+NAME
+ postfwd2 - postfix firewall daemon
+
+SYNOPSIS
+ postfwd2 [OPTIONS] [SOURCE1, SOURCE2, ...]
+
+ Ruleset: (at least one, multiple use is allowed):
+ -f, --file reads rules from
+ -r, --rule adds to config
+ -s, --scores = returns when score exceeds
+
+ Server:
+ -i, --interface listen on interface
+ -p, --port listen on port
+ --proto socket type (tcp or unix)
+ --server_socket e.g. tcp:127.0.0.1:10045
+ -u, --user set uid to user
+ -g, --group set gid to group
+ --umask umask for master filepermissions
+ --server_umask umask for server filepermissions
+ --pidfile create pidfile under
+ --min_servers spawn at least children
+ --max_servers do not spawn more than children
+ --min_spare_servers minimum idle children
+ --max_spare_servers maximum idle children
+
+ Cache:
+ -c, --cache sets the request-cache timeout to seconds
+ --cleanup-requests cleanup interval in seconds for request cache
+ --cache_interface listen on interface
+ --cache_port listen on port
+ --cache_proto socket type (tcp or unix)
+ --cache_socket e.g. tcp:127.0.0.1:10043
+ --cache_umask umask for cache filepermissions
+ --cacheid list of request items for cache-id
+ --cache-rdomain-only skip recipient localpart for cache-id
+ --cache-no-sender skip sender address for cache-id
+ --cache-no-size skip size for cache-id
+ --no_parent_request_cache disable parent request cache
+ --no_parent_rate_cache disable parent rate cache
+ --no_parent_dns_cache disable parent dns cache
+ --no_parent_cache disable all parent caches
+
+ Rates:
+ --cleanup-rates cleanup interval in seconds for rate cache
+
+ Control:
+ -k, --kill, --stop terminate postfwd2
+ --reload, --hup reload postfwd2
+ --watchdog watchdog timer in seconds
+ --respawn respawn delay in seconds
+ --failures max respawn failure counter
+ --daemons list of daemons to start
+ --dumpcache show cache contents
+ --dumpstats show statistics
+
+ DNS:
+ -n, --nodns skip any dns based test
+ --dns_timeout dns query timeout in seconds
+ --dns_timeout_max disable dnsbl after timeouts
+ --dns_timeout_interval reenable dnsbl after seconds
+ --cache-rbl-timeout default dns ttl if not specified in ruleset
+ --cache-rbl-default default dns pattern if not specified in ruleset
+ --cleanup-rbls cleanup old dns cache items every seconds
+ --dns_async_txt perform dnsbl A and TXT lookups simultaneously
+ --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
+
+ Optional:
+ -t, --test testing, always returns "dunno"
+ -S, --summary show stats every seconds
+ --noidlestats disables statistics when idle
+ --norulestats disables per rule statistics
+ -I, --instantcfg reloads ruleset on every new request
+ --config_timeout parser timeout in seconds
+
+ Logging:
+ -l, --logname label for syslog messages
+ --facility use syslog facility
+ --nodnslog do not log dns results
+ --anydnslog log any dns (even cached) results
+ --norulelog do not log rule actions
+ --nolog|--perfmon no logging at all
+ -v, --verbose verbose logging, use twice to increase
+ --debug list of debugging classes
+
+ Information (use only at command-line!):
+ -h, --help display this help and exit
+ -m, --manual shows program manual
+ -V, --version output version information and exit
+ -D, --defaults show postfwd2 settings and exit
+ -C, --showconfig show postfwd2 ruleset and exit (-v allowed)
+ -L, --stdout redirect syslog messages to stdout
+ -q, --quiet no syslogging, no stdout (-P works for compatibility)
+
+ Obsolete (only for compatibility with postfwd v1):
+ -d|--daemon, --shortlog, --dns_queuesize, --dns_retries
+
+DESCRIPTION
+ INTRODUCTION
+ postfwd2 is written to combine complex postfix restrictions in a ruleset
+ similar to those of the most firewalls. The program uses the postfix
+ policy delegation protocol to control access to the mail system before a
+ message has been accepted (please visit
+ for more information).
+
+ postfwd2 allows you to choose an action (e.g. reject, dunno) for a
+ combination of several smtp parameters (like sender and recipient
+ address, size or the client's TLS fingerprint). Also it offers simple
+ macros/acls which should allow straightforward and easy-to-read
+ configurations.
+
+ *Features:*
+
+ * Complex combinations of smtp parameters
+
+ * Combined RBL/RHSBL lookups with arbitrary actions depending on results
+
+ * Scoring system
+
+ * Date/time based rules
+
+ * Macros/ACLs, Groups, Negation
+
+ * Compare request attributes (e.g. client_name and helo_name)
+
+ * Internal caching for requests and dns lookups
+
+ * Built in statistics for rule efficiency analysis
+
+ CONFIGURATION
+ A configuration line consists of optional item=value pairs, separated by
+ semicolons (`;`) and the appropriate desired action:
+
+ [ [=><~]=; [=><~]=; ... ] action=
+
+ *Example:*
+
+ client_address=192.168.1.1 ; sender==no@bad.local ; action=REJECT
+
+ This will deny all mail from 192.168.1.1 with envelope sender
+ no@bad.local. The order of the elements is not important. So the
+ following would lead to the same result as the previous example:
+
+ action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
+
+ The way how request items are compared to the ruleset can be influenced
+ 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 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
+ identifier for each of it:
+
+ id=R_001 ; action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
+
+ You may use these identifiers as target for the `jump()` command (see
+ ACTIONS section below). Leading or trailing whitespace characters will
+ be ignored. Use '#' to comment your configuration. Others will
+ appreciate.
+
+ A ruleset consists of one or multiple rules, which can be loaded from
+ files or passed as command line arguments. Please see the COMMAND LINE
+ section below for more information on this topic.
+
+ Rules can span multiple lines by adding a trailing backslash "\"
+ character:
+
+ id=R_001 ; client_address=192.168.1.0/24; sender==no@bad.local; \
+ action=REJECT please use your relay from there
+
+ ITEMS
+ id - a unique rule id, which can be used for log analysis
+ ids also serve as targets for the "jump" command.
+
+ date, time - a time or date range within the specified rule shall hit
+ # FORMAT:
+ # Feb, 29th
+ date=29.02.2008
+ # Dec, 24th - 26th
+ date=24.12.2008-26.12.2008
+ # from today until Nov, 23rd
+ date=-23.09.2008
+ # from April, 1st until today
+ date=01.04.2008-
+
+ days, months - a range of weekdays (Sun-Sat) or months (Jan-Dec)
+ within the specified rule shall hit
+
+ score - when the specified score is hit (see ACTIONS section)
+ the specified action will be returned to postfix
+ scores are set global until redefined!
+
+ request_score - this value allows to access a request's score. it
+ may be used as variable ($$request_score).
+
+ rbl, rhsbl, - query the specified RBLs/RHSBLs, possible values are:
+ rhsbl_client, [//, //]
+ rhsbl_sender, (defaults: reply=^127\.0\.0\.\d+$ maxcache=3600)
+ rhsbl_reverse_client the results of all rhsbl_* queries will be combined
+ in rhsbl_count (see below).
+
+ rblcount, rhsblcount - minimum RBL/RHSBL hitcounts to match. if not specified
+ a single RBL/RHSBL hit will match the rbl/rhsbl items.
+ you may specify 'all' to evaluate all items, and use
+ it as variable in an action (see ACTIONS section)
+ (default: 1)
+
+ sender_localpart, - the local-/domainpart of the sender address
+ sender_domain
+
+ recipient_localpart, - the local-/domainpart of the recipient address
+ recipient_domain
+
+ helo_address - postfwd2 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, - postfwd2 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, - postfwd2 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 - postfwd2 version, contains "postfwd2 n.nn"
+ this enables version based checks in your rulesets
+ (e.g. for migration). works with old versions too,
+ because a non-existing item always returns false:
+ id=R01; version~=1.10; sender_domain==some.org \
+ ; action=REJECT sorry no access
+
+ Besides these you can specify any attribute of the postfix policy
+ delegation protocol. Feel free to combine them the way you need it (have
+ a look at the EXAMPLES section below).
+
+ Most values can be specified as regular expressions (PCRE). Please see
+ the table below for details:
+
+ # ==========================================================
+ # ITEM=VALUE TYPE
+ # ==========================================================
+ id=something mask = string
+ date=01.04.2007-22.04.2007 mask = date (DD.MM.YYYY-DD.MM.YYYY)
+ time=08:30:00-17:00:00 mask = time (HH:MM:SS-HH:MM:SS)
+ days=Mon-Wed mask = weekdays (Mon-Wed) or numeric (1-3)
+ months=Feb-Apr mask = months (Feb-Apr) or numeric (1-3)
+ score=5.0 mask = maximum floating point value
+ rbl=zen.spamhaus.org mask = //[,...]
+ rblcount=2 mask = numeric, will match if rbl hits >= 2
+ helo_address= mask = CIDR[,CIDR,...]
+ sender_ns_names=some.domain.tld mask = PCRE
+ sender_mx_names=some.domain.tld mask = PCRE
+ sender_ns_addrs= mask = CIDR[,CIDR,...]
+ sender_mx_addrs= mask = CIDR[,CIDR,...]
+ # ------------------------------
+ # Postfix version 2.1 and later:
+ # ------------------------------
+ client_address= mask = CIDR[,CIDR,...]
+ client_name=another.domain.tld mask = PCRE
+ reverse_client_name=another.domain.tld mask = PCRE
+ helo_name=some.domain.tld mask = PCRE
+ sender=foo@bar.tld mask = PCRE
+ recipient=bar@foo.tld mask = PCRE
+ recipient_count=5 mask = numeric, will match if recipients >= 5
+ # ------------------------------
+ # Postfix version 2.2 and later:
+ # ------------------------------
+ sasl_method=plain mask = PCRE
+ sasl_username=you mask = PCRE
+ sasl_sender= mask = PCRE
+ size=12345 mask = numeric, will match if size >= 12345
+ ccert_subject=blackhole.nowhere.local mask = PCRE (only if tls verified)
+ ccert_issuer=John+20Doe mask = PCRE (only if tls verified)
+ ccert_fingerprint=AA:BB:CC:DD:EE:... mask = PCRE (do NOT use "..." here)
+ # ------------------------------
+ # Postfix version 2.3 and later:
+ # ------------------------------
+ encryption_protocol=TLSv1/SSLv3 mask = PCRE
+ encryption_cipher=DHE-RSA-AES256-SHA mask = PCRE
+ encryption_keysize=256 mask = numeric, will match if keysize >= 256
+ ...
+
+ the current list can be found at
+ . Please read carefully
+ about which attribute can be used at which level of the smtp transaction
+ (e.g. size will only work reliably at END_OF_DATA level). Pattern
+ matching is performed case insensitive.
+
+ Multiple use of the same item is allowed and will compared as logical
+ OR, which means that this will work as expected:
+
+ id=TRUST001; action=OK; encryption_keysize=64; \
+ ccert_fingerprint=11:22:33:44:55:66:77:88:99; \
+ ccert_fingerprint=22:33:44:55:66:77:88:99:00; \
+ ccert_fingerprint=33:44:55:66:77:88:99:00:11; \
+ sender=@domain\.local$
+
+ client_address, rbl and rhsbl items may also be specified as
+ whitespace-or-comma-separated values:
+
+ id=SKIP01; action=dunno; \
+ client_address=192.168.1.0/24, 172.16.254.23
+ id=SKIP02; action=dunno; \
+ client_address= 10.10.3.32 \
+ 10.216.222.0/27
+
+ The following items must be unique:
+
+ id, minimum and maximum values, rblcount and rhsblcount
+
+ 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?
+
+ Request attributes can be compared by preceeding '$$' characters, e.g.:
+
+ id=R-003 ; client_name = !! $$helo_name ; action=WARN helo does not match DNS
+ # or
+ id=R-003 ; client_name = !!($$(helo_name)) ; action=WARN helo does not match DNS
+
+ This is only valid for PCRE values (see list above). The comparison will
+ 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
+
+ postfwd2 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:
+
+ # postfwd2 --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:
+
+ # postfwd2 --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 postfwd2
+ 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=R01; 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*
+
+ Actions will be executed, when all rule items have matched a request (or
+ at least one of any item list). You can refer to request attributes by
+ preceeding $$ characters, like:
+
+ id=R-003; client_name = !!$$helo_name; action=WARN helo '$$helo_name' does not match DNS '$$client_name'
+ # or
+ id=R-003; client_name = !!$$helo_name; action=WARN helo '$$(helo_name)' does not match DNS '$$(client_name)'
+
+ *postfix actions*
+
+ Actions will be replied to postfix as result to policy delegation
+ requests. Any action that postfix understands is allowed - see "man 5
+ access" or for a description. If
+ no action is specified, the postfix WARN action which simply logs the
+ event will be used for the corresponding rule.
+
+ postfwd2 will return dunno if it has reached the end of the ruleset and
+ no rule has matched. This can be changed by placing a last rule
+ containing only an action statement:
+
+ ...
+ action=dunno ; sender=@domain.local # sender is ok
+ action=reject # default deny
+
+ *postfwd2 actions*
+
+ postfwd2 actions control the behaviour of the program. Currently you can
+ specify the following:
+
+ jump ()
+ jumps to rule with id , use this to skip certain rules.
+ you can jump backwards - but remember that there is no loop
+ detection at the moment! jumps to non-existing ids will be skipped.
+
+ score ()
+ the request's score will be modified by the specified ,
+ which must be a floating point value. the modificator can be either
+ +n.nn adds n.nn to current score
+ -n.nn sustracts n.nn from the current score
+ *n.nn multiplies the current score by n.nn
+ /n.nn divides the current score through n.nn
+ =n.nn sets the current score to n.nn
+ if the score exceeds the maximum set by `--scores` option (see
+ COMMAND LINE) or the score item (see ITEMS section), the action
+ defined for this case will be returned (default: 5.0=>"REJECT postfwd2 score exceeded").
+
+ set (- =
,- =
,...)
+ this command allows you to insert or override request attributes, which then may be
+ compared to your further ruleset. use this to speed up repeated comparisons to large item lists.
+ please see the EXAMPLES section for more information. you may separate multiple key=value pairs
+ by "," characters.
+
+ rate (- /
//)
+ this command creates a counter for the given - , which will be increased any time a request
+ containing it arrives. if it exceeds
within seconds it will return to postfix.
+ rate counters are very fast as they are executed before the ruleset is parsed.
+ please note that is currently limited to postfix actions (no postfwd actions)!
+ # no more than 3 requests per 5 minutes
+ # from the same "unknown" client
+ id=RATE01 ; client_name==unknown ; \
+ action==rate(client_address/3/300/450 4.7.1 sorry, max 3 requests per 5 minutes)
+
+ size (- /
//)
+ this command works similar to the rate() command with the difference, that the rate counter is
+ increased by the request's size attribute. to do this reliably you should call postfwd2 from
+ smtpd_end_of_data_restrictions. if you want to be sure, you could check it within the ruleset:
+ # 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)
+
+ rcpt (- /
//)
+ 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 (:[:])
+ 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 postfwd2 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; 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; action=ask(127.0.0.1:10031:^dunno$)
+
+ wait ()
+ pauses the program execution for seconds. use this for
+ delaying or throtteling connections.
+
+ note ()
+ just logs the given string and continues parsing the ruleset.
+ if the string is empty, nothing will be logged.
+
+ quit ()
+ terminates the program with the given exit-code. postfix doesn`t
+ like that too much, so use it with care.
+
+ You can reference to request attributes, like
+
+ id=R-HELO ; helo_name=^[^\.]+$ ; action=REJECT invalid helo '$$helo_name'
+
+ These special attributes will be reset for any new rule:
+
+ rblcount - contains the number of RBL answers
+ rhsblcount - contains the number of RHSBL answers
+ matches - contains the number of matched items
+ dnsbltext - contains the dns TXT part of all RBL and RHSBL replies in the form
+ rbltype:rblname:; rbltype:rblname:; ...
+
+ These special attributes will be changed for any matching rule:
+
+ request_hits - contains ids of all matching rules
+
+ This means that it might be necessary to save them, if you plan to use
+ these values in later rules:
+
+ # set vals
+ id=RBL01 ; rhsblcount=all ; rblcount=all ; \
+ rbl=list.dsbl.org, bl.spamcop.net, dnsbl.sorbs.net, zen.spamhaus.org ; \
+ rhsbl_client=rddn.dnsbl.net.au, rhsbl.ahbl.org, rhsbl.sorbs.net ; \
+ rhsbl_sender=rddn.dnsbl.net.au, rhsbl.ahbl.org, rhsbl.sorbs.net ; \
+ action=set(HIT_rhls=$$rhsblcount,HIT_rbls=$$rblcount,HIT_txt=$$dnsbltext)
+
+ # compare
+ id=RBL02 ; HIT_rhls>=1 ; HIT_rbls>=1 ; action=554 5.7.1 blocked using $$HIT_rhls RHSBLs and $$HIT_rbls RBLs [INFO: $$HIT_txt]
+ id=RBL03 ; HIT_rhls>=2 ; action=554 5.7.1 blocked using $$HIT_rhls RHSBLs [INFO: $$HIT_txt]
+ id=RBL04 ; HIT_rbls>=2 ; action=554 5.7.1 blocked using $$HIT_rbls RBLs [INFO: $$HIT_txt]
+
+ MACROS/ACLS
+ Multiple use of long items or combinations of them may be abbreviated by
+ macros. Those must be prefixed by '&&' (two '&' characters). First the
+ macros have to be defined as follows:
+
+ &&RBLS { rbl=zen.spamhaus.org,list.dsbl.org,bl.spamcop.net,dnsbl.sorbs.net,ix.dnsbl.manitu.net; };
+
+ Then these may be used in your rules, like:
+
+ &&RBLS ; client_name=^unknown$ ; action=REJECT
+ &&RBLS ; client_name=(\d+[\.-_]){4} ; action=REJECT
+ &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_] ; action=REJECT
+
+ Macros can contain actions, too:
+
+ # definition
+ &&GONOW { action=REJECT your request caused our spam detection policy to reject this message. More info at http://www.domain.local; };
+ # rules
+ &&GONOW ; &&RBLS ; client_name=^unknown$
+ &&GONOW ; &&RBLS ; client_name=(\d+[\.-_]){4}
+ &&GONOW ; &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_]
+
+ Macros can contain macros, too:
+
+ # definition (note the trailing "\" characters)
+ &&RBLS { \
+ rbl=zen.spamhaus.org ; \
+ rbl=list.dsbl.org ; \
+ rbl=bl.spamcop.net ; \
+ rbl=dnsbl.sorbs.net ; \
+ rbl=ix.dnsbl.manitu.net ; \
+ };
+ &&DYNAMIC { \
+ client_name=^unknown$ ; \
+ client_name=(\d+[\.-_]){4} ; \
+ client_name=[\.-_](adsl|dynamic|ppp|)[\.-_] ; \
+ };
+ &&GOAWAY { &&RBLS; &&DYNAMIC; };
+ # rules
+ &&GOAWAY ; action=REJECT dynamic client and listed on RBL
+
+ Basically macros are simple text substitutions - see the "PARSER"
+ section for more information.
+
+ PLUGINS
+ Please visit
+
+ COMMAND LINE
+ *Ruleset*
+
+ The following arguments are used to specify the source of the postfwd2
+ ruleset. This means that at least one of the following is required for
+ postfwd2 to work.
+
+ -f, --file
+ Reads rules from . Please see the CONFIGURATION section
+ below for more information.
+
+ -r, --rule
+ Adds to ruleset. Remember that you might have to quote
+ strings that contain whitespaces or shell characters.
+
+ *Plugins*
+
+ --plugins
+ A file containing plugin routines for postfwd. Please see the
+ PLUGINS section for more information.
+
+ *Scoring*
+
+ -s, --scores =
+ Returns to postfix, when the request's score exceeds
+
+ Multiple usage is allowed. Just chain your arguments, like:
+
+ postfwd2 -r "- =
;action=" -f -f --plugins ...
+ or
+ postfwd2 --scores 4.5="WARN high score" --scores 5.0="REJECT postfwd2 score too high" ...
+
+ In case of multiple scores, the highest match will count. The order of
+ the arguments will be reflected in the postfwd2 ruleset.
+
+ *Networking*
+
+ postfwd2 can be run as daemon so that it listens on the network for
+ incoming requests. The following arguments will control it's behaviour
+ in this case.
+
+ -d, --daemon
+ postfwd2 will run as daemon and listen on the network for incoming
+ queries (default 127.0.0.1:10040).
+
+ -i, --interface
+ Bind postfwd2 to the specified interface (default 127.0.0.1).
+
+ -p, --port
+ postfwd2 listens on the specified port (default tcp/10040).
+
+ --proto
+ The protocol type for postfwd's socket. Currently you may use 'tcp' or 'unix' here.
+ To use postfwd2 with a unix domain socket, run it as follows:
+ postfwd2 --proto=unix --port=/somewhere/postfwd.socket
+
+ -u, --user
+ Changes real and effective user to .
+
+ -g, --group
+ Changes real and effective group to .
+
+ --umask
+ Changes the umask for filepermissions of the master process (pidfile).
+ Attention: This is umask, not chmod - you have to specify the bits that
+ should NOT apply. E.g.: umask 077 equals to chmod 700.
+
+ --cache_umask
+ Changes the umask for filepermissions of the cache process (unix domain socket).
+
+ --server_umask
+ Changes the umask for filepermissions of the server process (unix domain socket).
+
+ -R, --chroot
+ Chroot the process to the specified path.
+ Test this before using - you might need some libs there.
+
+ --pidfile
+ The process id will be saved in the specified file.
+
+ -l, --logname
+ Labels the syslog messages. Useful when running multiple
+ instances of postfwd.
+
+ --loglen
+ Truncates any syslog message after characters.
+
+ *Optional arguments*
+
+ These parameters influence the way postfwd2 is working. Any of them can
+ be combined.
+
+ -v, --verbose
+ Verbose logging displays a lot of useful information but can cause
+ your logfiles to grow noticeably. So use it with caution. Set the option
+ twice (-vv) to get more information (logs all request attributes).
+
+ -c, --cache (default=600)
+ Timeout for request cache, results for identical requests will be
+ cached until config is reloaded or this time (in seconds) expired.
+ A setting of 0 disables this feature.
+
+ --cache-no-size
+ Ignores size attribute for cache comparisons which will lead to better
+ cache-hit rates. You should set this option, if you don't use the size
+ item in your ruleset.
+
+ --cache-no-sender
+ Ignores sender address for cache comparisons which will lead to better
+ cache-hit rates. You should set this option, if you don't use the sender
+ item in your ruleset.
+
+ --cache-rdomain-only
+ This will strip the localpart of the recipient's address before filling the
+ cache. This may considerably increase cache-hit rates.
+
+ --cache-rbl-timeout (default=3600)
+ This default value will be used as timeout in seconds for rbl cache items,
+ if not specified in the ruleset.
+
+ --cache-rbl-default (default=^127\.0\.0\.\d+$)
+ Matches to rbl/rhsbl answers (regexp) if not specified in the ruleset.
+
+ --cacheid - ,
- , ...
+ This csv-separated list of request attributes will be used to construct
+ the request cache identifier. Use this only, if you know exactly what you
+ are doing. If you, for example, use postfwd2 only for RBL/RHSBL control,
+ you may set this to
+ postfwd2 --cache=3600 --cacheid=client_name,client_address
+ This increases efficiency of caching and improves postfwd's performance.
+ Warning: You should list all items here, which are used in your ruleset!
+
+ --cleanup-requests
(default=600)
+ The request cache will be searched for timed out items after this in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ --cleanup-rbls (default=600)
+ The rbl cache will be searched for timed out items after this in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ --cleanup-rates (default=600)
+ The rate cache will be searched for timed out items after this in
+ seconds. It is a minimum value. The cleanup process will only take place, when
+ a new request arrives.
+
+ -S, --summary (default=600)
+ Shows some usage statistics (program uptime, request counter, matching rules)
+ every seconds. This option is included by the -v switch.
+ This feature uses the alarm signal, so you can force postfwd2 to dump the stats
+ using `kill -ALRM ` (where is the process id of postfwd).
+
+ Example:
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Counters: 213000 seconds uptime, 39 rules
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Requests: 71643 overall, 49 last interval, 62.88% cache hits
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Averages: 20.18 overall, 4.90 last interval, 557.30 top
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Contents: 44 cached requests, 239 cached dnsbl results
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-001 matched: 2704 times
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-002 matched: 9351 times
+ Aug 19 12:39:45 mail1 postfwd[666]: [STATS] Rule ID: R-003 matched: 3116 times
+ ...
+
+ --no-rulestats
+ Disables per rule statistics. Keeps your log clean, if you do not use them.
+ This option has no effect without --summary or --verbose set.
+
+ -L, --stdout
+ Redirects all syslog messages to stdout for debugging. Never use this with postfix!
+
+ -t, --test
+ In test mode postfwd2 always returns "dunno", but logs according
+ to it`s ruleset. -v will be set automatically with this option.
+
+ -n, --nodns
+ Disables all DNS based checks like RBL checks. Rules containing
+ such elements will be ignored.
+
+ -n, --nodnslog
+ Disables logging of dns events.
+
+ --dns_timeout (default: 14)
+ Sets the timeout for asynchonous dns queries in seconds. This value will apply to
+ all dns items in a rule.
+
+ --dns_timeout_max (default: 10)
+ Sets the maximum timeout counter for dnsbl lookups. If the timeouts exceed this value
+ the corresponding dnsbl will be deactivated for a while (see --dns_timeout_interval).
+
+ --dns_timeout_interval (default=1200)
+ The dnsbl timeout counter will be cleaned after this interval in seconds. Use this
+ in conjunction with the --dns_timeout_max parameter.
+
+ --dns_async_txt
+ Perform dnsbl A and TXT lookups simultaneously (otherwise only for listings with at
+ least one A record). This needs more network bandwidth due to increased queries but
+ might increase throughput because the lookups can be parallelized.
+
+ --dns_max_ns_lookups (default=0)
+ maximum ns names to lookup up with sender_ns_addrs item. use 0 for no maximum.
+
+ --dns_max_mx_lookups (default=0)
+ maximum mx names to lookup up with sender_mx_addrs item. use 0 for no maximum.
+
+ -I, --instantcfg
+ The config files, specified by -f will be re-read for every request
+ postfwd2 receives. This enables on-the-fly configuration changes
+ 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.
+
+ --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
+ with postfix!
+
+ -C, --showconfig
+ Displays the current ruleset. Use -v for verbose output.
+
+ -V, --version
+ Displays the program version.
+
+ -h, --help
+ Shows program usage.
+
+ -m, --manual
+ Displays the program manual.
+
+ -D, --defaults
+ displays complete postfwd2 settings.
+
+ -P, --perfmon
+ This option turns of any syslogging and output. It is included
+ for performance testing.
+
+ REFRESH
+ In daemon mode postfwd2 reloads it's ruleset after receiving a HUP
+ signal. Please see the description of the '-I' switch to have your
+ configuration refreshed for every request postfwd2 receives.
+
+ EXAMPLES
+ ## whitelisting
+ # 1. networks 192.168.1.0/24, 192.168.2.4
+ # 2. client_names *.gmx.net and *.gmx.de
+ # 3. sender *@someshop.tld from 11.22.33.44
+ id=WL001; action=dunno ; client_address=192.168.1.0/24, 192.168.2.4
+ id=WL002; action=dunno ; client_name=\.gmx\.(net|de)$
+ id=WL003; action=dunno ; sender=@someshop\.tld$ ; client_address=11.22.33.44
+
+ ## TLS control
+ # 1. *@authority.tld only with correct TLS fingerprint
+ # 2. *@secret.tld only with keysizes >=64
+ id=TL001; action=dunno ; sender=@authority\.tld$ ; ccert_fingerprint=AA:BB:CC..
+ id=TL002; action=REJECT wrong TLS fingerprint ; sender=@authority\.tld$
+ id=TL003; action=REJECT tls keylength < 64 ; sender=@secret\.tld$ ; encryption_keysize=64
+
+ ## Combined RBL checks
+ # This will reject mail if
+ # 1. listed on ix.dnsbl.manitu.net
+ # 2. listed on zen.spamhaus.org (sbl and xbl, dns cache timeout 1200s instead of 3600s)
+ # 3. listed on min 2 of bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ # 4. listed on bl.spamcop.net and one of rhsbl.ahbl.org, rhsbl.sorbs.net
+ id=RBL01 ; action=REJECT listed on ix.dnsbl.manitu.net ; rbl=ix.dnsbl.manitu.net
+ id=RBL02 ; action=REJECT listed on zen.spamhaus.org ; rbl=zen.spamhaus.org/127.0.0.[2-8]/1200
+ id=RBL03 ; action=REJECT listed on too many RBLs ; rblcount=2 ; rbl=bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ id=RBL04 ; action=REJECT combined RBL+RHSBL check ; rbl=bl.spamcop.net ; rhsbl=rhsbl.ahbl.org, rhsbl.sorbs.net
+
+ ## Message size (requires message_size_limit to be set to 30000000)
+ # 1. 30MB for systems in *.customer1.tld
+ # 2. 20MB for SASL user joejob
+ # 3. 10MB default
+ id=SZ001; state==END-OF-MESSAGE; action=DUNNO; size<=30000000 ; client_name=\.customer1.tld$
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=20000000 ; sasl_username==joejob
+ id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=10000000
+ id=SZ100; state==END-OF-MESSAGE; action=REJECT message too large
+
+ ## Selective Greylisting
+ # 1. if listed on zen.spamhaus.org with results 127.0.0.10 or .11, dns cache timeout 1200s
+ # 2. Client has no rDNS
+ # 3. Client comes from several dialin domains
+ id=GR001; action=greylisting ; rbl=dul.dnsbl.sorbs.net, zen.spamhaus.org/127.0.0.1[01]/1200
+ id=GR002; action=greylisting ; client_name=^unknown$
+ id=GR003; action=greylisting ; client_name=\.(t-ipconnect|alicedsl|ish)\.de$
+
+ ## Date Time
+ date=24.12.2007-26.12.2007 ; action=450 4.7.1 office closed during christmas
+ time=04:00:00-05:00:00 ; action=450 4.7.1 maintenance ongoing, try again later
+ time=-07:00:00 ; sasl_username=jim ; action=450 4.7.1 to early for you, jim
+ time=22:00:00- ; sasl_username=jim ; action=450 4.7.1 to late now, jim
+ months=-Apr ; action=450 4.7.1 see you in may
+ days=!!Mon-Fri ; action=greylist
+
+ ## Usage of jump
+ # The following allows a message size of 30MB for different
+ # users/clients while others will only have 10MB.
+ id=R001 ; action=jump(R100) ; sasl_username=^(Alice|Bob|Jane)$
+ id=R002 ; action=jump(R100) ; client_address=192.168.1.0/24
+ id=R003 ; action=jump(R100) ; ccert_fingerprint=AA:BB:CC:DD:...
+ id=R004 ; action=jump(R100) ; ccert_fingerprint=AF:BE:CD:DC:...
+ id=R005 ; action=jump(R100) ; ccert_fingerprint=DD:CC:BB:DD:...
+ id=R099 ; state==END-OF-MESSAGE; action=REJECT message too big (max. 10MB); size=10000000
+ id=R100 ; state==END-OF-MESSAGE; action=REJECT message too big (max. 30MB); size=30000000
+
+ ## Usage of score
+ # The following rejects a mail, if the client
+ # - is listed on 1 RBL and 1 RHSBL
+ # - is listed in 1 RBL or 1 RHSBL and has no correct rDNS
+ # - other clients without correct rDNS will be greylist-checked
+ # - some whitelists are used to lower the score
+ id=S01 ; score=2.6 ; action=greylisting
+ id=S02 ; score=5.0 ; action=REJECT postfwd2 score too high
+ id=R00 ; action=score(-1.0) ; rbl=exemptions.ahbl.org,list.dnswl.org,query.bondedsender.org,spf.trusted-forwarder.org
+ id=R01 ; action=score(2.5) ; rbl=bl.spamcop.net, list.dsbl.org, dnsbl.sorbs.net
+ id=R02 ; action=score(2.5) ; rhsbl=rhsbl.ahbl.org, rhsbl.sorbs.net
+ id=N01 ; action=score(-0.2) ; client_name==$$helo_name
+ id=N02 ; action=score(2.7) ; client_name=^unknown$
+ ...
+
+ ## Usage of rate and size
+ # The following temporary rejects requests from "unknown" clients, if they
+ # 1. exceeded 30 requests per hour or
+ # 2. tried to send more than 1.5mb within 10 minutes
+ id=RATE01 ; client_name==unknown ; state==RCPT ; \
+ action==rate(client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
+ id=SIZE01 ; client_name==unknown ; state==END_OF_DATA ; \
+ action==size(client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
+
+ ## Macros
+ # definition
+ &&RBLS { rbl=zen.spamhaus.org,list.dsbl.org,bl.spamcop.net,dnsbl.sorbs.net,ix.dnsbl.manitu.net; };
+ &&GONOW { action=REJECT your request caused our spam detection policy to reject this message. More info at http://www.domain.local; };
+ # rules
+ &&GONOW ; &&RBLS ; client_name=^unknown$
+ &&GONOW ; &&RBLS ; client_name=(\d+[\.-_]){4}
+ &&GONOW ; &&RBLS ; client_name=[\.-_](adsl|dynamic|ppp|)[\.-_]
+
+ ## Groups
+ # definition
+ &&RBLS { \
+ rbl=zen.spamhaus.org ; \
+ rbl=list.dsbl.org ; \
+ rbl=bl.spamcop.net ; \
+ rbl=dnsbl.sorbs.net ; \
+ rbl=ix.dnsbl.manitu.net ; \
+ };
+ &&RHSBLS { \
+ ...
+ };
+ &&DYNAMIC { \
+ client_name==unknown ; \
+ client_name~=(\d+[\.-_]){4} ; \
+ client_name~=[\.-_](adsl|dynamic|ppp|)[\.-_] ; \
+ ...
+ };
+ &&BAD_HELO { \
+ helo_name==my.name.tld; \
+ helo_name~=^([^\.]+)$; \
+ helo_name~=\.(local|lan)$; \
+ ...
+ };
+ &&MAINTENANCE { \
+ date=15.01.2007 ; \
+ date=15.04.2007 ; \
+ date=15.07.2007 ; \
+ date=15.10.2007 ; \
+ time=03:00:00 - 04:00:00 ; \
+ };
+ # rules
+ id=COMBINED ; &&RBLS ; &&DYNAMIC ; action=REJECT dynamic client and listed on RBL
+ id=MAINTENANCE ; &&MAINTENANCE ; action=DEFER maintenance time - please try again later
+
+ # now with the set() command, note that long item
+ # lists don't have to be compared twice
+ id=RBL01 ; &&RBLS ; action=set(HIT_rbls=1)
+ id=HELO01 ; &&BAD_HELO ; action=set(HIT_helo=1)
+ id=DYNA01 ; &&DYNAMIC ; action=set(HIT_dyna=1)
+ id=REJECT01 ; HIT_rbls==1 ; HIT_helo==1 ; action=REJECT please see http://some.org/info?reject=01 for more info
+ id=REJECT02 ; HIT_rbls==1 ; HIT_dyna==1 ; action=REJECT please see http://some.org/info?reject=02 for more info
+ id=REJECT03 ; HIT_helo==1 ; HIT_dyna==1 ; action=REJECT please see http://some.org/info?reject=03 for more info
+
+ ## combined with enhanced rbl features
+ #
+ id=RBL01 ; rhsblcount=all ; rblcount=all ; &&RBLS ; &&RHSBLS ; \
+ action=set(HIT_dnsbls=$$rhsblcount,HIT_dnsbls+=$$rblcount,HIT_dnstxt=$$dnsbltext)
+ id=RBL02 ; HIT_dnsbls>=2 ; action=554 5.7.1 blocked using $$HIT_dnsbls DNSBLs [INFO: $$HIT_dnstxt]
+
+ PARSER
+ *Configuration*
+
+ The postfwd2 ruleset can be specified at the commandline (-r option) or
+ be read from files (-f). The order of your arguments will be kept. You
+ should check the parser with the -C | --showconfig switch at the command
+ line before applying a new config. The following call:
+
+ postfwd2 --showconfig \
+ -r "id=TEST; recipient_count=100; action=WARN mail with 100+ recipients" \
+ -f /etc/postfwd.cf \
+ -r "id=DEFAULT; action=dunno";
+
+ will produce the following output:
+
+ Rule 0: id->"TEST" action->"WARN mail with 100+ recipients"; recipient_count->"100"
+ ...
+ ... ...
+ ...
+ Rule : id->"DEFAULT" action->"dunno"
+
+ Multiple items of the same type will be added to lists (see the "ITEMS"
+ section for more info):
+
+ postfwd2 --showconfig \
+ -r "client_address=192.168.1.0/24; client_address=172.16.26.32; action=dunno"
+
+ will result in:
+
+ Rule 0: id->"R-0"; action->"dunno"; client_address->"192.168.1.0/24, 172.16.26.32"
+
+ Macros are evaluated at configuration stage, which means that
+
+ postfwd2 --showconfig \
+ -r "&&RBLS { rbl=bl.spamcop.net; client_name=^unknown$; };" \
+ -r "id=RBL001; &&RBLS; action=REJECT listed on spamcop and bad rdns";
+
+ will result in:
+
+ Rule 0: id->"RBL001"; action->"REJECT listed on spamcop and bad rdns"; rbl->"bl.spamcop.net"; client_name->"^unknown$"
+
+ *Request processing*
+
+ When a policy delegation request arrives it will be compared against
+ postfwd`s ruleset. To inspect the processing in detail you should
+ increase verbority using use the "-v" or "-vv" switch. "-L" redirects
+ log messages to stdout.
+
+ Keeping the order of the ruleset in general, items will be compared in
+ random order, which basically means that
+
+ id=R001; action=dunno; client_address=192.168.1.1; sender=bob@alice.local
+
+ equals to
+
+ id=R001; sender=bob@alice.local; client_address=192.168.1.1; action=dunno
+
+ Lists will be evaluated in the specified order. This allows to place
+ faster expressions at first:
+
+ postfwd2 --nodaemon -vv -L -r "id=RBL001; rbl=localrbl.local zen.spamhaus.org; action=REJECT" /some/where/request.sample
+
+ produces the following
+
+ [LOGS info]: compare rbl: "remotehost.remote.net[68.10.1.7]" -> "localrbl.local"
+ [LOGS info]: count1 rbl: "2" -> "0"
+ [LOGS info]: query rbl: localrbl.local 7.1.10.68 (7.1.10.68.localrbl.local)
+ [LOGS info]: count2 rbl: "2" -> "0"
+ [LOGS info]: match rbl: FALSE
+ [LOGS info]: compare rbl: "remotehost.remote.net[68.10.1.7]" -> "zen.spamhaus.org"
+ [LOGS info]: count1 rbl: "2" -> "0"
+ [LOGS info]: query rbl: zen.spamhaus.org 7.1.10.68 (7.1.10.68.zen.spamhaus.org)
+ [LOGS info]: count2 rbl: "2" -> "0"
+ [LOGS info]: match rbl: FALSE
+ [LOGS info]: Action: dunno
+
+ The negation operator !!() has the highest priority and therefore
+ will be evaluated first. Then variable substitutions are performed:
+
+ postfwd2 --nodaemon -vv -L -r "id=TEST; action=REJECT; client_name=!!($$heloname)" /some/where/request.sample
+
+ will give
+
+ [LOGS info]: compare client_name: "unknown" -> "!!($$helo_name)"
+ [LOGS info]: negate client_name: "unknown" -> "$$helo_name"
+ [LOGS info]: substitute client_name: "unknown" -> "english-breakfast.cloud8.net"
+ [LOGS info]: match client_name: TRUE
+ [LOGS info]: Action: REJECT
+
+ *Ruleset evaluation*
+
+ A rule hits when all items (or at least one element of a list for each
+ item) have matched. As soon as one item (or all elements of a list)
+ fails to compare against the request attribute the parser will jump to
+ the next rule in the postfwd2 ruleset.
+
+ If a rule matches, there are two options:
+
+ * Rule returns postfix action (dunno, reject, ...) The parser stops rule
+ processing and returns the action to postfix. Other rules will not be
+ evaluated.
+
+ * Rule returns postfwd2 action (jump(), note(), ...) The parser
+ evaluates the given action and continues with the next rule (except for
+ the jump() or quit() actions - please see the "ACTIONS" section for more
+ information). Nothing will be sent to postfix.
+
+ If no rule has matched and the end of the ruleset is reached postfwd2
+ will return dunno without logging anything unless in verbose mode. You
+ may place a last catch-all rule to change that behaviour:
+
+ ... ...
+ id=DEFAULT ; action=dunno
+
+ will log any request that passes the ruleset without having hit a prior
+ rule.
+
+ DEBUGGING
+ To debug special steps of the parser the '--debug' switch takes a list
+ of debug classes. Currently the following classes are defined:
+
+ all cache config debugdns devel dns getcache getdns
+ getdnspacket rates request setcache setdns
+ parent_cache parent_dns_cache parent_rate_cache parent_request_cache
+ child_cache child_dns_cache child_rate_cache child_request_cache
+
+ INTEGRATION
+ *Integration via daemon mode*
+
+ The common way to use postfwd2 is to start it as daemon, listening at a
+ specified tcp port. postfwd2 will spawn multiple child processes which
+ communicate with a parent cache. This is the prefered way to use
+ postfwd2 in high volume environments. Start postfwd2 with the following
+ parameters:
+
+ postfwd2 -d -f /etc/postfwd.cf -i 127.0.0.1 -p 10040 -u nobody -g nobody -S
+
+ For efficient caching you should check if you can use the options
+ --cacheid, --cache-rdomain-only, --cache-no-sender and --cache-no-size.
+
+ Now check your syslogs (default facility "mail") for a line like:
+
+ Aug 9 23:00:24 mail postfwd[5158]: postfwd2 n.nn ready for input
+
+ and use `netstat -an|grep 10040` to check for something like
+
+ tcp 0 0 127.0.0.1:10040 0.0.0.0:* LISTEN
+
+ If everything works, open your postfix main.cf and insert the following
+
+ 127.0.0.1:10040_time_limit = 3600 <--- integration
+ smtpd_recipient_restrictions = permit_mynetworks <--- recommended
+ reject_unauth_destination <--- recommended
+ check_policy_service inet:127.0.0.1:10040 <--- integration
+
+ Reload your configuration with `postfix reload` and watch your logs. In
+ it works you should see lines like the following in your mail log:
+
+ Aug 9 23:01:24 mail postfwd[5158]: rule=22, id=ML_POSTFIX, client=english-breakfast.cloud9.net[168.100.1.7], sender=owner-postfix-users@postfix.tld, recipient=someone@domain.local, helo=english-breakfast.cloud9.net, proto=ESMTP, state=RCPT, action=dunno
+
+ If you want to check for size or rcpt_count items you must integrate
+ postfwd2 in smtp_data_restrictions or smtpd_end_of_data_restrictions. Of
+ course you can also specify a restriction class and use it in your
+ access tables. First create a file /etc/postfix/policy containing:
+
+ domain1.local postfwdcheck
+ domain2.local postfwdcheck
+ ...
+
+ Then postmap that file (`postmap hash:/etc/postfix/policy`), open your
+ main.cf and enter
+
+ # Restriction Classes
+ smtpd_restriction_classes = postfwdcheck, ... <--- integration
+ postfwdcheck = check_policy_service inet:127.0.0.1:10040 <--- integration
+
+ 127.0.0.1:10040_time_limit = 3600 <--- integration
+ smtpd_recipient_restrictions = permit_mynetworks, <--- recommended
+ reject_unauth_destination, <--- recommended
+ ... <--- optional
+ check_recipient_access hash:/etc/postfix/policy, <--- integration
+ ... <--- optional
+
+ Reload postfix and watch your logs.
+
+ TESTING
+ First you have to create a ruleset (see Configuration section). Check it
+ with
+
+ postfwd2 -f /etc/postfwd.cf -C
+
+ There is an example policy request distributed with postfwd, called
+ 'request.sample'. Simply change it to meet your requirements and use
+
+ postfwd2 -f /etc/postfwd.cf
+
+ For network tests I use netcat:
+
+ nc 127.0.0.1 10040 =
+ - use ^ and/or $ in regular expressions
+ - use item lists (faster than single rules)
+ - use set() action on repeated item lists
+ - use jumps and rate limits
+ - use a pre-lookup rule for rbl/rhsbls with empty note() action
+
+ SEE ALSO
+ See for a description
+ of how Postfix policy servers work.
+
+LICENSE
+ postfwd2 is free software and released under BSD license, which
+ basically means that you can do what you want as long as you keep the
+ copyright notice:
+
+ Copyright (c) 2009, Jan Peter Kessler All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of the authors nor the names of his contributors
+ may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY ME ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+AUTHOR
+ Jan Peter Kessler . Let me know, if you
+ have any suggestions.
+
diff --git a/man/man8/postfwd.8 b/man/man8/postfwd.8
index 70a212e..4527675 100644
--- a/man/man8/postfwd.8
+++ b/man/man8/postfwd.8
@@ -129,7 +129,7 @@
.\" ========================================================================
.\"
.IX Title "POSTFWD 1"
-.TH POSTFWD 1 "2009-09-03" "perl v5.8.5" "User Contributed Perl Documentation"
+.TH POSTFWD 1 "2010-11-14" "perl v5.8.5" "User Contributed Perl Documentation"
.SH "NAME"
postfwd \- postfix firewall daemon
.SH "SYNOPSIS"
@@ -147,7 +147,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
\& -s, --scores = returns when score exceeds
.Ve
.PP
-.Vb 11
+.Vb 12
\& Networking:
\& -d, --daemon run postfwd as daemon
\& -i, --interface listen on interface
@@ -155,6 +155,7 @@ postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
\& --proto socket type (tcp or unix)
\& -u, --user set uid to user
\& -g, --group set gid to group
+\& --umask set umask for file permissions
\& -R, --chroot chroot the daemon to
\& --pidfile create pidfile under
\& -l, --logname label for syslog messages
@@ -674,15 +675,16 @@ postfwd actions control the behaviour of the program. Currently you can specify
\& by "," characters.
.Ve
.PP
-.Vb 8
+.Vb 9
\& rate (- /
//)
\& this command creates a counter for the given - , which will be increased any time a request
\& containing it arrives. if it exceeds
within seconds it will return to postfix.
\& rate counters are very fast as they are executed before the ruleset is parsed.
+\& please note that is currently limited to postfix actions (no postfwd actions)!
\& # no more than 3 requests per 5 minutes
\& # from the same "unknown" client
\& id=RATE01 ; client_name==unknown ; \e
-\& action==rate($$client_address/3/300/450 4.7.1 sorry, max 3 requests per 5 minutes)
+\& action==rate(client_address/3/300/450 4.7.1 sorry, max 3 requests per 5 minutes)
.Ve
.PP
.Vb 7
@@ -692,7 +694,7 @@ postfwd actions control the behaviour of the program. Currently you can specify
\& smtpd_end_of_data_restrictions. if you want to be sure, you could check it within the ruleset:
\& # size limit 1.5mb per hour per client
\& id=SIZE01 ; state==END_OF_DATA ; client_address==!!(10.1.1.1); \e
-\& 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
.PP
.Vb 8
@@ -703,7 +705,7 @@ postfwd actions control the behaviour of the program. Currently you can specify
\& 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)
+\& action==rcpt(client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
.Ve
.PP
.Vb 9
@@ -911,6 +913,13 @@ The following arguments will control it's behaviour in this case.
\& Changes real and effective group to .
.Ve
.PP
+.Vb 4
+\& --umask
+\& Changes the umask for filepermissions (unix domain sockets, pidfiles).
+\& Attention: This is umask, not chmod - you have to specify the bits that
+\& should NOT apply. E.g.: umask 077 equals to chmod 700.
+.Ve
+.PP
.Vb 3
\& -R, --chroot
\& Chroot the process to the specified path.
@@ -1179,14 +1188,15 @@ the '\-I' switch to have your configuration refreshed for every request postfwd
\& id=RBL04 ; action=REJECT combined RBL+RHSBL check ; rbl=bl.spamcop.net ; rhsbl=rhsbl.ahbl.org, rhsbl.sorbs.net
.Ve
.PP
-.Vb 7
+.Vb 8
\& ## Message size (requires message_size_limit to be set to 30000000)
\& # 1. 30MB for systems in *.customer1.tld
\& # 2. 20MB for SASL user joejob
\& # 3. 10MB default
-\& id=SZ001; state==END-OF-MESSAGE; action=REJECT message too large; size=30000000 ; client_name=\e.customer1.tld$
-\& id=SZ002; state==END-OF-MESSAGE; action=REJECT message too large; size=20000000 ; sasl_username==joejob
-\& id=SZ003; state==END-OF-MESSAGE; action=REJECT message too large; size=10000000
+\& id=SZ001; state==END-OF-MESSAGE; action=DUNNO; size<=30000000 ; client_name=\e.customer1.tld$
+\& id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=20000000 ; sasl_username==joejob
+\& id=SZ002; state==END-OF-MESSAGE; action=DUNNO; size<=10000000
+\& id=SZ100; state==END-OF-MESSAGE; action=REJECT message too large
.Ve
.PP
.Vb 7
@@ -1245,9 +1255,9 @@ the '\-I' switch to have your configuration refreshed for every request postfwd
\& # 1. exceeded 30 requests per hour or
\& # 2. tried to send more than 1.5mb within 10 minutes
\& id=RATE01 ; client_name==unknown ; state==RCPT ; \e
-\& action==rate($$client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
+\& action==rate(client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
\& id=SIZE01 ; client_name==unknown ; state==END_OF_DATA ; \e
-\& action==size($$client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
+\& action==size(client_address/1572864/600/450 4.7.1 sorry, max 1.5mb per 10 minutes)
.Ve
.PP
.Vb 8
diff --git a/man/man8/postfwd2.8 b/man/man8/postfwd2.8
new file mode 100644
index 0000000..e557ddd
--- /dev/null
+++ b/man/man8/postfwd2.8
@@ -0,0 +1,1671 @@
+.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.14
+.\"
+.\" Standard preamble:
+.\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Vb \" Begin verbatim text
+.ft CW
+.nf
+.ne \\$1
+..
+.de Ve \" End verbatim text
+.ft R
+.fi
+..
+.\" Set up some character translations and predefined strings. \*(-- will
+.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
+.\" double quote, and \*(R" will give a right double quote. | will give a
+.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to
+.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C'
+.\" expand to `' in nroff, nothing in troff, for use with C<>.
+.tr \(*W-|\(bv\*(Tr
+.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
+.ie n \{\
+. ds -- \(*W-
+. ds PI pi
+. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+. ds L" ""
+. ds R" ""
+. ds C` ""
+. ds C' ""
+'br\}
+.el\{\
+. ds -- \|\(em\|
+. ds PI \(*p
+. ds L" ``
+. ds R" ''
+'br\}
+.\"
+.\" If the F register is turned on, we'll generate index entries on stderr for
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
+.\" entries marked with X<> in POD. Of course, you'll have to process the
+.\" output yourself in some meaningful fashion.
+.if \nF \{\
+. de IX
+. tm Index:\\$1\t\\n%\t"\\$2"
+..
+. nr % 0
+. rr F
+.\}
+.\"
+.\" For nroff, turn off justification. Always turn off hyphenation; it makes
+.\" way too many mistakes in technical documents.
+.hy 0
+.if n .na
+.\"
+.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
+.\" Fear. Run. Save yourself. No user-serviceable parts.
+. \" fudge factors for nroff and troff
+.if n \{\
+. ds #H 0
+. ds #V .8m
+. ds #F .3m
+. ds #[ \f1
+. ds #] \fP
+.\}
+.if t \{\
+. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
+. ds #V .6m
+. ds #F 0
+. ds #[ \&
+. ds #] \&
+.\}
+. \" simple accents for nroff and troff
+.if n \{\
+. ds ' \&
+. ds ` \&
+. ds ^ \&
+. ds , \&
+. ds ~ ~
+. ds /
+.\}
+.if t \{\
+. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
+. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
+. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
+. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
+. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
+. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
+.\}
+. \" troff and (daisy-wheel) nroff accents
+.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
+.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
+.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
+.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
+.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
+.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
+.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
+.ds ae a\h'-(\w'a'u*4/10)'e
+.ds Ae A\h'-(\w'A'u*4/10)'E
+. \" corrections for vroff
+.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
+.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
+. \" for low resolution devices (crt and lpr)
+.if \n(.H>23 .if \n(.V>19 \
+\{\
+. ds : e
+. ds 8 ss
+. ds o a
+. ds d- d\h'-1'\(ga
+. ds D- D\h'-1'\(hy
+. ds th \o'bp'
+. ds Th \o'LP'
+. ds ae ae
+. ds Ae AE
+.\}
+.rm #[ #] #H #V #F C
+.\" ========================================================================
+.\"
+.IX Title "POSTFWD2 1"
+.TH POSTFWD2 1 "2010-11-14" "perl v5.8.5" "User Contributed Perl Documentation"
+.SH "NAME"
+postfwd2 \- postfix firewall daemon
+.SH "SYNOPSIS"
+.IX Header "SYNOPSIS"
+\&\fBpostfwd2\fR [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
+.PP
+.Vb 4
+\& Ruleset: (at least one, multiple use is allowed):
+\& -f, --file reads rules from
+\& -r, --rule adds to config
+\& -s, --scores = returns when score exceeds
+.Ve
+.PP
+.Vb 14
+\& Server:
+\& -i, --interface listen on interface
+\& -p, --port listen on port
+\& --proto socket type (tcp or unix)
+\& --server_socket e.g. tcp:127.0.0.1:10045
+\& -u, --user set uid to user
+\& -g, --group set gid to group
+\& --umask umask for master filepermissions
+\& --server_umask umask for server filepermissions
+\& --pidfile create pidfile under
+\& --min_servers spawn at least children
+\& --max_servers