angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Versenden von E-Mails aus einem Docker Container mit ISPConfig3 Hosts Postfix MTA

Verwenden Sie die IP-Adresse der Docker0 Bridge, um eine Verbindung herzustellen zu Postfix

28 Juni 2019
post main image
Original photo unsplash.com/@alejandroescamilla.

In der endlosen Anzahl von Problemen, auf die Sie stoßen und die Sie lösen, sah ich mich mit einer neuen Technologie konfrontiert: wie man E-Mails von meiner Python Docker App aus über den ISPConfig Host MTA (Mail Transfer Agent) sendet. Ich habe festgestellt, dass es zwei Möglichkeiten gibt, dies zu tun:

  • Senden Sie E-Mails von unserem Container an port 25 den Host, auf dem der MTA Empfänger lauscht.
  • Schreiben Sie die Mail-Datei in ein Verzeichnis auf dem Host und verwenden Sie ein Skript, um die Mail-Dateien in das Verzeichnis MTA

Vielleicht werde ich später die Methode der Mail-Dateien untersuchen, siehe auch Links unten.

Senden Sie E-Mails von unserem Container an port 25 den Host, auf dem der MTA Empfänger lauscht.

Dazu müssen wir die Docker Brücke docker0 verwenden. Aus den Docker Unterlagen:

Standardmäßig erstellt und konfiguriert der Docker Server im Docker0 des Hostsystems eine Netzwerkschnittstelle namens Docker0, die eine Ethernet-Brückenvorrichtung ist. Wenn Sie beim Starten eines Containers kein anderes Netzwerk angeben, ist der Container mit der Brücke verbunden und der gesamte Verkehr, der von und zum Container kommt, fließt über die Brücke zu dem, Docker daemonder die Routenführung im Namen des Containers übernimmt.

Docker konfiguriert Docker0 mit einer IP-Adresse, netmask,, und einem IP-Zuweisungsbereich. Containern, die mit der Standardbrücke verbunden sind, werden IP-Adressen innerhalb dieses Bereichs zugewiesen.

Das bedeutet, dass wir im Container, wenn wir telnet prüfen, ob wir uns mit der Verbindung MTA und dem Senden einer Testmail verbinden können, dies nicht tun können:

telnet localhost 25
telnet 127.0.0.1 25

sondern muss stattdessen die IP-Adresse der Docker Bridge verwenden:

telnet <docker0 IP address> 25

Du kannst die IP-Adresse des Dockers0 z.B. durch Ausführen erhalten:

ifconfig docker0

Test auf lokalem Rechner (Ubuntu 18.04 desktop)

Ich beschloss, dies zuerst auf meinem lokalen Rechner zu testen, anstatt in der Produktion herumzuspielen. Angenommen, Sie haben Docker installiert und nein MTA, benötigen Sie Folgendes, um diese Tests durchzuführen:

  • Überprüfen Sie die Listening-Ports und Anwendungen auf port 25. Führen Sie eine davon durch:
    sudo lsof -i -P -n | grep LISTEN | grep 25
    sudo netstat -tulpn | grep LISTEN | grep 25
  • Ein Arbeitsbehälter. Alpine ist für unseren Zweck in Ordnung. Geben Sie es durch Ausführen ein:
    docker run -it alpine /bin/sh
  • Telnet im Docker Container. Wenn Sie sich im Container befinden, installieren telnet Sie die Software durch Ausführen:
    apk add busybox-extras
  • Ein socket server Zuhören auf port 25. Ich habe folgendes verwendet:
    # description: python 3 socket server printing and echoing received data
    import socket
    import sys
    
    # host: all available interfaces
    host = ''
    # port: set to port 25 for our test
    port = 25
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print('socket created')
    try:
    	s.bind((host, port))
    except socket.error as msg:
    	print('bind failed, msg = {}'.format(msg))
    	sys.exit()
    print('bind done')
    
    s.listen(1)
    print('start listening')
    
    conn, addr = s.accept()
    print('Connection from ', addr)
    while True:
        data = conn.recv(1024)
        print('data = {}'.format(data))
        if not data: 
            break
        conn.sendall(data)
    conn.close()

Auf meinem lokalen Rechner ist die IP-Adresse der Docker0 Bridge: 172.17.0.1.

Zuerst prüfen wir, ob port 25 es verfügbar ist (sollte es sein):

sudo lsof -i -P -n | grep LISTEN | grep 25

Öffnen Sie ein weiteres Terminalfenster, starten Sie das Echo socket server:

sudo python3 listen_port.py

Dadurch wird etwas Text gedruckt:

socket created
bind done
start listening

Jetzt port 25 muss er in Gebrauch sein (von unserem socket server):

python3    5857            root    3u  IPv4 2925123970      0t0  TCP *:25 (LISTEN)

Öffnen Sie ein weiteres Terminalfenster, starten und betreten Sie den Docker Container:

docker run -it  alpine /bin/sh

Führen Sie'apk add busybox-extras' aus, um es zu installieren telnet.

Im Docker Containertyp:

telnet 127.0.0.1 25

Du wirst die Nachricht erhalten:

telnet: can't connect to remote host (127.0.0.1): Connection refused

Verwenden Sie nun stattdessen die IP-Adresse des Dockers0:

telnet 172.17.0.1 25

In den Terminalfenstern des socket server Gerätes sollten Sie die Meldung sehen:

Connection from  ('172.17.0.2', 38928)

Geben Sie im Docker Container einige Wörter ein, drücken Sie die Eingabetaste, etc. Sie sollten im Docker Container widergespiegelt werden und im socket server Terminalfenster zu sehen sein.

Beachten Sie, dass es zu einem Sequenzproblem kommen kann. Die Reihenfolge, in der Sie den Docker Container socket server und den Container starten. Auch wenn Sie das socket server Programm beenden und erneut starten, kann es zu einem Fehler kommen:

OSError: [Errno 98] Address already in use

Warten Sie in diesem Fall einige Zeit, bevor Sie es erneut starten.

So weit, so gut. Wir können über die Docker0-Brücke kommunizieren. Lassen Sie uns zum Produktionsserver übergehen.

Umsetzung in die Produktion (Debian Stretch + ISPConfig3)

Auf meinem Produktionsserver ist die IP-Adresse der Docker0 Bridge: 172.17.0.1

Für ISPConfig3 ist der MTA is Postfix. Die Konfiguration befindet sich in der Datei:

/etc/postfix/main.cf

Es gibt zwei Zeilen in dieser Datei, die geändert werden müssen:

inet_interfaces = all
...
mynetworks = 127.0.0.0/8 [::1]/128

Die Zeile mit ' 'inet_interfacessieht gut aus, hört Postfix bereits auf alle Interfaces, es ist nicht nötig, diese Zeile zu ändern. Die Zeile mit 'mynetworks' muss geändert werden, sie muss enthalten sein:

  • Die IP-Adresse der Docker0 Bridge, und,
  • Die IP-Adressen von Docker Bildern (die E-Mails senden werden)

Wir haben bereits die IP-Adresse der docker0 bridge, 172.17.0.0.1. Um die IP-Adressen unserer Docker Container zu finden, können wir sie überprüfen, indem wir sie zuerst ausführen:

docker ps

um die Container-ID zu erhalten, dann verwenden Sie inspect, z.B:

docker inspect c2c44e9bea28

Dies wird eine Menge Informationen geben und irgendwo am Ende der Zeile so etwas wie:

"IPAddress": "172.20.0.2"

Um alle Docker Adressen einzubinden, verwende ich das Subnetz 172.16.0.0.0/12, das ergibt einen IP-Bereich: 172.16.0.1 - 172.31.255.254. Die mynetworks Linie wird dann zu:

mynetworks = 127.0.0.0/8 [::1]/128  172.16.0.0/12

Nach dem Ändern dieser Zeile in /etc/postfix/main.cf und dem Speichern der Datei müssen wir neu starten Postfix:

service postfix restart

Um zu überprüfen, ob unsere Änderungen funktionieren, verwenden telnetwir wieder . Auf dem Hosttyp:

telnet 127.0.0.1 25

Es wird so etwas wie gedruckt:

220 server.example.com ESMTP Postfix (Debian/GNU)

Erinnere dich an diese Zeile! Geben Sie quit ein, um zu stoppen telnet. Jetzt starten wir einen kleinen Docker Container, wie wir es auf der lokalen Maschine getan haben:

