Senden Sie E-Mails aus dem Docker-Container mit dem Postfix des Hosts

18

Ich verwende einen Ubuntu 14.04 (Linux) Server. Ich habe Postfix und OpenDKIM sehr gut auf dem Server installiert und konfiguriert . Ich kann E - Mails an mich mit Befehlen wie senden echo hi | sendmail rootund postfix / opendkim fügt Header wie Message-Id, Dateund DKIM-Signature, uns darauf , die E - Mail an meine persönliche E - Mail - Adresse, und alles funktioniert super.

Jetzt möchte ich eine Anwendung erstellen, die in einem Docker- Container ausgeführt wird und auf die gleiche Weise E-Mails senden kann. Insbesondere möchte ich mich nicht um das Hinzufügen von Headern wie kümmern Message-Idund ich möchte nicht sehr viel Konfiguration oder Softwareinstallation innerhalb des Containers selbst durchführen.

Wie geht das am besten?

Gibt es eine Möglichkeit, den Container das sendmailausführbare Objekt auf dem Host ausführen zu lassen ?

Ich habe versucht, über das SMTP-Protokoll auf Port 25 von einem Container aus eine Verbindung zu Postfix herzustellen, aber Postfix scheint Nachrichten, die auf diese Weise empfangen wurden, anders zu behandeln. Ich glaube, es wurden keine Header hinzugefügt, sodass die Nachricht von Google Mail als Spam abgewiesen wurde (es war nicht einmal gut genug, um sie in meinem Spam-Ordner abzulegen).

Hier der Maillog-Inhalt

