1750 lines
79 KiB
Groff
1750 lines
79 KiB
Groff
.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.16)
|
|
.\"
|
|
.\" Standard preamble:
|
|
.\" ========================================================================
|
|
.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. \*(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-
|
|
.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\}
|
|
.\"
|
|
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
|
.ie \n(.g .ds Aq \(aq
|
|
.el .ds Aq '
|
|
.\"
|
|
.\" If the F register is turned on, we'll generate index entries on stderr for
|
|
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
|
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
|
.\" output yourself in some meaningful fashion.
|
|
.ie \nF \{\
|
|
. de IX
|
|
. tm Index:\\$1\t\\n%\t"\\$2"
|
|
..
|
|
. nr % 0
|
|
. rr F
|
|
.\}
|
|
.el \{\
|
|
. de IX
|
|
..
|
|
.\}
|
|
.\"
|
|
.\" 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 "POSTFWD1-ALL-IN-ONE 1"
|
|
.TH POSTFWD1-ALL-IN-ONE 1 "2013-04-18" "perl v5.14.2" "User Contributed Perl Documentation"
|
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
|
.\" way too many mistakes in technical documents.
|
|
.if n .ad l
|
|
.nh
|
|
.SH "NAME"
|
|
postfwd \- postfix firewall daemon
|
|
.SH "SYNOPSIS"
|
|
.IX Header "SYNOPSIS"
|
|
postfwd [\s-1OPTIONS\s0] [\s-1SOURCE1\s0, \s-1SOURCE2\s0, ...]
|
|
.PP
|
|
.Vb 3
|
|
\& Ruleset: (at least one, multiple use is allowed):
|
|
\& \-f, \-\-file <file> reads rules from <file>
|
|
\& \-r, \-\-rule <rule> adds <rule> to config
|
|
\&
|
|
\& Scoring:
|
|
\& \-s, \-\-scores <v>=<r> returns <r> when score exceeds <v>
|
|
\&
|
|
\& Control:
|
|
\& \-d, \-\-daemon run postfwd as daemon
|
|
\& \-k, \-\-kill stops daemon
|
|
\& \-\-reload reloads configuration
|
|
\& \-\-dumpstats displays usage statistics
|
|
\& \-\-dumpcache displays cache contents
|
|
\& \-\-delcache <item> removes an item from the request cache
|
|
\& \-\-delrate <item> removes an item from the rate cache
|
|
\&
|
|
\& Networking:
|
|
\& \-i, \-\-interface <dev> listen on interface <dev>
|
|
\& \-p, \-\-port <port> listen on port <port>
|
|
\& \-\-proto <proto> socket type (tcp or unix)
|
|
\& \-u, \-\-user <name> set uid to user <name>
|
|
\& \-g, \-\-group <name> set gid to group <name>
|
|
\& \-\-umask <mask> set umask for file permissions
|
|
\& \-R, \-\-chroot <path> chroot the daemon to <path>
|
|
\& \-\-pidfile <path> create pidfile under <path>
|
|
\& \-\-facility <f> syslog facility
|
|
\& \-\-socktype <s> syslog socktype
|
|
\& \-l, \-\-logname <label> label for syslog messages
|
|
\& \-\-loglen <int> truncates syslogs after <int> chars
|
|
\&
|
|
\& Caching:
|
|
\& \-c, \-\-cache <int> sets the request\-cache timeout to <int> seconds
|
|
\& \-\-cache\-no\-size ignores size attribute for caching
|
|
\& \-\-cache\-no\-sender ignores sender address in cache
|
|
\& \-\-cache\-rdomain\-only ignores localpart of recipient address in cache
|
|
\& \-\-cache\-rbl\-timeout default rbl timeout, if not specified in ruleset
|
|
\& \-\-cache\-rbl\-default default rbl response pattern to match (regexp)
|
|
\& \-\-cacheid <item>, .. list of attributes for request cache identifier
|
|
\& \-\-cleanup\-requests cleanup interval in seconds for request cache
|
|
\& \-\-cleanup\-rbls cleanup interval in seconds for rbl cache
|
|
\& \-\-cleanup\-rates cleanup interval in seconds for rate cache
|
|
\&
|
|
\& Optional:
|
|
\& \-t, \-\-test testing, always returns "dunno"
|
|
\& \-v, \-\-verbose verbose logging, use twice (\-vv) to increase level
|
|
\& \-S, \-\-summary <int> show some usage statistics every <int> seconds
|
|
\& \-\-norulelog disbles rule logging
|
|
\& \-\-norulestats disables per rule statistics
|
|
\& \-\-noidlestats disables statistics when idle
|
|
\& \-n, \-\-nodns disable dns
|
|
\& \-\-nodnslog disable dns logging
|
|
\& \-\-dns_async_txt perform dnsbl A and TXT lookups simultaneously
|
|
\& \-\-dns_timeout timeout in seconds for asynchonous dns queries
|
|
\& \-\-dns_timeout_max maximum of dns timeouts until a dnsbl will be deactivated
|
|
\& \-\-dns_timeout_interval interval in seconds for dns timeout maximum counter
|
|
\& \-\-dns_max_ns_lookups max names to look up with sender_ns_addrs
|
|
\& \-\-dns_max_mx_lookups max names to look up with sender_mx_addrs
|
|
\& \-I, \-\-instantcfg re\-reads rulefiles for every new request
|
|
\& \-\-config_timeout <i> parser timeout in seconds
|
|
\& \-\-keep_rates do not clear rate limit counters on reload
|
|
\& \-\-save_rates <file> save and load rate limits on disk
|
|
\& \-\-fast_limit_evaluation evaluate rate limits before ruleset is parsed
|
|
\& (please note the limitations)
|
|
\&
|
|
\& Plugins:
|
|
\& \-\-plugins <file> loads postfwd plugins from file
|
|
\&
|
|
\& Informational (use only at command\-line!):
|
|
\& \-C, \-\-showconfig shows ruleset summary, \-v for verbose
|
|
\& \-L, \-\-stdoutlog redirect syslog messages to stdout
|
|
\& \-P, \-\-perfmon no syslogging, no stdout
|
|
\& \-V, \-\-version shows program version
|
|
\& \-h, \-\-help shows usage
|
|
\& \-m, \-\-manual shows program manual
|
|
.Ve
|
|
.SH "DESCRIPTION"
|
|
.IX Header "DESCRIPTION"
|
|
.SS "\s-1INTRODUCTION\s0"
|
|
.IX Subsection "INTRODUCTION"
|
|
postfwd 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).
|
|
.PP
|
|
postfwd 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 \s-1TLS\s0 fingerprint). Also it offers simple macros/acls
|
|
which should allow straightforward and easy-to-read configurations.
|
|
.PP
|
|
\&\fIFeatures:\fR
|
|
.PP
|
|
* Complex combinations of smtp parameters
|
|
.PP
|
|
* Combined \s-1RBL/RHSBL\s0 lookups with arbitrary actions depending on results
|
|
.PP
|
|
* Scoring system
|
|
.PP
|
|
* Date/time based rules
|
|
.PP
|
|
* Macros/ACLs, Groups, Negation
|
|
.PP
|
|
* Compare request attributes (e.g. client_name and helo_name)
|
|
.PP
|
|
* Internal caching for requests and dns lookups
|
|
.PP
|
|
* Built in statistics for rule efficiency analysis
|
|
.SS "\s-1CONFIGURATION\s0"
|
|
.IX Subsection "CONFIGURATION"
|
|
A configuration line consists of optional item=value pairs, separated by semicolons
|
|
(`;`) and the appropriate desired action:
|
|
.PP
|
|
.Vb 1
|
|
\& [ <item1>=<value>; <item2>=<value>; ... ] action=<result>
|
|
.Ve
|
|
.PP
|
|
\&\fIExample:\fR
|
|
.PP
|
|
.Vb 1
|
|
\& client_address=192.168.1.1 ; sender==no@bad.local ; action=REJECT
|
|
.Ve
|
|
.PP
|
|
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:
|
|
.PP
|
|
.Vb 1
|
|
\& action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
|
|
.Ve
|
|
.PP
|
|
The way how request items are compared to the ruleset can be influenced in the following way:
|
|
.PP
|
|
.Vb 10
|
|
\& ====================================================================
|
|
\& ITEM == VALUE true if ITEM equals VALUE
|
|
\& ITEM => VALUE true if ITEM >= VALUE
|
|
\& ITEM =< VALUE true if ITEM <= VALUE
|
|
\& ITEM > VALUE true if ITEM > VALUE
|
|
\& ITEM < VALUE true if ITEM < VALUE
|
|
\& ITEM =~ VALUE true if ITEM ~= /^VALUE$/i
|
|
\& ITEM != VALUE false if ITEM equals VALUE
|
|
\& ITEM !> VALUE false if ITEM >= VALUE
|
|
\& ITEM !< VALUE false if ITEM <= VALUE
|
|
\& ITEM !~ VALUE false if ITEM ~= /^VALUE$/i
|
|
\& ITEM = VALUE default behaviour (see ITEMS section)
|
|
\& ====================================================================
|
|
.Ve
|
|
.PP
|
|
To identify single rules in your log files, you may add an unique identifier for each of it:
|
|
.PP
|
|
.Vb 1
|
|
\& id=R_001 ; action=REJECT ; client_address=192.168.1.1 ; sender==no@bad.local
|
|
.Ve
|
|
.PP
|
|
You may use these identifiers as target for the `\fIjump()\fR` command (see \s-1ACTIONS\s0 section below). Leading
|
|
or trailing whitespace characters will be ignored. Use '#' to comment your configuration. Others will
|
|
appreciate.
|
|
.PP
|
|
A ruleset consists of one or multiple rules, which can be loaded from files or passed as command line
|
|
arguments. Please see the \s-1COMMAND\s0 \s-1LINE\s0 section below for more information on this topic.
|
|
.PP
|
|
Since postfwd version 1.30 rules spanning span multiple lines can be defined by prefixing the following
|
|
lines with one or multiple whitespace characters (or '}' for macros):
|
|
.PP
|
|
.Vb 4
|
|
\& id=RULE001
|
|
\& client_address=192.168.1.0/24
|
|
\& sender==no@bad.local
|
|
\& action=REJECT no access
|
|
.Ve
|
|
.PP
|
|
postfwd versions prior to 1.30 require trailing ';' and '\e'\-characters:
|
|
.PP
|
|
.Vb 4
|
|
\& id=RULE001; \e
|
|
\& client_address=192.168.1.0/24; \e
|
|
\& sender==no@bad.local; \e
|
|
\& action=REJECT no access
|
|
.Ve
|
|
.SS "\s-1ITEMS\s0"
|
|
.IX Subsection "ITEMS"
|
|
.Vb 2
|
|
\& 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\*(Aqs 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\e.0\e.0\e.\ed+$ 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 \*(Aqall\*(Aq 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 \- postfwd tries to look up the helo_name. use
|
|
\& helo_address=!!(0.0.0.0/0) to check for unknown.
|
|
\& Please do not use this for positive access control
|
|
\& (whitelisting), as it might be forged.
|
|
\&
|
|
\& sender_ns_names, \- postfwd tries to look up the names/ip addresses
|
|
\& sender_ns_addrs of the nameservers for the sender domain part.
|
|
\& Please do not use this for positive access control
|
|
\& (whitelisting), as it might be forged.
|
|
\&
|
|
\& sender_mx_names, \- postfwd tries to look up the names/ip addresses
|
|
\& sender_mx_addrs of the mx records for the sender domain part.
|
|
\& Please do not use this for positive access control
|
|
\& (whitelisting), as it might be forged.
|
|
\&
|
|
\& version \- postfwd version, contains "postfwd n.nn"
|
|
\& this enables version based checks in your rulesets
|
|
\& (e.g. for migration). works with old versions too,
|
|
\& because a non\-existing item always returns false:
|
|
\& # version >= 1.10
|
|
\& id=R01; version~=1\e.[1\-9][0\-9]; sender_domain==some.org \e
|
|
\& ; action=REJECT sorry no access
|
|
\&
|
|
\& ratecount \- only available for rate(), size() and rcpt() actions.
|
|
\& contains the actual limit counter:
|
|
\& id=R01; action=rate(sender/200/600/REJECT limit of 200 exceeded [$$ratecount hits])
|
|
\& id=R02; action=rate(sender/100/600/WARN limit of 100 exceeded [$$ratecount hits])
|
|
.Ve
|
|
.PP
|
|
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 \s-1EXAMPLES\s0 section below).
|
|
.PP
|
|
Most values can be specified as regular expressions (\s-1PCRE\s0). Please see the table below
|
|
for details:
|
|
.PP
|
|
.Vb 10
|
|
\& # ==========================================================
|
|
\& # 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
|
|
\& ...
|
|
.Ve
|
|
.PP
|
|
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-MESSAGE level).
|
|
Pattern matching is performed case insensitive.
|
|
.PP
|
|
Multiple use of the same item is allowed and will compared as logical \s-1OR\s0, which means that this will work as expected:
|
|
.PP
|
|
.Vb 5
|
|
\& 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\e.local$
|
|
.Ve
|
|
.PP
|
|
client_address, rbl and rhsbl items may also be specified as whitespace-or-comma-separated values:
|
|
.PP
|
|
.Vb 4
|
|
\& 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
|
|
.Ve
|
|
.PP
|
|
The following items currently have to be unique:
|
|
.PP
|
|
.Vb 1
|
|
\& id, minimum and maximum values, rblcount and rhsblcount
|
|
.Ve
|
|
.PP
|
|
Any item can be negated by preceeding '!!' to it, e.g.:
|
|
.PP
|
|
.Vb 1
|
|
\& id=HOST001 ; hostname == !!secure.trust.local ; action=REJECT only secure.trust.local please
|
|
.Ve
|
|
.PP
|
|
or using the right compare operator:
|
|
.PP
|
|
.Vb 1
|
|
\& id=HOST001 ; hostname != secure.trust.local ; action=REJECT only secure.trust.local please
|
|
.Ve
|
|
.PP
|
|
To avoid confusion with regexps or simply for better visibility you can use '!!(...)':
|
|
.PP
|
|
.Vb 1
|
|
\& id=USER01 ; sasl_username = !!( (bob|alice) ) ; action=REJECT who is that?
|
|
.Ve
|
|
.PP
|
|
Request attributes can be compared by preceeding '$$' characters, e.g.:
|
|
.PP
|
|
.Vb 3
|
|
\& 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
|
|
.Ve
|
|
.PP
|
|
This is only valid for \s-1PCRE\s0 values (see list above). The comparison will be performed as case insensitive exact match.
|
|
Use the '\-vv' option to debug.
|
|
.PP
|
|
These special items will be reset for any new rule:
|
|
.PP
|
|
.Vb 5
|
|
\& 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>; ...
|
|
.Ve
|
|
.PP
|
|
These special items will be changed for any matching rule:
|
|
.PP
|
|
.Vb 1
|
|
\& request_hits \- contains ids of all matching rules
|
|
.Ve
|
|
.PP
|
|
This means that it might be necessary to save them, if you plan to use these values in later rules:
|
|
.PP
|
|
.Vb 6
|
|
\& # set vals
|
|
\& id=RBL01 ; rhsblcount=all; rblcount=all
|
|
\& action=set(HIT_rhls=$$rhsblcount,HIT_rbls=$$rblcount,HIT_txt=$$dnsbltext)
|
|
\& 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
|
|
\&
|
|
\& # 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]
|
|
.Ve
|
|
.SS "\s-1FILES\s0"
|
|
.IX Subsection "FILES"
|
|
Since postfwd1 v1.15 and postfwd2 v0.18 long item lists can be stored in separate files:
|
|
.PP
|
|
.Vb 1
|
|
\& id=R001 ; ccert_fingerprint==file:/etc/postfwd/wl_ccerts ; action=DUNNO
|
|
.Ve
|
|
.PP
|
|
postfwd will read a list of items (one item per line) from /etc/postfwd/wl_ccerts. comments are allowed:
|
|
.PP
|
|
.Vb 6
|
|
\& # client1
|
|
\& 11:22:33:44:55:66:77:88:99
|
|
\& # client2
|
|
\& 22:33:44:55:66:77:88:99:00
|
|
\& # client3
|
|
\& 33:44:55:66:77:88:99:00:11
|
|
.Ve
|
|
.PP
|
|
To use existing tables in key=value format, you can use:
|
|
.PP
|
|
.Vb 1
|
|
\& id=R001 ; ccert_fingerprint==table:/etc/postfwd/wl_ccerts ; action=DUNNO
|
|
.Ve
|
|
.PP
|
|
This will ignore the right-hand value. Items can be mixed:
|
|
.PP
|
|
.Vb 3
|
|
\& id=R002 ; action=REJECT
|
|
\& client_name==unknown
|
|
\& client_name==file:/etc/postfwd/blacklisted
|
|
.Ve
|
|
.PP
|
|
and for non pcre (comma separated) items:
|
|
.PP
|
|
.Vb 2
|
|
\& 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
|
|
.Ve
|
|
.PP
|
|
You can check your configuration with the \-\-show_config option at the command line:
|
|
.PP
|
|
.Vb 1
|
|
\& # postfwd \-\-showconfig \-\-rule=\*(Aqaction=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1\*(Aq
|
|
.Ve
|
|
.PP
|
|
should give something like:
|
|
.PP
|
|
.Vb 1
|
|
\& Rule 0: id\->"R\-0"; action\->"DUNNO"; client_address\->"=;10.1.0.0/16, =;194.123.86.10, =;186.4.6.12, =;192.168.2.1"
|
|
.Ve
|
|
.PP
|
|
If a file can not be read, it will be ignored:
|
|
.PP
|
|
.Vb 3
|
|
\& # postfwd \-\-showconfig \-\-rule=\*(Aqaction=DUNNO; client_address=10.1.0.0/16, file:/etc/postfwd/wl_clients, 192.168.2.1\*(Aq
|
|
\& [LOG warning]: error: file /etc/postfwd/wl_clients not found \- file will be ignored ?
|
|
\& Rule 0: id\->"R\-0"; action\->"DUNNO"; client_address\->"=;10.1.0.0/16, =;192.168.2.1"
|
|
.Ve
|
|
.PP
|
|
File items are evaluated at configuration stage. Therefore postfwd needs to be reloaded if a file has changed.
|
|
.PP
|
|
If you want to specify a file, that will be reloaded for each request, you can use lfile: and ltable:
|
|
.PP
|
|
.Vb 1
|
|
\& id=R001; client_address=lfile:/etc/postfwd/client_whitelist; action=dunno
|
|
.Ve
|
|
.PP
|
|
This will check the modification time of /etc/postfwd/client_whitelist every time the rule is evaluated and reload it as
|
|
necessary. Of course this might increase the system load, so please use it with care.
|
|
.PP
|
|
The \-\-showconfig option illustrates the difference:
|
|
.PP
|
|
.Vb 3
|
|
\& ## evaluated at configuration stage
|
|
\& # postfwd2 \-\-nodaemon \-L \-\-rule=\*(Aqclient_address=table:/etc/postfwd/clients; action=dunno\*(Aq \-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=\*(Aqclient_address=ltable:/etc/postfwd/clients; action=dunno\*(Aq \-C
|
|
\& Rule 0: id\->"R\-0"; action\->"dunno"; client_address\->"=;ltable:/etc/postfwd/clients"
|
|
.Ve
|
|
.PP
|
|
Files can refer to other files. The following is valid.
|
|
.PP
|
|
.Vb 2
|
|
\& \-\- FILE /etc/postfwd/rules.cf \-\-
|
|
\& id=R001; client_address=file:/etc/postfwd/clients_master.cf; action=DUNNO
|
|
\&
|
|
\& \-\- 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
|
|
.Ve
|
|
.PP
|
|
Note 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.
|
|
.SS "\s-1ACTIONS\s0"
|
|
.IX Subsection "ACTIONS"
|
|
\&\fIGeneral\fR
|
|
.PP
|
|
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:
|
|
.PP
|
|
.Vb 3
|
|
\& id=R\-003; client_name = !!$$helo_name; action=WARN helo \*(Aq$$helo_name\*(Aq does not match DNS \*(Aq$$client_name\*(Aq
|
|
\& # or
|
|
\& id=R\-003; client_name = !!$$helo_name; action=WARN helo \*(Aq$$(helo_name)\*(Aq does not match DNS \*(Aq$$(client_name)\*(Aq
|
|
.Ve
|
|
.PP
|
|
\&\fIpostfix actions\fR
|
|
.PP
|
|
Actions will be replied to postfix as result to policy delegation requests. Any action that postfix understands is allowed \- see
|
|
\&\*(L"man 5 access\*(R" or <http://www.postfix.org/access.5.html> for a description. If no action is specified, the postfix \s-1WARN\s0 action
|
|
which simply logs the event will be used for the corresponding rule.
|
|
.PP
|
|
postfwd 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:
|
|
.PP
|
|
.Vb 3
|
|
\& ...
|
|
\& action=dunno ; sender=@domain.local # sender is ok
|
|
\& action=reject # default deny
|
|
.Ve
|
|
.PP
|
|
\&\fIpostfwd actions\fR
|
|
.PP
|
|
postfwd actions control the behaviour of the program. Currently you can specify the following:
|
|
.PP
|
|
.Vb 4
|
|
\& 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\*(Aqs 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 postfwd 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> was limited to postfix actions (no postfwd actions) for postfwd versions <1.33!
|
|
\& # 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)
|
|
\& Please note also that the order of rate limits in your ruleset is important, which means
|
|
\& that this:
|
|
\& # works as expected
|
|
\& id=R001; action=rcpt(sender/500/3600/REJECT limit of 500 recipients per hour for sender $$sender exceeded)
|
|
\& id=R002; action=rcpt(sender/200/3600/WARN state YELLOW for sender $$sender)
|
|
\& leads to different results than this:
|
|
\& # rule R002 never gets executed
|
|
\& id=R001; action=rcpt(sender/200/3600/WARN state YELLOW for sender $$sender)
|
|
\& id=R002; action=rcpt(sender/500/3600/REJECT limit of 500 recipients per hour for sender $$sender exceeded)
|
|
\&
|
|
\& 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\*(Aqs size attribute. to do this reliably you should call postfwd 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 ; protocol_state==END\-OF\-MESSAGE ; 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\*(Aqs 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 ; protocol_state==END\-OF\-MESSAGE ; client_address!=10.1.1.1
|
|
\& action=rcpt(client_address/3/3600/450 4.7.1 sorry, max 3 recipients per hour)
|
|
\&
|
|
\& rate5321,size5321,rcpt5321 (<item>/<max>/<time>/<action>)
|
|
\& same as the corresponding non\-5321 functions, with the difference that the localpart of
|
|
\& sender oder recipient addresses are evaluated case\-sensitive according to rfc5321. That
|
|
\& means that requests from bob@example.local and BoB@example.local will be treated differently
|
|
\&
|
|
\& ask (<addr>:<port>[:<ignore>])
|
|
\& allows to delegate the policy decision to another policy service (e.g. postgrey). the first
|
|
\& and the second argument (address and port) are mandatory. a third optional argument may be
|
|
\& specified to tell postfwd to ignore certain answers and go on parsing the ruleset:
|
|
\& # example1: query postgrey and return it\*(Aqs answer to postfix
|
|
\& id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031)
|
|
\& # example2: query postgrey but ignore the answer, if it matches \*(AqDUNNO\*(Aq
|
|
\& # and continue parsing postfwd\*(Aqs ruleset
|
|
\& id=GREY; client_address==10.1.1.1; action=ask(127.0.0.1:10031:^dunno$)
|
|
\&
|
|
\& mail(server/helo/from/to/subject/body)
|
|
\& This command is deprecated. You should try to use the sendmail() action instead.
|
|
\& Very basic mail command, that sends a message with the given arguments. LIMITATIONS:
|
|
\& This basically performs a telnet. No authentication or TLS are available. Additionally it does
|
|
\& not track notification state and will notify you any time, the corresponding rule hits.
|
|
\&
|
|
\& sendmail(sendmail\-path::from::to::subject::body)
|
|
\& Mail command, that uses an existing sendmail binary and sends a message with the given arguments.
|
|
\& LIMITATIONS: The command does not track notification state and will notify you any time, the
|
|
\& corresponding rule hits (which could mean 100 mails for a mail with 100 recipients at RCPT stage).
|
|
\&
|
|
\& 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 (noop).
|
|
\&
|
|
\& quit (<code>)
|
|
\& terminates the program with the given exit\-code. postfix doesn\`t
|
|
\& like that too much, so use it with care.
|
|
.Ve
|
|
.PP
|
|
You can reference to request attributes, like
|
|
.PP
|
|
.Vb 1
|
|
\& id=R\-HELO ; helo_name=^[^\e.]+$ ; action=REJECT invalid helo \*(Aq$$helo_name\*(Aq
|
|
.Ve
|
|
.SS "\s-1MACROS/ACLS\s0"
|
|
.IX Subsection "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:
|
|
.PP
|
|
.Vb 1
|
|
\& &&RBLS { rbl=zen.spamhaus.org,list.dsbl.org,bl.spamcop.net,dnsbl.sorbs.net,ix.dnsbl.manitu.net; };
|
|
.Ve
|
|
.PP
|
|
Then these may be used in your rules, like:
|
|
.PP
|
|
.Vb 3
|
|
\& &&RBLS ; client_name=^unknown$ ; action=REJECT
|
|
\& &&RBLS ; client_name=(\ed+[\e.\-_]){4} ; action=REJECT
|
|
\& &&RBLS ; client_name=[\e.\-_](adsl|dynamic|ppp|)[\e.\-_] ; action=REJECT
|
|
.Ve
|
|
.PP
|
|
Macros can contain actions, too:
|
|
.PP
|
|
.Vb 6
|
|
\& # 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=(\ed+[\e.\-_]){4}
|
|
\& &&GONOW ; &&RBLS ; client_name=[\e.\-_](adsl|dynamic|ppp|)[\e.\-_]
|
|
.Ve
|
|
.PP
|
|
Macros can contain macros, too:
|
|
.PP
|
|
.Vb 10
|
|
\& # definition
|
|
\& &&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=(\ed+[\e.\-_]){4}
|
|
\& client_name=[\e.\-_](adsl|dynamic|ppp|)[\e.\-_]
|
|
\& };
|
|
\& &&GOAWAY { &&RBLS; &&DYNAMIC; };
|
|
\& # rules
|
|
\& &&GOAWAY ; action=REJECT dynamic client and listed on RBL
|
|
.Ve
|
|
.PP
|
|
Basically macros are simple text substitutions \- see the \*(L"\s-1PARSER\s0\*(R" section for more information.
|
|
.SS "\s-1PLUGINS\s0"
|
|
.IX Subsection "PLUGINS"
|
|
\&\fBDescription\fR
|
|
.PP
|
|
The plugin interface allow you to define your own checks and enhance postfwd's
|
|
functionality. Feel free to share useful things!
|
|
.PP
|
|
\&\fBWarning\fR
|
|
.PP
|
|
Note that the plugin interface is still at devel stage. Please test your plugins
|
|
carefully, because errors may cause postfwd to break! It is also
|
|
allowed to override attributes or built-in functions, but be sure that you know
|
|
what you do because some of them are used internally.
|
|
.PP
|
|
Please keep security in mind, when you access sensible ressources and never, ever
|
|
run postfwd as privileged user! Also never trust your input (especially hostnames,
|
|
and e\-mail addresses).
|
|
.PP
|
|
\&\fB\s-1ITEMS\s0\fR
|
|
.PP
|
|
Item plugins are perl subroutines which integrate additional attributes to requests
|
|
before they are evaluated against postfwd's ruleset like any other item of the
|
|
policy delegation protocol. This allows you to create your own checks.
|
|
.PP
|
|
plugin-items can not be used selective. these functions will be executed for every
|
|
request postfwd receives, so keep performance in mind.
|
|
.PP
|
|
.Vb 1
|
|
\& SYNOPSIS: %result = postfwd_items_plugin{<name>}(%request)
|
|
.Ve
|
|
.PP
|
|
means that your subroutine, called <name>, has access to a hash called \f(CW%request\fR,
|
|
which contains all request attributes, like \f(CW$request\fR{client_name} and must
|
|
return a value in the following form:
|
|
.PP
|
|
.Vb 1
|
|
\& save: $result{<item>} = <value>
|
|
.Ve
|
|
.PP
|
|
this creates the new item <item> containing <value>, which will be integrated in
|
|
the policy delegation request and therefore may be used in postfwd's ruleset.
|
|
.PP
|
|
.Vb 2
|
|
\& # do NOT remove the next line
|
|
\& %postfwd_items_plugin = (
|
|
\&
|
|
\& # EXAMPLES \- integrated in postfwd. no need to activate them here.
|
|
\&
|
|
\& # allows to check postfwd version in ruleset
|
|
\& "version" => sub {
|
|
\& my(%request) = @_;
|
|
\& my(%result) = (
|
|
\& "version" => $NAME." ".$VERSION,
|
|
\& );
|
|
\& return %result;
|
|
\& },
|
|
\&
|
|
\& # sender_domain and recipient_domain
|
|
\& "address_parts" => sub {
|
|
\& my(%request) = @_;
|
|
\& my(%result) = ();
|
|
\& $request{sender} =~ /@([^@]*)$/;
|
|
\& $result{sender_domain} = ($1 || \*(Aq\*(Aq);
|
|
\& $request{recipient} =~ /@([^@]*)$/;
|
|
\& $result{recipient_domain} = ($1 || \*(Aq\*(Aq);
|
|
\& return %result;
|
|
\& },
|
|
\&
|
|
\& # do NOT remove the next line
|
|
\& );
|
|
.Ve
|
|
.PP
|
|
\&\fB\s-1COMPARE\s0\fR
|
|
.PP
|
|
Compare plugins allow you to define how your new items should be compared to the ruleset.
|
|
These are optional. If you don't specify one, the default (== for exact match, =~ for \s-1PCRE\s0, ...)
|
|
will be used.
|
|
.PP
|
|
.Vb 1
|
|
\& SYNOPSIS: <item> => sub { return &{$postfwd_compare{<type>}}(@_); },
|
|
\&
|
|
\& # do NOT remove the next line
|
|
\& %postfwd_compare_plugin = (
|
|
\&
|
|
\& EXAMPLES \- integrated in postfwd. no need to activate them here.
|
|
\&
|
|
\& # Simple example
|
|
\& # SYNOPSIS: <result> = <item> (return &{$postfwd_compare{<type>}}(@_))
|
|
\& "client_address" => sub { return &{$postfwd_compare{cidr}}(@_); },
|
|
\& "size" => sub { return &{$postfwd_compare{numeric}}(@_); },
|
|
\& "recipient_count" => sub { return &{$postfwd_compare{numeric}}(@_); },
|
|
\&
|
|
\& # Complex example
|
|
\& # SYNOPSIS: <result> = <item>(<operator>, <ruleset value>, <request value>, <request>)
|
|
\& "numeric" => sub {
|
|
\& my($cmp,$val,$myitem,%request) = @_;
|
|
\& my($myresult) = undef; $myitem ||= "0"; $val ||= "0";
|
|
\& if ($cmp eq \*(Aq==\*(Aq) {
|
|
\& $myresult = ($myitem == $val);
|
|
\& } elsif ($cmp eq \*(Aq=<\*(Aq) {
|
|
\& $myresult = ($myitem <= $val);
|
|
\& } elsif ($cmp eq \*(Aq=>\*(Aq) {
|
|
\& $myresult = ($myitem >= $val);
|
|
\& } elsif ($cmp eq \*(Aq<\*(Aq) {
|
|
\& $myresult = ($myitem < $val);
|
|
\& } elsif ($cmp eq \*(Aq>\*(Aq) {
|
|
\& $myresult = ($myitem > $val);
|
|
\& } elsif ($cmp eq \*(Aq!=\*(Aq) {
|
|
\& $myresult = not($myitem == $val);
|
|
\& } elsif ($cmp eq \*(Aq!<\*(Aq) {
|
|
\& $myresult = not($myitem <= $val);
|
|
\& } elsif ($cmp eq \*(Aq!>\*(Aq) {
|
|
\& $myresult = not($myitem >= $val);
|
|
\& } else {
|
|
\& $myresult = ($myitem >= $val);
|
|
\& };
|
|
\& return $myresult;
|
|
\& },
|
|
\&
|
|
\& # do NOT remove the next line
|
|
\& );
|
|
.Ve
|
|
.PP
|
|
\&\fB\s-1ACTIONS\s0\fR
|
|
.PP
|
|
Action plugins allow to define new postfwd actions. By setting the \f(CW$stop\fR\-flag you can decide to
|
|
continue or to stop parsing the ruleset.
|
|
.PP
|
|
.Vb 2
|
|
\& SYNOPSIS: (<stop rule parsing>, <next rule index>, <return action>, <logprefix>, <request>) =
|
|
\& <action> (<current rule index>, <current time>, <command name>, <argument>, <logprefix>, <request>)
|
|
\&
|
|
\& # do NOT remove the next line
|
|
\& %postfwd_actions_plugin = (
|
|
\&
|
|
\& # EXAMPLES \- integrated in postfwd. no need to activate them here.
|
|
\&
|
|
\& # note(<logstring>) command
|
|
\& "note" => sub {
|
|
\& my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
|
\& my($myaction) = \*(Aqdunno\*(Aq; my($stop) = 0;
|
|
\& log_info "[RULES] ".$myline." \- note: ".$myarg if $myarg;
|
|
\& return ($stop,$index,$myaction,$myline,%request);
|
|
\& },
|
|
\&
|
|
\& # skips next <myarg> rules
|
|
\& "skip" => sub {
|
|
\& my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
|
\& my($myaction) = \*(Aqdunno\*(Aq; my($stop) = 0;
|
|
\& $index += $myarg if ( $myarg and not(($index + $myarg) > $#Rules) );
|
|
\& return ($stop,$index,$myaction,$myline,%request);
|
|
\& },
|
|
\&
|
|
\& # dumps current request contents to syslog
|
|
\& "dumprequest" => sub {
|
|
\& my($index,$now,$mycmd,$myarg,$myline,%request) = @_;
|
|
\& my($myaction) = \*(Aqdunno\*(Aq; my($stop) = 0;
|
|
\& map { log_info "[DUMP] rule=$index, Attribute: $_=$request{$_}" } (keys %request);
|
|
\& return ($stop,$index,$myaction,$myline,%request);
|
|
\& },
|
|
\&
|
|
\& # do NOT remove the next line
|
|
\& );
|
|
.Ve
|
|
.SS "\s-1COMMAND\s0 \s-1LINE\s0"
|
|
.IX Subsection "COMMAND LINE"
|
|
\&\fIRuleset\fR
|
|
.PP
|
|
The following arguments are used to specify the source of the postfwd ruleset. This means
|
|
that at least one of the following is required for postfwd to work.
|
|
.PP
|
|
.Vb 3
|
|
\& \-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.
|
|
.Ve
|
|
.PP
|
|
\&\fIScoring\fR
|
|
.PP
|
|
.Vb 2
|
|
\& \-s, \-\-scores <val>=<action>
|
|
\& Returns <action> to postfix, when the request\*(Aqs score exceeds <val>
|
|
.Ve
|
|
.PP
|
|
Multiple usage is allowed. Just chain your arguments, like:
|
|
.PP
|
|
.Vb 3
|
|
\& postfwd \-r "<item>=<value>;action=<result>" \-f <file> \-f <file> ...
|
|
\& or
|
|
\& postfwd \-\-scores 4.5="WARN high score" \-\-scores 5.0="REJECT postfwd score too high" ...
|
|
.Ve
|
|
.PP
|
|
In case of multiple scores, the highest match will count. The order of the arguments will be
|
|
reflected in the postfwd ruleset.
|
|
.PP
|
|
\&\fIControl\fR
|
|
.PP
|
|
.Vb 3
|
|
\& \-d, \-\-daemon
|
|
\& postfwd will run as daemon and listen on the network for incoming
|
|
\& queries (default 127.0.0.1:10040).
|
|
\&
|
|
\& \-k, \-\-kill
|
|
\& Stops a running postfwd daemon.
|
|
\&
|
|
\& \-\-reload
|
|
\& Reloads configuration.
|
|
\&
|
|
\& \-\-dumpstats
|
|
\& Displays program usage statistics.
|
|
\&
|
|
\& \-\-dumpcache
|
|
\& Displays cache contents.
|
|
\&
|
|
\& \-\-delcache <item>
|
|
\& Removes an item from the request cache. Use \-\-dumpcache to identify objects.
|
|
\& E.g.:
|
|
\& # postfwd \-\-dumpcache
|
|
\& ...
|
|
\& %rate_cache \-> %sender=gmato@jqvo.org \-> %RATE002+2_600 \-> @count \-> \*(Aq1\*(Aq
|
|
\& %rate_cache \-> %sender=gmato@jqvo.org \-> %RATE002+2_600 \-> @maxcount \-> \*(Aq2\*(Aq
|
|
\& ...
|
|
\& # postfwd \-\-delrate="sender=gmato@jqvo.org"
|
|
\& rate cache item \*(Aqsender=gmato@jqvo.org\*(Aq removed
|
|
\&
|
|
\& \-\-delrate <item>
|
|
\& Removes an item from the rate cache. Use \-\-dumpcache to identify objects.
|
|
.Ve
|
|
.PP
|
|
\&\fINetworking\fR
|
|
.PP
|
|
postfwd 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.
|
|
.PP
|
|
.Vb 2
|
|
\& \-i, \-\-interface <dev>
|
|
\& Bind postfwd to the specified interface (default 127.0.0.1).
|
|
\&
|
|
\& \-p, \-\-port <port>
|
|
\& postfwd listens on the specified port (default tcp/10040).
|
|
\&
|
|
\& \-\-proto <type>
|
|
\& The protocol type for postfwd\*(Aqs socket. Currently you may use \*(Aqtcp\*(Aq or \*(Aqunix\*(Aq here.
|
|
\& To use postfwd with a unix domain socket, run it as follows:
|
|
\& postfwd \-\-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 (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.
|
|
\&
|
|
\& \-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.
|
|
\&
|
|
\& \-\-facility <f>
|
|
\& sets the syslog facility, default is \*(Aqmail\*(Aq
|
|
\&
|
|
\& \-\-socktype <s>
|
|
\& sets the Sys::Syslog socktype to \*(Aqnative\*(Aq, \*(Aqinet\*(Aq or \*(Aqunix\*(Aq.
|
|
\& Default is to auto\-detect this depening on module version and os.
|
|
\&
|
|
\& \-l, \-\-logname <label>
|
|
\& Labels the syslog messages. Useful when running multiple
|
|
\& instances of postfwd.
|
|
\&
|
|
\& \-\-loglen <int>
|
|
\& Truncates any syslog message after <int> characters.
|
|
.Ve
|
|
.PP
|
|
\&\fIPlugins\fR
|
|
.PP
|
|
.Vb 3
|
|
\& \-\-plugins <file>
|
|
\& Loads postfwd plugins from file. Please see http://postfwd.org/postfwd.plugins
|
|
\& or the plugins.postfwd.sample that is available from the tarball for more info.
|
|
.Ve
|
|
.PP
|
|
\&\fIOptional arguments\fR
|
|
.PP
|
|
These parameters influence the way postfwd is working. Any of them can be combined.
|
|
.PP
|
|
.Vb 4
|
|
\& \-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\*(Aqt 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\*(Aqt use the sender
|
|
\& item in your ruleset.
|
|
\&
|
|
\& \-\-cache\-rdomain\-only
|
|
\& This will strip the localpart of the recipient\*(Aqs 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\e.0\e.0\e.\ed+$)
|
|
\& 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 postfwd only for RBL/RHSBL control,
|
|
\& you may set this to
|
|
\& postfwd \-\-cache=3600 \-\-cacheid=client_name,client_address
|
|
\& This increases efficiency of caching and improves postfwd\*(Aqs 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 postfwd 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, \-\-stdoutlog
|
|
\& Redirects all syslog messages to stdout for debugging. Never use this with postfix!
|
|
\&
|
|
\& \-t, \-\-test
|
|
\& In test mode postfwd 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
|
|
\& postfwd 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.
|
|
\&
|
|
\& \-\-keep_rates (default=0)
|
|
\& With this option set postfwd does not clear the rate limit counters on reload. Please
|
|
\& note that you have to restart (not reload) postfwd with this option if you change
|
|
\& any rate limit rules.
|
|
\&
|
|
\& \-\-save_rates (default=none)
|
|
\& With this option postfwd saves existing rate limit counters to disk and reloads them
|
|
\& on program start. This allows persistent rate limits across program restarts or reboots.
|
|
\& Please note that postfwd needs read and write access to the specified file.
|
|
\&
|
|
\& \-\-fast_limit_evaluation (default=0)
|
|
\& Once a ratelimit was set by the ruleset, future requests will be evaluated against it
|
|
\& before consulting the ruleset. This mode was the default behaviour until v1.30.
|
|
\& With this mode rate limits will be faster, but also eventually set up
|
|
\& whitelisting\-rules within the ruleset might not work as expected.
|
|
\& LIMITATIONS: This option does not allow nested postfwd commands like
|
|
\& action=rate(sender/3/60/wait(3))
|
|
\& This option doe not work with the strict\-rfc5321 rate() functions.
|
|
.Ve
|
|
.PP
|
|
\&\fIInformational arguments\fR
|
|
.PP
|
|
These arguments are for command line usage only. Never ever use them with postfix spawn!
|
|
.PP
|
|
.Vb 2
|
|
\& \-C, \-\-showconfig
|
|
\& Displays the current ruleset. Use \-v for verbose output.
|
|
\&
|
|
\& \-P, \-\-perfmon
|
|
\& This option turns of any syslogging and output. It is included
|
|
\& for performance testing.
|
|
\&
|
|
\& \-V, \-\-version
|
|
\& Displays the program version.
|
|
\&
|
|
\& \-h, \-\-help
|
|
\& Shows program usage.
|
|
\&
|
|
\& \-m, \-\-manual
|
|
\& Displays the program manual.
|
|
.Ve
|
|
.SS "\s-1REFRESH\s0"
|
|
.IX Subsection "REFRESH"
|
|
In daemon mode postfwd reloads it's ruleset after receiving a \s-1HUP\s0 signal. Please see the description of
|
|
the '\-I' switch to have your configuration refreshed for every request postfwd receives.
|
|
.SS "\s-1EXAMPLES\s0"
|
|
.IX Subsection "EXAMPLES"
|
|
.Vb 7
|
|
\& ## 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=\e.gmx\e.(net|de)$
|
|
\& id=WL003; action=dunno ; sender=@someshop\e.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\e.tld$ ; ccert_fingerprint=AA:BB:CC..
|
|
\& id=TL002; action=REJECT wrong TLS fingerprint ; sender=@authority\e.tld$
|
|
\& id=TL003; action=REJECT tls keylength < 64 ; sender=@secret\e.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; protocol_state==END\-OF\-MESSAGE; action=DUNNO; size<=30000000 ; client_name=\e.customer1.tld$
|
|
\& id=SZ002; protocol_state==END\-OF\-MESSAGE; action=DUNNO; size<=20000000 ; sasl_username==joejob
|
|
\& id=SZ002; protocol_state==END\-OF\-MESSAGE; action=DUNNO; size<=10000000
|
|
\& id=SZ100; protocol_state==END\-OF\-MESSAGE; action=REJECT message too large
|
|
\&
|
|
\& ## Selective Greylisting
|
|
\& ##
|
|
\& ## Note that postfwd does not include greylisting. This setup requires a running postgrey service
|
|
\& ## at port 10031 and the following postfix restriction class in your main.cf:
|
|
\& ##
|
|
\& ## smtpd_restriction_classes = check_postgrey, ...
|
|
\& ## check_postgrey = check_policy_service inet:127.0.0.1:10031
|
|
\& #
|
|
\& # 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=check_postgrey ; rbl=dul.dnsbl.sorbs.net, zen.spamhaus.org/127.0.0.1[01]/1200
|
|
\& id=GR002; action=check_postgrey ; client_name=^unknown$
|
|
\& id=GR003; action=check_postgrey ; client_name=\e.(t\-ipconnect|alicedsl|ish)\e.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=check_postgrey
|
|
\&
|
|
\& ## 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 ; protocol_state==END\-OF\-MESSAGE; action=REJECT message too big (max. 10MB); size=10000000
|
|
\& id=R100 ; protocol_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=check_postgrey
|
|
\& id=S02 ; score=5.0 ; action=REJECT postfwd 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 ; protocol_state==RCPT
|
|
\& action=rate(client_address/30/3600/450 4.7.1 sorry, max 30 requests per hour)
|
|
\& id=SIZE01 ; client_name==unknown ; protocol_state==END\-OF\-MESSAGE
|
|
\& 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=(\ed+[\e.\-_]){4}
|
|
\& &&GONOW ; &&RBLS ; client_name=[\e.\-_](adsl|dynamic|ppp|)[\e.\-_]
|
|
\&
|
|
\& ## 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~=(\ed+[\e.\-_]){4}
|
|
\& client_name~=[\e.\-_](adsl|dynamic|ppp|)[\e.\-_]
|
|
\& ...
|
|
\& };
|
|
\& &&BAD_HELO{
|
|
\& helo_name==my.name.tld
|
|
\& helo_name~=^([^\e.]+)$
|
|
\& helo_name~=\e.(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\*(Aqt 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]
|
|
.Ve
|
|
.SS "\s-1PARSER\s0"
|
|
.IX Subsection "PARSER"
|
|
\&\fIConfiguration\fR
|
|
.PP
|
|
The postfwd 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:
|
|
.PP
|
|
.Vb 4
|
|
\& postfwd \-\-showconfig \e
|
|
\& \-r "id=TEST; recipient_count=100; action=WARN mail with 100+ recipients" \e
|
|
\& \-f /etc/postfwd.cf \e
|
|
\& \-r "id=DEFAULT; action=dunno";
|
|
.Ve
|
|
.PP
|
|
will produce the following output:
|
|
.PP
|
|
.Vb 5
|
|
\& Rule 0: id\->"TEST" action\->"WARN mail with 100+ recipients"; recipient_count\->"100"
|
|
\& ...
|
|
\& ... <content of /etc/postfwd.cf> ...
|
|
\& ...
|
|
\& Rule <n>: id\->"DEFAULT" action\->"dunno"
|
|
.Ve
|
|
.PP
|
|
Multiple items of the same type will be added to lists (see the \*(L"\s-1ITEMS\s0\*(R" section for more info):
|
|
.PP
|
|
.Vb 2
|
|
\& postfwd \-\-showconfig \e
|
|
\& \-r "client_address=192.168.1.0/24; client_address=172.16.26.32; action=dunno"
|
|
.Ve
|
|
.PP
|
|
will result in:
|
|
.PP
|
|
.Vb 1
|
|
\& Rule 0: id\->"R\-0"; action\->"dunno"; client_address\->"192.168.1.0/24, 172.16.26.32"
|
|
.Ve
|
|
.PP
|
|
Macros are evaluated at configuration stage, which means that
|
|
.PP
|
|
.Vb 3
|
|
\& postfwd \-\-showconfig \e
|
|
\& \-r "&&RBLS { rbl=bl.spamcop.net; client_name=^unknown$; };" \e
|
|
\& \-r "id=RBL001; &&RBLS; action=REJECT listed on spamcop and bad rdns";
|
|
.Ve
|
|
.PP
|
|
will result in:
|
|
.PP
|
|
.Vb 1
|
|
\& Rule 0: id\->"RBL001"; action\->"REJECT listed on spamcop and bad rdns"; rbl\->"bl.spamcop.net"; client_name\->"^unknown$"
|
|
.Ve
|
|
.PP
|
|
\&\fIRequest processing\fR
|
|
.PP
|
|
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 \*(L"\-v\*(R" or \*(L"\-vv\*(R" switch. \*(L"\-L\*(R" redirects log messages to stdout.
|
|
.PP
|
|
Keeping the order of the ruleset in general, items will be compared in random order, which basically means that
|
|
.PP
|
|
.Vb 1
|
|
\& id=R001; action=dunno; client_address=192.168.1.1; sender=bob@alice.local
|
|
.Ve
|
|
.PP
|
|
equals to
|
|
.PP
|
|
.Vb 1
|
|
\& id=R001; sender=bob@alice.local; client_address=192.168.1.1; action=dunno
|
|
.Ve
|
|
.PP
|
|
Lists will be evaluated in the specified order. This allows to place faster expressions at first:
|
|
.PP
|
|
.Vb 1
|
|
\& postfwd \-vv \-L \-r "id=RBL001; rbl=localrbl.local zen.spamhaus.org; action=REJECT" /some/where/request.sample
|
|
.Ve
|
|
.PP
|
|
produces the following
|
|
.PP
|
|
.Vb 11
|
|
\& [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
|
|
.Ve
|
|
.PP
|
|
The negation operator !!(<value>) has the highest priority and therefore will be evaluated first. Then variable substitutions are performed:
|
|
.PP
|
|
.Vb 1
|
|
\& postfwd \-vv \-L \-r "id=TEST; action=REJECT; client_name=!!($$heloname)" /some/where/request.sample
|
|
.Ve
|
|
.PP
|
|
will give
|
|
.PP
|
|
.Vb 5
|
|
\& [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
|
|
.Ve
|
|
.PP
|
|
\&\fIRuleset evaluation\fR
|
|
.PP
|
|
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 postfwd ruleset.
|
|
.PP
|
|
If a rule matches, there are two options:
|
|
.PP
|
|
* Rule returns postfix action (dunno, reject, ...)
|
|
The parser stops rule processing and returns the action to postfix. Other rules will not be evaluated.
|
|
.PP
|
|
* Rule returns postfwd action (\fIjump()\fR, \fInote()\fR, ...)
|
|
The parser evaluates the given action and continues with the next rule (except for the \fIjump()\fR or \fIquit()\fR actions \- please see the \*(L"\s-1ACTIONS\s0\*(R" section
|
|
for more information). Nothing will be sent to postfix.
|
|
.PP
|
|
If no rule has matched and the end of the ruleset is reached postfwd will return dunno without logging anything unless in verbose mode. You may
|
|
simply place a last `catch\-allA\*^X rule to change that behaviour:
|
|
.PP
|
|
.Vb 2
|
|
\& ... <your rules> ...
|
|
\& id=DEFAULT ; action=dunno
|
|
.Ve
|
|
.PP
|
|
will log any request that passes the ruleset without having hit a prior rule.
|
|
.SS "\s-1INTEGRATION\s0"
|
|
.IX Subsection "INTEGRATION"
|
|
\&\fIIntegration via daemon mode\fR
|
|
.PP
|
|
The common way to use postfwd is to start it as daemon, listening at a specified tcp port.
|
|
As postfwd will run in a single instance (multiplexing mode), it will take most benefit of
|
|
it`s internal caching in that case. Start postfwd with the following parameters:
|
|
.PP
|
|
.Vb 1
|
|
\& postfwd \-d \-f /etc/postfwd.cf \-i 127.0.0.1 \-p 10040 \-u nobody \-g nobody \-S
|
|
.Ve
|
|
.PP
|
|
For efficient caching you should check if you can use the options \-\-cache\-rdomain\-only, \-\-cache\-no\-sender
|
|
and \-\-cache\-no\-size.
|
|
.PP
|
|
Now check your syslogs (default facility \*(L"mail\*(R") for a line like:
|
|
.PP
|
|
.Vb 1
|
|
\& Aug 9 23:00:24 mail postfwd[5158]: postfwd n.nn ready for input
|
|
.Ve
|
|
.PP
|
|
and use `netstat \-an|grep 10040` to check for something like
|
|
.PP
|
|
.Vb 1
|
|
\& tcp 0 0 127.0.0.1:10040 0.0.0.0:* LISTEN
|
|
.Ve
|
|
.PP
|
|
If everything works, open your postfix main.cf and insert the following
|
|
.PP
|
|
.Vb 4
|
|
\& 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
|
|
.Ve
|
|
.PP
|
|
Reload your configuration with `postfix reload` and watch your logs. In it works you should see
|
|
lines like the following in your mail log:
|
|
.PP
|
|
.Vb 1
|
|
\& 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
|
|
.Ve
|
|
.PP
|
|
If you want to check for size or rcpt_count items you must integrate postfwd 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:
|
|
.PP
|
|
.Vb 3
|
|
\& domain1.local postfwdcheck
|
|
\& domain2.local postfwdcheck
|
|
\& ...
|
|
.Ve
|
|
.PP
|
|
Then postmap that file (`postmap hash:/etc/postfix/policy`), open your main.cf and enter
|
|
.PP
|
|
.Vb 3
|
|
\& # 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
|
|
.Ve
|
|
.PP
|
|
Reload postfix and watch your logs.
|
|
.PP
|
|
\&\fIIntegration via xinetd\fR
|
|
.PP
|
|
There might be several reasons for you to use postfwd via a tcp wrapper package like xinetd (see <http://www.xinetd.org/>).
|
|
I won`t discuss that here. If you plan to do so, just add the following line to your /etc/services file:
|
|
.PP
|
|
.Vb 2
|
|
\& # postfwd port
|
|
\& postfwd 10040/tcp
|
|
.Ve
|
|
.PP
|
|
Then create a file '/etc/xinetd.d/postfwd':
|
|
.PP
|
|
.Vb 10
|
|
\& {
|
|
\& interface = 127.0.0.1
|
|
\& socket_type = stream
|
|
\& protocol = tcp
|
|
\& wait = no
|
|
\& user = nobody
|
|
\& server = /usr/local/bin/postfwd
|
|
\& server_args = \-f /etc/postfwd.cf
|
|
\& disable = no
|
|
\& }
|
|
.Ve
|
|
.PP
|
|
and restart the xinetd daemon (usually a \s-1SIGHUP\s0 should be fine). If you experience problems
|
|
you might want to check your system's log for xinetd errors like \*(L"socket already in use\*(R".
|
|
.PP
|
|
The integration with postfix is similar to the \fIIntegration via daemon mode\fR section above.
|
|
Reload postfix and watch your logs to see if everything works.
|
|
.SS "\s-1TESTING\s0"
|
|
.IX Subsection "TESTING"
|
|
First you have to create a ruleset (see Configuration section). Check it with
|
|
.PP
|
|
.Vb 1
|
|
\& postfwd \-f /etc/postfwd.cf \-C
|
|
.Ve
|
|
.PP
|
|
There is an example policy request distributed with postfwd, called 'request.sample'.
|
|
Simply change it to meet your requirements and use
|
|
.PP
|
|
.Vb 1
|
|
\& postfwd \-f /etc/postfwd.cf <request.sample
|
|
.Ve
|
|
.PP
|
|
You should get an answer like
|
|
.PP
|
|
.Vb 1
|
|
\& action=<whateveryouconfigured>
|
|
.Ve
|
|
.PP
|
|
For network tests I use netcat:
|
|
.PP
|
|
.Vb 1
|
|
\& nc 127.0.0.1 10040 <request.sample
|
|
.Ve
|
|
.PP
|
|
to send a request to postfwd. If you receive nothing, make sure that postfwd is running and
|
|
listening on the specified network settings.
|
|
.SS "\s-1PERFORMANCE\s0"
|
|
.IX Subsection "PERFORMANCE"
|
|
Some of these proposals might not match your environment. Please check your requirements and test new options carefully!
|
|
.PP
|
|
.Vb 7
|
|
\& \- 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
|
|
.Ve
|
|
.SS "\s-1SEE\s0 \s-1ALSO\s0"
|
|
.IX Subsection "SEE ALSO"
|
|
See <http://www.postfix.org/SMTPD_POLICY_README.html> for a description
|
|
of how Postfix policy servers work.
|
|
.SH "LICENSE"
|
|
.IX Header "LICENSE"
|
|
postfwd is free software and released under \s-1BSD\s0 license, which basically means
|
|
that you can do what you want as long as you keep the copyright notice:
|
|
.PP
|
|
Copyright (c) 2007, Jan Peter Kessler
|
|
All rights reserved.
|
|
.PP
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
.PP
|
|
.Vb 9
|
|
\& * 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.
|
|
.Ve
|
|
.PP
|
|
\&\s-1THIS\s0 \s-1SOFTWARE\s0 \s-1IS\s0 \s-1PROVIDED\s0 \s-1BY\s0 \s-1ME\s0 ``\s-1AS\s0 \s-1IS\s0'' \s-1AND\s0 \s-1ANY\s0 \s-1EXPRESS\s0 \s-1OR\s0 \s-1IMPLIED\s0 \s-1WARRANTIES\s0,
|
|
\&\s-1INCLUDING\s0, \s-1BUT\s0 \s-1NOT\s0 \s-1LIMITED\s0 \s-1TO\s0, \s-1THE\s0 \s-1IMPLIED\s0 \s-1WARRANTIES\s0 \s-1OF\s0 \s-1MERCHANTABILITY\s0 \s-1AND\s0 \s-1FITNESS\s0
|
|
\&\s-1FOR\s0 A \s-1PARTICULAR\s0 \s-1PURPOSE\s0 \s-1ARE\s0 \s-1DISCLAIMED\s0. \s-1IN\s0 \s-1NO\s0 \s-1EVENT\s0 \s-1SHALL\s0 \s-1BE\s0 \s-1LIABLE\s0 \s-1FOR\s0 \s-1ANY\s0 \s-1DIRECT\s0,
|
|
\&\s-1INDIRECT\s0, \s-1INCIDENTAL\s0, \s-1SPECIAL\s0, \s-1EXEMPLARY\s0, \s-1OR\s0 \s-1CONSEQUENTIAL\s0 \s-1DAMAGES\s0 (\s-1INCLUDING\s0, \s-1BUT\s0
|
|
\&\s-1NOT\s0 \s-1LIMITED\s0 \s-1TO\s0, \s-1PROCUREMENT\s0 \s-1OF\s0 \s-1SUBSTITUTE\s0 \s-1GOODS\s0 \s-1OR\s0 \s-1SERVICES\s0; \s-1LOSS\s0 \s-1OF\s0 \s-1USE\s0, \s-1DATA\s0, \s-1OR\s0
|
|
\&\s-1PROFITS\s0; \s-1OR\s0 \s-1BUSINESS\s0 \s-1INTERRUPTION\s0) \s-1HOWEVER\s0 \s-1CAUSED\s0 \s-1AND\s0 \s-1ON\s0 \s-1ANY\s0 \s-1THEORY\s0 \s-1OF\s0 \s-1LIABILITY\s0,
|
|
\&\s-1WHETHER\s0 \s-1IN\s0 \s-1CONTRACT\s0, \s-1STRICT\s0 \s-1LIABILITY\s0, \s-1OR\s0 \s-1TORT\s0 (\s-1INCLUDING\s0 \s-1NEGLIGENCE\s0 \s-1OR\s0 \s-1OTHERWISE\s0)
|
|
\&\s-1ARISING\s0 \s-1IN\s0 \s-1ANY\s0 \s-1WAY\s0 \s-1OUT\s0 \s-1OF\s0 \s-1THE\s0 \s-1USE\s0 \s-1OF\s0 \s-1THIS\s0 \s-1SOFTWARE\s0, \s-1EVEN\s0 \s-1IF\s0 \s-1ADVISED\s0 \s-1OF\s0 \s-1THE\s0
|
|
\&\s-1POSSIBILITY\s0 \s-1OF\s0 \s-1SUCH\s0 \s-1DAMAGE\s0.
|
|
.SH "AUTHOR"
|
|
.IX Header "AUTHOR"
|
|
Jan\ Peter\ Kessler\ <info\ (\s-1AT\s0)\ postfwd\ (\s-1DOT\s0)\ org>. Let me know, if you have any suggestions.
|