Archiv des Autors: oliwel

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 Net::DNS;
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 => $INFO,
                            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;
my $local;
my $domain = '';
($local, $domain) = split /\@/, $sender;

eval{
DEBUG("Sender is $sender - 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 = "";
    } elsif(need_spf($domain)) {
        $domain = "serverpilot.net";
        my $srs = new Mail::SRS( Secret  => "ReplaceMe" );
        # sender is second argument in @args
        $args[1] = $srs->forward($sender, 'mail@srs.serverpilot.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 ()
    {
        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 => 'serverpilot',
        KeyFile => "/var/qmail/dkim/rsa.private",
    );

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

    $dkim->CLOSE;

    $fh_head = new File::Temp();
    my $dkim_sig = $dkim->signature->as_string;
    $dkim_sig =~ s{\r}{}gxms;
    print $fh_head $dkim_sig . "\n";
    close $fh_head;
    INFO("DKIM added for $sender");
    DEBUG("DKIM Sig: " . $dkim_sig );

    $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 || '';
return 1;
    return unless ($domain);

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

    open(my $users, "    my $found = 0;
    while (<$users>) {
        if ($_ =~ qr/$pattern/) {
            $found = 1;
            last;
        }
    }
    close $users;
    return $found;
}


sub need_spf {

    my $domain  = shift || '';

    # fail safe
    return 1 unless ($domain);

    my $res = Net::DNS::Resolver->new(
        tcp_timeout => 2,
        udp_timeout => 2,
    );
    my $reply = $res->search( $domain, "TXT");
    if (!$reply) {
        WARN('DNS Query failed for domain ' . $domain);
 DEBUG('DNS Error: ' . $res->errorstring);
        return 1;
    }
       
    my $need_srs = 0;
    foreach my $rr ($reply->answer) {
        if ($rr->can('txtdata')) {
            if ($rr->txtdata =~ /\Av=spf/) {
 DEBUG('SPF Record found ' . $rr->txtdata);

                # if rackport is in spf record stop checks - its ok
 return 0 if ($rr->txtdata =~ /spf\.rackport\.net/);
 
 # non rackport spf record found, so srs is needed
                $need_srs = 1;
            }
        }
    }
    return $need_srs;

}

__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

vpopmail

Vpopmail ist zwar auch schon steinalt und etwas „rostig“ aber es funktioniert noch immer 😉
Die Quellen gibt es auf Sourceforge zum Download: https://sourceforge.net/projects/vpopmail/

# Benutzer und Gruppe anlegen
groupadd -g 89 vpopmail
useradd -g vpopmail -u 89 vpopmail

# Da ich ein Altsystem mit Bestand migriere benötige ich einen abweichenden Verzeichnisnamen für das Maildir:
sed -i -e 's|Maildir|.maildir|g' \
      vchkpw.c vconvert.c vdelivermail.c \
      vpopbull.c vpopmail.c vqmaillocal.c \
      vuserinfo.c maildirquota.c || die

# Konfigurieren
./configure\
--enable-auth-module=mysql\
--enable-sql-logging\
--enable-valias\
--enable-mysql-limits\
--enable-qmaildir=/var/qmail\
--enable-qmail-newu=/var/qmail/bin/qmail-newu\
--enable-qmail-inject=/var/qmail/bin/qmail-inject\
--enable-qmail-newmrh=/var/qmail/bin/qmail-newmrh\
--enable-vpopuser=vpopmail\
--enable-vpopgroup=vpopmail\
--enable-file-locking\
--enable-md5-passwords\
--enable-logging\
--enable-auth-logging\
--enable-log-name=vpopmail\
--enable-qmail-ext\
--disable-tcpserver-file\
--disable-roaming-users\
--disable-clear-passwd\
--enable-libdir=/usr/lib\
--enable-domainquotas\
--disable-users-big-dir

# Die lokale Mailzustellung soll per dovecot-lda erfolgen - das ginge zwar auch über alias Einträge und/oder maildrop, geht aber auch über einen kleinen Trick nativ aus vdelivermail und reduziert dabei auch noch die Prozessanzahl.
echo '
#define MAILDROP 1
#define MAILDROP_PROG "-f /usr/lib/dovecot/dovecot-lda -d $EXT@$HOST"
' >> config.h

# Jetzt noch bauen und installieren
make && make install

# Da wir eine mysql Datenbank als Backend benutzen wollen, müssen wir nun noch die SQL Zugangsdaten in der Datei /home/vpopmail/etc/vpopmail.mysql eintragen. Der User muß zumindest zum Start das Recht haben Tabellen anzulegen.

# Jetzt Domain und User anlegen und eine Testmail senden
vadddomain -r acme.test
vadduser -r mail@acme.test
echo "Hallo" | /var/qmail/bin/sendmail  -v mail@acme.test

# Die Mail sollte nun unter /home/vpopmail/domains/acme.test/mail/.maildir ankommen


Dovecot

Die verschiedenen Dovecot-Komponenten erledigen hier folgenden Aufgaben:
* POP/IMAP Server
* lokale Mailzustellung mit dovecot-lda
* Mailfilter / Abwesenheitsnachrichten per Sieve
* Authentication Backend für SMTP User
cat > /etc/dovecot/dovecot-sql.conf.ext <<EOF
driver = mysql 
connect = host=dbserver dbname=vpopmail user=vpopmail password=secret
default_pass_scheme = SSHA256 

password_query = \
  SELECT pw_name as username, pw_domain as domain, pw_passwd as password, pw_dir as userdb_home \
  FROM vpopmail WHERE pw_name = '%n' AND pw_domain = '%d'

user_query = SELECT pw_dir as home \
  FROM vpopmail WHERE pw_name = '%n' AND pw_domain = '%d'

iterate_query = SELECT concat(pw_name,'@',pw_domain) AS user FROM vpopmail
cat > /etc/dovecot/conf.d/99-local.conf <<EOF
# 2.2.27 (c0f36b0): /etc/dovecot/dovecot.conf
# Pigeonhole version 0.4.16 (fed8554)
doveconf: Warning: service auth { client_limit=1000 } is lower than required under max. load (1250)
doveconf: Warning: service anvil { client_limit=1000 } is lower than required under max. load (1003)
# OS: Linux 4.15.18-9-pve x86_64 Debian 9.6 
auth_cache_negative_ttl = 0
auth_cache_size = 50 M
auth_debug = yes
auth_mechanisms = plain cram-md5
auth_verbose = yes
auth_verbose_passwords = sha1:8
debug_log_path = /var/log/dovecot/debug.log
default_process_limit = 250
disable_plaintext_auth = no
first_valid_uid = 89
last_valid_uid = 89
log_path = /var/log/dovecot/dovecot.log
login_greeting = Serverpilot.net Mail Service
mail_gid = 89
mail_home = maildir:/var/mail/vhosts/%d/%n
mail_location = maildir:~/.maildir
mail_plugins = quota
mail_uid = 89
maildir_very_dirty_syncs = yes
managesieve_notify_capability = mailto
managesieve_sieve_capability = fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext
namespace inbox {
  inbox = yes
  location = 
  mailbox Drafts {
    auto = subscribe
    special_use = \Drafts
  }
  mailbox Junk {
    auto = subscribe
    special_use = \Junk
  }
  mailbox Sent {
    auto = subscribe
    special_use = \Sent
  }
  mailbox "Sent Messages" {
    special_use = \Sent
  }
  mailbox Trash {
    auto = subscribe
    special_use = \Trash
  }
  prefix = 
}
passdb {
  driver = pam
}
passdb {
  args = /etc/dovecot/dovecot-sql.conf.ext
  driver = sql
}
plugin {
  quota = maildir:User quota
  quota_rule = *:storage=10M
  quota_rule2 = Trash:storage=+1M
  sieve = /home/vpopmail/domains/%d/%n/sieve/active-script.sieve
  sieve_default = /home/vpopmail/sieve/default.sieve
  sieve_default_name = Standardfilter
  sieve_dir = /home/vpopmail/domains/%d/%n/sieve/scripts/
}
protocols = imap pop3 sieve
service auth {
  unix_listener auth-master {
    group = vpopmail
    mode = 0600
    user = vpopmail
  }
  unix_listener auth-qmail {
    group = nofiles
    mode = 0600
    user = qmaild
  }
}
service imap-login {
  process_min_avail = 5
  service_count = 0
}
ssl = no
ssl_cert = </etc/dovecot/ssl/serverpilot.net.crt
ssl_cipher_list = ALL:!EXPORT:!LOW:!MEDIUM:!aNULL:+RC4:@STRENGTH
ssl_dh_parameters_length = 2048
ssl_protocols = TLSv1.2 TLSv1.1 SSLv3 TLSv1
userdb {
  driver = passwd
}
userdb {
  args = /etc/dovecot/dovecot-sql.conf.ext
  driver = sql
}
protocol lda {
  auth_socket_path = /var/run/dovecot/auth-master
  debug_log_path = /var/log/dovecot/lda-debug.log
  hostname = mail.serverpilot.net
  log_path = /var/log/dovecot/lda.log
  mail_plugin_dir = /usr/lib/dovecot/modules
  mail_plugins = quota sieve
  postmaster_address = nic@rackport.net
  sendmail_path = /var/qmail/bin/sendmail
}
protocol imap {
  mail_max_userip_connections = 40
}

EOF
# dovecot-lda läuft als Benutzer vpopmail und muss hier ein log schreiben!
mkdir -m775 /var/log/dovecot
chown vpopmail /var/log/dovecot

Sieve

Zum Vorsortieren von Spam bzw. zum Einrichten von Abwesenheitsnachrichten soll die Filtersprache Sieve zum Einsatz kommen. Über den ManageSieve Dienst können die Nutzer dies selber einrichten (per Plugin in Roundcube) – damit die User einen Startpunkt haben, stellen wir ein Muster-Script bereit.

mkdir /home/vpopmail/sieve
cat > /home/vpopmail/sieve/default.sieve <<EOF
require ["fileinto","vacation"];
# rule:[Urlaub]
if false # true
{
	vacation :subject "Abwesenheitsnachricht" "Ich befinde mich derzeit im Urlaub, ihre Mail wird nicht gelesen oder weitergeleitet.";
}
# rule:[Spam]
if false # header :is "X-Spam-Status" "Yes"
{
	fileinto "Junk";
}
EOF
# Das Script muss dann noch kompiliert werden
sievec /home/vpopmail/sieve/default.sieve

Weiter gehts danach mit der Einrichtung von vpopmail

Basissystem und sqmail

System vorbereiten, sqmail mit Abhängigkeiten installieren

# Repos und Pakete aktualisieren
aptitude update && aptitude upgrade

# Postfix entfernen
aptitude remove postfix

# Abhängigkeiten und ein paar Tools installieren
aptitude install git vim build-essentials libssl1.0-dev libperl-dev libmysql++-dev

# Daemontools und Dovecot Pakete
aptitude install daemontools daemontools-run \
    dovecot-core dovecot-imapd dovecot-pop3d \
    dovecot-mysql dovecot-managesieved  dovecot-sieve 

# Quellpakete von sqmail runterladen
mkdir /usr/local/src && cd /usr/local/src
wget --no-check-certificate \
   https://www.fehcom.de/sqmail/sqmail-3.3.22.tgz \
   https://www.fehcom.de/ipnet/ucspi-tcp6/ucspi-tcp6-1.10.5.tgz \
   https://www.fehcom.de/ipnet/ucspi-ssl/ucspi-ssl-0.10.6.tgz \
   https://www.fehcom.de/ipnet/fehQlibs/fehQlibs-10.tgz

# Das Debianpaket benutzt /etc/service - sqmail erwartet die Dateien aber in /service also machen wir einen symlink
ln -s /etc/service /service

# fehQlibs installieren
cd /usr/local
tar -axf fehQlibs-10.tgz
ln -s fehQlibs-10 /usr/local/qlibs
cd qlibs && make && make install

# ucspi und sqmail
cd /package
tar -axf /usr/local/src/ucspi-tcp6-1.10.5.tgz
tar -axf /usr/local/src/ucspi-ssl-0.10.9.tgz 
tar -axf /usr/local/src/sqmail-3.3.23.tgz
cd /package/net/ucspi-tcp6/ucspi-tcp6-1.10.5/
package/install
cd /package/host/superscript.com/net/ucspi-ssl-0.10.9/
package/install
cd /package/mail/sqmail/sqmail-3.3.23/
package/install
# Wer bin ich - das muss der FQDN sein!
cd /var/qmail/control
hostname -f > me 
# FQDN und Kurzname als lokale Adressen qualifzieren
hostname >> locals 
hostname -f >> locals
# root und postmaster Konto einem lokalen User zuweisen und leeres maildir anlegen
echo "myuser" > /var/qmail/alias/.qmail-root
echo "myuser" > /var/qmail/alias/.qmail-postmaster
maildirmake.dovecot ~myuser/.maildir myuser

# Script für qmail-send anpassen (/service/qmail-send/run)
#!/bin/sh
exec env - PATH="/var/qmail/bin:$PATH" \
     qmail-start ./.maildir/

# Daemontools starten und qmail-send hochfahren
service daemontools start
svc -u /service/qmail-send

# Ausprobieren ;)
echo "Hallo" | /var/qmail/bin/sendmail  -v myuser

Weiter gehts danach mit der Einrichtung von Dovecot

Schokoladenbrot

Zutaten

  • 250g Butter
  • 250g Zucker
  • 6 Eier
  • 250g Schokolade (gerieben)
  • 250g gemahlene Mandeln
  • 100g Mehl
  • 250g Kuvertüre
  • 100ml Sahne
  • 1 EL Puderzucker

Zubereitung

  1. Butter, Zucker und Eier schaumig rühren
  2. Schokolade, Mehl und Mandeln unterrühren
  3. Masse gleichmässig auf dem Backblech verteilen
  4. bei 180 Grad ca. 20 Minuten backen
  5. Sahne erwärmen (nicht kochen) und kleingehakte Kuvertüre darin auflösen, anschließend Puderzucker einrühren
  6. Schokoladenglasur auf dem noch warmen Brot verteilen
  7. Nach dem Erkalten in Rechtecke schneiden

Rheinische Erbsensuppe (Familienrezept)

Zutaten

  • 1 Kg getrocknete ungeschälte Erbsen ( Linsen )
  • 1 Kg Kartoffeln
  • 2 Stangen Lauch
  • 3 mittelgroße Möhren
  • Ein Bund Petersilie
  • Eine ½ Sellerieknolle mit Kraut
  • ½ Glas Brühe
  • Vorderhaxe ca. 1Kg
  • 400 g Bauchspeck
  • 4 Mettenden

Zubereitung

  1. Die Erbsen in Wasser einweichen (mind. 12 Stunden)
  2. Suppengrün kleinschneiden
  3. Sellerie in kleine Würfel schneiden
  4. Haxe, Speck, Suppengrün, Sellerie in der Brühe 45 Minuten garen lassen
  5. Kartoffeln würfeln
  6. Fleisch kleinschneiden
  7. Alles zusammen ca. 2 Stunden bei kleiner Hitze garen

Gleisplan Update

Der Gleisplan hat mal wieder ein Update erfahren, die Brücke der Nebenbahn liegt nun in einem 15 Grad Winkel zur Plattenkante, das Industriegebiet besteht aus einer Gleisharfe mit Zufahrt am Fuße des Bahnhofs entlang und die Zufahrt zum E-Lok BW liegt zwischen Personen- und  Güterbahnhof.

Nächster Halt: Hauptbahnhof

Nach längerer Beitragsabstinenz gibt’s heute mal wieder ein Update. Der Bahnhof der Nebenbahn diente als Versuchsobjekt für meine ersten Gleisschotter-Unternehmungen, die Brückenköpfe samt Zufahrten für die 140cm Kastenbrücke über das Industriegebiet sind ebenfalls fertig so daß eine erste Probefahrt des VT628 in seiner künftigen Heimat möglich war.

Die Hochtrasse mit der Bahnhofsausfahrt hat auch Fortschritte gemacht – die Gleise bis zur Einfahrweiche sind verlegt, der Abzweig für das Dampf-BW mit der anschließenden Güterbahn ebenfalls:

Als nächster Bauabschnitt steht nun der große Bahnhof an, die Korkunterlage ist fertig verklebt und die Gleise zur ersten „Stellprobe“ aufgelegt. Im Hintergrund sieht man die nochmal die Bahnhofsausfahrt über die Hochbrücke und am linken oberen BIldrand den Bahnhof der Nebenbahn.

Endspurt im Schattenbahnhof

Weihnachtszeit ist Modellbahnzeit und so konnte ich die letzten Tage den Schattenbahnhof ein ganzes Stück weiterbringen. Nachdem ich kurzerhand nochmal das komplette Abstell- und Steuerungskonzept überarbeitet habe, mussten ein paar bereits verlegte Gleise wieder ausgebaut werden und bei den anschließenden Testfahrten fand sich auch noch der ein oder andere Fehler in der Elektrik.

Der Abstellbereich „Nord“ ist soweit fertig verlegt, die fast 80 Lötstellen (6 Parallelgleise mit je 3 Abstellabschnitten, jeder mit zwei Bahnstrom-Zuleitungen und zwei Rückmelde-Kontakten plus eine Masse-Zuleitung pro Gleis) sind gemacht, Kabel verlegt und die Gleise fixiert.Hier der Blick von der Anlagenmitte nach „Westen“ mit Durchsicht auf die unten nochmal gezeigten Verbindungstrassen der Paradestrecke:

Im südlichen Abstellbahnhof (nur 6×2 Halteabschnitte) fehlen noch die mittleren Haltebereiche weil mir leider die Kontaktlaschen ausgegangen sind – nachdem die linke Hälfte aber bereits fertig verlegt ist, konnte ich zum Abschluß heute die noch fehlenden Vertikal-Spanten aufstellen und die Grundplatte für den Bahnhof der Neubaustrecke „probelegen“:

Parallel zum Einbau der Spanten wurden auch die vorbereiteten Trassenbretter für den Rücklauf der Paradestrecke provisorisch eingebaut – durch die Länge und Krümmung am Ende wäre es nicht möglich gewesen diese später zu montieren:

Zum Schluß hier noch ein etwas größerer Überblick: