# escape any character \c = c (\n = n, \\ = \, ...). This format allow non-ascii
# strings and string concatenation " abcd " "ef" = " abcd ef".
# - ASCII-only strings can be written without double-quotes. They can be splitted
-# into several lines using shell-like escaping of EOL. A string begins on the
-# first non-blank character. This king of string can not contain semi-colons.
+# into several lines using shell-like escaping of EOL. A string begins and ends on
+# on a non-blank character. This king of string can not contain semi-colons.
# eg:
# The following format are equivalent:
# (1) this is a str\
# - a list of type-specific parameters
# - a list of hooks (on_hookname)
#
+# Format:
+# A filter look likes that:
+#
+# filter_name {
+# type = type_name;
+# param1 = parameter value 1;
+# ...
+# on_hook1 = action1;
+# on_hook2 = action2;
+# ...
+# }
+#
# Hooks:
# A filter can returns different values. Each return value is given a name. The
# configuration associates an action to run to a return value name.
#
+# A hook action has the format: (counter:id:incr:)?(filter_name|postfix:ACTION)
+#
+# The action can contains the reference to a counter to update. This counters are "message"-wide
+# counters that stay valid until the end of the filtering of the message. This counters are useful
+# to trig different actions depending on the number of failures encountered during the processing
+# of a message. There are 64 counters (0..63), accessible from all the filters. By specifying
+# counter:id:incr as a prefix of the hook action, you tell postlicyd to add (incr) to counter
+# (id) when this hook is reached. The "counter" filter type allow you to run actions depending
+# on the value of a counter.
+#
# The action can be either a postfix access(5) value or a filter name. Postfix access
-# parameters must be prefixed by 'postfix:'.
+# parameters must be prefixed by 'postfix:'. The text argument given to a postfix reply
+# may contain format strings to be replaced by the parameters of the query. This arguments
+# have the following format: ${fieldname}
#
# eg:
# on_match = postfix:REJECT Blacklisted;
+# on_fail = postfix:450 Greylisted, see http://www.example.org/${sender_domain}.html
+# on_error = counter:0:1:postfix:DUNNO
+# on_match = counter:63:10:whitelist
#
# Filter:
# Current defined filter types are:
-# - rbl: match the client_address against one or more blacklist files from a rbl
+# - iplist: match the client_address against one or more blacklist files from a rbl
# Parameters:
-# - file: (non)?lock:weight:filename
-# declare a file to load. If lock is given, the blacklist is locked into the
+# - file: (no)?lock:weight:filename
+# declare a file to load. If lock is given, the klist is locked into the
# RAM. The weight is a number giving the weight of this blaclist file in the
# score of the IP
+# - rbldns: (no)?lock:weight:filename
+# this is an alias for file.
+# - dns: weight:hostname
+# use a rbl via DNS resolution with the given weight. If a DNS lookup error occurs
+# the IP is considered as beeing "not found".
# - soft_threshold: score (default: 1)
# minimum score to match the soft_match return value
# - hard_threshold: score (default: 1)
# Return value:
# The score of a query is the sum of the weight of the blacklist it matched.
# - If the IP can not be parsed, returns error
+# - If no rbl was available (no file and all dns down), returns error.
# - If the score is strictly greater >= than hard_threshold, returns hard_match
# - If the score is strictly greater >= than soft_threshold, returns soft_match
# - Else, returns fail
-#
+
+# Lookup in a rbl
+spamhaus_and_abuseat {
+ type = iplist;
+
+ # configuration
+ file = lock:10:/var/spool/postlicyd/rbl.spamhaus.org;
+ file = lock:1:/var/spool/postlicyd/cbl.abuseat.org;
+ soft_threshold = 1;
+ hard_threshold = 11;
+
+ # hooks
+ on_soft_match = greylist;
+ on_hard_match = postfix:REJECT optional text;
+ on_fail = postfix:OK;
+ on_error = postfix:DUNNO;
+}
+
+
+# - strlist: match strings from the query against a list of list.
+# Parameters:
+# - file: (no)?lock:(partial-)?(pre|suf)fix:weight:filename
+# declare a file to load. If lock is given, the list is locked into the
+# RAM. Prefix/Suffix is a parameter to tell the matcher which is the most
+# efficient storage order. The strings are internally stored into a trie that
+# allow high compression if a lot of prefix are shared by several strings. If
+# you choose "prefix", string are stored in the natural order in memory and
+# prefix compression is performed. If you choose "suffix", strings are stored
+# in reverse order in memory and suffix compression is performed. If you add "partial-"
+# to the match order, the entry will match if the file contains a prefix (resp. suffix)
+# of the string. The weight is a number giving the weight of this list in the string score.
+# e.g.:
+# * a file that contains ".polytechnique.org" in "partial-suffix" mode will match
+# all subdomains of "polytechnique.org".
+# * a file that contains "postmaster@" in "partial-prefix" mode will match all
+# postmaster emails.
+# * a file open without "partial-" modifier match exact strings.
+# - rbldns: (no)?lock:weight:filename
+# declare a rbldns zone file to load. This is exactly the same as file excepted that it wraps
+# parsing of hostname to split them into 2 categories:
+# * names beginning with '*' are sorted as 'domains' and are matched as suffix
+# * names starting with an alphanumirical character are sorted as 'hostnames' and are
+# process via exact matching.
+# - dns: weight:hostname
+# use a rhbl via DNS resolution with the given weight. If a DNS lookup error occurs
+# the hostname is considered as beeing "not found". This can only be used with "hostnames"
+# typed fields.
+# - soft_threshold: score (default: 1)
+# minimum score to match the soft_match return value
+# - hard_threshold: score (default: 1)
+# minimum score to match the hard_match return value
+# - fields: field_name(,field_name)*
+# list of field the match the string against.
+# currently only email OR hostname fields are supported. You MUST choose only
+# one of these types per strlist, and be carefull that the field you requested
+# are available in the protocol state you want to use this filter for.
+# * hostname fields: helo_name, client_name, reverse_client_name, sender_domain,
+# recipient_domain
+# * email fields: sender, recipient
+# No space is allowed in this parameter.
+# Return value:
+# The score of a query is the sum of the weight of the list it matched.
+# - If no rhbl was available (no file and all dns down), returns error.
+# - If the score is strictly greater >= than hard_threshold, returns hard_match
+# - If the score is strictly greater >= than soft_threshold, returns soft_match
+# - Else, returns fail
+# State:
+# - to match helo_name, you must be on HELO state or later
+# (stmpd_helo_restrictions)
+# - to match sender, you must be on MAIL state or later
+# (smtpd_sender_restrictions)
+# - to match recipient, you must on RCPT state (stmpd_recipient_restrictions)
+# - client_name and reverse_client_name are always available
+
+# Whitelist some clients
+client_whitelist {
+ type = strlist;
+
+ # configuration
+ file = lock:1:suffix:/var/spool/postlicyd/client_whitelist;
+ rbldns = lock:1:/va/spool/postlicyd/abuse.rfc-ignorant.org;
+ fields = client_name,sender_domain,helo_name;
+
+ # hooks
+ on_hard_match = postfix:OK;
+ on_fail = spamhaus_and_abuseat;
+}
+
+
# - greylist: greylister
# Paramters:
# - path: /my/path/ (required)
# - prefix: name (default: "")
# prefix to the name of the greylist database
# - lookup_by_host: boolean (default: false)
-# perform lookup per host instead of domain.
+# perform lookup per host. The default behaviour is to remove the last number of the IP
+# to match a domain. This behaviour is disabled if a part of the IP is contained in the
+# hostname (look like a dialup ip from a provider). With this flag on, the "domain"
+# matching is always disable.
+# - no_sender: boolean (default: false)
+# do not use the sender address. Default behaviour is to greylist using the tuple
+# (client_address, sender, recipient). With this flag on, the sender is not used.
+# - no_recipient: boolean (default: false)
+# same as no_sender but with recipient.
# - delay: number (default: 300)
# number of seconds the client must wait before retrial.
# - retry_window: (default: 2 * 24 * 3600)
# - client_awl: number (default: 5)
# number of successful greylisting before the client get whitelisted (0 means,
# no-auto-whitelist).
+# - max_age: number (default: 30 * 3600)
+# lifetime of a greylist/whitelist session: ie, if a client does ne reappear during
+# max_age seconds, the entries associated to this client are invalidated.
# Return value:
# - if the client is whitelisted, returns whitelist
# - if the client is greylisted, returns greylist
# - if a error occured (not currently possible), returns error
# State:
-# this filter is a recipient filter and works in RCPT state only
-# (smtpd_recipient_restrictions).
-#
-# Format:
-# A filter look likes that:
-#
-# filter_name {
-# type = type_name;
-# param1 = parameter value 1;
-# ...
-# on_action1 = action;
-# }
-
+# this filter is a recipient filter and works in RCPT state onl if no_recipient
+# is not specified (smtpd_recipient_restrictions). If no_sender is not given, this
+# requires a sender name, and so must be called after MAIL TO.
+# Perform greylisting
greylist {
type = greylist;
on_whitelist = postfix:OK;
}
-spamhaus_and_abuseat {
- type = rbl;
+
+# - match: direct matching against the query fields
+# Parameters:
+# - match_all: boolean
+# if true, the filter won't match until all conditions
+# are verified. If false, the filter match on the first
+# verified condition.
+# - condition: field_name OP (value)
+# * the field_name is one of the field name of the query
+# emitted by postfix. This list with description of each
+# field is available at:
+# http://www.postfix.org/SMTPD_POLICY_README.html
+# postlicyd also support fields sender_domain and recipient_domain
+# * OP is an operator. Available operators are:
+# == field_name is strictly equal to value
+# =i field_name is case insensitively equal to value
+# != field_name is not equal to value
+# !i field_name is not case insensitively equal to value
+# >= field_name contains value
+# >i field_name contains case insensitively value
+# <= field_name is contained by value
+# <i field_name is contained case insensitively by value
+# #= field_name is empty or not set
+# #i field_name is not empty
+# Return value:
+# - if the conditions are verified (according to match_all strategy), return match
+# - if the conditions are not verified, return fail
+
+# match one of the condition: "stress mode activated", "client_name contains debian.org" or
+# "recipient is empty"
+match {
+ type = match;
# configuration
- file = lock:10:/var/spool/postlicyd/rbl.spamhaus.org;
- file = lock:1:/var/spool/postlicyd/cbl.abuseat.org;
- soft_threshold = 1;
- hard_threshold = 9;
+ match_all = false;
+ condition = stress == yes;
+ condition = client_name >= debian.org;
+ condition = recipient #=;
- # hooks
- on_soft_match = greylist;
- on_hard_match = postfix:REJECT optional text;
- on_fail = postfix:OK;
- on_error = postfix:DUNNO;
+ # hook
+ on_match = postfix:OK;
+ on_fail = counter:0:1:greylist;
}
+# - counter: trig actions depending on the value of a counter
+# Parameters:
+# - counter: the id of the counter to trig on (0 -> 63)
+# - hard_threshold: minimum counter value to trig the hard_match hook
+# - soft_threshold: minimum counter value to trig the soft_match hook
+# Return value:
+# - hard_match if the counter with the given id is greater or equal to hard_threshold
+# - soft_match if the counter value is between soft_threshold and hard_threshold
+# - fail if the counter value is below soft_match
+
+# match if the counter 0 value is greater than 8, or between 5 and 7
+counter {
+ type = counter;
+
+ # configuration
+ counter = 0;
+ hard_threshold = 8;
+ soft_threshold = 5;
+
+ # hook
+ on_hard_match = postfix:REJECT ${sender_domain};
+ on_soft_match = greylist;
+ on_fail = counter:1:10:match;
+}
+
# ENTRY POINTS
#
# Access policy daemon can be used at several protocol states. For each of this states,
# - data_filter: called on the DATA command (smtpd_data_restrictions)
# - end_of_data_filter: called on the END-OF-DATA command
# (smtpd_end_of_data_restrictions)
-# - ertn_filter: called on the ETRN command (stmpd_etrn_restrictions)
+# - etrn_filter: called on the ETRN command (stmpd_etrn_restrictions)
# - verify_filter: called on the VRFY command (no postfix hook ?)
-recipient_filter = spamhaus_and_abuseat;
+recipient_filter = client_whitelist;
+
+
+# SERVER PORT
+#
+# Port to which the server is bound. The default is 10000. If the port is specified as
+# a command line parameter, the value specified on command line overides this value.
+#
+# You must RESTART the server to change the port (reload does not affect the port).
+
+port = 10000;
# vim:set syntax=conf: