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

Envío de correo desde un Docker contenedor utilizando ISPConfig3 hosts Postfix MTA

Utilice la dirección IP de puente docker0 para conectarse a Postfix

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

En el sinfín de problemas que encuentras y resuelves cuando empiezas a usar una nueva tecnología, me enfrentaba a uno nuevo: cómo enviar correo electrónico desde mi Python Docker aplicación usando el ISPConfig host MTA (Mail transfer Agent). Encontré que hay dos maneras de hacer esto:

  • Enviar correo desde nuestro contenedor al port 25 host donde se MTA está escuchando.
  • Escriba el archivo de correo en un directorio del host y utilice un script para transferir los archivos de correo a la carpeta MTA

Tal vez más adelante investigaré el método de archivos de correo, ver también los enlaces de abajo.

Enviar correo desde nuestro contenedor al port 25 host donde se MTA está escuchando.

Para ello debemos utilizar el Docker puente llamado docker0. De los Docker médicos:

Por defecto, el Docker servidor crea y configura el docker0 del sistema host una interfaz de red llamada docker0, que es un dispositivo puente ethernet. Si no se especifica una red diferente al iniciar un contenedor, el contenedor se conecta al puente y todo el tráfico que viene y va al contenedor fluye por el puente hacia el , Docker daemonque se encarga del enrutamiento en nombre del contenedor.

Docker configura el docker0 con una dirección IP, netmask, y un rango de asignación de IP. A los contenedores que están conectados al puente por defecto se les asignan direcciones IP dentro de este rango.''.

Esto significa que en el contenedor, cuando se utiliza telnet para comprobar si podemos conectarnos a la MTA y podemos enviar un correo de prueba, no podemos hacerlo:

telnet localhost 25
telnet 127.0.0.1 25

pero en su lugar debe usar la dirección IP del puente de conexión:

telnet <docker0 IP address> 25

Puede obtener la dirección IP del docker0, por ejemplo, ejecutando:

ifconfig docker0

Prueba en máquina local (Ubuntu 18.04 desktop)

Decidí probar esto primero en mi máquina local en lugar de perder el tiempo con la producción. Asumiendo que usted ha Docker instalado y no MTAhay, usted necesita lo siguiente para hacer estas pruebas:

  • Compruebe los puertos de escucha y las aplicaciones en busca de port 25... Ejecute una de ellas:
    sudo lsof -i -P -n | grep LISTEN | grep 25
    sudo netstat -tulpn | grep LISTEN | grep 25
  • Un contenedor de trabajo Alpine está bien para nuestro propósito. Introdúzcalo corriendo:
    docker run -it alpine /bin/sh
  • Telnet en el Docker contenedor. Cuando esté en el contenedor, instale telnet ejecutando:
    apk add busybox-extras
  • Una socket server escucha en port 25... Usé lo siguiente:
    # 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()

En mi máquina local, la dirección IP del docker0 bridge es: 172.17.0.1.

Primero, comprobamos si port 25 está disponible (debería estarlo):

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

Abra otra ventana de terminal, inicie el eco socket server..:

sudo python3 listen_port.py

Esto imprime algo de texto:

socket created
bind done
start listening

Ahora port 25 debe estar en uso (por nuestro socket server):

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

Abra otra ventana de terminal, inicie e introduzca el Docker contenedor:

docker run -it  alpine /bin/sh

Ejecute'apk add busybox-extras' para instalar telnet.

En el tipo de Docker contenedor:

telnet 127.0.0.1 25

Recibirá el mensaje:

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

Ahora usa la dirección IP del docker0 en su lugar:

telnet 172.17.0.1 25

En las ventanas de los terminales de los socket server terminales debería ver el mensaje:

Connection from  ('172.17.0.2', 38928)

En el Docker contenedor, escriba algunas palabras, presione Enter, etc. Deberían resonar en el Docker contenedor y debería verlos en la ventana del socket server terminal.

Tenga en cuenta que puede haber un problema de secuencia, el orden en el que se inicia el socket server y el Docker contenedor. Además, cuando termine el socket server y lo inicie de nuevo, es posible que se produzca un error:

OSError: [Errno 98] Address already in use

