greylist.conf(5)
π greylist.conf(5) π
okay bestie, sit DOWN, because this is the manual page for greylist.conf(5) and it is genuinely iconic. π
β¨ NAME β¨
greylist.conf β the configuration file for milter-greylist(8). yep that's it, that's the name, slay.
πΈ DESCRIPTION πΈ
So. greylist.conf is literally the thing that configures how milter-greylist(8) operates. the format? super simple, no cap:
- every line = a keyword + optional arguments
- any line that starts with a
#is a comment and gets straight up ignored, bye π - blank lines are ignored too, they're just vibes
- comments at the end of lines are accepted in some situations, but do NOT take them for granted bestie, don't test it
- a statement can be continued onto the next line by using a backslash. anything after the backslash gets ignored. so like, keep it tidy. β¨
π¦ WHITELIST π¦
the primary use of greylist.conf is to set up the milter-greylist(8) whitelist. but it also offers a handy blacklist feature, because why not, main character energy. π
Access-lists (ACL) are how you do all that. ACL let the administrator specify complex conditions on:
- sender IP
- sender DNS address
- sender e-mail address
- recipient e-mail address
and if support for DNSRBL was built-in, you can even use DNSRBL in ACL. obsessed. β¨
An access-list entry starts with the racl keyword, followed by an optional id quoted string, then the greylist, whitelist, blacklist, or continue keyword, and then by any set of the following clauses:
addrdomainfromrawfromrcptrcptcounthelosm_macrotimeauthtlsspf(if build with SPF support)geoip(if build with GeoIP support)p0f(if build with p0f support)ldapcheck(if build with--with-openldap)urlcheck(if built with--with-libcurl)dnsrbl(if built with--enable-dnsrbl)nsupdate(if supported by the DNS resolver)
A message will match an ACL entry when it complies with all of its clauses. all of them. every single one. no skipping. π«Ά
The greylist, whitelist, and blacklist keywords will cause the message to be respectively greylisted, accepted or rejected if all ACL clauses match β and then ACL evaluation is terminated right there. done. periodt.
If an ACL with the continue keyword does match, nothing is decided and the next ACL gets evaluated. it's giving "we'll circle back". π
Clauses can be negated by prefixing them with the not keyword. and now, the detail of all possible clauses:
addrβ this clause specifies a netblock of source IP addresses. the syntax is an IP address, followed by a slash, followed by a CIDR netmask. example time:
racl whitelist addr 127.0.0.0/8
racl whitelist addr 192.168.3.0/24
racl whitelist addr ::1
if the netmask is omitted, /32 is assumed for an IPv4 address and /128 is assumed for an IPv6 address. bestie you should at least whitelist localhost (127.0.0.1/8), and if you've got user clients connecting to the machine, whitelist the addresses they connect from β unless you want them getting error messages when they send e-mail. your call, but like. π
domainβ this clause selects source machines based on their DNS name, performing a suffix search. for instance, this will whitelist any machine in theexample.netdomain:
racl whitelist domain example.net
Suffix search matching means, for example, that gle.com will match google.com. if you want domain names to match on subdomain boundaries (e.g. gle.com will match mail.gle.com and gle.com but not google.com) then enable domainexact.
The name resolution is made by Sendmail, which hands it to milter-greylist(8). as a result, it is impossible to use DNS aliases here. on the other hand, this will work even if your DNS resolver is not thread-safe. silver lining β¨
fromβ this is used to select sender e-mail addresses. you should NOT use that feature, because sender e-mail addresses can be trivially forged. like trivially trivially. π
Please note that the sender e-mail is cleared from spaces and anything leading an equal (=) sign so that mailing-lists return-address with unique return paths can still match. if you need the unedited sender address, use a rawfrom clause instead.
Here is an example:
racl whitelist from postmaster@example.com
rawfromβ same asfrombut the sender e-mail address is not modified to stip anything.rcptβ this is used to select recipient addresses. Example:
racl greylist rcpt John.Doe@example.net
rcptcountβ followed by an operator and a recipient count, this is used to select the amount of recipients. Example:
racl blacklist rcptcount >= 25 msg "No more than 25 recipients, please"
heloβ followed by a quoted string or a regular expression, this can be used to filter on the HELO string.sm_macroβ this is used to select a Sendmail macro value. see the section on that topic for more information.timeβ this is used to specify a time set. it should be followed by a quoted string ofcrontab(5)-like time specification. here's an example that whitelists mail addressed to a single recipient during office hours (from 8:00 to 16:59 from monday to friday):
racl whitelist time "* 8-16 * * 1-5" rcpt info@example.net
geoipβ this is used to specify a country, as reported by GeoIP. the country code must be upper case, and is only available if milter-greylist was built with GeoIP support. special country codeZZis used when the country cannot be determined (this happens for private addresses, for instance). thegeoipdbstatement can be used to specify the location of the GeoIP database. thegeoipv6dbstatement can be used to specify the location of the GeoIPv6 database. thegeoip2dbstatement can be used to specify the location of the GeoIP2 (maxminddb) database. so many databases bestie πp0fβ this is used to match against the remote system OS fingerprint genre and detail, obtained from p0f. it is only available if milter-greylist was built with p0f support.p0fclauses can be used with a quoted string for case-insensitive substring match, or against regular expressions. thep0fsockstatement can be used to specify the location of the p0f socket.authβ this is used to select a user that succeeded SMTP AUTH. in order to select any user that succeeds SMTP AUTH, you can use a regular expression matching, like below:
racl whitelist auth /.*/
Using such a clause automatically disable global STARTTLS and SMTP AUTH whitelisting, like if the noauth keyword would have been used. heads up. β οΈ
tlsβ this is used to select the distinguished name (DN) of a user that succeeded STARTTLS. using such a clause automatically disable global STARTTLS and SMTP AUTH whitelisting, like if thenoauthkeyword would have been used. same energy as above, bestie.spfβ this is used to test SPF status. possible values arepass,softfail,fail,neutral,unknown,error,none, andself. the first seven values are plain SPF validation status. theselfvalue is a special test that checks the server's local IP address against the sender's SPF record. if that test validates, odds are good that the sender SPF record is wide open, and this is a hint that SPF should not be trusted. π
In order to use spf self, Postfix users must specify the local address in the configuration file, using the localaddr option.
Absence of any value after the spf keyword is a synonym for spf pass. this is present for backward compatibility. π«Ά
The spf clause is only available if SPF support was compiled in. using it will disable global SPF whitelisting, like if the nospf keyword would have been used.
ldapcheckβ this is used to query an LDAP directory. see the section on that topic for more information.urlcheckβ this is used to query an external configuration source through an URL. see the section on that topic for more information.dnsrblβ this is used to select a DNSRBL. see the section on that topic for more information.nsupdateβ this always-matching clause performs a DNS update, see the section on that topic for more information.
π« regex & matching tea π«
The domain, from, and rcpt clauses may be used with regular expressions. the regular expressions must be enclosed by slashes (/). no escaping is available to provide a slash inside a regular expression, so just do not use it. regular expressions follow the format described in re_format(7). here's an example:
racl greylist rcpt /@example\.net$/
When regular expressions are not used, from and rcpt perform a case insensitive substring match with leading and trailing brackets, spaces and tabs stripped out. domain performs a case insensitive suffix match. this means, for example, that gle.com will match google.com. if you want domain names to match on subdomain boundaries (e.g. gle.com will match mail.gle.com and gle.com but not google.com) then enable domainexact.
π optional parameters on match π
An ACL entry can also hold various optional parameters used on match:
delay, autowhite, flushaddr, nolog, code, ecode, report, maxpeek, addheader, addfooter, and msg. so many little extras, it's giving β¨optionsβ¨.
delayβ specify the greylisting delay used before the message can be accepted. this overrides thegreylistglobal setting, and it only makes sense on anracl greylistentry.autowhiteβ specify the autowhitelisting duration for messages matching this ACL. this overrides theautowhiteglobal setting, and it only makes sense on anracl greylistentry. Example:
racl greylist rcpt JDoe@example.net delay 15m autowhite 3d
racl greylist rcpt root@example.net delay 1h autowhite 3d
flushaddrβ if a message matches the rule, any entry in the greylist or autowhite databases matching the sender IP is removed. used with a DNSRBL blacklist ACL, it is useful for freeing the database from entries set up by a machine which is known to be a spammer. Example:
racl blacklist dnsrbl "known-spammer" flushaddr
nologβ do not generate syslog message if this rule matches. Example:
racl whitelist default nolog
codeecodemsg
These 3 values can be used to choose the SMTP code, extended code and reply message for temporary failures and rejects. Example:
racl blacklist dnsrbl "spamstomp" msg "IP caught by spamstomp"
racl greylist default code "451" ecode "4.7.1"
The msg strings accepts format string substitution as documented in the FORMAT STRINGS section. for instance, %A gets substituted by the ACL line number. cute, right? π
None of the last 3 values makes sense for a whitelist entry. sorry bestie, them's the rules. π
reportβ overrides whatever text theX-Greylistheader shows for messages thatmilter-greylist(8)lets through β whether that's because they're whitelisted or because they actually survived greylisting (peep the REPORTING section for the deets). You can do substitutions in this string, exactly as documented in FORMAT STRINGS. πmaxpeekβ this one only makes sense inside a RCPT-stage ACL, okay? It overrides the globalmaxpeeksetting for the DATA-stage handling of the message. And heads up: it does absolutely nothing if the globalmaxpeekis set to0. πaddheaderβ this quoted string is an RFC822 header that gets added to the message. Format string substitution is supported here too. No check is done for header length standard compliance, so pls make sure the substituted string stays shorter than 2048 characters, bestie. Headers get appended to the bottom, unless you specify an optional comma-separated positive index after the quoted string β in that case the header gets inserted at that index point from the top. Example time:
racl continue addheader "X-Personnal-Header: Hello world!",1
addfooterβ appends a footer to the message. The usual escape sequences like\nwork for special characters. The string is subject to format string expansion as described in the FORMAT STRINGS section. The footer will not be appended ifmilter-greylistwasn't able to capture the whole message, somaxpeekmust be set appropriately. π
Entries in the access-list are evaluated sequentially, so order is literally everything. The first matching entry is the one that decides if a message gets whitelisted or greylisted. And there's a special default clause you can drop into the last ACL entry to act as a wildcard. π¦
Here are a few complete ACL examples:
Example 1:
racl whitelist from friend@toto.com rcpt grandma@example.com
racl whitelist from other.friend@example.net rcpt grandma@example.com
racl greylist rcpt grandma@example.com
racl whitelist default
Example 2:
racl whitelist addr 193.54.0.0/16 domain friendly.com
racl greylist rcpt user1@atmine.com
racl greylist rcpt user2@atmine.com
racl greylist rcpt user3@atmine.com
racl whitelist default
Example 3:
racl whitelist rcpt /@.*otherdomain\.org$/
racl whitelist addr 192.168.42.0/24 rcpt user1@mydomain.org
racl whitelist from friend@example.net rcpt /@.*mydomain\.org$/
racl whitelist rcpt user2@mydomain.org
racl greylist rcpt /@.*mydomain\.org$/
racl whitelist default
π«Ά DATA-STAGE ACL π«Ά
ACLs using the racl keyword get evaluated at the RCPT stage of the SMTP transaction. But bestie, you can also have an ACL evaluated at the DATA stage of the SMTP transaction, using the dacl keyword β provided the message went through RCPT-stage ACL, and possibly greylisting. π
Heads up: you cannot use thegreylistaction at DATA-stage if the RCPT-stage ACL that matched had agreylistaction itself. Just so you know.
The following clauses can be used to work on message content:
dkimβ DKIM status (if built with DKIM support). Possible values arepass,fail,unknown,error, andnone.headerβ string or regular expression searched in message headers.bodyβ string or regular expression searched in message body.msgsizeβ operator followed by a message size (korMsuffix allowed for kilobytes or megabytes). Example:
dacl blacklist msgsize >= 4M msg "No more than 4 MB please"
spamdβ SpamAssassin score (if built with SpamAssassin support). If used without a comparison operator,spamdis true if the score is above threshold. Thespamdsockkeyword can be used to specify the location of the spamd socket.
Example 1:
spamdsock unix "/var/spamassassin/spamd.sock"
racl whitelist default
dacl greylist spamd
Example 2:
spamdsock inet "127.0.0.1:783"
racl whitelist default
dacl blacklist spamd > 15 msg "Your message is considered spam."
dacl greylist spamd > 10 delay 2h
dacl greylist spamd > 5 delay 1h
Note that if there are multiple recipients, a rcpt clause at DATA stage evaluates to true if it matches any of them. And if you wanna match an exact set of recipients, you can use multiple rcpt clauses along with a rcptcount clause. β¨
πΈ LISTS πΈ
It is often useful to group several users or sender IP addresses into a single ACL. This can be done with lists. Lists must be first defined and given a name before they can be used in ACL entries. Example:
list "my users" rcpt { user1@example.com user2@example.com }
list "local" addr { 192.0.2.0/24 10.0.0.0/8 }
racl whitelist list "local"
racl greylist list "my users"
racl whitelist default
π BACKWARD COMPATIBILITY π
Previous versions of milter-greylist(8) used addr, domain, from, and rcpt lines, without the racl keyword. Access-list management is intended to replace them. These lines are still accepted by milter-greylist(8), but they are deprecated. π milter-greylist(8) handles them as access-list entries with a single clause. They are added at the head of the access-list, so using these keywords and access-lists may lead to unspecified behaviour. Do not mix them, fr fr.
Test mode (using -T) is also deprecated. Access-list semantics do not depend on this flag.
milter-greylist(8) also used to only have a RCPT-stage ACL, which was configured through acl statements. These have been replaced by racl statements (as opposed to dacl statements for DATA-stage ACL). acl statements are still accepted for backward compatibility and are a synonym for racl statements.
π MX SYNC π
Synchronization of the greylist among multiple MX is configured using the peer keyword. List each other MX IP addresses using the peer keyword. Example:
peer 192.0.2.18
peer 192.0.2.17
peer 192.0.2.22 timeout 7
peer 192.0.2.38 timeout 5m
You can list the local machine in the peer statements β it will be ignored. π
The timeout clause sets a peer communication timeout to have proper retrial in case of a slow MX peer. The default value is 3 seconds. The special value of 0 disables the connection retrials.
By default, milter-greylist will listen on all interfaces using TCP port 5252 or the port number given by service named mxglsync if defined in /etc/services or other directory service. This behaviour can be changed by using the syncaddr keyword. A few examples:
syncaddr *
syncaddr * port 7689
syncaddr 192.0.2.2 port 9785
syncaddr 2001:db8::1:c3b5:123
syncaddr 2001:db8::1:c3b5:123 port 1234
Using '*' as the address means to bind to all local interfaces' addresses. Note that if you are not using the default port, all MXs must use the same port number. π
For outbound connections the system is selecting one of the possible addresses. If you want to use a specific IP you can use:
syncsrcaddr 123.456.78.9
π TEXT DUMP π
milter-greylist(8) uses a text dump of its database to resume operation after a crash. The dump is performed at regular time interval, but since it's a heavy operation, you might wanna configure a particular time interval using the dumpfreq option.
If the dumpfreq value is too small, it will kill performance. If it is too high, you will loose a bigger part of the database on a crash. π¬
Set dumpfreq to 0 to get a dump on each change (kills performance), set it to -1 to never dump to a file (unsafe, as you lose the whole greylist on each crash), or give a time value for the delay between dumps. The time is given in seconds, except if a unit is given: m for minutes, h for hours, and d for days.
You may further improve the performance of the dump operation at the expense of humanly readable timestamp, which by default appears as a comment at the end of each line in the dumpfile. You may disable generation of this comment by specifying dump_no_time_translation option in the configuration file. This is specifically recommended if your dumpfile grows to 100's of megabytes β it can reduce the time needed for the dump operation by the order of magnitude! π₯
β REPORTING β
By default, milter-greylist(8) will add a X-Greylist header to any message it handles. The header shows what happened to the message: delayed or not delayed, and why. The following options can be used in greylist.conf to alter this behavior:
report noneβ never add aX-Greylistheader.report delaysβ only add a header if the message was delayed.report nodelaysβ add a header if the message was not delayed. The header explains why the message was not delayed.report allβ always add a header. This is the default.
π¦ SENDER CALLBACK SYSTEMS π¦
Sender callback systems are another anti-spam measure that attempts to send a DSN to the sender address before accepting a message. If that fails, then the sender address is wrong and the message is rejected. Such systems usually stop their callback check at the RCPT stage of the SMTP transaction.
Greylisting temporarily rejects at the RCPT stage, so sender callback and greylisting love to fight each other. π milter-greylist(8) proposes a workaround to that problem with the delayedreject option. For messages coming from <> (that is, for DSN), it will cause the temporary reject to happen at the DATA stage of the SMTP transaction instead of the RCPT stage. That way, milter-greylist(8) will cope much better with sender callback systems. π
This has a minor drawback (and this is why it is not enabled by default): for a multi recipient DSN, whitelisted recipient will not be honoured β the message will be delayed for everyone. π
π SENDMAIL MACROS π
Any sendmail macro can be used as a clause in the access list. You need to define a (macro, value) pair using the sm_macro keyword before using it. Here is an example that uses the {client_resolve} macro to apply a larger greylisting delay to hosts that have a bogus reverse DNS:
sm_macro "maybe_forged" "{client_resolve}" "FORGED"
racl greylist sm_macro "maybe_forged" delay 1h
racl greylist default delay 15m
A regular expression can be used as the macro value. It must be surrounded with slashes and not by quotes. The special value unset can also be used to match an unset macro:
sm_macro "not_foo" "{foo}" unset
Note that any Sendmail macro that is not exported using the Milter.macros.envrcpt setting of sendmail.cf will be seen as unset from milter-greylist. β¨
π DNSRBL π
DNS Reverse Black List β DNSRBL for short β is the glow-up that lets a whole ACL get toggled on and off. But bestie, they gotta be defined and named first, you can't just manifest them. Here's an example where hosts stuck in the SORBS dynamic pool get a bigger greylisting delay (and yes, that pool includes DSL and cable customer ranges, which are famously riddled with spamware infections, no cap):
dnsrbl "SORBS DUN" dnsbl.sorbs.net 127.0.0.10/32
racl greylist dnsrbl "SORBS DUN" delay 1h
racl greylist default delay 15m
A DNSRBL definition kicks off with the dnsrbl keyword, then the quoted name of the DNSRBL, then the DNS domain where addresses get looked up, then the answer we're counting as a positive hit. That's it, that's the whole vibe.
π¨ Two things, and I mean it: DNSRBL support only exists if you turned it on with the--enable-dnsrblconfig flag. And please, please make suremilter-greylist(8)is linked against a thread-safe DNS resolver β otherwise it shall crash. Like, crash crash. π
πΈ DNS updates πΈ
ACLs can trigger a DNS update, which is how you feed a DNSRBL. This functionality is enabled at build time β but only if the DNS resolver actually has DNS update support, so it's giving "depends on your build" energy.
Configuration happens with the nsupdate statement, which you're allowed to use several times, plus the optional tsig statement if you want authenticated DNS update. Example syntax:
tsig "dns-update" "hmac-md5" "1B2M2Y8AsgTpgAmY7PhCfg=="
nsupdate "bl.example.net" { rname "%j.bl.example.net" rvalue "127.0.0.2" tsig "dns-update" }
The options for nsupdate are:
rnameβ the created record name, which uses format strings.rvalueβ the created record value, which uses format strings.serversβ a quoted comma-separated list of the DNS servers the update should be sent to. Default is to use the system configuration, usually from/etc/resolv.conf.ttlβ TTL of the created DNS record. Default is0seconds.classβ created record class, as a numeric value. Default is1, for the IN class.typeβ created record type, as a numeric value. Default is1, for the A type.tsigβ the name of atsigconfiguration, which must have been supplied before. If unspecified, unauthenticated DNS updates are performed.
Once it's configured, DNS updates can be used in any ACL:
racl blacklist rcpt spamtrap@example.net nsupdate "bl.example.net"
β¨ PROPERTIES β¨
Properties are variables that can be set, evaluated and printed in an ACL. A property can be dropped once the current recipient is processed, or it can be retained until the message is processed. They get created through these always-matching ACL clauses:
set $name="value"β sets a property that will be retained for all next recipients. The right hand side of the clause may be a quoted string (which gets substituted using format strings, as described in the FORMAT STRINGS section), a number, or another property name (which gets substituted by the property value).rset $name="value"β same as thesetclause, except the property gets dropped once the current user has been processed.urlcheckβ these clauses cause properties to be fetched from an external web service. See the URL checks section below for the deets.ldapcheckβ these clauses cause properties to be fetched from an LDAP directory. See the LDAP CHECKS section below for the deets.
Properties can be used as the left-hand side part of ACL clauses:
racl blacklist $name "badvalue"
racl blacklist $name /badregex/
If used on the left-hand side and prefixed by a star, the property gets evaluated as a glob(7) pattern. In the example below, if the sender DNS domain is test.example.net and $domain is *.example.net, we get a match:
racl blacklist *$domain "%f"
They're also available as the right-hand side of many ACL clauses:
racl continue set $badword="spam"
racl blacklist body $badword
Values of properties can be obtained in any quoted string that is subject to format string expansion:
racl continue set $webmaster="webmaster@example.net"
racl blacklist domain evil.net msg "blacklisted, ask $P{webmaster} why"
When the property value is a number, it can be increased or decreased using this syntax:
racl continue set $score+=5
racl continue set $score-=5
Here again, the right hand side may be a quoted string, a number, or another property. For the quoted string and property cases, a conversion is first made to an integer, using 0 as the value on failure. And note β no arithmetic evaluation occurs. For instance, the quoted string "1 + 1" will be evaluated as 0. Yes really. π
The log ACL clause can be helpful when you need to figure out what happens to property values during the ACL flow. See the CUSTOM REPORT section for more details.
π¦ URL checks π¦
milter-greylist(8) is able to query external sources of information through various URLs β if it was built with --with-libcurl. Here's an example:
urlcheck "glusr" "http://www.example.net/mgl-config?rcpt=%r" 5
racl greylist urlcheck "glusr" delay 15m
racl whitelist default
That trailing 5 at the end of the urlcheck definition is the maximum number of simultaneous connections we want to launch on this URL. For each message, the URL gets queried, with % format tags being substituted. For instance, %r is substituted by the recipient. See the FORMAT STRINGS section for the complete list of substitutions.
milter-greylist(8) expects an answer containing a list of \n terminated lines, with key: value pairs. The most basic answer to get a match is:
milterGreylistStatus: Ok
TRUE can be used as an alias for Ok here.
The answer can be more complex, with keys that will overload the ACL settings:
milterGreylistDelayβ the greylisting delay to use (time unit suffix allowed).milterGreylistAutowhiteβ the autowhite delay to use (time unit suffix allowed).milterGreylistFlushAddrβ the value is ignored. If this key is present, then the IP address for the sender machine will be flushed from the greylist and autowhite databases.milterGreylistCodeβ the SMTP code to return (e.g.:551).milterGreylistECodeβ the SMTP extended code to return (e.g.:5.7.1).milterGreylistMsgβ the string to return with SMTP codes.milterGreylistReportβ the string to display in theX-Greylistheader.milterGreylistIgnoreβ this line will be ignored, without warnings in the logs.milterGreylistActionβ this feature is nifty but use it with caution, as it makes the access list a bit difficult to understand. By specifying the valuesgreylist,whitelist, orblacklist, it is possible to overload the ACL action itself.
The ACL will match if any of the above keys is returned: milterGreylistStatus is not mandatory.
Optional keywords can be appended to a urlcheck definition:
postmsgβ on a DATA-stage ACL, this causes the message to be sent (up tomaxpeekbytes) in a POST request. Here's an example:
urlcheck "extfilter" "http://www.example.net/f.cgi" 5 postmsg
dacl blacklist urlcheck "extfilter"
dacl whitelist default
getpropβ gather the properties returned by the URL and reuse them in the ACL. The gathered properties can be accessed in the current and following ACL by prefixing them with a dollar ($).clearβ this causes gathered properties to be cleared on each new recipient. This avoids properties for several recipients mixing together.forkβ tellsmilter-greylist(8)to fork a separate instance of itself for performing the queries. Use it if you encounter thread-safety problems.forkis not compatible withpostmsg.domatchβ causes theldapcheckclause to be evaluated in the ACL. Default behavior is to ignore the result and just fetch properties, except if the LDAP directory is unreachable, in which case a temporary failure occurs. Thefixldapcheckglobal settings may also be used to globally cause allldapcheckandurlcheckclauses to match.
Here's an example that uses various DNSRBLs depending on a per-recipient setting stored in the dnsrbl attribute of an LDAP directory:
dnsrbl "RBL2" "rbl.example.net" "127.0.0.2"
dnsrbl "RBL3" "rbl.example.net" "127.0.0.3"
dnsrbl "RBL4" "rbl.example.net" "127.0.0.4"
urlcheck "userconf" "ldap://localhost/dc=example,dc=net?milterGreylistStatus,dnsrbl?one?mail=%r" 5 getprop clear
racl blacklist urlcheck "userconf" $dnsrbl "RBL2" dnsrbl "RBL2"
racl blacklist $dnsrbl "RBL3" dnsrbl "RBL3"
racl blacklist $dnsrbl "RBL4" dnsrbl "RBL4"
Note that when matching gathered properties, format strings and regex can be used. π
π LDAP CHECKS π
If milter-greylist was built with --with-openldap, then you can also use ldapcheck for pulling information from an LDAP directory. This works exactly like urlcheck, except that properties are always collected β the getprop option is implicit.
A list of LDAP URLs to use can be specified with the ldapconf keyword. The network timeout is optional.
ldapconf "ldap://localhost ldaps://ldap.example.net" timeout 2s
When ldaps:// is used, the system's ldap.conf file is used to locate x509 certificates.
When defining LDAP queries with the ldapcheck statement, note that the scheme and host part of the URL are ignored. Servers listed in ldapconf are used instead.
π RATE LIMIT π
The ratelimit keyword specifies a ratelimit configuration to be used in access lists. It must be followed by the rate limit configuration name, what is being accounted (i.e.: session for SMTP sessions, rcpt for recipients, data for bytes in body and headers), the actual limit, and the sampling period. Example:
ratelimit "internalclients" rcpt 10 / 1m
racl blacklist addr 192.0.2.0/24 ratelimit "internalclients" \
msg "you speak too much"
The ratelimit keyword can also have an option key statement, which determines the set of keys for message accounting. The default is %i for per-IP-address accounting (see the FORMAT STRINGS sections for the possible syntax of this field). Here's an example that configures a rate limit of 100 messages per hour for each individual recipient-IP set:
ratelimit "internalclients" rcpt 100 / 1h key "%r%i"
racl blacklist addr 192.0.2.0/24 ratelimit "internalclients" \
msg "you speak too much"
π CUSTOM REPORTS π
ok so the stat keyword lets you build your own custom report for milter-greylist activity. iconic behavior honestly. you give it an output (either a file or an external command) plus a format string. here's a lil example bestie:
stat ">>/var/log/milter-greylist.log" "%T{%T},%i,%f,%r,%A\n"
if your output starts with >> or >, congrats, it's a file. use >> to append to a file that already exists, and use > to overwrite it (gone, reduced to atoms, so be careful π). if the output starts with a | then it's a shell command instead, like this:
stat "|logger -p local7.info" "%T{%T},%i,%f,%r,%A\n"
the format string gets substituted the exact same way URL checks format strings do: %r gets swapped for the recipient, %f for the sender, and so on and so forth. run, don't walk, to the FORMAT STRINGS section for the full list of available substitutions. β¨
there's also an always-matching log ACL clause that you can use to yeet a formatted string to syslog with LOG_INFO level. example time:
racl continue rcpt /@example\.com$/ log "I was here"
π COMMAND-LINE FLAG EQUIVALENTS π
most milter-greylist(8) command-line options have equivalent options that you can set in the config file. big news. but heads up: if a command line option is supplied, it always overrides the config file. always. no discussion. π
and if a command-line equivalent keyword gets used more than once? the last keyword wins and overrides all the previous ones. periodt.
verboseβ turns on debug output. equivalent to the-vflag.quietβ do not tell clients how much time remains before their e-mail will be accepted. equivalent to the-qflag.nodetachβ do not fork and go into the background. equivalent to the-Dflag.noauthβ greylist clients regardless if they succeeded SMTP AUTH or STARTTLS. equivalent to the-Aflag.noaccessdbβ normallymilter-greylist(8)will whitelist a message ifsendmail(8)defines a${greylist}macro set toWHITE. this enables complex whitelisting rules based on the Sendmail access DB. this option inhibits that behavior. πnospfβ greylist clients regardless if they are SPF-compliant. equivalent to the-Sflag.testmodeβ enable test mode. equivalent to the-Tflag. > this option is deprecated, bestie. it's giving legacy.greylistβ the argument sets how much timemilter-greylist(8)will want the client to wait between the first attempt and the time the message is accepted. time is given in seconds, except if a unit is given:mfor minutes,hfor hours, anddfor days. thegreylistkeyword is equivalent to the-woption. example that sets the delay to 45 minutes:
greylist 45m
autowhiteβ sets the auto-whitelisting duration, equivalent to the-acommand-line option. same as with thegreylistkeyword, units can be supplied. here's an example for a 3 day long auto-whitelisting:
autowhite 3d
pidfileβ makesmilter-greylist(8)write its PID into the file given as argument, exactly like the-Pcommand line argument does. the path to the file must be absolute and it must be enclosed in quotes. example:
pidfile "/var/run/greylist.pid"
dumpfileβ chooses the location of the greylist dump file, like the-dcommand line option does. the path must be absolute and enclosed in quotes. it can optionally be followed by an octal permission mode. example:
dumpfile "/var/milter-greylist/greylist.db" 640
subnetmatchβ equivalent to the-Lcommand line option. takes a slash followed by a CIDR mask as argument, and it commands the subnet matching feature. example, for a class C wide matching:
subnetmatch /24
subnetmatch6β equivalent to the-Mcommand line option. takes a slash followed by a prefixlen as argument, and it commands the subnet matching feature. example, for a subnet wide matching:
subnetmatch6 /64
socketβ like the-pcommand line option, this keyword specifies the socket used to communicate withsendmail(8). it must be enclosed in quotes and can optionally be followed by an octal permission mode (valid values are666,660or600β any other value causes an error, so don't try it π):
socket "/var/milter-greylist/milter-greylist.sock" 660
userβ should be followed by a quoted user login and optionally a colon followed by a groupname. like the-uoption, this is used to runmilter-greylist(8)as a non root user. example:
user "smmsp"
π¦ MISCELLANEOUS π¦
these options have no command line equivalent. they're just built different:
logfacβ sets the syslog facility for messages. can be set to any of the standard facilities:kern,user,mail,daemon,auth,syslog,lpr,news,uucp,cron,authpriv,ftp,local0,local1,local2,local3,local4,local5,local6,local7. can also be set tononeto disable syslog output completely. (silence era π)timeoutβ used to control how long greylist tuples are retained in the database. value is in seconds, except if a suffix is given (mfor minutes,hfor hours,dfor days). default is 5 days.extendedregexβ use extended regular expressions instead of basic regular expressions.unbracketβ attempt to resolve sender address when the MTA handed it as a bracketed IP address (e.g.:[192.0.2.18]). default is to leave it as is.maxpeekβ limit (in bytes) how much of messages are examined for header and body searches.lazyawβ make auto-whitelist look at just the IP instead of the (sender IP, sender e-mail address, recipient e-mail address) tuple. lowkey efficient of it.domainexactβ match on subdomain boundaries instead of the default suffix matching. e.g. ifdomainexactis not enabled (the default) thengle.comwill matchgoogle.comin addition togle.com. ifdomainexactis enabled then domain names will match on subdomain boundaries (e.g.gle.comwill matchmail.gle.comandgle.combut notgoogle.com).drac dbβ tell where the DRAC DB file is. this is only available if DRAC support was compiled in. example:
drac db "/usr/local/etc/drac.db"
nodracβ disable DRAC.logexpiredβ this option causes greylist entries that expire to be logged via syslog. this lets you easily collect the IP addresses and sender names and use them for blacklisting, SPAM scoring, etc. normally, expiration's are only logged if thedebugoption is set, but that generates a lot of extra messages. so this is the chill version. β¨localaddrβ this keyword can be used to manually define the local MTA's IP address for such uses asspf selfandp0fin absence of the milter-API{if_addr}macro support in your MTA (Postfix, Sun/Oracle CommSuite Messaging Server). this is not so useful when using Sendmail, since it serves the macro and the local address can be detected automatically.
the configuration file is reloaded automatically once it is modified when new e-mail arrives. most configuration keywords will take effect immediately, except the following, which will only take effect after a restart of milter-greylist(8): nodetach, pidfile, socket, and user. π
the dumpfreq option can be changed dynamically, but the change will only take effect after the next dump.
multiraclβ by default, once a RCPT-stage ACL whitelists a recipient, the next recipient gets automatically whitelisted. this historical behavior can be considered a bug, and this option disables it. not me fixing the ancient bug bestie π«Ά
π« FORMAT STRINGS π«
Okay bestie, this is the fun part. Lots of statements in the config file take format strings, and inside those strings you can drop in these %-prefixed tokens and they get swapped out for real values. Here's the complete list of available substitutions β though ngl, not every single one of them is relevant in every context. Just so you know. π
%rβ the message recipient e-mail address. Heads up: this one is not substituted forDATAstage ACL, because a message can have a whole squad of recipients and the manpage isn't picking favorites.%fβ the message sender e-mail address.%iβ the sender machine IP address.%jβ the reversed sender machine IP address. So like,192.0.2.12becomes12.2.0.192. Flip era. π%Iβ the sender machine IP address masked by a CIDR. Example:%I{/24}.%dβ the sender machine DNS address.%hβ the SMTP transactionHELOstring.%mrβ the mailbox part of%r(the bit before the@sign). Again, not substituted forDATAstage ACL, multiple recipients and all that.%srβ the site part of%r(the bit after the@sign). Same deal: not substituted forDATAstage ACL.%mfβ the mailbox part of%f(before the@sign).%sfβ the site part of%f(after the@sign).%mdβ the machine part of%d(before the first.sign).%sdβ the site part of%d(after the first.sign).%Xcβ the SMTP code returned.%Xeβ the SMTP extended code returned.%Xmβ the SMTP message returned.%Xhβ the message displayed in theX-Greylistheader.%Dβ comma-separated list of DNSRBL for which the sender host matched.%Mβ a sendmail macro value. Examples:%Mjor%M{if_addr}.%gβ a regex back reference. Like,%g{\2}gets substituted by the string matching the second parenthesis group in all ACL regex clauses. Regex girlies rise. π§%Tβ a brace-enclosedstrftime(3)format string, substituted by the system time. Example:%T{%Y%m%d:%H%M%S}.%vβ milter-greylist's version.%Gβ Offset to GMT (e.g.:-0100).%Cβ Sender IP country code, as reported by GeoIP. Only available if milter-greylist was built with GeoIP support.%Fxβ p0f OS fingerprint genre and detail. Only available if milter-greylist was built with p0f support.%Vβ Shortcut to"milter-greylist-%v (%Mj [%M{if_addr}]); %T{%a, %d %b %Y %T} %G (%T{%Z})". Yes it's a whole vibe in one token, obsessed.%Sβ the action performed:accept,tempfail, orreject.%Aβ the line number of the ACL that caused the action.%aβ the id string of the ACL that caused the action. If no id was given, the line number is used instead.%cAβ the line number of the ACL being evaluated, whether it matches or not.%caβ the id string of the ACL being evaluated, whether it matches or not. If no id was given, the line number is used instead.%Etβ total elapsed time in seconds before a greylisted message has been accepted.%Ehβ hours elapsed.%Emβ minutes elapsed (modulo one hour).%Esβ seconds elapsed (modulo one minute).%Eβ shortcut to%Eh:%Em:Es.%Rtβ total remaining time in seconds before a greylisted message will be accepted.%Rhβ hours remaining.%Rmβ minutes remaining (modulo one hour).%Rsβ seconds remaining (modulo one minute).%Rβ shortcut to%Rh:%Rm:Rs.%Hsβ SpamAssassin score (if build with SpamAssassin support).%pnβ Name of last LDAP or CURL gathered property that matched an ACL.%pvβ Value of last LDAP or CURL gathered property that matched an ACL.%prβ Recipient that caused storage of the last matching LDAP or CURL gathered property.%Pβ a LDAP or CURL gathered property. Example:%P{mail}. Note that this copes very badly with multivalued properties. Like, genuinely badly. Fair warning bestie. π%Qdβ DKIM query result (pass,fail, etc.), if built with DKIM support.%Qsβ SPF query result (pass,(soft)fail, etc.), if built with SPF support.%QAβ Authentication results summary, suitable forAuthentication-Resultsheader. Will included formatted query results from DKIM and SPF if checked.%QSβ SPF result, suitable forReceived-SPFheader. Available when compiled withlibspf2.%%β a single%character. The escape hatch. π«Ά
π AUTHORS π
The one and only Emmanuel Dreyfus β <manu@netbsd.org>. π
And fr fr, milter-greylist received many contributions from (in alphabetical order, we're respectful here): Adrian Dabrowski, Aida Shinra, Adam Katz, Alexander Lobodzinski, Alexandre Cherif, Alexey Popov, Andrew McGill, Attila Bruncsak, Benoit Branciard, Bernhard Schneider, Bob Smith, Constantine A. Murenin, Chris Bennett, Christian Pelissier, Cyril Guibourg, Dan Hollis, David Binderman, Denis Solovyov, Elrond, Enrico Scholz, Eugene Crosser, Fabien Tassin, Fredrik Pettai, Gary Aitken, Georg Horn, Gert Doering, Greg Troxel, Guido Kerkewitz, Hajimu Umemoto, Hideki ONO, Ivan F. Martinez, Jacques Beigbeder, Jean Benoit, Jean-Jacques Puig, Jeff Rife, Jim Klimov, Jobst Schmalenbach, Joe Pruett, Joel Bertrand, Johann E. Klasek, Johann Klasek, John Thiltges, John Wood, Jorgen Lundman, Kazuyuki Yoshida, Klas Heggemann, Kouhei Sutou, Laurence Moindrot, Lev Walkin, Manuel Badzong, Markus Wennrich, Mart Pirita, Martin Paul, Matt Kettler, Mattheu Herrb, Matthias Scheler, Matthieu Herrb, Michael Fromme, Moritz Both, Nerijus Baliunas, Ole Hansen, Pavel Cahyna, Pascal Lalonde, Per Holm, Petar Bogdanovic, Petr Kristof, Piotr Wadas, R P Herrold, Ralf S. Engelschall, Ranko Zivojnovic, Remy Card, Rick Adams, Rogier Maas, Romain Kang, Rudy Eschauzier, Stephane Lentz, Steven Hiscocks, Thomas Scheunemann, Tim Mooney, Vincent Dufresne, Wolfgang Solfrank, and Yaroslav Boychuk. An entire group chat of legends. π
Thanks to Helmut Messerer and Thomas Pfau for their feedback on the first releases of this software. Early adopters, we salute you. π«‘
π¦ SEE ALSO π¦
Go read these next, bestie:
Evan Harris's paper:
http://projects.puremagic.com/greylisting/
milter-greylist's web site:
http://hcpnet.free.fr/milter-greylist/
And that's the whole page, start to finish. This was greylist.conf(5) β dated May 10, 2005, the day this icon was born. Go forth and greylist responsibly, bestie. Slay. Periodt. π
β¨
π¦ where this page comes from
- alpine 3.24: milter-greylist-doc 4.6.4-r5 Β· BSD-3-Clause
- debian trixie: milter-greylist 4.6.4-4
- ubuntu jammy: milter-greylist 4.6.4-1
- ubuntu noble: milter-greylist 4.6.4-3build2
- ubuntu questing: milter-greylist 4.6.4-5
- ubuntu resolute: milter-greylist 4.6.4-5build1
- ubuntu stonking: milter-greylist 4.6.4-5build1
- Copyright (c) 2004-2013 Emmanuel Dreyfus
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms