Alexander (HH): Mailserver unter Linux - Rat erwünscht

Beitrag lesen

Moin Moin!

* IMAP-Accounts
Ich kann bisher nicht finden, wie ich für Binc IMAP einen Mail-Account anlege. Ich gehe davon aus, dass ich dem Mailserver erstmal klarmachen muss, welche IMAP-Accounts es geben soll - also zumindest Benutzernamen, Passwort und Basisverzeichnis des IMAPdir-Verzeichnisbaums für diesen User.

Nö, mußt Du nicht. Alle Unix-User des Systems sind automatisch IMAP-User.

In der bincimap.conf kann ich ja nur *einen* Pfad eintragen

Richtig. Der Pfad bezieht sich außerdem noch auf das aktuelle Verzeichnis. Weil, naja, kommt gleich ...

  • aber auch keine Login-Namen und Passwörter.

Das ist ja auch gar nicht nötig. checkpassword überprüft, ob Benutzername und Passwort gültig sind. Und zwar genauso wie login, d.h. anhand der Unix-Accounts.

bincimap wird gar nicht direkt aufgerufen, sondern über eine Kaskade von Programmen, die jedes für sich nur einen kleinen Job erledigen.

(x)inetd sorgt dafür, dass bincimap-up mit STDIN und STDOUT zum IMAP-Client verbunden wird.

bincimap-up wertet die Konfiguration aus, springt ins Home-Verzeichnis des Users, springt von da in den konfigurierten Pfad (typischerweise "Maildir", damit ist das aktuelle Verzeichnis dann $HOME/Maildir), reduziert die Privilegien, und ersetzt sich selbst via exec() durch checkpassword. (Für System-Accounts, z.B. mit ungültigem Home-Verzeichnis in /etc/passwd, oder schlicht mit UID=0 (root), bricht bincimap-up schon vorher ab.)

checkpassword prüft, ob Benutzername und Password passen, und ersetzt sich bei erfolgreichem Login durch das nächste Programm in der Kette, typischerweise bincimap.

bincimap selbst erwartet die Mails im aktuellen Verzeichnis und spricht den Teil von IMAP, der nichts mehr mit Login zu tun hat.

Ich möchte eine Verzeichnisstruktur in etwa nach folgendem Muster realisieren:
/
+- mailsrv
    +- account1
    |   +- inbox
    |   +- sent
    |   +- anotherfolder
    |
    +- account2
    |   +- inbox
    |   +- sent
    |   +- anotherfolder
    |
    +- account2
    |   +- inbox
    |   +- sent
    |   +- anotherfolder
... etc.

So wollte ich das auch haben. Wie Du siehst, geht bincimap aber davon aus, dass Mails im $HOME des jeweiligen Benutzers liegen. Ich habe daher in die Kette noch ein kleines Programm eingeklinkt, dass das Verzeichnis vor dem Aufruf von bincimapd noch einmal wechselt, zu /var/spool/maildir/$USERNAME.

cat gotomaildir.c

  
/*  
 * Usage: gotomaildir /var/spool/maildir program [args]  
 */  
  
#include <sys/types.h> /* for struct passwd, uid_t */  
#include <pwd.h> /* for getpwuid() */  
#include <unistd.h> /* for chdir(), getuid(), execv() */  
  
int main(int argc, char ** argv)  
{  
        struct passwd * pw;  
  
        /*  
         * argv[0] = '/path/to/gotomaildir'  
         * argv[1] = maildir  
         * argv[2] = program  
         * argv[3..n] = args  
         */  
        if (argc<3) return 111;  
  
        /* chdir to maildir */  
        if (chdir(argv[1])==-1) return 111;  
  
        /* remove argv[0] and argv[1] */  
        argv++;  
        argv++;  
  
        /* get user login */  
        if (NULL==(pw=getpwuid(getuid()))) return 111;  
  
        /* chdir to username */  
        if (chdir(pw->pw_name)==-1) return 111;  
  
        /* now we are in /var/spool/maildir/$username */  
  
        execv(argv[0],argv);  
        return 111;  
}  

* Start des IMAP-Daemons
Der Start des Servers scheint mir sehr umständlich und kompliziert realisiert - so umständlich, dass ich ihn noch nicht begriffen habe.

Das ist nicht umständlich, das ist Unix. ;-)