Sep 28 23:35:52 dantooine postfix/smtpd[4306]: connect from unknown[172.17.0.95]
Sep 28 23:35:52 dantooine postfix/smtpd[4306]: DD457889B: client=unknown[172.17.0.95]
Sep 28 23:35:52 dantooine postfix/cleanup[4309]: DD457889B: message-id=<>
Sep 28 23:35:52 dantooine spamd[3175]: spamd: connection from localhost [::1]:59471 to port 783, fd 6
Sep 28 23:35:52 dantooine spamd[3175]: spamd: handle_user (getpwnam) unable to find user: 'someone'
Sep 28 23:35:52 dantooine spamd[3175]: spamd: still running as root: user not specified with -u, not found, or set to root, falling back to nobody
Sep 28 23:35:52 dantooine spamd[3175]: spamd: processing message (unknown) for someone:65534
Sep 28 23:35:52 dantooine spamd[3175]: spamd: clean message (2.5/5.0) for someone:65534 in 0.0 seconds, 331 bytes.
Sep 28 23:35:52 dantooine spamd[3175]: spamd: result: . 2 - MISSING_DATE,MISSING_FROM,MISSING_MID,UNPARSEABLE_RELAY scantime=0.0,size=331,user=someone,uid=65534,required_score=5.0,rhost=localhost,raddr=::1,rport=59471,mid=(unknown),autolearn=no autolearn_force=no
Sep 28 23:35:52 dantooine opendkim[3179]: DD457889B: can't determine message sender; accepting
Sep 28 23:35:53 dantooine postfix/qmgr[3664]: DD457889B: from=<[email protected]>, size=275, nrcpt=1 (queue active)
Sep 28 23:35:53 dantooine postfix/smtpd[4306]: disconnect from unknown[172.17.0.95]
Sep 28 23:35:53 dantooine postfix/smtp[4311]: DD457889B: to=<[email protected]>, relay=gmail-smtp-in.l.google.com[2607:f8b0:4003:c05::1b]:25, delay=0.25, delays=0.12/0.01/0.03/0.09, dsn=5.7.1, status=bounced (host gmail-smtp-in.l.google.com[2607:f8b0:4003:c05::1b] said: 550-5.7.1 [fd17:8b70:893a:44bf:fe73:6c21] Our system has detected that 550-5.7.1 this message is likely unsolicited mail. To reduce the amount of spam 550-5.7.1 sent to Gmail, this message has been blocked. Please visit 550-5.7.1 http://support.google.com/mail/bin/answer.py?hl=en&answer=188131 for 550 5.7.1 more information. su20si7357528oeb.94 - gsmtp (in reply to end of DATA command))
Sep 28 23:35:53 dantooine postfix/cleanup[4309]: 254E688A0: message-id=<[email protected]>
Sep 28 23:35:53 dantooine postfix/bounce[4330]: DD457889B: sender non-delivery notification: 254E688A0
Sep 28 23:35:53 dantooine postfix/qmgr[3664]: 254E688A0: from=<>, size=3374, nrcpt=1 (queue active)
Sep 28 23:35:53 dantooine postfix/qmgr[3664]: DD457889B: removed
Sep 28 23:35:53 dantooine postfix/virtual[4331]: 254E688A0: to=<[email protected]>, relay=virtual, delay=0.01, delays=0/0/0/0, dsn=2.0.0, status=sent (delivered to maildir)
Sep 28 23:35:53 dantooine postfix/qmgr[3664]: 254E688A0: removed
David Grayson
quelle
Bitte posten Sie den Header Ihrer E-Mail (die von GMAIL als Spam identifiziert wurde)
masegaloeh
Die E-Mail, die ich senden wollte, hatte nur einen ToHeader, einen SubjectHeader und einen einzeiligen Text. Ich bin mir nicht sicher, wie ich sagen soll, welche Header Postfix hatte, nachdem es die Milters durchlaufen hat. Weißt du wie? Hier ist die Ausgabe in / var / log / syslog, die zeigt, wie sie von Postfix verarbeitet und von Google Mail abgelehnt wurde: gist.github.com/DavidEGrayson/fbf65c8290c049a1f262
David Grayson

Antworten:

8

Da Sie eine funktionierende Lösung haben, werde ich hier versuchen, ein anderes Verhalten zu erklären, wenn Sie Telnet zu Postfix (SMTP) und wenn Sie Sendmail (Nicht-SMTP) verwenden.

Zu Ihrer Information, OpenDKIM wird per Postfix mit Milter-Mechanismus aufgerufen . Über diese offizielle Dokumentation erhalten Sie einige Informationen zur Implementierung von Milter in Postfix . Hier das Diagramm von Milter Hook in Postfix.

             SMTP-only       non-SMTP
             filters         filters
                ^ |            ^ |
                | v            | |
Network ->  smtpd(8)           | |
                       \       | V
Network ->  qmqpd(8)    ->  cleanup(8)  ->  incoming
                       /
            pickup(8)
               :
Local   ->  sendmail(1)

Sie können sehen, dass sendmail-way (non-SMTP) und telnet-way (SMTP) unterschiedliche Verarbeitungsreihenfolgen haben.

  • Die Nicht-SMTP-E-Mail wird vor dem Einspritzen in milter bereinigt. Cleanup - Dämon war verantwortlich für das Hinzufügen von fehlenden Header: (Resent-) Von :, An :, Message-Id :, und Datum: . Daher hat Ihre E-Mail einen vollständigen Header, wenn sie in OpenDKIM Milter eingespeist wird, selbst wenn die ursprüngliche E-Mail einen unvollständigen Header hatte.

  • Die SMTP-E-Mail wird an OpenDKIM milter gesendet, bevor eine Bereinigung durchgeführt wird. Wenn Ihre ursprüngliche E-Mail daher einen unvollständigen Header hatte, kann es sein, dass opendkim die Signatur der E-Mail verweigert. Der From: -Header war obligatorisch (siehe RFC 6376 ). Wenn eine E-Mail nicht vorhanden ist, verweigert OpenDKIM das Signieren der E-Mail und gibt eine Warnung aus

    can't determine message sender; accepting
    

Da ich docker nie benutze, weiß ich nicht, welche Einschränkungen sendmail / pickup innerhalb eines Containers hat. Ich denke, die Problemumgehung von David Grayson war sicher genug, um sicherzustellen, dass OpenDKIM die Nachricht signiert.

masegaloeh
quelle
Das war aufschlussreich; Danke dir. Leider sehe ich immer noch keine bessere Lösung als meine aktuelle Lösung (beschrieben in meiner Antwort).
David Grayson
Der offensichtliche Grund war, die App zu reparieren, um From:Header in Ihre E-Mail hinzuzufügen :)
masegaloeh
Aber ich müsste auch Dinge hinzufügen, von Message-Iddenen ich nicht viel weiß und die ich wahrscheinlich falsch verstehen würde. Es scheint einfacher zu sein, den Cleanup-Daemon sich darum kümmern zu lassen.
David Grayson
Tatsächlich war die Nachrichten-ID nicht obligatorisch, wie in RFC 6376 angegeben . Standardmäßig war der obligatorische Header nur der FromHeader. Wenn Sie jedoch eine eigene Nachrichten-ID generieren möchten, können Sie eine Empfehlung wie diese verwenden. IETF Draft
masegaloeh 29.09.14
6

