85.1.42. pepsi-sendmail

submit mail locally through the Sendmail interface

Handbuchabschnitt:

1

85.1.42.1.1. Name

pepsi-sendmail - eine lokal verfasste Nachricht an Pepsi übergeben und dabei die traditionelle /usr/sbin/sendmail-Schnittstelle bereitstellen.

85.1.42.1.2. Übersicht

sendmail [-t] [-i] [-f SENDER] [-F NAME] [-v] [-N NOTIFY] [-R RET] [-V ENVID] [-c CONFIG] [RECIPIENT …]

mailq

newaliases

85.1.42.1.3. Beschreibung

pepsi-sendmail liest eine Nachricht von der Standardeingabe und liefert sie über einen lokalen UNIX-Domain-Socket bei pepsi-ingress ein. Es existiert, damit Software, die von einem MTA die Bereitstellung von /usr/sbin/sendmail erwartet — cron, mail(1), git send-email, logwatch, PHPs mail() —, Mail über Pepsi versenden kann, ohne umkonfiguriert zu werden.

Das Debian-Paket installiert es unter den Pfaden, die Debian Policy §11.6 von einem Mail Transport Agent verlangt, jeweils als Symlink auf das vereinheitlichte pepsi-Binärprogramm — außer /usr/lib/sendmail, das auf /usr/sbin/sendmail zeigt —, das anhand des Namens verzweigt, unter dem es aufgerufen wurde:

Pfad

Verhält sich wie

/usr/sbin/sendmail

der hier beschriebene Submission-Client

/usr/lib/sendmail

dasselbe (historischer Pfad)

/usr/bin/mailq

sendmail -bp

/usr/bin/newaliases

sendmail -bi

/usr/sbin/rmail

der Submission-Client (UUCP-Zustellung)

Eine Quellinstallation beansprucht diese Pfade nicht, weil sie demjenigen MTA gehören, den das System bereits hat; übergeben Sie INSTALL_MTA_LINKS=yes an make install, um sie bewusst zu übernehmen.

85.1.42.1.4. Authentifizierung

pepsi-sendmail hält kein Privileg und behauptet keine Identität. Es ist weder setuid noch setgid, und es benötigt keinen Eintrag in MYNETWORKS.

Stattdessen liest pepsi-ingress die Benutzer-ID des sich verbindenden Prozesses vom Kernel (SO_PEERCRED, pro Listener mit AUTH_PEERCRED aktiviert), löst sie über die passwd-Datenbank zu einem Login auf und schlägt dieses Login in USERNAME_MAP genau so nach, wie es einen am Submission-Port eintreffenden SASL-Benutzernamen nachschlagen würde. Die Regeln aus RFC 6409 §6/§8.1 gelten dann unverändert: Ein lokales Programm darf nur einen Umschlagabsender und ein From: verwenden, auf die sein eigenes Konto abgebildet ist, und ein Konto, das auf keine Adresse abgebildet ist, wird rundweg abgewiesen.

Daraus folgen zwei Dinge:

  • Der Socket darf für alle beschreibbar sein. Ihn öffnen zu können, gewährt nichts, denn es entscheidet nicht darüber, als wer Sie senden können. Verweigern Sie einem Konto den Zugang, indem Sie es in USERNAME_MAP auf keine Adresse abbilden, nicht durch Anpassen der Berechtigungen.

  • Nichts im Netzwerk erhält irgendwelche Rechte. Das ist der wesentliche Unterschied dazu, dem Loopback-Interface in MYNETWORKS zu vertrauen, was eine Netzwerkposition statt eines Benutzers authentifiziert: Bei jener Anordnung kann jeder lokale Prozess als beliebiger Absender senden, und eine Nachricht, die dieser Host an sich selbst weiterleitet, tritt erneut als frische lokale Submission ein, was in eine Schleife münden kann.

Weil Submissions authentifiziert sind, werden sie mit gesetztem state.local_origin gespeichert, sodass die Pipeline sie als eigene Mail dieses Hosts behandelt — sie mit der Domain des Autors DKIM-signiert und ihre Weiterleitung erlaubt.

85.1.42.1.5. Die Nachricht

