Re: [exim] Authenticated Ratelimiting.

Top Page
Delete this message
Reply to this message
Author: Chris Russell
Date:  
To: exim-users@exim.org
Subject: Re: [exim] Authenticated Ratelimiting.
Hi Grant,

Something that isn't clear from your request is - is this due to brute force attacks or number of valid users logging in.

If you are being brute forced, logging the failed attempts then using something like fail2ban is a better solution.

From here, brute force against Dovecot/Exim is has increased expotentially within the last month or so.

Ta

Chris


-----Original Message-----
From: exim-users-bounces+chris.russell=knowledgeit.co.uk@??? [mailto:exim-users-bounces+chris.russell=knowledgeit.co.uk@exim.org] On Behalf Of Grant Peel
Sent: 21 October 2013 00:30
To: exim-users@???
Subject: [exim] Authenticated Ratelimiting.

Hi all,



Recent events have left us wanting to add rate limiting to our exim configuration.



I have seen several examples in various places on the web and everything I can find in the exim specification, and I must say I am a little more than confused.



All I really want to do is ratelimit everyone (locally authenticated) to 250/hour - as a start anyways.



A while back, we implemented authentication checking with includes some ratelimiting, via 'Lena's" post:

https://lists.exim.org/lurker/message/20121117.144211.c96f81fc.pl.html



Any primers or suggestion(s) would be appreciated.



-G



Here is our configuration file as it stands now.



######################################################################

#                    MAIN CONFIGURATION SETTINGS                     #


######################################################################

primary_hostname = server.ourdomain.ext

domainlist relay_to_domains =

domainlist local_domains = /etc/virtual/domains

domainlist filtered_domains = /etc/virtual/filtered_domains

hostlist filtering_hosts = /etc/virtual/filtering_hosts

hostlist relay_from_hosts = /etc/virtual/domains

hostlist blacklisted_domains = /etc/virtual/blacklist

hostlist spf_bypass = /etc/virtual/spf_bypass

hostlist whitelist = /etc/virtual/whitelist

trusted_users = mailnull:root:webmail:www

exim_user = mailnull

exim_group = mail

never_users =

host_lookup = *

rfc1413_hosts = *

rfc1413_query_timeout = 5s

ignore_bounce_errors_after = 0s

timeout_frozen_after = 0s

auto_thaw = 6h

return_path_remove

untrusted_set_sender = *

helo_allow_chars = _

daemon_smtp_ports = 25 : 109 : 587

bounce_message_file = /usr/local/etc/exim/bounce_message_file

warn_message_file = /usr/local/etc/exim/warn_message_file

return_size_limit = 10000

bounce_return_message = true

bounce_return_size_limit = 1000

delay_warning = 72h

smtp_accept_max = 100

smtp_accept_max_per_host = 10

smtp_return_error_details = yes

log_selector = +incoming_interface +deliver_time +delivery_size
+received_sender \

+received_recipients +sender_on_delivery +subject +address_rewrite
+all_parents

# log_selector = +all

message_logs = true



#######################################################################

#                  Added for Authentication Limiting.                 #


#######################################################################

acl_smtp_rcpt = acl_check_rcpt

acl_smtp_connect = acl_check_connect

acl_smtp_auth = acl_check_auth

acl_smtp_mail = acl_check_mail

acl_smtp_quit = acl_check_quit

acl_smtp_notquit = acl_check_notquit

LIM = 100

PERIOD = 1h

WARNTO = me@???

EXIMBINARY = /usr/local/sbin/exim -f root

SHELL = /bin/sh

#######################################################################

#                  End of additions.                                  #


#######################################################################



#

# An Attempt at greylisting

#



hide mysql_servers = localhost/exim_db/exim/<passwd>:



GREYLIST_TEST = SELECT IF(NOW() > block_expires, 2, 1) \

                FROM exim_greylist \


                WHERE relay_ip = '${quote_mysql:$sender_host_address}' \


                AND from_domain = '${quote_mysql:$sender_address_domain}' \


                AND record_expires > NOW()




