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

Verzending van post vanuit een dockercontainer met behulp van ISPConfig3 hosts Postfix MTA

Gebruik het IP-adres van de docker0 bridge om een verbinding te maken met Postfix

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

In het eindeloze aantal problemen die je tegenkomt, en op te lossen, toen ik nieuwe technologie begon te gebruiken, werd ik geconfronteerd met een nieuwe: hoe kan ik e-mail te verzenden vanuit mijn Python docker app met behulp van de ISPConfig host MTA (Mail transfer Agent). Ik vond dat er twee manieren zijn om dit te doen:

  • Stuur mail vanuit onze container naar port 25 de host waar ze MTA naartoe luisteren.
  • Schrijf het mailbestand naar een directory op de host en gebruik een script om de mailbestanden over te brengen naar de MTA

Misschien zal ik later de mail-bestanden methode onderzoeken, zie ook de links hieronder.

Stuur mail vanuit onze container naar port 25 de host waar ze MTA naartoe luisteren.

Om dit te doen moeten we de dokkersbrug gebruiken die Docker0 heet. Van de Docker dokters:

Standaard maakt en configureert de Docker server de docker0 van het hostsysteem, een netwerkinterface genaamd docker0, die een ethernet bridge apparaat is. Als je bij het starten van een container geen ander netwerk opgeeft, wordt de container aangesloten op de brug en al het verkeer dat van en naar de container gaat, stroomt over de brug naar de daemon Docker , die de routing voor de container verzorgt.

Docker configureert docker0 met een IP-adres, netmasker en IP-toewijzingsbereik. Containers die verbonden zijn met de standaard bridge krijgen IP-adressen toegewezen binnen dit bereik.'.

Dit betekent dat we in de container, wanneer we met telnet controleren of we verbinding kunnen maken met de MTA en een testmail kunnen versturen, dit niet kunnen doen:

telnet localhost 25
telnet 127.0.0.1 25

maar moet in plaats daarvan het IP-adres van de dokkersbrug gebruiken:

telnet <docker0 IP address> 25

U kunt het docker0 IP adres opvragen door bijvoorbeeld het docker0 IP adres te gebruiken:

ifconfig docker0

Test op lokale machine (Ubuntu 18.04 bureaublad)

Ik besloot om dit eerst op mijn lokale machine te testen in plaats van de productie te verstoren. Ervan uitgaande dat u de docker geïnstalleerd heeft en nee MTA, heeft u het volgende nodig om deze tests te doen:

  • Controleer de luisterpoorten en -toepassingen op port 25. Laat er een van lopen:
    sudo lsof -i -P -n | grep LISTEN | grep 25
    sudo netstat -tulpn | grep LISTEN | grep 25
  • Een werkcontainer Alpine is prima voor ons doel. Voer het in door te rennen:
    docker run -it alpine /bin/sh
  • Telnet in de dockercontainer. In de container, installeer telnet door te draaien:
    apk add busybox-extras
  • Een socket server die aanstaat port 25. Ik heb het volgende gebruikt:
    # 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()

Op mijn lokale machine, is het docker0 bridge IP adres van mijn lokale machine: 172.17.0.1.

Eerst controleren we of port 25 het beschikbaar is (zou moeten zijn):

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

Open een ander terminalvenster en start de echoende socket server:

sudo python3 listen_port.py

Hierop staat wat tekst:

socket created
bind done
start listening

port 25 Moet nu in gebruik zijn (door onze socket server):

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

Open een ander terminalvenster, start en ga de dockercontainer in:

docker run -it  alpine /bin/sh

Voer 'apk add busybox-extras' uit om telnet te installeren.

In het type dokwerkercontainer:

telnet 127.0.0.1 25

U krijgt het bericht wel te horen:

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

Gebruik nu het docker0 IP adres in plaats daarvan:

telnet 172.17.0.1 25

In de terminal vensters van de socket server zou je het bericht moeten zien:

Connection from  ('172.17.0.2', 38928)

In de dockercontainer typt u enkele woorden, drukt u op Enter, enz. Ze moeten worden herhaald in de docker container en je moet ze zien in het socket server terminal venster.

Houd er rekening mee dat er een sequentieprobleem kan zijn. de volgorde waarin u de Socket Server en de dockercontainer start. Ook wanneer u de socket server afsluit en opnieuw start kan er een fout optreden:

OSError: [Errno 98] Address already in use

Wacht in dit geval enige tijd alvorens opnieuw te beginnen.