En este caso, espere algún tiempo antes de comenzar de nuevo.

Hasta ahora todo bien. Podemos comunicarnos a través del puente docker0. Pasemos al servidor de producción.

Implementar en producción (Debian Tramo + ISPConfig3)

En mi servidor de producción, la dirección IP del docker0 bridge es: 172.17.0.1

Para ISPConfig3 la MTA es Postfix. La configuración está en el archivo:

/etc/postfix/main.cf

Hay dos líneas en este archivo que pueden necesitar cambios:

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

La línea con ' 'inet_interfacesse ve bien, Postfix ya escucha todas las interfaces, no hay necesidad de cambiar esta línea. La línea con ' 'mynetworksdebe ser cambiada, debe incluir:

  • La dirección IP del puente docker0, y,
  • Las direcciones IP de las Docker imágenes (que serán las que envíen el correo)

Ya tenemos la dirección IP del puente docker0, 172.17.0.1. Para encontrar las direcciones IP de nuestros Docker contenedores podemos inspeccionarlos, primero ejecute:

docker ps

para obtener la identificación del contenedor, luego use inspect, e.g..:

docker inspect c2c44e9bea28

Esto dará un montón de información y en algún lugar en la parte inferior de la línea algo como:

"IPAddress": "172.20.0.2"

Para incluir todas las Docker direcciones que uso la subred 172.16.0.0.0/12, esto da un rango de IP: 172.16.0.1 - 172.31.255.254. La mynetworks línea se convierte entonces en:

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

Después de cambiar esta línea en /etc/postfix/main.cf y guardar el archivo debemos reiniciar Postfix:

service postfix restart

Para comprobar si nuestros cambios están funcionando utilizamos de nuevo telnet. En el tipo de host:

telnet 127.0.0.1 25

Imprimirá algo como:

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

¡Recuerda esta línea! Escriba"quit to stop telnet" (salir para parar) Ahora empezamos un pequeño Docker contenedor, como hicimos en la máquina local:

docker run -it  alpine /bin/sh

y de nuevo añadir telnet ejecutando 'apk add busybox-extras'. Por supuesto:

telnet 127.0.0.1 25

fallará:

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

Pero:

telnet 172.17.0.1 25

ahora debería darle el mensaje de la MTA, el mismo mensaje que en el host:

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

Como prueba final, podemos enviarnos un correo a nosotros mismos desde el contenedor usando sendmail. Obtenga las sendmail opciones:

sendmail --help

En algún lugar está la línea:

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

Esto significa que podemos cambiar la dirección por defecto por la de docker0. Para enviar un correo, escriba:

sendmail -S 172.17.0.1 yourname@yourdomain.yourextension

Empieza a escribir:

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

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

Escriba Ctrl-D para enviar el correo. puede que desee inspeccionar /var/log/mail.log para su mensaje de correo. Lo hice y desafortunadamente hubo un error, el mensaje en mail.log:

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

Además, se comprobó que la dirección del remitente no era un dominio totalmente cualificado. Puede comprobar esto ejecutando sendmail la opción Verbose (-v). Apareció:

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

La solución es añadir la opción MAIL FROM SENDER (-f) en la línea de comandos:

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

Empieza a escribir de nuevo:

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

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

Escriba Ctrl-D para enviar el correo. Ahora el correo se envía y debería aparecer en su buzón (o spam). ¡Misión completada!

Uso de un servidor local SMTP para la depuración

Hay un Python liner que actúa como un servidor SMPT y puede ser útil para la depuración, el indicador -d añade información de depuración:

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

Observaciones finales

Existe un problema con el método descrito, es decir, véase también los enlaces:

Postfix debe iniciarse después de que se haya sacado el inteface docker0".

Debido a que el puente de Docker red puede no estar listo en el arranque del sistema, postfix puede fallar al no poder enlazarse a esa dirección.

Todavía no he buscado soluciones, pero me da miedo. Si se reinicia el servidor no debo olvidarme de comprobar si se puede enviar correo. Por esta razón el método'mail files' puede ser una mejor solución ya que no hay dependencias en docker0 bridge.

Enlaces / créditos

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

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.