Lokale Erzeuger schreiben Nachrichten so, wie eine Textdatei geschrieben wird, daher räumt der Client sie auf, bevor irgendetwas anderes sie zu sehen bekommt:

  • Jedes bloße LF wird zu CRLF. SMTP ist CRLF-gerahmt und pepsi-ingress setzt das durch (ein bloßes LF in DATA wird mit 500 5.6.0 invalid line framing abgelehnt), das kann also nicht dem Server überlassen werden.

  • Ein From:-Feld wird vorangestellt, wenn die Nachricht keines hat, gebaut aus -F und dem Umschlagabsender; ein Anzeigename, der RFC-5322-Sonderzeichen enthält, wird in Anführungszeichen gesetzt.

  • Jedes Bcc:-Feld wird entfernt, ob -t verwendet wurde oder nicht — es stehen zu lassen würde die verdeckten Empfänger allen anderen auf dem Umschlag offenbaren.

  • Ein Empfänger ohne @ — das To: root von cron — wird mit [pepsi-sendmail] DEFAULT_DOMAIN vervollständigt, bevor Duplikate (ohne Rücksicht auf Groß-/Kleinschreibung) entfernt werden, sodass dieselbe zweimal angegebene Adresse einmal eingeliefert wird.

Die Transaktion selbst ist das Minimum, das ein Socket auf demselben Host braucht: die Begrüßung lesen, EHLO, MAIL FROM (mit BODY=8BITMIME, wenn die Nachricht 8-Bit-Bytes enthält und der Server 8BITMIME angekündigt hat, und mit den -R/-V-Parametern nur, wenn er DSN angekündigt hat), ein RCPT TO je Empfänger (das -N unter derselben Bedingung mitführt), DATA mit angewandtem Dot-Stuffing nach RFC 5321 §4.5.2 und ein QUIT nach bestem Bemühen — ein misslungener Abschied kann eine angenommene Nachricht nicht in einen Fehler verwandeln. Kein STARTTLS, kein SASL und kein MTA-STS: Die Gegenstelle ist ein Dateisystem-Socket auf diesem Host, der Transport kann nicht beobachtet werden, und die Authentifizierung ist Sache des Kernels.

85.1.42.1.6. Optionen

-t

Liest die Empfänger aus den To:-, Cc:- und Bcc:-Headern der Nachricht. Sie werden zu allen auf der Kommandozeile angegebenen Empfängern hinzugefügt (wie bei Postfix), und eine bereits auf der Kommandozeile stehende Adresse wird nicht wiederholt. Bcc:-Header werden so oder so aus der Nachricht entfernt, mit oder ohne -t.

-f SENDER, -r SENDER

Setzt den Umschlagabsender. Unterliegt USERNAME_MAP: Der Server weist einen Absender zurück, zu dem das aufrufende Konto nicht berechtigt ist. Weggelassen, wird das Login des aufrufenden Kontos verwendet, qualifiziert mit [pepsi-sendmail] DEFAULT_DOMAIN (und das wörtliche nobody@ dieser Domain, wenn das Login nicht aufgelöst werden kann). Es ist ein Standardwert aus Höflichkeit und keine Behauptung: Der Server leitet die Identität ohnehin aus den Peer-Zugangsdaten neu ab.

-F NAME

Anzeigename für einen From:-Header, der erzeugt wird, wenn die Nachricht keinen hat.

-i, -oi

Eine Zeile, die nur einen Punkt enthält, nicht als Ende der Eingabe behandeln. Aus Kompatibilitätsgründen akzeptiert; die Eingabe wird immer bis zum Dateiende gelesen.

-v

Gibt den SMTP-Dialog auf der Standardfehlerausgabe aus und stellt die gewöhnliche Pepsi-Log-Ausgabe wieder her. Ohne ihn protokolliert das Programm nur Warnungen und Fehler: Ein sendmail-Binärprogramm läuft aus cron und aus Skripten heraus, die die Standardfehlerausgabe einfangen und per Mail zurückschicken, sodass das übliche informative Geplauder jede erfolgreiche Nachricht in eine zweite Nachricht verwandeln würde.