Sie müssen auf inet_interfacesdocker bridge ( docker0) in der Postfix-Konfiguration zeigen, die sich unter set befindet/etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Weitere interne Arbeitsdetails beim Senden von E-Mails vom Docker über Postfix, die auf dem Host installiert sind

Satish Gandham
quelle
Danke für den Link! Der relevante Teil war für mich so etwas wie hinzufügen , 172.17.0.0/16um mynetworksin /etc/postfix/main.cfund service postfix restart.
JSchirrmacher
5

Dies ist eine halbe oder zumindest eine getestete Antwort, da ich derzeit das gleiche Problem bearbeite. Ich hoffe, dass jemand helfen kann, das, was ich verpasst habe, zu verdeutlichen.

Die Antwort vom OP (David Grayson) klingt für mich wie eine Neuerfindung der Postdrop-Mail-Spool, aber die Verwendung dieser Mail-Spool klingt nach einem vielversprechenden Ansatz.

Die von postfix bereitgestellte / usr / bin / sendmail-Kompatibilitätsschnittstelle übergibt Mail an postdrop (sgid postdrop), sodass Mail in der Maildrop-Warteschlange unter / var / spool / postfix / maildrop gespeichert werden kann. Dies sollte im Docker-Container geschehen. Der Rest des Postfix sollte hoffentlich nicht im Container laufen müssen.

Ich bin also Host-Mount / var / spool / postfix / maildrop und / var / spool / postfix / public. Ich kann E-Mails in der Host-Umgebung an / var / spool / postfix / maildrop senden lassen, da ich das Warteschlangenverzeichnis für Maildrop angehängt habe. Da ich gemountet habe /var/spool/postfix/public, maildropkann ich signalisieren pickup, die Mail aus der Queue zu holen. Leider sind die beteiligten UIDs und GIDs, es sei denn, ich kümmere mich darum, was bedeutet, dass die Abholung im Hostverzeichnis die Spooldateien nicht lesen kann und die Postfix-Installation die Berechtigungen im Maildrop-Verzeichnis in der Hostumgebung durcheinander bringt.

Trotzdem scheint dies zu funktionieren:

$ cat Dockerfile 
FROM debian:jessie
# Ids from parent environment

    RUN groupadd -g 124 postfix && \
        groupadd -g 125 postdrop && \
    useradd -u 116 -g 124 postfix

    RUN apt-get update && \
      DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
        postfix \
        bsd-mailx

    CMD echo test mail | mail [email protected]

$ sudo docker build   .
...
Successfully built 16316fcd44b6

$ sudo docker run   -v /var/spool/postfix/maildrop:/var/spool/postfix/maildrop \
  -v /var/spool/postfix/public:/var/spool/postfix/public 16316fcd44b6

Obwohl es funktioniert, bin ich nicht sonderlich glücklich mit der harten Kodierung der Uids und Gids. Dies bedeutet, dass nicht überall derselbe Container gezählt werden kann. Ich denke jedoch, dass ich, anstatt das Volume vom Host aus zu mounten, es von einem Container aus mounte, auf dem Postfix ausgeführt wird, niemals Konflikte verursachen werde und ich nur eine Postfix-Installation benötige, um E-Mails von vielen Containern abzurufen. Ich habe diese Uids und Gids in einem Basis-Image festgelegt, von dem alle meine Container erben.

