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

Envoi de courrier à partir d'un Docker conteneur utilisant ISPConfig3 hôtes Postfix MTA

Utilisez l'adresse IP du pont docker0 pour vous connecter à Postfix

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

Dans le nombre infini de problèmes que vous rencontrez, et que vous résolvez, lorsque j'ai commencé à utiliser une nouvelle technologie, j'étais confronté à une nouvelle : comment envoyer des e-mails depuis mon Python Docker application en utilisant l' ISPConfig hôte MTA (Mail transfer Agent). J'ai trouvé qu'il y a deux façons de le faire :

  • Envoyer le courrier de notre conteneur à l'hôte de l'hôte sur lequel il est en MTA train d'écouter
  • Ecrivez le fichier de messagerie dans un répertoire sur l'hôte et utilisez un script pour transférer les fichiers de messagerie vers le répertoire MTA

Peut-être que plus tard j'étudierai la méthode des fichiers de courrier électronique, voir aussi les liens ci-dessous.

Envoyer le courrier de notre conteneur à l'hôte de l'hôte sur lequel il est en MTA train d'écouter

Pour ce faire, nous devons utiliser le Docker pont appelé docker0. De la part des Docker docteurs :

Par défaut, le Docker serveur crée et configure le docker0 du système hôte une interface réseau appelée docker0, qui est un périphérique pont Ethernet. Si vous ne spécifiez pas un réseau différent au démarrage d'un conteneur, le conteneur est connecté au pont et tout le trafic en provenance et à destination du conteneur passe par le pont vers le routeur, Docker daemonqui gère l'acheminement au nom du conteneur.

Docker configure docker0 avec une adresse IP et une plage d'attribution IP netmask. Les conteneurs qui sont connectés au pont par défaut se voient attribuer des adresses IP dans cette plage.''.

Cela signifie que dans le conteneur, lorsque nous utilisons telnet pour vérifier si nous pouvons nous connecter à la MTA et peut envoyer un mail de test, nous ne pouvons pas faire :

telnet localhost 25
telnet 127.0.0.1 25

mais doit plutôt utiliser l'adresse IP du pont docker :

telnet <docker0 IP address> 25

Vous pouvez obtenir l'adresse IP du docker0 par exemple en exécutant :

ifconfig docker0

Test sur machine locale (Ubuntu 18.04 desktop)

J'ai décidé de le tester d'abord sur ma machine locale au lieu de m'amuser sur la production. En supposant que vous avez Docker installé et non MTA, vous avez besoin des éléments suivants pour effectuer ces tests :

  • Vérifiez les ports d'écoute et les applications pour port 25. Exécutez l'un des :
    sudo lsof -i -P -n | grep LISTEN | grep 25
    sudo netstat -tulpn | grep LISTEN | grep 25
  • Un conteneur de travail, c' Alpine est parfait pour notre usage. Saisissez-le en cours d'exécution :
    docker run -it alpine /bin/sh
  • Telnet dans le Docker conteneur. Une fois dans le conteneur, installez-le telnet en cours d'exécution :
    apk add busybox-extras
  • Une socket server écoute sur port 25. J'ai utilisé ce qui suit :
    # 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()

Sur ma machine locale, l'adresse IP du pont docker0 est : 172.17.0.1.

Tout d'abord, nous vérifions si port 25 elle est disponible (elle devrait l'être) :

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

Ouvrir une autre fenêtre de terminal, démarrer l'écho socket server:

sudo python3 listen_port.py

Ceci imprime du texte :

socket created
bind done
start listening

Maintenant port 25 doit être utilisé (par notre socket server) :

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

Ouvrir une autre fenêtre de terminal, démarrer et entrer dans le Docker conteneur :

docker run -it  alpine /bin/sh

Exécutez'apk add busybox-extras' pour installer telnet.

Dans le type de Docker conteneur :

telnet 127.0.0.1 25

Vous obtiendrez le message :

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

Utilisez maintenant l'adresse IP docker0 à la place :

telnet 172.17.0.1 25

Dans les fenêtres du terminal, socket server vous devriez voir le message :

Connection from  ('172.17.0.2', 38928)

Dans le Docker conteneur, tapez quelques mots, appuyez sur Entrée, etc. Ils doivent être répétés dans le Docker conteneur et vous devez les voir dans la fenêtre du socket server terminal.

Sachez qu'il peut y avoir un problème de séquence. l'ordre dans lequel vous démarrez le Docker conteneur socket server et le conteneur. De même, lorsque vous terminez socket server et redémarrez l'application, il se peut que vous obteniez une erreur :

