SRS / DKIM per qmail-remote wrapper Script

Ohne SPF/SRS und DKIM ist es inzwischen fast unmöglich Mail an Google oder GMX zu senden. Support für SRS soll im nächsten sqmail Release nativ kommen aber nachdem ich für DKIM sowieso einen Wrapper brauchte habe ich gleich beides integiert. Das nachfolgende Script wird an die Stelle des Original qmail-remote binary kopiert.

Ich DKIM nutze ich nur EINEN Schlüssel für alle Domains und SRS verwendet anstatt der Originaldomain ebenfalls eine systemweite Domain weil es einfacher war das dem vorgeschalteten Spam-Filter beizubringen.

#!/usr/bin/perl 
#
# Based on dkimsign.pl written by Jason Long, jlong@messiah.edu.

use strict;
use warnings;

use Data::Dumper;
use Mail::SRS;
use Mail::DKIM::Signer;
use Mail::DKIM::TextWrap;
use Pod::Usage;
use File::Temp;
use Log::Log4perl qw(:easy);
my $expiration;
my $identity;

my @args = @ARGV;

Log::Log4perl->easy_init( { level => $DEBUG,
                            file    => ">>/tmp/qmail-remote.log" } );
 
# check if sender is from a local domain
my $sender = $args[1];
my ($nn, $domain) = $sender =~ m{ \@([\w+]\.)?([\w-]+\.[\w-]+) \z}xms;

eval{
DEBUG("Domain is $domain");
if (!is_local_domain($domain)) {
    # Not a local domain -> add srs but not for postmaster
    if ($sender =~ /^postmaster\@/) {
        DEBUG("Remote postmaster address $sender");
        $domain = "";
    } else {
        $domain = "srs-catch-domain.net";
        my $srs = new Mail::SRS( Secret  => "SRSSecret" );
        # sender is second argument in @args
        $args[1] = $srs->forward($sender, 'mail@srs-catch-domain.net');
        INFO("SRS $sender -> " . $args[1]);
    }
}

my $cmd;
my $fh_msg = new File::Temp();
my $fh_head; # must be defined here to avoid early deletion of tempfile
# disables DKIM for forwarded postmaster mail 
if (!$domain) {
    DEBUG("No DKIM");
    while (<STDIN>)
    {
        print $fh_msg $_;
    }
    $cmd = "/var/qmail/bin/qmail-remote.orig @ARGV < $fh_msg";
} else {
    my $dkim = new Mail::DKIM::Signer(
        Policy => \&signer_policy,
        Algorithm => 'rsa-sha1',
        Method => 'relaxed',
        Selector => 'default',
        KeyFile => "/var/qmail/dkim/rsa.private",
    );

    while (<STDIN>)
    {
        print $fh_msg $_;
        {
            chomp $_;
            s/\015?$/\015\012/s;
        }
    $dkim->PRINT($_);
    } 

    $dkim->CLOSE;

    $fh_head = new File::Temp();
    print $fh_head $dkim->signature->as_string . "\n";
    close $fh_head;
    INFO("DKIM added for $sender");
    DEBUG("DKIM Sig: " . $fh_head );

    $cmd = "cat $fh_head $fh_msg | /var/qmail/bin/qmail-remote.orig @args";
}

die "Error handling message" unless($cmd);
system($cmd) && die "DKIM Error ($cmd)";
};

if ($@) {
    die "Z$@";
}

sub signer_policy
{
    my $dkim = shift;

    use Mail::DKIM::DkSignature;

    $dkim->domain($domain);

    my $sig = Mail::DKIM::Signature->new(
            Algorithm => $dkim->algorithm,
            Method => $dkim->method,
            Headers => $dkim->headers,
            Domain => $dkim->domain,
            Selector => $dkim->selector,
            defined($expiration) ? (Expiration => time() + $expiration) : (),
            defined($identity) ? (Identity => $identity) : (),
        );
    $dkim->add_signature($sig);
    return;
}

sub is_local_domain {

    my $domain  = shift;

    $domain =~ s/\./\\./;
    my $pattern = '\A\+'.$domain.'-';

    open(my $users, "</var/qmail/users/assign") || die "Can not open users file $@";
    my $found = 0;
    while (<$users>) {
        if ($_ =~ qr/$pattern/) {
            $found = 1;
            last;
        }
    }
    close $users;
    return $found;
}

__END__

Um die SRS Rückläufer wieder an den Originalempfänger zuzustellen, wird für die SRS Domain ein .qmail-default Eintrag angelegt der alle eingehenden Mails an ein Maildrop Filter weiterleitet (| maildrop ~vpopmail/etc/mailfilter.srs-reverse ).
Das Filterscript stellt die Mails per qmail-inject zu bzw. schiebt diese in eine Sammelmailbox falls das SRS Decoding schief geht:

NEWRCPT=/usr/local/sbin/srsr.pl $EXT
if ($NEWRCPT) {
to "| /var/qmail/bin/qmail-inject $NEWRCPT"
}
to "/var/vpopmail/domains/5/srs.serverpilot.net/bounces"

Das SRS Script selber verwendet wieder Mail::SRS:

#!/usr/bin/perl

use strict;
use Mail::SRS;

my $rcpt = shift || '';
eval {
    my $srs = new Mail::SRS( Secret  => "SRSSecret" );
    print $srs->reverse($rcpt.'@srs.serverpilot.net');
} if ($rcpt =~ /\ASRS/);
# Do not handle non SRS Mail