# Fail2Ban filter for sendmail spam/relay type failures
#
# Some of the below failregex will only work properly, when the following
# options are set in the .mc file (see your Sendmail documentation on how
# to modify it and generate the corresponding .cf file):
#
# FEATURE(`delay_checks')
# FEATURE(`greet_pause', `500')
# FEATURE(`ratecontrol', `nodelay', `terminate')
# FEATURE(`conncontrol', `nodelay', `terminate')
#
# ratecontrol and conncontrol also need corresponding options ClientRate:
# and ClientConn: in the access file, see documentation for ratecontrol and
# conncontrol in the sendmail/cf/README file.

[INCLUDES]

before = common.conf

[Definition]

_daemon = (?:(sm-(mta|acceptingconnections)|sendmail))
__prefix_line = %(known/__prefix_line)s(?:\w{14,20}: )?
addr = (?:(?:IPv6:)?<IP6>|<IP4>)
# mta_dname -- matches name of MTA daemon (typically specified in DAEMON_OPTIONS),
# normally something without spaces like MTA-v4 or Deamon0, etc. If it'd contain spaces, one can
# rewrite it in jail using  `filter = %(known/filter)s[mta_dname="[^,]+"]` or in .local overwrite
# of the filter. (we would not use catch-alls here to satisfy obscure artificial case).
mta_dname = \S+

prefregex = ^\s*(?:<mail\.[^\>]+> )?<F-MLFID>%(__prefix_line)s</F-MLFID><F-CONTENT>.+</F-CONTENT>$

cmnfailre = ^ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=(?:550 5\.7\.1(?: (?P=email)\.\.\.)?(?: Relaying denied\.)? (?:IP name possibly forged \[(\d+\.){3}\d+\]|Proper authentication required\.|IP name lookup failed \[(\d+\.){3}\d+\]|Fix reverse DNS for \S+)|[45]5[13] [45]\.1\.8(?: (?P=email)\.\.\.)? Domain of sender address \S+ does not (?:exist|resolve)|550 5\.[71]\.1 (?P=email)\.\.\. (Rejected: .*|User unknown))$
            ^ruleset=check_relay(?:, arg\d+=\S*)*, relay=(\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=421 4\.3\.2 (Connection rate limit exceeded\.|Too many open connections\.)$
            ^rejecting commands from (\S* )?\[%(addr)s\] due to pre-greeting traffic after \d+ seconds$
            ^(?:\S+ )?\[%(addr)s\]: (?:(?i)expn|vrfy) \S+ \[rejected\]$
            ^<[^@]+@[^>]+>\.\.\. (?:No such user here|User unknown)$
            ^<F-NOFAIL>from=<[^@]+@[^>]+></F-NOFAIL>, size=\d+, class=\d+, nrcpts=\d+,(?: bodytype=\w+,)? proto=E?SMTP, daemon=%(mta_dname)s, relay=(?:\S+ )?\[%(addr)s\]$

mdre-normal =

mdre-extra = ^(?:\S+ )?\[%(addr)s\](?: \(may be forged\))? did not issue \S+ during connection

mdre-aggressive = %(mdre-extra)s
                  ^lost input channel from (?:\S+ )?\[%(addr)s\] to %(mta_dname)s after rcpt$
                  ^ruleset=check_rcpt, arg1=(?P<email><\S+@\S+>), relay=(?:\S+ )?\[%(addr)s\](?: \(may be forged\))?, reject=(?:450 4\.4\.0(?: (?P=email)\.\.\.)?(?: Relaying temporarily denied\.)?(?: Cannot resolve PTR record for (\d+\.){3}\d+))$

failregex = %(cmnfailre)s
            <mdre-<mode>>

# Parameter "mode": normal (default), extra or aggressive
# Usage example (for jail.local):
#   [sendmail-reject]
#   filter = sendmail-reject[mode=extra]
#
mode = normal

ignoreregex = 

journalmatch = SYSLOG_IDENTIFIER=sm-mta + _SYSTEMD_UNIT=sendmail.service

# DEV NOTES:
# 
# Regarding the multiline regex:
#
# "No such user" lines generate a failure and needs to be matched together with
# another line with the HOST, therefore no-failure line was added as regex, that
# contains HOST (see line with tag <F-NOFAIL>).
#
# Note the capture <F-MLFID>, includes both the __prefix_lines (which includes
# the sendmail PID), but also the `\w{14}` which the the sendmail assigned 
# mail ID (todo: check this is necessary, possible obsolete).
# Avoid moving <F-MLFID> into the entire prefregex because the grouped messages we 
# need have different syslog levels (info vs notice) that break the group if BSD verbose format is set
#
# Author: Daniel Black, Fabian Wenk and Sergey Brester aka sebres.
# Rewritten using prefregex by Serg G. Brester.