Im Klartext: Es GIBT KEINEN Daemon bei binc. Viele kleine Programme arbeiten zusammen, um einen großen Job zu erledigen. Genauso wie man z.B. find, xargs, cat und wc kombinieren kann, um herauszufinden, wie viele Codezeilen ein Projekt hat (find . -name '*.c' -or -name '*.h' -print0 | xargs -0 cat | wc -l).

Natürlich kann man dafür auch ein eigenes, einzelnes Programm schreiben. Die Eleganz kommt, wenn Du mehr willst. Entweder stopfst Du das eine große Programm mit Features voll, die Du alle debuggen mußt, oder Du fügst nur ein weiteres Programm in die Kette ein, das einen weiteren Aspekt implementiert.

Im Beispiel könntest Du z.B. sagen, dass leere Zeilen nicht als Code zählen sollen:

find . -name '*.c' -or -name '*.h' -print0 | xargs -0 cat | grep -v ^$ |wc -l

Voila, nicht eine Zeile Programmcode für die neue Funktion geschrieben.

Für binc erlaubt diese Technik überhaupt erst, die Mail-Verzeichnisse aus dem HOME-Verzeichnis des Users herauszuhalten. Siehe oben, gotomaildir.c.

Und weil sehr schnell sehr lange Kommandozeilen entstehen, die in inetd.conf nicht wirklich hübsch sind, habe ich binc in ein Wrapper-Script verpackt:

  
#!/bin/ash  
# ^-- ash is smaller and faster than bash  
# If you don't have ash, use bash.  
  
BINCETC=/opt/bincimap/etc  
BINCBIN=/opt/bincimap/bin  
CHKPWBIN=/opt/bincimap/bin  
GOTOBIN=/opt/bincimap/bin  
MAILDIR=/var/spool/maildir  
  
exec \  
        "$BINCBIN/bincimap-up" --conf="$BINCETC/bincimap.conf" --logtype=syslog -- \  
        "$CHKPWBIN/checkpassword" \  
        "$GOTOBIN/gotomaildir" \  
        "$MAILDIR" "$BINCBIN/bincimapd"  

In inetd.conf steht dann nur:

  
#imap2   stream  tcp     nowait  root    /usr/sbin/tcpd  imapd  
imap2   stream  tcp     nowait  root    /opt/bincimap/bin/imap-wrapper /opt/bincimap/bin/imap-wrapper  

Weil gotomaildir schon ins passende Verzeichnis wechselt, steht in bincimap.conf in der Mailbox-Sektion für path nur "".

binc legt die notwendigen Verzeichnisse ggf. selbst an, nur das leere Maildir-Verzeichnis für die User mußt Du selbst anlegen, mit Mode 770 (drwxrwx---), Eigentümer ist der jeweilige User (damit binc dort schreiben kann), Gruppe ist mail (damit exim dort schreiben kann). INBOX/cur, INBOX/new und INBOX/tmp kannst Du auch angelegen, mit identischen Rechten. Der Rest geht definitiv per IMAP-Client via binc.

Lässt sich das nicht einfach auf die "übliche" Methode /etc/init.d/imapd start abbilden?

Nein, wozu? Binc wird über (x)inetd gestartet, wie diverse andere TCP- und UDP-Server auch.

Was hat es mit dem inetd bzw. den zusätzlichen daemontools auf sich?
Wozu sind die gut?

Och komm, inetd ist in der Wikipedia beschrieben.

Den Job, eingehende TCP-Verbindungen anzunehmen, hat inetd, xinetd, oder ucspi-tcp. Die Verbindung wird via STDIN und STDOUT weitergereicht an bincimap-up.

Bincimap-up kümmert sich um Konfiguration und Privilegien, reicht weiter an checkpassword.

Checkpassword kümmert sich um Login, ersetzt sich dann per exec() durch bincimapd (Bei mir: durch gotomaildir, dass sich nach einem chdir() dann durch bincimapd ersetzt).

Bincimapd kümmert sich um IMAP nach der Login-Phase und um die Mail-Dateien.

Daemontools brauchst Du nicht zwingend für binc, aber sie ersparen Dir das Theater mit PID-Files, zickenden Init-Scripts usw. Siehe mein anderes Posting von heute.

