Discussion:
Authenticated User Ratelimiting
Grant Peel
2013-10-21 12:13:29 UTC
Permalink
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 = ***@ourdomain.ext
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/
Chris Wilson
2013-10-21 13:05:33 UTC
Permalink
Hi Grant,
Post by Grant Peel
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.
This is what I use:

acl_smtp_auth = acl_check_auth

...

begin acl

...

acl_check_auth:

defer
! hosts = 217.155.111.88/29 : 82.68.244.64/29
ratelimit = 30 / 1h / strict / $sender_host_address
# delay = 30s
message = Too many auth attempts, slow down
log_message = Sender $sender_host_address AUTH rate \
$sender_rate/$sender_rate_period exceeds limit \
($sender_rate_limit)

So in your case, you probably want "ratelimit = 250 / 1h / strict /
global" to use the same key (the word "global") for all IP addresses.

Note that this will allow a remote host to deny service to your users by
making a large number of auth attempts, and the failure mode is very
public as users' clients will show an error message when they try to
authenticate. So probably you really want "/ $sender_host_address" instead
of "/ global". You probably also want to except known IP addresses from
this "defer" statement, to keep your customers/users happy.

You may want to trial this with "warn" instead of "defer" as the verb, so
that you'll see messages in the logs if users exceed it. Combined with
"delay = 5s" you will actually succeed in slowing down cracking attempts a
lot, without ever failing an auth request.

So I recommend you start with something like this:

warn
! hosts = 217.155.111.88/29 : 82.68.244.64/29
ratelimit = 30 / 1h / strict / $sender_host_address
delay = 5s
# message = Too many auth attempts, slow down
log_message = Sender $sender_host_address AUTH rate \
$sender_rate/$sender_rate_period exceeds limit \
($sender_rate_limit)

Cheers, Chris.
--
_____ __ _
\ __/ / ,__(_)_ | Chris Wilson <chris+***@qwirx.com> Cambs UK |
/ (_/ ,\/ _/ /_ \ | Security/C/C++/Java/Ruby/Perl/SQL Developer |
\__/_/_/_//_/___/ | We are GNU : free your mind & your software |
--
## 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/
Grant Peel
2013-10-21 14:13:20 UTC
Permalink
-----Original Message-----
Sent: October-21-13 9:06 AM
To: Grant Peel
Subject: Re: [exim] Authenticated User Ratelimiting
Hi Grant,
Post by Grant Peel
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.
acl_smtp_auth = acl_check_auth
...
begin acl
...
defer
! hosts = 217.155.111.88/29 : 82.68.244.64/29
ratelimit = 30 / 1h / strict / $sender_host_address
# delay = 30s
message = Too many auth attempts, slow down
log_message = Sender $sender_host_address AUTH rate \
$sender_rate/$sender_rate_period exceeds limit \
($sender_rate_limit)
So in your case, you probably want "ratelimit = 250 / 1h / strict /
global" to use
the same key (the word "global") for all IP addresses.
Note that this will allow a remote host to deny service to your users by
making
a large number of auth attempts, and the failure mode is very public as
users'
clients will show an error message when they try to authenticate. So
probably
you really want "/ $sender_host_address" instead of "/ global". You
probably
also want to except known IP addresses from this "defer" statement, to
keep
your customers/users happy.
You may want to trial this with "warn" instead of "defer" as the verb, so
that
you'll see messages in the logs if users exceed it. Combined with "delay =
5s"
you will actually succeed in slowing down cracking attempts a lot, without
ever
failing an auth request.
warn
! hosts = 217.155.111.88/29 : 82.68.244.64/29
ratelimit = 30 / 1h / strict / $sender_host_address
delay = 5s
# message = Too many auth attempts, slow down
log_message = Sender $sender_host_address AUTH rate \
$sender_rate/$sender_rate_period exceeds limit \
($sender_rate_limit)
Cheers, Chris.
--
_____ __ _
,\/ _/
/_ \ | Security/C/C++/Java/Ruby/Perl/SQL Developer | \__/_/_/_//_/___/ |
We
are GNU : free your mind & your software |
Hi Chris,

Thanks for the expedient reply, much appreciated.

Two (quick?) questions:

1) My config seems to me to be quite complex and as such I don't want to
break it, where in the ACL_CHECK_AUTH list should I place the paragraph? I
am guessing right at the start....