docker run -it  alpine /bin/sh

und fügen telnet Sie durch Ausführen von 'apk add busybox-extras' erneut hinzu. Natürlich:

telnet 127.0.0.1 25

wird fehlschlagen:

telnet: can't connect to remote host (127.0.0.1): Connection refused

Aber:

telnet 172.17.0.1 25

sollte Ihnen nun die Nachricht des MTA, die gleiche Nachricht wie auf dem Host geben:

220 server.example.com ESMTP Postfix (Debian/GNU)

Als letzten Test können wir uns selbst eine Mail aus dem Container schicken mit sendmail. Hol dir die sendmail Optionen:

sendmail --help

Irgendwo gibt es die Grenze:

-S HOST[:PORT]	Server (default $SMTPHOST or 127.0.0.1)

Das bedeutet, dass wir die Standardadresse auf die von docker0 ändern können. Um eine Mail zu senden, geben Sie:

sendmail -S 172.17.0.1 yourname@yourdomain.yourextension

Beginnen Sie mit der Eingabe:

To: someone@example.com
Subject: My docker test mail

Hello this is a mail from my docker container. 
Thank you.

Geben Sie Strg-D ein, um die Mail zu senden, und überprüfen Sie /var/log/log/mail.log auf Ihre Mail-Nachricht. Ich tat es und leider gab es einen Fehler, die Nachricht in mail.log:

You cannot send mail from 4449d8888ddd since that domain cannot receive mail

Bei der weiteren Prüfung stellte sich heraus, dass die Absenderadresse keine voll qualifizierte Domain war. Sie können dies überprüfen, indem Sie die Option Verbose (-v) sendmail verwenden. Es zeigte sich:

sendmail: send:'MAIL FROM:<root@1f12e2cef814>'

Die Lösung besteht darin, die Option MAIL FROM SENDER (-f) auf der Kommandozeile hinzuzufügen:

sendmail -v -S 172.17.0.1 -f info@example.com yourname@yourdomain.yourextension

Beginnen Sie erneut mit der Eingabe:

To: yourname@yourdomain.yourextension
From: someone@example.com
Subject: My docker test mail

Hello this is a mail from my docker container. 
Thank you.

Geben Sie Strg-D ein, um die Mail zu senden. Nun ist die Mail verschickt und sollte in Ihrem Postfach (oder Spam) erscheinen. Mission abgeschlossen!

Verwendung eines lokalen SMTP Servers zum Debuggen

Es gibt einen Python einzigen Liner, der als SMPT-Server fungiert und für das Debugging nützlich sein kann, das Flag -d fügt Debugginginformationen hinzu:

sudo python -m smtpd -d -n -c  DebuggingServer 172.17.0.1:1025

Schlussbemerkungen

Es gibt ein Problem mit der beschriebenen Methode, d.h. siehe auch Links:

Postfix muss nach dem Aufrufen der docker0-Schnittstelle gestartet werden'.

Da die Docker Netzwerkbrücke beim Systemstart möglicherweise noch nicht bereit ist, kann es vorkommen, dass der Postfix nicht startet, weil er sich nicht an diese Adresse binden kann.

Ich habe noch nicht nach Lösungen gesucht, aber es ist beängstigend. Wenn der Server neu gestartet wird, darf ich nicht vergessen zu prüfen, ob E-Mails gesendet werden können. Aus diesem Grund kann die Methode der'mail files' eine bessere Lösung sein, da es keine Abhängigkeiten von docker0 bridge gibt.

Links / Impressum

Configure sendmail inside a docker container
https://stackoverflow.com/questions/26215021/configure-sendmail-inside-a-docker-container

Send an email from a Docker container through an external MTA with ssmtp
https://www.michelebologna.net/2019/send-an-email-from-a-docker-container/

Sending email from docker through Postfix installed on the host
http://satishgandham.com/2016/12/sending-email-from-docker-through-postfix-installed-on-the-host/

Sending email inside a docker container to hosts smtp running postfix
https://serverfault.com/questions/817353/sending-email-inside-a-docker-container-to-hosts-smtp-running-postfix

Using system postfix as mail relay for docker containers
https://markusbenning.de/blog/2017/08/16/using-system-postfix-as-mail-relay-for-docker-containers.html

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.