-c CONFIG, –config CONFIG

Liest die Pepsi-Konfiguration aus CONFIG statt vom üblichen Ort (siehe pepsi.conf(5)). Eine Pepsi-Erweiterung, nicht Teil der sendmail-Grammatik; der Wert darf angehängt (-c/etc/pepsi/pepsi.conf) oder getrennt stehen.

Gefahrlos zu beachten, obwohl dies die lokale Submission-Schnittstelle ist, denn das Programm hält keinerlei Privileg: Es auf eine andere Konfiguration zu richten ändert nur, mit welchem Socket es sich verbindet, und der Server leitet die Identität des Absenders weiterhin über SO_PEERCRED aus der uid des Aufrufers ab. Es wird -c geschrieben und nicht wie in sendmail -C, weil -C bereits eine alternative sendmail.cf bezeichnet, was etwas anderes ist und abgelehnt wird (siehe unten).

-N NOTIFY, -R RET, -V ENVID

Parameter für Zustellungsstatusbenachrichtigungen nach RFC 3461, die an den Server übergeben werden, wenn er die DSN-Erweiterung ankündigt. Siehe pepsi-stage-bounce(1).

NOTIFY ist NEVER oder eine kommagetrennte Liste aus SUCCESS, FAILURE und DELAY (RFC 3461 §4.1); RET ist FULL oder HDRS (§4.3). Alles andere ist ein Benutzungsfehler und kein 501 vom Server. ENVID wird auf dem Draht xtext-kodiert (§4.4) und auf die 100 Zeichen gekürzt, die die Grammatik erlaubt.

Ein Wert für -f, -F, -N, -R oder -V — oder ein Empfänger —, der ein Steuerzeichen enthält, wird mit EX_USAGE abgelehnt. Diese Werte werden in SMTP-Befehlszeilen und in ein erzeugtes From:-Feld eingesetzt, und SMTP-Befehle sind durch CRLF getrennt (RFC 5321 §4.1.1), sodass ein Wagenrücklauf oder Zeilenvorschub darin einen zusätzlichen Befehl — einen Empfänger, den der Aufrufer nie angegeben hat — oder ein zusätzliches Header-Feld einschleusen würde. Das wiegt am schwersten bei einem Aufrufer, der Benutzereingaben direkt durchreicht, etwa einer Webanwendung, die -f verwendet.

-bm

Liest eine Nachricht und liefert sie ein. Der Standardwert.

-bp

Gibt die Warteschlange aus. Pepsis Warteschlange liegt in der Datenbank statt in einem Spool-Verzeichnis, und sie zu lesen erfordert eine Rolle, die ein gewöhnlicher Benutzer nicht hält; daher gibt dies einen Hinweis auf der Standardfehlerausgabe aus und endet mit 70; verwenden Sie pepsi-queue(1) für die Warteschlange selbst und pepsi-status(1) für eine Zusammenfassung. Hinweis für die Überwachung: mailq „scheitert“ auf einer gesunden Pepsi-Installation daher immer.

-bi

Baut die Alias-Datenbank neu auf. Pepsis Alias-Map ist eine einfache Textdatei, die pepsi-stage-aliases(1) immer dann neu liest, wenn sich ihre Änderungszeit ändert, also gibt es nichts neu aufzubauen, und dies beendet sich erfolgreich, ohne etwas zu tun — noch bevor überhaupt nach der Konfigurationsdatei gesucht wird, denn Betreuerskripte von Paketen führen newaliases bedingungslos aus, und es darf auf einem Host nicht fehlschlagen, dessen Konfiguration unlesbar oder noch nicht geschrieben ist.

-I

Synonym für -bi: eine erfolgreiche Nulloperation, wie es auch newaliases ist.

-h N, -L TAG, -X FILE, -B TYPE, -p PROTOCOL, -O NAME=VALUE

Werden angenommen und ignoriert, so wie Postfix‘ sendmail(1) sie annimmt. Sie beschreiben Hop-Zähler, Logging und Entscheidungen zur Einplanung der Zustellung, die die Pipeline für sich selbst trifft; jede verbraucht ihren Wert, ob angehängt oder getrennt, sodass der Wert nicht für einen Empfänger gehalten wird. Nur Flags zur Warteschlangenverwaltung und Daemon-Flags werden abgelehnt, weil sie falsch darstellen würden, was das Programm tut.