OSError: [Errno 98] Address already in use

Dans ce cas, attendez un certain temps avant de le redémarrer.

Pour l'instant, tout va bien. Nous pouvons communiquer via le pont docker0. Allons au serveur de production.

Mise en œuvre en cours de production (Debian volet + ISPConfig3)

Sur mon serveur de production, l'adresse IP du pont docker0 est : 172.17.0.1

Pour ISPConfig3 c' MTA est Postfix. La configuration se trouve dans le fichier :

/etc/postfix/main.cf

Il y a deux lignes dans ce fichier qui peuvent nécessiter des modifications :

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

La ligne avec ' 'inet_interfacesa l'air bien, écoute Postfix déjà toutes les interfaces, pas besoin de changer cette ligne. La ligne avec ' 'mynetworksdoit être changée, elle doit inclure :

  • L'adresse IP du pont docker0, et,
  • Les adresses IP des Docker images (qui enverront du courrier)

Nous avons déjà l'adresse IP du pont docker0, 172.17.0.1. Pour trouver les adresses IP de nos Docker conteneurs, nous pouvons les inspecter dans un premier temps :

docker ps

pour obtenir le numéro d'identification du conteneur, puis utilisez inspecter, par exemple :

docker inspect c2c44e9bea28

Cela donnera beaucoup d'informations et quelque part en bas de la ligne quelque chose comme :

"IPAddress": "172.20.0.2"

Pour inclure toutes les Docker adresses, j'utilise le sous-réseau 172.16.0.0.0/12, cela donne une plage IP : 172.16.0.1 - 172.31.255.254. La mynetworks ligne devient alors :

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

Après avoir modifié cette ligne dans /etc/postfix/main.cf et enregistré le fichier, nous devons redémarrer Postfix:

service postfix restart

Pour vérifier si nos changements fonctionnent, nous utilisons telnetà nouveau . Sur le type d'hôte :

telnet 127.0.0.1 25

Il imprimera quelque chose comme :

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

Souviens-toi de cette ligne ! Tapez quit pour arrêter telnet. Maintenant, nous commençons un petit Docker conteneur, comme nous l'avons fait sur la machine locale :

docker run -it  alpine /bin/sh

et ajoutez telnet à nouveau en lançant'apk add busybox-extras'. Bien sûr que oui :

telnet 127.0.0.1 25

échouera :

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

Mais :

telnet 172.17.0.1 25

devrait maintenant vous donner le message du MTA, le même message que sur l'hôte :

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

En guise de test final, nous pouvons nous envoyer un mail à nous-mêmes à partir du conteneur à l'aide de sendmail. Obtenez les sendmail options :

sendmail --help

Quelque part, il y a la limite :

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

Cela signifie que nous pouvons changer l'adresse par défaut à celle de docker0. Pour envoyer un mail, tapez :

sendmail -S 172.17.0.1 yourname@yourdomain.yourextension

Commencez à taper :

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

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

Tapez Ctrl-D pour envoyer le message. Vous pouvez consulter /var/log/mail.log pour trouver votre message. Je l'ai fait et malheureusement il y a eu une erreur, le message dans mail.log :

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

En vérifiant plus loin, il est apparu que l'adresse de départ n'était pas un domaine entièrement qualifié. Vous pouvez le vérifier en utilisant sendmail l'option Verbose (-v). Ça s'est vu :

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

La solution est d'ajouter l'option MAIL FROM SENDER (-f) sur la ligne de commande :

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

Recommencez à taper :

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

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

Tapez Ctrl-D pour envoyer le mail. Maintenant le mail est envoyé et devrait apparaître dans votre boîte aux lettres (ou spam). Mission accomplie !

Utilisation d'un serveur local SMTP pour le débogage

Il y a Python un liner qui agit comme un serveur SMPT et peut être utile pour le débogage, l'option -d ajoute des informations de débogage :

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

Remarques finales

Il y a un problème avec la méthode décrite, à savoir, voir aussi les liens :

Postfix doit être démarré après que l'interface docker0 ait été mise en place'.

Parce que le pont Docker réseau n'est peut-être pas encore prêt au démarrage du système, postfix peut ne pas démarrer car il ne peut pas se lier à cette adresse.

Je n'ai pas encore cherché de solutions, mais c'est effrayant. Si le serveur est redémarré, je ne dois pas oublier de vérifier si le courrier peut être envoyé. Pour cette raison, la méthode'mail files' peut être une meilleure solution car il n'y a pas de dépendance sur le pont docker0.

Liens / crédits

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

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.