Ich frage mich allerdings, ob dies wirklich ein guter Ansatz ist. Bei einer so einfachen E-Mail-Konfiguration und wenn für den Container kein Daemon verwendet wird, um die Zustellung erneut zu versuchen, ist ein einfacher lokaler MTA wie msmtp möglicherweise besser geeignet. Es würde über TCP an ein Relay auf demselben Host übertragen, auf dem das Spoolen stattfinden würde.

Bedenken hinsichtlich des msmtp-Ansatzes umfassen:

  • Es besteht mehr die Möglichkeit, E-Mails zu verlieren, wenn das SMTP-Relay, an das es gesendet wird, nicht verfügbar ist. Wenn es sich um ein Relay auf demselben Host handelt, ist die Wahrscheinlichkeit von Netzwerkproblemen gering, aber ich muss vorsichtig sein, wie ich den Relay-Container neu gestartet habe.
  • Performance?
  • Fängt die Post an, fallen zu lassen, wenn eine große Menge Post durchläuft?

Im Allgemeinen scheint die gemeinsame Postfix-Spool-Methode eher eine fragile Konfiguration zu sein, die eingerichtet werden muss. Es ist jedoch weniger wahrscheinlich, dass sie zur Laufzeit fehlschlägt (Relay nicht verfügbar, sodass E-Mails gelöscht werden).

mc0e
quelle
4

Ich habe beschlossen, dass der Container E-Mails in eine Datei in einem bestimmten Verzeichnis schreibt, auf das sowohl vom Container als auch vom Host aus als Docker-Volume zugegriffen werden kann.

Ich habe ein Shell-Skript namens mailsender.sh erstellt, das Mails aus einem angegebenen Verzeichnis liest, sie an sendmail sendet und sie dann löscht:

#!/bin/bash
# Runs on the host system, reading mails files from a directory
# and piping them to sendmail -t and then deleting them.

DIR=$1

if [ \! \( -d "$DIR" -a -w "$DIR" \) ]
then
  echo "Invalid directory given: $DIR"
  exit 1
fi

echo "`date`: Starting mailsender on directory $DIR"

cd $DIR

while :
do
  for file in `find . -maxdepth 1 -type f`
  do
    echo "`date`: Sending $file"
    sendmail -t < $file
    rm $file
  done
  sleep 1
done

Ubuntu verwendet Upstart, also habe ich eine Datei mit dem Namen erstellt /etc/init/mailsender.conf, um dieses Skript in einen Daemon zu verwandeln:

description "sends mails from directory"
start on stopped rc RUNLEVEL=[2345]
stop on runlevel[!2345]
respawn
exec start-stop-daemon --start --make-pidfile --pidfile /var/run/mailsender.pid --exec
/path/to/mailsender.sh /var/mailsend

Ich kann den Dienst mit starten und mit start mailsenderbeenden stop mailsender. Ich kann mir die Logs anschauen /var/log/upstart/mailsender.logund natürlich mit Hilfe der PID-Datei überwachen.

Sie müssen das /var/mailsendVerzeichnis erstellen und dann über den Docker-Container zugänglich machen, indem Sie das Argument -v /var/mailsend:/var/mailsendzu Ihrem docker runBefehl hinzufügen .

David Grayson
quelle
Vielleicht ist so etwas wie mini_sendmail hilfreich? Es wird in Containern verwendet, z. B. als Brücke zwischen der isolierten Container-App und dem sendmail-Serverdämon auf dem Container-Hostsystem. cyberciti.biz/tips/… acme.com/software/mini_sendmail
Mikl
Wenn die E-Mail über SMTP an Postfix gesendet wird, kann Postfix die E-Mail meines Erachtens nicht bereinigen. Vielleicht würde es funktionieren, wenn Sie einen MTA hätten, der konfigurierbarer ist (oder wir haben herausgefunden, wie wir Postfix besser konfigurieren können).
David Grayson