Ενα 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.
Σκέφτομαι:
1. Εάν τρέχεις MIMEDefang, δε θα μπορούσες να το εντάξεις μέσα στα checks του mimedefang-filter; Θα γλίτωνες ένα Perl interpreter παραπάνω.
2. Μία κλήση TCP για κάθε χρήστη και για κάθε mail; Μπορώ να δω δύο βελτιώσεις:
2a. Να γίνεται με UDP (απλό ή ONC-RPC).
2b. Να έχεις δύο limits, ένα soft και ένα hard, ώστε να εφαρμόσεις κάποιας μορφής caching. Εάν δηλαδή κάποιος έχει «αρκετά» άδειο το mbox του (π.χ. < 50%) να μη χρειάζεται να ξαναρωτήσεις το backend για μία ώρα.
Για το (1): Προφανώς ναι, αλλά δεν τρέχω MIMEDefang :)
Για το (2): Μπορεί το 1 TCP για κάθε mail να φαίνεται «ακριβό», αλλά όσα mails γίνονται tempfail λόγω overquota, δεν προχωράνε σε άλλα 3 συστήματα που ακολουθούν, κι έτσι το συνολικό κέρδος είναι πολύ μεγαλύτερο..
Με UDP πως θα έπαιρνα reply?
Η ιδέα του caching, αν και σε κάνει near-realtime, πολύ ενδιαφέρουσα..
Πάντως, αυτό είναι απλώς ένα proof-of-concept backend webservice. Η πραγματικότητα είναι χειρότερη (κάνει και 1 LDAP lookup για κάθε mail, γιατί υπάρχουν πολλά backends και variable quotas) :)
«Με UDP πως θα έπαιρνα reply?»
Αντί να τρέχεις ένα web server στο backend, τρέχεις ένα μικρό UDP server. Το «πρωτόκολλο» μπορεί να είναι απλό: Ο client στέλνει ένα CHECK_QUOTA username, και ο server απαντά QUOTA_OK ή QUOTA_FULL. Χρειάζεσαι και ένα query timeout.
I _hate_ LDAP.