milter quota for Maildir

This is a backend cgi that works with Maildir, and complements the previous milter quota posting.

Be careful! This is only a proof-of-concept. It does not do any sort of caching, nor does it use UDP.

Apparently, the standard Maildir++ specification dictates that everyone must update the maildirsize file when doing any operation in Maildirs. So we don’t have to do heavy filesystem operations to get current mailbox size, we can read it from this file..

#!/usr/bin/perl
# This is quota.cgi for Maildir # http://wiki.dovecot.org/Quota/Maildir

use CGI qw(:standard);

$username = param("u");
exit if ($username eq "");

$home = "/var/mail";

$file=$home . "/" . $username . "/Maildir/maildirsize";
open(FILE, "< $file") or die "OK $file does not exist.";
@lines = <FILE>;
close(FILE);

($quota, $msgquota) = split(" ", $lines[0]);
$quota=int($quota); $msgquota=int($msgquota);
$line=0; $size=0; $msgs=0;
do {
 $line++;
 ($msgsize, $msgcount) = split(" ", $lines[$line]);
 $size+=$msgsize; $msgs+=$msgcount;
} while ($line< =$#lines);

if (($size>$quota) || (($msgs>$msgquota) && ($msgquota!=0))) {
    print "OVERQUOTA $username $size $quota $msgs $msgquota\n";
} else {
    print "OK $username $size $quota $msgs $msgquota\n";
}

(Ref)

Advertisement

milter verify from

Μετά το milter-quota, ένα ακόμα milter βασισμένο στο sample.pl του Sendmail::Milter. Χρειάζεται και το Net::LDAP.

Υποθέτει ότι έχουμε έναν authoritative LDAP με όλα τα emails για τα domains που είναι υπεύθυνος ο mail server μας. Αν κάποιος πάει να μας στείλει mail με envelope from address (από δικό μας domain) που όμως δεν υπάρχει στον LDAP, το κάνουμε REJECT. Ετσι γλιτώνουμε το spam και backscatter από πολλά malware που στέλνουν κάνοντας randomize το from address και κολάνε το domain στο τέλος..


use Net::LDAP;

sub envfrom_callback
{
        my $ctx = shift;
        my @args = @_;

        $email=@args[0]; # format is <user @dom.ain>
        $email =~ s/< (.*)>/$1/; # strip <>
        ($username, $domain) = split (/@/, $email);
        #print "username=$username domain=$domain\n";

        $ldap = Net::LDAP->new("ldapserver");
        $ldap->bind("cn=postmaster,ou=sendmail,dc=mail", password=>"xxxx");
        $mesg = $ldap->search(filter=>"(sendmailMTAClassValue=$domain)",
                 base=>"sendmailMTAClassName=LDAPRoute,ou=classes,ou=sendmail,dc=mail");
        @entries = $mesg->entries;

        if (@entries) { # domain exists in our LDAP
                #print "domain $domain exists in our LDAP, so perform user lookup\n";
                $mesg2 = $ldap->search(filter=>"(mailLocalAddress=$username\@$domain)",
                        base=>"ou=$domain,ou=hosted,dc=mail");
                @entries2 = $mesg2->entries;

                if (@entries2) { # user exists in ldap
                        #print "address $username\@$domain exists in ldap. accept.\n";
                        return SMFIS_CONTINUE;
                } else {
                        #print "address $username\@$domain does not exist in ldap. reject.\n";
                        $ctx->setreply('550', '5.7.1', "Reject: your From address $email does not exist."); #rfc3463
                        return SMFIS_REJECT;
                }
        } else { # domain is not in our LDAP
                #print "domain $domain does not exist in LDAP. accept.\n";
                return SMFIS_CONTINUE;
        }
}

Αλλα παρόμοια milters:

milter quota

Ενα milter για να γλιτώσουμε (κι άλλο) backscatter.

Υποθέτουμε οτι στο mail σύστημά μας έχουμε Frontend και Backend mail servers.

Ενας τυπικός frontend mail server κάνει μία σειρά από ελέγχους, πχ. Reverse DNS Lookups, dnsbl lookups, whitelist/blacklist checks, Level-1 attachment cut-off, Anti-Virus scan κλπ.

Το mail που περνάει επιτυχώς όλα τα παραπάνω, πρέπει να πάει στους backend servers και αφού γραφτεί στο mailbox, να περιμένει τον χρήστη να το διαβάσει. Στην περίπτωση όμως που ο χρήστης υπερβαίνει το quota του, τότε το backend δε θα καταφέρει να γράψει το mail στο mailbox, και θα αναγκαστεί να στείλει ένα NDR bounce message, το οποίο είναι backscatter..

Χρειαζόμαστε ένα μικρό (web)service στο backend, που θα μας λέει αν ένα mailbox είναι στο όριο ή όχι (overquota).

Για παράδειγμα αυτό το cgi script:

#!/usr/bin/perl
# This is /cgi-bin/quota.cgi

use CGI qw(:standard);

$username = param("u");
exit if ($username eq "");

$quota=200*1024*1024; # assume 200MB quota
$home="/var/mail"; # assume mbox format

$size = ( -s "$home/$username" );

print "Content-Type: text/plain\n\n");

if ($size>quota) {
    print "OVERQUOTA $username $size $quota\n";
} else {
    print "OK $username $size $quota\n";
}

Στο frontend εγκαθιστούμε τα perl modules LWP και Sendmail::Milter.

Παίρνουμε το sample.pl από το Sendmail::Milter και το διαμορφώνουμε κατάλληλα:

use LWP::Simple;

sub envrcpt_callback
{
        my $ctx = shift;
        my @args = @_;

        $email=@args[0]; # format is <user @dom.ain>
        $email =~ s/< (.*)>/$1/; # strip <>
        ($username, $domain) = split (/@/, $email);

        $be_response = get("http://$backend/cgi-bin/quota.cgi?u=$username");

        if ($be_response =~ /OVERQUOTA/) {
                #print "$email overquota\n";
                #RCODE 452 Requested action not taken: insufficient system storage (RFC 2821)
                #XCODE 4.2.2 Mailbox full (RFC 3463)
                $ctx->setreply('452', '4.2.2', "Quota exceeded for $email, try again later");
                return SMFIS_TEMPFAIL;
        }
        else {
                #print "$email not overquota\n";
                return SMFIS_CONTINUE;
        }
}

Το mail προς κάποιο overquota mailbox γίνεται tempfail και όχι reject, ώστε αν ο χρήστης αδειάσει το mailbox του να πάρει τα mails αργότερα..

Update: There is now a proof-of-concept cgi script for Maildir.