2) Is the Auth 'per authenticated user' per IP address? i.e. I don't want to
block a specific IP for all users due to 1 users overage ... hope I worded
that question correctly.

Regards,

-G
--
## 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/
Chris Wilson
2013-10-21 14:50:00 UTC
Permalink
Hi Grant,
Post by Grant Peel
1) My config seems to me to be quite complex and as such I don't want to
break it, where in the ACL_CHECK_AUTH list should I place the paragraph? I
am guessing right at the start....
Since the other two statements are drop statements, it doesn't matter that
much. Tempfailing the attackers could waste more of their time, and your
server's resources. Swings and roundabouts. But it won't break anything if
you put it at the end.
Post by Grant Peel
2) Is the Auth 'per authenticated user' per IP address? i.e. I don't want to
block a specific IP for all users due to 1 users overage ... hope I worded
that question correctly.
I think what you mean is that if one user on a specific IP address
authenticates too many times, then you don't want to lock out all other
users on the same IP address. You can do that, but it means that an
attacker can try lots of different usernames with the same password
without triggering the ratelimit.

To do it anyway, change the key from "$sender_host_address" to
"$sender_host_address:$authenticated_id".

Cheers, Chris.
--
_____ __ _
\ __/ / ,__(_)_ | Chris Wilson <chris+***@qwirx.com> Cambs UK |
/ (_/ ,\/ _/ /_ \ | Security/C/C++/Java/Ruby/Perl/SQL Developer |
\__/_/_/_//_/___/ | We are GNU : free your mind & your software |
--
## 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/
Ian Eiloart
2013-10-21 16:44:01 UTC
Permalink
Post by Grant Peel
Hi Chris,
Thanks for the expedient reply, much appreciated.
1) My config seems to me to be quite complex and as such I don't want to
break it, where in the ACL_CHECK_AUTH list should I place the paragraph? I
am guessing right at the start....
2) Is the Auth 'per authenticated user' per IP address? i.e. I don't want to
block a specific IP for all users due to 1 users overage ... hope I worded
that question correctly.
Hi,

I think you mean you want a limit of 250 messages per authenticated user, per hour. Not, as Chris has read it, 250 messages per hour for the whole system.

For that, I’d use this:
ratelimit = 400 / 1d / per_rcpt / strict / off-lim-$authenticated_id

The "off-lim-" enables me to distinguish records from this rule from records created by other rules in my configuration. $authenticated_id distinguishes one user from another.
--
Ian Eiloart
Postmaster, University of Sussex
+44 (0) 1273 87-3148
--
## 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/
Jeremy Harris
2013-10-21 13:11:46 UTC
Permalink
Post by Grant Peel
Recent events have left us wanting to add rate limiting to our exim
configuration.
[...]
Post by Grant Peel
Any primers or suggestion(s) would be appreciated.
Docs at exim.org, Chapter 42, about section 36 or so...
--
Cheers,
Jeremy
--
## 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/
L***@lena.kiev.ua
2013-10-22 11:01:13 UTC
Permalink
From: "Grant Peel"
Recent events have left us wanting to add rate limiting to our exim
configuration.
Can you elaborate which events? Outgoing spam from a customer?
Outgoing spam using a password stolen from a customer?
A while back, we implemented authentication checking with includes some
https://lists.exim.org/lurker/message/20121117.144211.c96f81fc.pl.html
Did it catch the spammer? If not, how do you think why?
--
## 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/
Loading...