-m, -U, -n, -s, -G, -Ac, -Am, -oX, -d…

Werden angenommen und ignoriert und nehmen keinen eigenen Wert. -oX ist die Optionsschreibweise im alten Stil, die cron weiterhin ausgibt (-odi, -oem), und -d ist sendmails Debug-Flag, dessen Stufe daran angehängt ist.

-bs, -bv

Vom Parser angenommen, aber nicht unterstützt: Jedes gibt auf der Standardfehlerausgabe aus, warum, und endet mit 70. -bs (SMTP auf der Standardeingabe sprechen) hat hier keinen Zweck — verbinden Sie sich direkt mit dem Submission-Socket. -bv (Adressverifikation) lässt sich ohne die Routing-Tabellen nicht beantworten, und eine Antwort zu erfinden wäre schlimmer als abzulehnen.

-bd, -bD, -q…

Wird beim Parsen abgelehnt. Pepsis lauschender Server ist pepsi-ingress(1), und seine Warteschlange wird fortlaufend von pepsi-dispatch(1) geleert; diese stillschweigend anzunehmen würde einen Daemon oder einen Warteschlangenlauf suggerieren, den es nicht gibt.

-C wird abgelehnt, statt wie -c behandelt zu werden: In der sendmail-Grammatik benennt es eine alternative sendmail.cf, die hier kein Gegenstück hat, und eine Pepsi-Konfiguration stillschweigend aus einem Pfad zu lesen, der für etwas anderes gedacht ist, wäre schlimmer als abzulehnen. Die Pepsi-Schreibweise ist -c/--config, oben dokumentiert; ohne beides wird die Konfigurationsdatei auf dem üblichen Weg gefunden (siehe pepsi.conf(5)).

85.1.42.1.7. Konfiguration

[pepsi-sendmail]

SOCKET

Pfad des UNIX-Domain-Submission-Sockets von pepsi-ingress. Standardwert /run/pepsi/submission.sock. pepsi-ingress liest dieselbe Option, um zu entscheiden, was es bedient, sodass die beiden Enden nicht auf verschiedene Pfade gerichtet werden können und kein Listener-Abschnitt nötig ist.

Der Sonderwert none schaltet die lokale Submission vollständig ab: Der Server bindet keinen Socket, und dieses Programm verweigert die Ausführung.

EHLO_NAME

Der in EHLO angekündigte Name. Standardwert ist [pepsi-ingress] HOSTNAME, oder localhost, wenn dieser nicht gesetzt ist. Kosmetisch: Der Server authentifiziert die Gegenstelle anhand ihrer Zugangsdaten, nicht anhand dieses Namens.

DEFAULT_DOMAIN

Domain, die an eine nackte, unqualifizierte Adresse angehängt wird — das root, das cron und Systemwerkzeuge ohne @ an sendmail übergeben. Standardwert ist [pepsi-ingress] HOSTNAME, oder localhost, falls das nicht gesetzt ist.

Sie ist auch die Domain des voreingestellten Umschlagabsenders, wenn -f weggelassen wird (<login>@<DEFAULT_DOMAIN>) — der Rückfall, den die eigene Einlieferungs-Identitätsabbildung des Servers auf einen Benutzernamen anwendet, für den sie keinen Eintrag hat, sodass die beiden ohne USERNAME_MAP schon von der Konstruktion her übereinstimmen. Das ist immer noch nur, wie die Nachricht adressiert wird: Von wem sie sein darf, ergibt sich aus der uid der Gegenstelle über SO_PEERCRED und die USERNAME_MAP, und diese Option zu setzen kann das nicht ausweiten.

