Enviar correo desde el contenedor Docker con Postfix del host

18

Estoy ejecutando un servidor Ubuntu 14.04 (Linux). He instalado y configurado Postfix y OpenDKIM muy bien en el servidor; Puedo enviar mensajes de correo electrónico a mí mismo con comandos como echo hi | sendmail root, y postfix / opendkim a añadir encabezados tales como Message-Id, Datey DKIM-Signature, reenviar el correo electrónico a mi dirección de correo electrónico personal, y todo funciona muy bien.

Ahora me gustaría crear una aplicación que se ejecute en un contenedor Docker y pueda enviar correos electrónicos con la misma facilidad. En particular, no quiero preocuparme por agregar encabezados como Message-Id, y no quiero hacer mucha configuración o instalación de software dentro del contenedor.

¿Cuál es la mejor manera de hacer esto?

¿Hay alguna forma de permitir que el contenedor ejecute lo sendmailejecutable en el host?

Traté de hacer una conexión a Postfix desde un contenedor usando el protocolo SMTP en el puerto 25, pero Postfix parece tratar los mensajes recibidos de esa manera de manera diferente; Creo que no agregó ningún encabezado, por lo que el mensaje fue rechazado directamente como spam por gmail (ni siquiera era lo suficientemente bueno como para colocarlo en mi carpeta de Spam).

Aquí el contenido del maillog

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
fuente
Por favor, publique el encabezado de su correo electrónico (el que GMAIL identificó erróneamente como spam)
masegaloeh
El correo electrónico que intentaba enviar solo tenía un Toencabezado, Subjectencabezado y un cuerpo de una línea. No estoy seguro de cómo saber qué encabezados tenía después de que Postfix lo revisara, ¿sabes cómo? Aquí está el resultado en / var / log / syslog que muestra cómo fue procesado por Postfix y rechazado por Gmail: gist.github.com/DavidEGrayson/fbf65c8290c049a1f262
David Grayson

Respuestas:

8

Debido a que tiene una solución que funciona, aquí trataré de explicar diferentes comportamientos cuando hace telnet para postfix (SMTP) y cuando usa sendmail (no SMTP).

FYI, OpenDKIM será invocado por postfix con mecanismo Milter . Puede obtener información sobre la implementación más fácil en postfix a través de esta documentación oficial . Aquí el diagrama del gancho milter en postfix.

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

Puede ver que sendmail-way (no SMTP) y telnet-way (SMTP) tienen un orden de procesamiento diferente.

  • El correo electrónico que no sea SMTP se procesará mediante limpieza antes de inyectarlo en milter. Demonio de la limpieza era responsable de añadir cabeceras faltantes: (resentimiento) De :, Para :, Mensaje-Id :, y Fecha: . Por lo tanto, su correo electrónico tendrá un encabezado completo cuando se inyecte en OpenDKIM milter, incluso el correo electrónico original tenía un encabezado incompleto.

  • El correo electrónico SMTP se inyectará en OpenDKIM milter antes de que se realice el proceso de limpieza. Por lo tanto, si su correo electrónico original tenía un encabezado incompleto, opendkim puede negarse a firmar el correo electrónico. El encabezado De: era obligatorio (consulte RFC 6376 ) y si un correo electrónico no lo tiene, OpenDKIM se negará a firmar el correo electrónico y le dará una advertencia.

    can't determine message sender; accepting
    

Como nunca uso docker, entonces no sé qué limitación en sendmail / pickup dentro de un contenedor. Creo que la solución alternativa de David Grayson fue lo suficientemente segura como para garantizar que OpenDKIM firme el mensaje.

masegaloeh
fuente
Eso fue esclarecedor; gracias. Desafortunadamente, todavía no veo ninguna solución mejor que mi solución actual (descrita en mi respuesta).
David Grayson
La razón obvia fue arreglar la aplicación para agregar From:encabezado en su correo electrónico :)
masegaloeh
Pero también tendría que agregar cosas de las Message-Idque no sé mucho y probablemente me equivocaría ... parece más fácil dejar que el demonio de la limpieza se encargue de eso.
David Grayson
En realidad, el ID de mensaje no era obligatorio, como decía RFC 6376 . Por defecto, el encabezado obligatorio era solo el Fromencabezado. Pero, si desea generar su propia ID de mensaje, puede usar una recomendación como este Borrador de IETF
masegaloeh
6

Tienes que señalar el inet_interfacespuente acoplable (docker0 ) en la configuración de postfix ubicada en set/etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Más detalles de trabajo internos en el envío de correo electrónico desde docker a través de postfix-instaló-en-el-host

Satish Gandham
fuente
Gracias por el enlace! La parte pertinente para mí fue la de añadir algo así como 172.17.0.0/16que mynetworksen /etc/postfix/main.cfy service postfix restart.
JSchirrmacher
5

Esta es una respuesta a medias, o al menos a medias, ya que actualmente estoy trabajando en el mismo problema. Espero que alguien pueda ayudar a desarrollar lo que me he perdido.

La respuesta del OP (David Grayson) me suena como una reinvención de la bobina de correo postdrop, pero usar esa bobina de correo suena como un enfoque prometedor, así que aquí es donde he llegado.

La interfaz de compatibilidad / usr / bin / sendmail proporcionada por postfix pasa el correo a postdrop, que es sgid postdrop, lo que le permite almacenar el correo en la cola de maildrop en / var / spool / postfix / maildrop. Esto debería ocurrir en el contenedor acoplable. Es de esperar que el resto de postfix no tenga que ejecutarse en el contenedor.

Entonces, estoy montando el host / var / spool / postfix / maildrop y / var / spool / postfix / public. Puedo enviar el correo a / var / spool / postfix / maildrop en el entorno host, ya que he montado el directorio de cola de maildrop. Porque he montado /var/spool/postfix/public, maildroppuedo señalarpickup que recoja el correo de la cola. Desafortunadamente, los uids y gids involucrados a menos que me ocupe de eso, lo que significa que la recolección en el directorio host no puede leer los archivos de spool, y peor aún, la instalación de postfix desordena los permisos en el directorio maildrop en el entorno host.

Aún así, esto parece funcionar:

$ 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

Si bien funciona, no estoy terriblemente feliz con la codificación dura de los uids y gids. Esto significa que no se puede contar el mismo contenedor para ejecutarlo en todas partes. Sin embargo, me imagino que si en lugar de montar el volumen desde el host lo monte desde un contenedor que ejecuta postfix, entonces nunca entrará en conflicto, y solo necesito una instalación de postfix para obtener el correo de muchos contenedores. Establecería esos uids y gids en una imagen base de la que heredan todos mis contenedores.

Sin embargo, me pregunto si este es realmente un buen enfoque. Con una configuración de correo tan simple, y ningún demonio en uso en el contenedor para volver a intentar la entrega, un MTA local más simple como msmtp podría ser más apropiado. Se entregaría a través de TCP a un relé en el mismo host, donde se produciría la cola.

Las preocupaciones con el enfoque msmtp incluyen:

  • más posibilidad de perder correo si el relé smtp al que envía no está disponible. Si se trata de un relé en el mismo host, entonces la posibilidad de problemas de red es baja, pero tendría que tener cuidado con cómo reinicié el contenedor del relé.
  • ¿actuación?
  • Si pasa una gran ráfaga de correo, ¿comienza a caerse el correo?

En general, el enfoque de spool de postfix compartido parece más probable que sea una configuración frágil para configurar, pero es menos probable que falle en el tiempo de ejecución (retransmisión no disponible, por lo que el correo se descartó).

mc0e
fuente
4

Decidí que la forma en que el contenedor enviará correo es escribiéndolo en un archivo en un directorio particular, al que se podrá acceder tanto desde el contenedor como desde el host como un "volumen" de Docker.

Hice un script de shell llamado mailsender.sh que lee correos de un directorio específico, los envía a sendmail y luego los elimina:

#!/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 usa el sistema de arranque, así que creé un archivo llamado /etc/init/mailsender.confpara convertir este script en un demonio:

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

Puedo iniciar el servicio con start mailsendery detenerlo stop mailsender. Puedo ver sus registros /var/log/upstart/mailsender.logy, por supuesto, puedo monitorearlo usando el archivo PID.

Debe crear el /var/mailsenddirectorio y luego hacerlo accesible desde el contenedor Docker agregando el argumento -v /var/mailsend:/var/mailsenda su docker runcomando.

David Grayson
fuente
¿Quizás algo como mini_sendmail será útil? Se utiliza en contenedores, como un puente entre la aplicación aislada de contenedores y el demonio del servidor sendmail en el sistema host de contenedores. cyberciti.biz/tips/… acme.com/software/mini_sendmail
Mikl
Si está enviando el correo electrónico a Postfix a través de SMTP, no creo que Postfix lo limpie. Tal vez si tuviera un MTA que fuera más configurable (o descubrimos cómo configurar Postfix mejor) funcionaría.
David Grayson