Tot nu toe zo goed. We kunnen communiceren via de docker0 brug. Laten we naar de productieserver gaan.

Werktuig op productie (Debian Stretch + ISPConfig3)

Op mijn productieserver staat het docker0 bridge IP adres op mijn productieserver: 172.17.0.1

Voor ISPConfig3 van de MTA is Postfix. De configuratie staat in het bestand:

/etc/postfix/main.cf.

Er zijn twee regels in dit bestand die mogelijk moeten worden gewijzigd:

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

De regel met 'inet_interfaces' ziet er goed uit, luistert Postfix al naar alle interfaces, het is niet nodig om deze regel te veranderen. De regel met 'mynetworks' moet worden gewijzigd, deze moet ook de regel met 'mynetworks' bevatten:

  • Het IP-adres van de docker0 brug, en,
  • De IP-adressen van dockerafbeeldingen (die de mail zullen verzenden)

We hebben al het IP adres van de docker0 bridge, 172.17.0.1. Om de IP-adressen van onze dockercontainers te vinden, kunnen we ze inspecteren, eerst uitvoeren:

docker ps

om de container id te krijgen, gebruik dan inspecteren, bijvoorbeeld...:

docker inspect c2c44e9bea28

Dit zal veel informatie geven en ergens onderaan de lijn iets dergelijks:

"IPAddress": "172.20.0.2"

Om alle dockeradressen op te nemen gebruik ik het subnet 172.16.16.0.0/12, dit geeft een IP-bereik: 172.16.0.1 - 172.31.255.254. De mynetworks lijn wordt dan de mynetworks lijn:

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

Na het veranderen van deze regel in /etc/postfix/main.cf en het opslaan van het bestand moeten we het bestand opnieuw opstarten Postfix:

service postfix restart

Om te controleren of onze wijzigingen werken gebruiken we opnieuw telnet. Op het gastheertype:

telnet 127.0.0.1 25

Het zal iets als...:

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

Denk aan deze regel! Type stop om telnet te stoppen. Nu starten we een kleine dokterscontainer, zoals we deden op de lokale machine:

docker run -it  alpine /bin/sh

en opnieuw telnet toevoegen door 'apk add busybox-extras' uit te voeren. Natuurlijk:

telnet 127.0.0.1 25

zal falen:

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

Maar:

telnet 172.17.0.1 25

zou je nu de boodschap van de MTA, dezelfde boodschap moeten geven als op de host:

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

Als laatste test kunnen we vanuit de container een mail naar ons zelf sturen met behulp van sendmail. De verzendopties voor de verzendopties:

sendmail --help

Ergens daar is de lijn:

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

Dit betekent dat we het standaard adres kunnen veranderen in dat van docker0. Om een mail te versturen, typt u:

sendmail -S 172.17.0.1 yourname@yourdomain.yourextension

Begin met typen:

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

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

Typ Ctrl-D om de mail te versturen. U wilt misschien /var/log/mail.log voor uw mailbericht controleren. Dat heb ik gedaan en helaas was er een fout, de boodschap in mail.log:

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

Bij nader onderzoek bleek dat het van-adres geen volledig gekwalificeerd domein is. U kunt dit controleren door sendmail met de optie Verbose (-v) uit te voeren. Het bleek:

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

De oplossing is om de MAIL VAN Optie VERZENDER (-f) toe te voegen aan de opdrachtregel:

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

Begin opnieuw te typen:

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

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

Typ Ctrl-D om de mail te versturen. Nu is de mail verzonden en zou deze in uw mailbox (of spam) moeten verschijnen. Missie voltooid!

Een lokale SMTP server gebruiken voor het debuggen van bugs

Er is Python één liner die fungeert als een SMPT-server en nuttig kan zijn voor het debuggen, de -d vlag voegt debug-informatie toe:

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

Slotopmerkingen

Er is een probleem met de beschreven methode en dat wil zeggen, zie ook links:

Postfix moet worden gestart nadat de docker0 inteface naar voren is gebracht'.

Omdat de docker netwerk bridge nog niet klaar is bij het opstarten van het systeem, kan het zijn dat de docker netwerk bridge nog niet klaar is bij het opstarten van het systeem, omdat het niet kan binden aan dat adres.

Ik heb nog niet naar oplossingen gezocht, maar het is eng. Als de server opnieuw wordt opgestart moet ik niet vergeten om te controleren of de mail kan worden verzonden. Om deze reden kan de 'mail bestanden' methode een betere oplossing zijn omdat er geen afhankelijkheden zijn van docker0 bridge.

Links / credits

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

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.