Chris Edwards wrote:
> Andrew Hearn wrote:
> > We're an ISP and use exim as a smarthost/relay for our customers.
> > is there a way to ratelimit or deny hosts based on how many messages
> > they've sent that have been rejected/deferred by the recipients MX?
Deferred can be normal (non-selective greylisting - I don't recommend it,
but some recipients' MX do use it). So, I'd count rejections only.
> > Eg, if we've tried to relay messages for a host, and 100 in the past
> > hour have been denied or deferred by the recipients MX, then we can
> > block the sender from sending more. (as well as log it and contact the
> > customer)
I assume that all your customers have static IP-addresses listed in
+relay_from_hosts (in case of dynamic IP-addresses you need to map
$sender_host_address to a customer ID in the first "warn" line; if the mapping
operation is expensive then you can move that line to acl_check_connect).
> We do this. Local senders who trip a limit are added to a list such that
> everything they send is frozen, until a human can investigate.
>
> I'm not aware this can be achieved purely inside Exim.
Not tested:
LIM = 100
PERIOD = 1h
WARNTO = abuse@???
DIR = /var/spool/exim
EXIMBINARY = /usr/local/sbin/exim
SHELL = /bin/sh
...
acl_check_rcpt:
...
warn set acl_c_customer = $sender_host_address
warn set acl_c_msg = The customer $acl_c_customer is blocked because \
has sent mail to more than LIM invalid recipients during PERIOD.
deny hosts = +relay_from_hosts
condition = ${if exists{DIR/blocked_customers}}
condition = ${if eq{${lookup{$acl_c_customer}lsearch\
{DIR/blocked_customers}{1}{0}}}{1}}
message = $acl_c_msg
warn set acl_m_invrec =
hosts = +relay_from_hosts
!verify = recipient/defer_ok/callout=10s,defer_ok,use_sender
set acl_m_invrec = recipient verification: $recipient_verify_failure \
failed: $acl_verify_message
drop condition = $acl_m_invrec
ratelimit = LIM / PERIOD / per_rcpt / strict / customer-$acl_c_customer
continue = ${run{SHELL -c "echo $acl_c_customer \
>>DIR/blocked_customers; \N{\N echo Subject: customer \
$acl_c_customer blocked; echo; echo automatic message:; \
echo $acl_c_msg; \N}\N | EXIMBINARY WARNTO"}}
message = $acl_c_msg
deny condition = $acl_m_invrec
message = $acl_m_invrec
accept hosts = +relay_from_hosts
control = submission/domain=
...
After the customer is fined (according to contract), the abuse staff can
manually delete the line with the customer ID from the blocked_customers file.
You can change the two "message = $acl_c_msg" lines to two lines:
control = freeze
add_header = X-Customer: $acl_c_customer
and "deny" and "drop" to "accept" in those paragraphs. Then after deleting
the line the abuse staff can either unfreeze or delete accumulated messages
using exipick.