GREYLIST_ADD = INSERT INTO exim_greylist \

                SET relay_ip = '${quote_mysql:$sender_host_address}', \


                from_domain = '${quote_mysql:$sender_address_domain}', \


                block_expires = DATE_ADD(NOW(), INTERVAL 1 MINUTE), \


                record_expires = DATE_ADD(NOW(), INTERVAL 14 DAY), \


                origin_type = 'AUTO', \


                create_time = NOW()




GREYLIST_UPDATE = UPDATE exim_greylist \

                SET record_expires = DATE_ADD(now(), INTERVAL 14 DAY) \


                WHERE relay_ip = '${quote_mysql:$sender_host_address}' \


                AND from_domain = '${quote_mysql:$sender_address_domain}' \


                AND record_expires > NOW()




######################################################################

#                       ACL CONFIGURATION                            #


#         Specifies access control lists for incoming SMTP mail      #


######################################################################



begin acl



######################################################################

#                 Added for the Authentication Limiting              #


######################################################################



acl_check_auth:

drop message = authentication is allowed only once per message in order \

        to slow down bruteforce cracking


        set acl_m_auth = ${eval10:0$acl_m_auth+1}


        condition = ${if >{$acl_m_auth}{2}}


        delay = 22s




drop message = blacklisted for bruteforce cracking attempt

        set acl_c_authnomail = ${eval10:0$acl_c_authnomail+1}


        condition = ${if >{$acl_c_authnomail}{4}}


        continue = ${run{SHELL -c "echo $sender_host_address \


           >>$spool_directory/blocked_IPs; \


           \N{\N echo Subject: $sender_host_address blocked; echo; echo \


           for bruteforce auth cracking attempt.; \


           \N}\N | EXIMBINARY WARNTO"}}




accept



acl_check_quit:



warn condition = ${if def:authentication_failed}

        condition = $authentication_failed


        logwrite = :reject: quit after authentication failed: \


                            ${sg{$sender_rcvhost}{\N[\n\t]+\N}{\040}}


        ratelimit = 7 / 5m / strict / per_conn


        continue = ${run{SHELL -c "echo $sender_host_address \


           >>$spool_directory/blocked_IPs; \


           \N{\N echo Subject: $sender_host_address blocked; echo; echo \


           for bruteforce auth cracking attempt.; \


           \N}\N | EXIMBINARY WARNTO"}}




acl_check_notquit:



warn condition = ${if def:authentication_failed}

        condition = $authentication_failed


        logwrite = :reject: $smtp_notquit_reason after authentication
failed: \


                            ${sg{$sender_rcvhost}{\N[\n\t]+\N}{\040}}


        condition = ${if eq{$smtp_notquit_reason}{connection-lost}}


        ratelimit = 7 / 5m / strict / per_conn


        continue = ${run{SHELL -c "echo $sender_host_address \


           >>$spool_directory/blocked_IPs; \


           \N{\N echo Subject: $sender_host_address blocked; echo; echo \


           for bruteforce auth cracking attempt.; \


           \N}\N | EXIMBINARY WARNTO"}}




acl_check_mail:



         accept set acl_c_authnomail = 0




acl_check_connect:

drop message = $sender_host_address locally blacklisted for a bruteforce \

                  auth (username+password) cracking attempt


        condition = ${if exists{$spool_directory/blocked_IPs}}


        condition = ${lookup{$sender_host_address}lsearch\


                    {$spool_directory/blocked_IPs}{1}{0}}




accept



############################################################################
##

#                 End of the Authentication Limit additions.
#


############################################################################
##



acl_check_rcpt:



       accept  hosts           = :


                logwrite        = ACL - ACCEPTED (EMPTY LIST)


        deny    local_parts     = ^.*[@%!/|] : ^\\.


                log_message     = ACL - DENIED (LOCAL PART SYNTAX)




############################################################################
#

#       This block replaced with the three paragraphs below
#


#       for authentication limiting.
#


#        accept  authenticated   = *
#


#                logwrite        = ACL - ACCEPTED
#


#                endpass
#


############################################################################
#



    accept authenticated = *


        set acl_m_user = $authenticated_id


        condition = ${if
exists{$spool_directory/blocked_authenticated_users}}


        condition = ${lookup{$acl_m_user}lsearch\


                    {$spool_directory/blocked_authenticated_users}{1}{0}}


        control = freeze/no_tell


        control = submission/domain=


        add_header = X-Authenticated-As: $acl_m_user




    accept authenticated = *


        !verify = recipient/defer_ok/callout=10s,defer_ok,use_sender


        ratelimit = LIM / PERIOD / per_rcpt / user-$acl_m_user


        continue = ${run{SHELL -c "echo $acl_m_user \


           >>$spool_directory/blocked_authenticated_users; \


           \N{\N echo Subject: user $acl_m_user blocked; echo; echo because \


           has sent mail to LIM invalid recipients during PERIOD.; \


           \N}\N | EXIMBINARY WARNTO"}}


        control = freeze/no_tell


        control = submission/domain=


        add_header = X-Authenticated-As: $acl_m_user




    accept authenticated = *


        control = submission/domain=


        logwrite        = ACL - ACCEPTED (authenticated block)


########################################################################

#            END of authentication replacement.                        #


########################################################################



########################################################################

#      This block Replaced with the following                         #


#           for Authentication Limitiing:                              #


#        accept  hosts           = +relay_from_hosts                   #


#                logwrite        = ACL - ACCEPTED                      #


#                endpass                                               #


########################################################################

accept hosts = !@[] : +relay_from_hosts

        set acl_m_user = $sender_host_address


                         # or username from RADIUS


        condition = ${if exists{$spool_directory/blocked_relay_users}}


        condition = ${lookup{$acl_m_user}lsearch\


                    {$spool_directory/blocked_relay_users}{1}{0}}


        control = freeze/no_tell


        control = submission/domain=


        add_header = X-Relayed-From: $acl_m_user




accept hosts = !@[] : +relay_from_hosts

        !verify = recipient/defer_ok/callout=10s,defer_ok,use_sender


        ratelimit = LIM / PERIOD / per_rcpt / relayuser-$acl_m_user


        continue = ${run{SHELL -c "echo $acl_m_user \


           >>$spool_directory/blocked_relay_users; \


           \N{\N echo Subject: relay user $acl_m_user blocked; echo; echo \


           because has sent mail to LIM invalid recipients during PERIOD.; \


           \N}\N | EXIMBINARY WARNTO"}}


        control = freeze/no_tell


        control = submission/domain=


        add_header = X-Relayed-From: $acl_m_user




accept hosts = +relay_from_hosts

        control = submission/domain=


        logwrite        = ACL - ACCEPTED (hosts block)


######################################################################

#            END of authentication Replacement.                      #


######################################################################



######################################################################

# Hello checks added November 1 2009.

######################################################################



# If the remote host greets with an IP address, then reject the mail.

        deny    message         = RATWARE - IP address.


                log_message     = ACL - DENIED - RATWARE remote host used IP
address in HELO/EHLO greeting


                condition       = ${if isip
{$sender_helo_name}{true}{false}}




# Likewise if the peer greets with one of our own names

        deny    message         = RATWARE - Fake HELO Domain


                log_message     = ACL - DENIED - RATWARE remote host used
our name in HELO/EHLO greeting.


                condition       = ${if match_domain{$sender_helo_name}\



{$primary_hostname:+local_domains:+relay_to_domains}\

                                {true}{false}}


        deny    message         = RATWARE - No HELO


                log_message     = ACL - DENIED - RATWARE remote host did not
present HELO/EHLO greeting.


                condition       = ${if def:sender_helo_name {false}{true}}




# If HELO verification fails, we add a X-HELO-Warning: header in the message.

        warn    message         = X-HELO-Warning: Remote host
$sender_host_address \


                                ${if def:sender_host_name
{($sender_host_name) }}\


                                incorrectly presented itself as $sender_helo_name


                log_message     = ACL - WARN - UNVERIFIABLE HELO/EHLO
greeting.


                !verify         = helo




# An attempt to block using list of subjects ###########################################

#        deny    message         = X-Blackhole: Yes


#                log_message     = REJECTED - Subject in blocksubject list -
$h_Subject


#                condition       = ${if
exists{/etc/virtual/blocksubject.txt}\


#
{${lookup{$h_Subject:}wildlsearch{/etc/virtual/blocksubject.txt}{yes}{no}}}\

#                                  {no}}


############################################################################
#############



#######################################################################

# Mail is being rejected on some hosts because the mail MX is only set to SMARTHOST

# and our server is rejecting it because some mail servers see the lesser priorty

# MX and try to oour server directly instead of going though SMARTHOST.

        accept  domains         = +filtered_domains


                hosts           = +filtering_hosts


                verify          = recipient


                log_message     = ACL - ACCEPTED - MXTEXT


        deny    message         = Please use the public MX server for the
domain $domain


                domains         = +filtered_domains


                ! hosts         = +filtering_hosts


                log_message     = ACL - DENIED - MXTEST


######################################################################



######################################################################

# DNS checks

######################################################################

# The results of these checks are cached, so multiple recipients

# does not translate into multiple DNS lookups.

#

# If the connecting host is in one of a select few DNSbls, then

# reject the message. Be careful when selecting these lists; many

# would cause a large number of false postives, and/or have no

# clear removal policy.

#

#       deny    dnslists        = dnsbl.sorbs.net : \


#               dnsbl.njabl.org : \


#               cbl.abuseat.org : \


#               bl.spamcop.net


#               message         = RBL - $sender_host_address is listed in
$dnslist_domain\


#                               ${if def:dnslist_text { ($dnslist_text)}}


#####################################################################



        deny    senders         = :


                condition       = ${if > {$recipients_count}{2}{1}}


                message         = Bounces must have only a single recipient


                log_message     = ACL - DENIED - BACKSCATTER - RECIPIENTS
$recipients_count


        deny    message         = rejected because $sender_host_address was
\


                                found in our blacklist


                hosts           = +blacklisted_domains


                log_message     = ACL - DENIED - BLACKLISTED DOMAIN FOUND IN
$blacklisted_domains


        deny


                ! condition    = ${lookup
dnsdb{defer_never,ptr=$sender_host_address}{yes}}


                log_message    = ACL - DENIED - NO PTR [rDNS] FOUND FOR
$sender_host_address


                message        = We do not accept mail from hosts with
missing \


                                or incorrect rDNS.


        deny    senders         = :


                ! hosts         = +whitelist


                ! domains       = +local_domains


                dnslists        = ips.backscatterer.org


                message         = This message looks like a bounce, and your
server is listed at \


                                ips.backscatterer.org, so I assume that this is "backscatter". \


                                Please configure your mail server to not send "backscatter spam". \


                                For advice, try http://www.dontbouncespam.org/


                log_message     = ACL - DENIED - BACKSCATTER - INCOMING


        warn    set acl_m2      = ${lookup mysql{GREYLIST_TEST}{$value}{0}}


        defer   ! hosts         = +whitelist


                ! hosts         = +relay_from_hosts


                ! authenticated = *


                condition       = ${if eq{$acl_m2}{0}{yes}}


                condition       = ${lookup mysql{GREYLIST_ADD}{yes}{no}}


                message         = Now greylisted - please try again in 1
minute.


                log_message     = ACL - DEFERED - ADDING TO GREYLIST


        defer   ! hosts         = +whitelist


                ! hosts         = +relay_from_hosts


                ! authenticated = *


                condition       = ${if eq{$acl_m2}{1}{yes}}


                message         = Still greylisted - please try again in 1
minute.


                log_message     = ACL - DEFERED - STILL GREYLISTED


        defer


                ! hosts         = +whitelist


                ! hosts         = +relay_from_hosts


                ! authenticated = *


                condition       = ${lookup mysql{GREYLIST_UPDATE}{no}{no}}


                message         = Greylist update failed


                log_message     = ACL - DEFERED - GREYLIST UPDATE FAILED


        require verify          = sender


        accept  hosts           = +spf_bypass


                verify          = recipient


                spf             = fail


                logwrite        = SPF - REFLEXION $sender_host_address is OK
for \


                                $sender_address_domain


        deny    message         = SPF - INCOMING $sender_host_address \


                                is not allowed to send mail from $sender_address_domain


                spf             = fail


                log_message     = ACL - DENIED - SPF Mismatch


        accept  domains         = +local_domains


                endpass


                message         = unknown user


                verify          = recipient


        accept  domains         = +relay_to_domains


                endpass


                message         = unrouteable address


                verify          = recipient




######################################################################

#                      ROUTERS CONFIGURATION                         #


#               Specifies how addresses are handled                  #


######################################################################

#     THE ORDER IN WHICH THE ROUTERS ARE DEFINED IS IMPORTANT!       #


# An address is passed to each router in turn until it is accepted. #

######################################################################



begin routers



dnslookup_owm_www:

driver = dnslookup

domains = ! +local_domains

condition = ${if eq {$sender_host_address}{127.0.0.1} {yes}{no}}

transport = remote_smtp

ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8

no_more



dnslookup_local:

driver = dnslookup

domains = ! +local_domains

condition = ${lookup {$sender_address_domain} lsearch {/etc/virtual/domains} {yes}{no}}

transport = remote_smtp

ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8

no_more



dnslookup_bounce:

driver = dnslookup

domains = ! +local_domains

condition = ${if eq {$sender_address_local_part}{} {yes}{no}}

transport = remote_smtp

ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8

no_more



dnslookup_alias:

driver = dnslookup

domains = ! +local_domains

transport = remote_smtp

ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8

no_more



spamcheck_router:

driver = accept

no_verify

condition = "${if and { {!def:h_X-Spam-Flag:} {!eq {$received_protocol}\

{spam-scanned}}} {1}{0}}"

transport = spamcheck



virtual_alias:

driver = redirect

allow_defer

allow_fail

data = ${lookup {$local_part} lsearch {/home/$domain/mail/aliases}}

domains = /etc/virtual/domains

require_files = /home/$domain/mail/aliases

condition = ${lookup {$local_part} lsearch {/home/$domain/mail/aliases} {yes}{no}}

qualify_preserve_domain

retry_use_local_part

check_ancestor

one_time

file_transport = address_file

pipe_transport = address_pipe

reply_transport = address_reply



autoreply_router:

driver = accept

require_files = /home/$domain/mail/auto-replies/$local_part

transport = autoreply_transport

no_verify

unseen



virtual_localuser:

driver = accept

require_files = /etc/virtual/$domain/passwd

domains = /etc/virtual/domains

condition = ${lookup {$local_part} lsearch {/etc/virtual/$domain/passwd}{$value}}

transport = virtual_localdelivery



virtual_catchall:

driver = redirect

allow_defer

allow_fail

data = ${lookup {catchall} lsearch {/home/$domain/mail/aliases}}

domains = /etc/virtual/domains

require_files = /home/$domain/mail/aliases

condition = ${lookup {catchall}lsearch{/home/$domain/mail/aliases}
{yes}{no}}

qualify_preserve_domain

retry_use_local_part

check_ancestor

one_time

file_transport = address_file

pipe_transport = address_pipe

reply_transport = address_reply



localuser:

driver = accept

check_local_user

condition = ${lookup {$sender_helo_name} lsearch {/etc/virtual/domains}{YES}{NO}}

transport = local_delivery



######################################################################

#                      TRANSPORTS CONFIGURATION                      #


######################################################################

#                       ORDER DOES NOT MATTER                        #


#     Only one appropriate transport is called for each delivery.    #


######################################################################



begin transports



remote_smtp:

        driver = smtp


        return_path_add = true




autoreply_transport:

driver = pipe

command = /usr/local/bin/autoreply.pl
/home/$domain/mail/auto-replies/$local_part



spamcheck:

driver = pipe

command = /usr/local/sbin/exim -oMr spam-scanned -bS

use_bsmtp = true

transport_filter = /usr/local/bin/spamc -u ${lookup{$domain}lsearch{/etc/virtual/domains_users}}

home_directory = "/tmp"

current_directory = "/tmp"

# must use a privileged user to set $received_protocol on the way back in!

user = mailnull

group = mailnull

log_output = true

return_fail_output = false

return_path_add

message_prefix =

message_suffix =



virtual_localdelivery:

driver = appendfile

create_directory = true

directory_mode = 700

file = /var/spool/virtual/${domain}/${local_part}

headers_remove = "Bcc"

return_path_add

user = ${lookup{$domain}lsearch{/etc/virtual/domains_users}}

group = mail

mode = 660



local_delivery:

driver = appendfile

file = /$home/mail/$local_part

delivery_date_add

envelope_to_add

return_path_add

user = mailnull

group = mail

mode = 0660



address_pipe:

driver = pipe

return_output

user = <anotheruser>



address_file:

driver = appendfile

delivery_date_add

envelope_to_add

return_path_add



address_reply:

driver = autoreply



######################################################################

#                      RETRY CONFIGURATION                           #


######################################################################



begin retry



# Domain               Error       Retries


# ------               -----       -------


*                      quota_7d


*                      quota       F,72h,1h;


*                      *           F,60m,10m; F,11h,30m; F,12h,1h;
F,144h,2h;




######################################################################

#                      REWRITE CONFIGURATION                         #


######################################################################



# There are no rewriting specifications in this default configuration file.



begin rewrite



######################################################################

#                   AUTHENTICATION CONFIGURATION                     #


######################################################################



# There are no authenticator specifications in this default configuration file.



begin authenticators



# For Netscape/Mozilla

plain:

driver = plaintext

public_name = PLAIN

server_condition = "${if and{ {!eq{$2}{}}{!eq{$3}{}} \

{crypteq {$3} {${lookup {${local_part:$2}} lsearch \

                            {/etc/virtual/${domain:$2}/passwd}\


                            {$value} {*:*}}}} } {1}{0}}"


server_set_id = $2



# For Outlook/Outlook Express

login:

driver = plaintext

public_name = LOGIN

server_prompts = "Username:: : Password::"

server_condition = "${if and{ {!eq{$1}{}}{!eq{$2}{}} \

{crypteq {$2} {${lookup {${local_part:$1}} lsearch \

                            {/etc/virtual/${domain:$1}/passwd}\


                            {$value} {*:*}}}} } {1}{0}}"


server_set_id = $1



# End of Exim configuration file

--
## List details at https://lists.exim.org/mailman/listinfo/exim-users
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/

website: www.knowledgeit.co.uk | blog: www.knowledgeit.co.uk/blog | twitter: @KnowledgeITUK

Knowledge Limited, Company Registration: 1554385
Registered Office: New Century House, Crowther Road, Washington, Tyne & Wear. NE38 0AQ
Leeds Office: Viscount Court, Leeds Road, Rothwell, Leeds. LS26 0GR

Tel: 0845 142 0020. Fax: 0845 142 0021

E-Mail Disclaimer: This e-mail message is intended to be received only by persons entitled to receive the confidential information it may contain. E-mail messages to clients of Knowledge IT may contain information that is confidential and legally privileged. Please do not read, copy, forward, or store this message unless you are an intended recipient of it. If you have received this message in error, please forward it to the sender and delete it completely from your computer system.

Please consider the environment before printing this email.