Die zugehörige Serverseite braucht überhaupt keine Konfiguration. pepsi-ingress bedient diesen Socket bedingungslos und liest genau dieselbe SOCKET-Option, um zu entscheiden, was es bindet — sodass Client und Server nicht auf verschiedene Pfade gerichtet werden können und es keinen Listener-Abschnitt zu schreiben, im Gleichschritt zu halten oder zu vergessen gibt. Der von ihm synthetisierte Listener liegt fest, denn nichts daran ist eine Wahl: MODE = plain, SUBMISSION = yes (sodass die RFC-6409-Header-Korrekturen greifen) und AUTH_PEERCRED = yes, auf einem Socket mit Modus 0666.

Ein optionaler [pepsi-ingress-listener-*]-Abschnitt würde die Einrichtung stattdessen davon abhängig machen, dass drei verschiedene Dinge übereinstimmen — dass der Abschnitt existiert, dass sein UNIXPATH zu SOCKET passt und dass das Laufzeitverzeichnis für den Server beschreibbar ist —, jedes einzelne leicht falsch zu machen und jedes Mal mit demselben Symptom: Cron-Mail hört stillschweigend auf. Von einem Host, auf dem ein MTA läuft, wird erwartet, dass er /usr/sbin/sendmail bereitstellt.

Unter systemd wird der Deskriptor geerbt statt gebunden, wenn das ausgelieferte pepsi-ingress.socket bereits ein ListenStream= für denselben Pfad trägt. Dabei geht es nicht um Privilegien — dies ist kein privilegierter Port —, sondern um das Verzeichnis. /run/pepsi wird mit pepsi-httpd und der Setup-Apply-Türklingel geteilt, und systemd gibt ein RuntimeDirectory= dem User= der deklarierenden Unit und ändert bei jedem Start dessen Eigentümer; ein unprivilegiertes pepsi-ingress, das dort seinen eigenen Socket anlegt, scheitert, sobald eine andere dieser Units neu startet. Von der .socket-Unit gebunden, wird der Knoten von systemd als root angelegt, bevor der Dienst startet, sodass der Server überhaupt keinen Schreibzugriff auf das Verzeichnis braucht — und der Socket bleibt verfügbar, während der Dienst neu startet, sodass ein gleichzeitiges /usr/sbin/sendmail wartet, statt zu scheitern.

Der Deskriptor wird über die Adresse, an die er gebunden ist, zugeordnet, nicht über einen FD_INDEX. Ein Index ist eine Position unter den ListenStream=-Einträgen der Unit, ändert sich also, sobald ein Port hinzukommt oder wegfällt — eine zweite Deklaration, die mit einer Datei im Gleichschritt zu halten wäre, die sie nicht sehen kann. Den Kernel zu fragen, woran jeder übergebene Deskriptor gebunden ist, hat diesen Fehlermodus nicht: Einen SMTP-Port hinzuzufügen oder zu entfernen nummeriert nichts um, ein nur ausgehender Host, der Port 25 weglässt, braucht keine Änderung, und die Unit und die Konfiguration können sich nicht darüber uneins sein, welcher Socket dies ist. Passt kein übergebener Deskriptor — kein systemd, oder ein SOCKET, das die Unit nicht bindet —, bindet pepsi-ingress den Pfad selbst.

Wenn Sie SOCKET ändern, ändern Sie das ListenStream= der Unit entsprechend oder entfernen Sie es: Ein Eintrag, auf dem nichts annimmt, ist schlimmer als keiner, denn der Aufrufer verbindet sich und blockiert dann, bis sein eigener Lese-Timeout abläuft (zwei Minuten, dann EX_TEMPFAIL), statt sofort abgewiesen zu werden. pepsi-ingress schreibt beim Start eine Warnung ins Journal, die jeden übergebenen Deskriptor nennt, den kein Listener beansprucht hat.

Das Binden des Sockets zu verfehlen ist fatal: pepsi-ingress verweigert den Start, statt hochzukommen und nur einen Teil dessen zu bedienen, worum es gebeten wurde. Ein Server, der mit stillschweigend fehlender lokaler Submission läuft, ist genau der Fehler, den diese Anordnung verhindern soll, und er ist unsichtbar, bis Mail verloren geht.

