Eine minimalistische REST API zur Postfix Maildir, basierend aus busybox httpd cgi-bin und 75 zeilen shell script.
Download des kompletten Tarball mit Sources und Doku hier:
Achtung: Das hier ist ein Alpha PreRelease in Playground! Noch nicht produktiv einsetzen.
Bevor du es nutzen kannst, musst du auf deinem Mailserver noch das tokenverzeichnis anlegen und tokens als symlinks setzen. Wie das geht, steht in der help/index.html innerhalb des projekts.
Tarball auf deinem mailserver herunterladen und entpacken. Dann auf diese Weise starten:
mkdir /etc/apitokens/ chown vmail:vmail /etc/apitokens chmod u=rx,g=rx,o= /etc/apitokens tar xvzf minimail.0.6.tar.gz cd minimail ./run
busybox httpd bindet dann an port 5555 auf dem loopback. Dort findest du auch eine README, bzw. alle weiteren Informationen in der angezeigten index.html
Du kannst (und solltest) davor dann einen TLS Proxy einsetzen, falls du die API öffentlich via https erreichbar machen willst. Dort kannst du dann auch das ratelimiting oder weiteren Zugriffsschutz, IP-Filter, etc. abbilden. Für erste Test reicht dir sicherlich auch ein ssh tunnel.
Frage: Wie kann ich die API schnell testen?
Antwort: Batteries included. Im Unterordner "demo/" ist eine minimalistische Implementierung in Form der Web-Applikation "LoveLetter" zu finden, die du im Browser nutzen kannst. Ich fand den Namen schön, und letztlich ist einfach das UTF8 Symbol so benannt. Diese Implementierung nutzt die derzeitigen Funktionen der API, kann also Mails und Ordner abrufen und darstellen (Plain). Zudem kann man in der Demo-Applikation auch im Ordner "api" das Erstellen von neuen Drafts einschließlich Chunking, Store, Activate und Send durchexerzieren. Du hast auch Tastaturbefehle wie ESC, STRG+s und STRG+alt+s.
Frage: Was soll der Bullshit mit der Datenübergabe im Pfad, statt als POST oder PUT Data?
Antwort: Ganz einfach. Wie auch schon im MiniKnot-Konzept beschrieben geht es hier primär um Transparenz und Logging. Da alle Nutzdaten in Hex Chunks zerteilt und dann als Payload in der URL übermittelt werden, können diese auch sehr bequem in den Logs des Webservers mit protokolliert und überwacht oder gar rekonstruiert werden. Zudem können Pfadproxies zum Einsatz kommen, die dann für unterschiedliche Tokens andere Ziele verwenden. Auch sind chunks direkt auf Patterns scanbar. Sensibele Payload sollte ohnehin Ende-Zu-Ende verschlüsselt sein. Es ist korrekt, dass sich durch die Chunk Requests natürlich ein deutlicher Overhead ergibt - erst Recht in Verbindung mit TLS. Hierbei ist im Dokument aber auch HTTP/3.0 und UDP Traffic genannt, der diese Bürde künftig lindert. Desweiteren kann im Client durch das bewusste Chunking auch eine Resilienz implementiert werden. Sprich einzelne Chunks können erneut gesendet werden, wenn kein 200er Statuscode des Servers zurückgeliefert wurde. Das ist bei fragilen Verbindungen zum erstellten der Drafts dann deutlich robuster als ein Transfer am Stück. Der Chunk ist dann wiederholbar und der Schreibprozess kann also genau an dieser Stelle fortsetzen. Bei sehr großen Daten hat das klare Vorteile beim Upload. Durch den atomaren write zwischen chunkfile und draft, sowie der Möglichkeit des clearing einer chunk amalgamation, besteht für den client also auch ein hoher Freiheitsgrad für das Implementieren eines eigenen Queings und Redundanz zu mehreren Einheiten. (als verteilter Storage).
Idealerweise wird dazu in der api auch der chunkwrite nicht durch append im file, sondern atomar als chunk in separatem file realisiert, das im anschluss an die chunk amalgamation appended wird, um auch gegen partial writes und outages auf serverseite immun zu sein. Im jetztigen Fall ist das auch einfach mit sponge zu realisieren, bzw. aufgrund der geringen chunksize sogar im standard pipebuffer.
Dies ist derzeit noch nicht realisiert, aber geplant und leicht zu implementieren.
Du findest die Idee Schwachsinn oder willst Anmerkungen und Kommentare loswerden? Schreib mir gern eine mail.
Frage: Kann ich an andere Ports und auch wildcard statt loopback binden?
Antwort: Selbstverständlich. Das ist ja nur ein busybox httpd. In der run file findest du alles relevante. Beispielweise kannst du auch mit sowas rumspielen, wenn dein systemuser "maxmustermann" heißt und du an alle auf deinem System verfügbaren IPv4-Adressen binden willst:
cd minimail cat run | sed "s/vmail/maxmustermann/g;s/127.0.0.1/0.0.0.0/g" | sh
Die run file ist eigentlich für den Einsatz mit den daemontools gedacht. Dann passt du sie einfach direkt an. Obiges Beispiel ist nur für schnelles Live-Testen ohne Anpassungen an Code oder Config.
Frage: Wie steht es mit der Sicherheit?
Antwort: Hast du das Token, hast du Zugriff auf die gesymlinkte Mailbox. Geht dein Symlink auf einen readonly bindmount deiner maildir, kannst du damit im System die Rechte weiter einschränken. Idealweise ergänzt du aber auch in deinem vorgeschalteten TLS Proxy dann noch http auth oder andere Sicherheitsfaktoren, sowie Ratelimit etc. Auch kannst du Tokens beliebig rotieren lassen. Auf deinem System also z.B. noch TOTP mit einfließen lassen, indem du einfach die Symlinks und Token-Namen periodisch änderst. Es ist sehr flexibel.
Und zur Sicherheit des Systems selbst. So sicher, wie der busybox httpd, die kleinen überschaubaren shellscripte im cgi-bin verzeichnis und so sicher wie der systemuser mit dessen rechten der prozess dann läuft. Wir versuchen durch extreme Limitierung der gültigen Characters die Möglichkeit von path traversal und anderen Injections zu minimieren. Wenn du einen interessanten Angriffsvektor siehst, gibt bescheid.
Wichtig ist letztlich, dass die /etc/apitokens/ nicht gelistet werden darf von anderen Projekten, da sonst die Tokens bekannt sind.
Frage: Kann ich auch separate Unterordner sharen?
Antwort: Klar. Gleiches Prinzip. Setze das Ziel deinen separaten Token-Symlinks direkt auf den gewünschten Unterordner einer Mailbox Maildir. Damit ist mit dem Token Zugriff nur auf diesen Ordner möglich.