[exim] Authenticated Ratelimiting.

Top Page
Delete this message
Reply to this message
Author: Grant Peel
Date:  
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