Einen eigenen Listener-Abschnitt für denselben Pfad zu schreiben wird unterstützt und ist der Weg, dem Socket andere Optionen zu geben — eine Gruppe, einen strengeren Modus, keine Peer-Zugangsdaten. Ein Abschnitt, der diesen Pfad bedient, ersetzt den eingebauten Listener, statt mit ihm zu kollidieren.

85.1.42.1.8. Abschalten

SOCKET = none deaktiviert die lokale Submission vollständig: pepsi-ingress bedient keinen Socket, und pepsi-sendmail verweigert die Ausführung (Exit EX_SOFTWARE, bevor es die Nachricht liest — eine dauerhafte Fehlkonfiguration, die cron also nicht wiederholen darf). Programme auf dem Host müssen sich dann wie jeder andere Client auf 587 oder 465 authentifizieren. pepsi-setup meldet dies einmal, denn ein Mailserver ohne lokalen Submission-Weg ist ungewöhnlich genug, dass ein Operator, der das nicht beabsichtigt hat, davon erfahren sollte.

85.1.42.1.9. Exit-Status

Die traditionellen sysexits.h-Werte, auf die cron und andere Aufrufer reagieren:

0

die Nachricht wurde angenommen

64

Benutzungsfehler (EX_USAGE)

65

die Nachricht war unbrauchbar, z. B. fand -t keinen Empfänger

66

die Nachricht konnte nicht von der Standardeingabe gelesen werden

67

ein Empfänger wurde als unbekannt zurückgewiesen — ein RFC-3463-Status x.1.x oder ein blankes 550 (EX_NOUSER)

69

jede andere dauerhafte Zurückweisung durch den Server (EX_UNAVAILABLE)

70

interner Fehler, z. B. konnte die Konfiguration nicht geparst werden, oder SOCKET = none hat die lokale Einlieferung abgeschaltet (EX_SOFTWARE); ebenso der Exit für die angenommenen, aber nicht unterstützten Modi -bp/mailq, -bs und -bv, von denen jeder sich auf der Standardfehlerausgabe erklärt

75

vorübergehender Fehler — der Socket war nicht erreichbar, ein Befehl lief in eine Zeitüberschreitung, der Server hat die Verbindung geschlossen oder fehlerhaftes SMTP geantwortet, oder er hat mit einem 4xx zurückgestellt; der Aufrufer sollte es erneut versuchen, statt die Mail als verloren zu betrachten (EX_TEMPFAIL)

77

der Server hat aus Richtliniengründen abgelehnt — ein RFC-3463-Status x.7.x, typischerweise ein Absender, den das aufrufende Konto nicht verwenden darf (EX_NOPERM)

Eine dauerhafte Zurückweisung wird anhand des erweiterten Statuscodes am Anfang der Antwort des Servers klassifiziert, nie durch Durchsuchen ihrer Prosa, sodass 550 5.1.1 unknown user (see the 5.7.1 policy page) als unbekannter Empfänger gemeldet wird und nicht als Ablehnung aus Richtliniengründen.

85.1.42.1.10. Dateien

/run/pepsi/submission.sock

Der Einlieferungs-Socket, sofern nicht [pepsi-sendmail] SOCKET einen anderen Pfad benennt. Beide Enden lesen diese eine Option, sodass der Client und pepsi-ingress sich nicht darüber uneinig sein können, wo er liegt.

Wenn -c/–config nicht angegeben ist, ist die Konfiguration die erste existierende Datei aus der folgenden Liste — dieselbe Liste, die jede Pepsi-Komponente durchsucht:

  • $XDG_CONFIG_HOME/pepsi.conf

  • $HOME/.config/pepsi.conf

  • /etc/pepsi/pepsi.conf

  • /etc/pepsi.conf

85.1.42.1.11. Beispiele

Eine Nachricht versenden und die Empfänger aus ihren Headern nehmen:

printf 'To: ops@example.org\nSubject: nightly\n\nall good\n' | sendmail -t

Den Dialog beobachten, während eine Ablehnung diagnostiziert wird:

sendmail -v -f alice@example.org bob@example.net < message.eml

85.1.42.1.12. Siehe auch

pepsi-ingress(1), pepsi-queue(1), pepsi-status(1), pepsi-stage-aliases(1), pepsi.conf(5)