* Dokumentation
Gibt es nirgends eine ordentliche Beschreibung der Konfiguration von Binc IMAP? Alles was ich finde, sind immer nur Bruchstücke, selbst bincimap.org hält sich mit konkreten Informationen sehr zurück.

Wie wäre es mit dem README in den Sources? Man-Pages sind auch dabei. Es hilft allerdings ENORM, "the djb way" verstanden zu haben.

Der feine Trick bei daemontools wie bei binc ist, dass die Programme massiv ausnutzen, dass ein Programm mit sehr vielen Parametern aufgerufen werden kann. Dabei nimmt das erste aufgerufene Programm gar keine oder nur sehr wenige Parameter für sich, der Rest ist das nächste aufzurufende Programm und dessen Parameter. Das erste Programm ersetzt sich dabei selbst per exec() durch das nächste Programm. (Unter Windows funktioniert das mangels brauchbarer Prozess-API nicht.)

Die Kommandozeilen sind daher sehr lang und ohne Umbruch einfach unverständlich.

Beispiel:
    bincimap-up --conf=/etc/bincimap.conf --logtype=syslog -- checkpassword gotomaildir /var/spool/maildir bincimapd

Sauber eingerückt (mit \ am Zeilenende, um es vor der Shell zu verstecken)
    bincimap-up --conf=/etc/bincimap.conf --logtype=syslog -- \     checkpassword                                            \     gotomaildir /var/spool/maildir                           \     bincimapd

Aufgerufen wird bincimap-up mit allen Parametern. bincimap-up nimmt sich selbst (argv[0]) und alle Parameter bis einschließlich "--" weg, und ruft am Ende exec() mit den übrig gebliebenen Parametern auf. Also:

checkpassword gotomaildir /var/spool/maildir bincimapd

Checkpassword nimmt nur sich selbst weg, ruft am Ende per exec() den Rest auf. Also:

gotomaildir /var/spool/maildir bincimapd

Gotomaildir nimmt sich selbst und einen Parameter weg, ruft am Ende wieder den Rest per exec() auf:

bincimapd

Jedes Programm auf dem Weg zu bincimapd hat eine einzige, kleine Aufgabe absolviert und sich dann selbst aus dem Speicher befördert, ohne dass sich Dinge wie STDIN, STDOUT, Environment oder Prozess-ID geändert hätten (außer genau das ist Aufgabe des jeweiligen Programms). Das ist wesentlich Resourcen schondender als ein fettes Binary, dass alles auf einmal implementiert. Ganz nebenbei wird so z.B. der Code, der mit Root-Rechten läuft, komplett aus dem Speicher geworfen. Fehler in bincimapd können nicht mehr zu erhöhten Privilegien führen, weil bincimapd nur noch mit Benutzerrechten läuft. So muß auch nur der Code in bincimap-up auf Root-Lücken überprüft werden, nicht der gesamte Code. Das geht mit kurzem, übersichtlichen Code wesentlich einfacher als mit einer All-in-one-Lösung.

Und wenn Du nicht willst, dass IMAP-User sich mit dem Unix-Passwort anmelden, sondern mit einem anderen, schreibst Du Dir "einfach" einen Ersatz für checkpassword, der sich an das Protokoll hält. Ganz trivial könntest Du z.B. verlangen, dass das IMAP-Passwort das rückwärts geschriebene Unix-Passwort ist. Dann nimmst Du den Source von checkpassword, drehst das Passwort vor der Prüfung um, und compilierst das ganze als checkreversepassword. Dann ersetzt Du checkpassword durch checkreversepassword im Aufruf von bincimap-up und schon müssen IMAP-User ihr Passwort rückwärts tippen.

Die Möglichkeiten sind fast unbegrenzt, z.B. eine Passwort-Datei im HOME des Users, eine SQLite-Datei mit IMAP-Passwörtern, LDAP, gar kein Passwort, dafür aber eine feste IP-Adresse, RSA-Token-Code + Passwort. Alles, ohne an binc herumzupatchen.

Anscheinend hat Alexander Foken ja schon Erfahrung mit dem Einsatz; zumindest er müsste mich da eigentlich ein wenig unterstützen können.

Klar, aber irgendwann hab ich auch mal Internet-freie Zeit. Du hast leider Pech gehabt, dass Du erstens keinen Service-Vertrag mit mir hast und zweitens die freie Zeit genau Deinen Thread getroffen hat.

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".