Thursday, October 13, 2005
Cyrus, Exim and over-quota messages
We run Exim as our mail transport agent
delivering into mailboxes run by the Cyrus IMAP server. We're
making use of LMTP to
deliver mail from Exim to Cyrus.
One of my little bug-bears about this setup is the way it handles users
who're over quota. The typical Cyrus response is to (rightly) refuse to
deliver the message, which results in Exim bouncing the message "local
delivery failed". There are two problems here — we're immediately
bouncing mail for what's likely to be an transient problem, and we're
sending it back with an essentially useless error message.
I've been looking for a solution to this, albeit not very hard, for a while.
Today
David and I came up with a
fairly neat one based on some ideas
I'd read on the
web. It's based on an Exim
router
that's checked at
verify time.
We start with a Perl script that's run
regularly (once an hour in our case) from cron. The purpose of this script is to
work out which mailboxes are over quota and to write an lsearch
type ACL file that lists these usernames. The value for each username is a
:defer: rule that'll cause mail to be deferred.
Once we've got that running and successfully writing a data file we can use,
we create an Exim router to check for over quota users. The router's a
little special in that it's only used to verify
time (meaning you need to verify recipients). The advantage of doing
this check at verify time is that the defer happens before the SMTP session
sends DATA. In other words, it's done before the bulk of the message is
transferred. The :defer: keyword causes Exim to return a 451 error
code, meaning that the remote MTA queues the message and retries later
— queuing mail is not our problem ;-) The mail will hang around on
the remote system until such time as it times
out, after which a bounce message (containing a useful error message!)
should be generated.
Our router for this looks something along these lines:
overquota_router:
driver = redirect
local_part_suffix_optional
local_part_suffix = +imap
domains = +local_domains
data = ${lookup{${lc:$local_part}}lsearch{/usr/local/etc/exim/overquota.txt}}
allow_defer
verify_only
Some key points in this are verify_only, which ensures that the
router is skipped during actual delivery, and allow_defer which
allows the :defer: rule. The local_part_suffix* bits are
peculiar to our setup and probably aren't necessary for others.
It's important that this router comes before the actual
router that does Cyrus delivery (probably via Cyrus's deliver(8))
and answers for any domains
that deliver into Cyrus.
You'll also need to ensure that you're verifying
recipients in the acl_smtp_rcpt
ACL section. A typical way of doing this is an ACL that looks like:
accept domains = +local_domains
endpass
verify = recipient
message = No router would accept this message.
One last thing you need to do is turn on smtp_return_error_details.
This'll ensure that the error message from the router is returned to the
client (rather than "451 Temporary local problem - please try later").
Once you've done all this, test things using exim -bh
before you go live. You should have an SMTP session that looks similar to
the following (with the right values for your system):
220 imap.example.net ESMTP Exim 4.51 Thu, 13 Oct 2005 20:56:24 +0200
EHLO someone.example.net
250-imap.example.net Hello guy at someone.example.net [192.168.0.2]
250-SIZE 8388608
250-PIPELINING
250-STARTTLS
250 HELP
MAIL FROM:<guy@example.net>
250 OK
RCPT TO:<overquotauser@example.net>
451 The users mailbox is full. Please try re-sending your e-mail later.
You'll need to ensure that cron keeps your over quota lsearch list
up-to-date. You'll also need to realise that there's a small delay between
users going over quota and their being caught by this system (and again
between when they drop below quota and when they drop out of this system).
This delay depends on how often you update your quota ACL file and this, in
turn, depends on how many users you have and how loaded your box is (the
script does take a while to run on large systems).
I hope this recipe is useful to someone else. YMMV and all that.
posted by guy at: 22:05 SAST |
path: /systems |
permanent link
