Jared Johnson wrote:
I like this approach, but it's had its complications for us because we don't want to make the compromise of doing the rest of the checks at a given stage needlessly; e.g. if our RBL plugin hits, we don't want to bother doing RDNS and SPF lookups only to say 'sorry, you were on an RBL' afterward. Have you come up with an intuitive way to deal with this sort of thing? We have come up with some ways, but I don't know that I'd call it intuitive, yet :)

One thing we've done that dealt with the problem somewhat was to expand the idea of the DSN object to be more customizable and to specify all the information we need for logging, and for deciding what to do when a particular filter hits. It hasn't really addressed the issue completely, we've got three or four different methods in place dealing with variations on the same problem right now...

The following general model for all my filtering plugins looks like this. This example is looking for bad From:, using a register hook that reads and constructs a "bad from" regexp in $self->{_badfrom}:

    return DECLINED
      if $transaction->notes('filter') !~ /yes/i #!filtering or
          || !$self->{f_badfrom}                 #no config or
          || $transaction->notes('reason');      #already zapped?

    my $line = $transaction->header->get('From'); #get thing to check

    if ( $line =~ /$self->{f_badfrom}/o ) {       #check
my $content = &NTMqplib::genreason( 'badfrom', $1, "Bad From $line" );
        $transaction->notes( 'reason', $content );
    }
    return DECLINED;

[Genreason produces common format error messages.]

The model works by setting the notes "reason" for _why_ you want to block the email, and once set, it shuts off all subsequent filtering.

You really can't get much simpler than that.

I use the exact same model for whitelisting, except that it uses a different note.

Then, a subsequent plugin decides to DENY or not by something like this:

if ($transaction->notes{reason} && !$transaction->notes{whitelist}) {
        return DENY;
}

I do all my logging by a variation of logterse, which logs reasons and various additional things in addition to what the published logterse does.

One log line for every email that contains everything there is to know about the email. Since the reasons are a common format, post-processing the logs to derive reason effectiveness (right down to which entry in which config file triggered it) is trivial.

You can design more complex systems that allow you to parameterize the "behaviour on hit" stuff. Eg: all the stuff about rejecting, when to reject, whether to include the reason note in the reject, quarantine, forwarding, marking, etcetera.

What it would take would be coming up with a model, perhaps some library support, and converting all of the plugins to it. Most plugins would become much simpler. They generally wouldn't need their own whitelisting mechanisms at all, and you don't have to configure their hit behaviour either.

I strongly believe that one of qpsmtpd's biggest impediments to broad deployment is the adhoc/inconsistent nature of the plugin suite, particularly all the inconsistent configuration directives. Another thing is the essentially useless logging (the released qpsmtpd doesn't even include logterse I think).

You can't build an "enterprise level" MTA out of the current plugin suite. You have to recode them all for consistent behaviour and construct your own logging. The qpsmtpd _core_, on the other hand, is just fine as is ;-)

Reply via email to