Enviar correo desde un contenedor Docker con el Postfix del host

Estoy ejecutando un servidor Ubuntu 14.04 (Linux). He instalado y configurado Postfijo y OpenDKIM muy bien en el servidor; puedo enviarme correos electrónicos a mí mismo con comandos como echo hi | sendmail root, y postfix / opendkim agregará encabezados como Message-Id, Date, y DKIM-Signature, reenvío el correo electrónico a mi dirección de correo electrónico personal y todo funciona de maravilla.

Ahora me gustaría crear una aplicación que se ejecute en un Ventana acoplable contenedor y puede 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 en sí.

¿Cuál es la mejor manera de hacerlo?

¿Hay alguna forma de dejar que el contenedor ejecute el sendmail ejecutable en el host?

Intenté 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 Gmail rechazó el mensaje como spam (ni siquiera era lo suficientemente bueno como para colocarlo en mi carpeta de correo no deseado).

Aquí el contenido de 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 6Sep 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 nobodySep 28 23:35:52 dantooine spamd[3175]: spamd: processing message (unknown) for someone:65534Sep 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=noSep 28 23:35:52 dantooine opendkim[3179]: DD457889B: can't determine message sender; acceptingSep 28 23:35:53 dantooine postfix/qmgr[3664]: DD457889B: from=<whoever@example.com>, 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=<someone@gmail.com>, 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=<20140928233553.254E688A0@myserver.example.com>Sep 28 23:35:53 dantooine postfix/bounce[4330]: DD457889B: sender non-delivery notification: 254E688A0Sep 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: removedSep 28 23:35:53 dantooine postfix/virtual[4331]: 254E688A0: to=<whoever@example.com>, 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

Tienes que señalar inet_interfaces a docker Bridge (docker0) en la configuración de postfix ubicada en set /etc/postfix/main.cf

inet_interfaces = <docker0_ip>

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

Debido a que tiene una solución que funciona, aquí trataré de explicar el comportamiento diferente cuando realiza telnet a postfix (SMTP) y cuando usa sendmail (no SMTP).

Para su información, OpenDKIM será invocado por postfix con Mecanismo de ordeño. Puede obtener información sobre la implementación de milter en postfix a través de esto documentación oficial. Aquí el diagrama del gancho de milter en postfix.

             SMTP-only       non-SMTP             filters         filters                ^ |            ^ |                | v            | |Network ->  smtpd(8)           | |                       \       | VNetwork ->  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 inyectarse en milter. Demonio de limpieza fue responsable de agregar encabezados faltantes: (Reenviar-) De:, A:, Id de mensaje:, 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 lleve a cabo cualquier procesamiento 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 De: el encabezado era obligatorio (ver 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, no se qué limitación hay en sendmail / pickup dentro del contenedor a. Creo que la solución alternativa de David Grayson fue lo suficientemente segura como para garantizar que OpenDKIM recibiera el mensaje.

Decidí que la forma en que el contenedor enviará el correo es escribiéndolo en un archivo en un directorio en 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 los correos de un directorio especificado, 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=$1if [ \! \( -d "$DIR" -a -w "$DIR" \) ]then  echo "Invalid directory given: $DIR"  exit 1fiecho "`date`: Starting mailsender on directory $DIR"cd $DIRwhile :do  for file in `find . -maxdepth 1 -type f`  do    echo "`date`: Sending $file"    sendmail -t < $file    rm $file  done  sleep 1done

Ubuntu usa upstart, así que creé un archivo llamado /etc/init/mailsender.conf para convertir este script en un demonio:

description "sends mails from directory"start on stopped rc RUNLEVEL=[2345]stop on runlevel[!2345]respawnexec start-stop-daemon --start --make-pidfile --pidfile /var/run/mailsender.pid --exec/path/to/mailsender.sh /var/mailsend

Puedo iniciar el servicio con start mailsender y para con stop mailsender. Puedo mirar sus registros en /var/log/upstart/mailsender.log y, por supuesto, puedo monitorearlo usando el archivo PID.

Necesitas crear el /var/mailsend directorio y luego hacerlo accesible desde el contenedor de la ventana acoplable agregando el argumento -v /var/mailsend:/var/mailsend a su docker run comando.

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

La respuesta del OP (David Grayson) me suena como una reinvención de la cola de correo postdrop, pero usar esa cola de correo suena como un enfoque prometedor, así que aquí es a 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 Docker. Es de esperar que el resto de postfix no tenga que ejecutarse en el contenedor.

Entonces, estoy montando en el host /var/spool/postfix /maildrop y/var/spool/postfix / public. Puedo hacer que el correo se entregue 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, maildrop puede señalar pickup para recoger el correo de la cola. Desafortunadamente, los UID y GID involucrados a menos que me encargue de eso, lo que significa que los usuarios en el directorio host no pueden leer los archivos de cola y, lo que es peor, la instalación de postfix estropea 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 myemail@example.com$ 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 muy contento con la codificación rígida de los UID y GID. Esto significa que no se puede contar el mismo contenedor para que se ejecute igual en todas partes. Sin embargo, me imagino que si en lugar de montar el volumen desde el host lo monto desde un contenedor que ejecuta postfix, entonces nunca entrará en conflicto, y solo necesito una instalación de postfix para enviar correo desde muchos contenedores. Establecería esos UID y GID 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 sin un 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 de espera.

Las preocupaciones con el enfoque msmtp incluyen:

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

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

Por favor, publique el encabezado de su correo electrónico (el que GMAIL identificó erróneamente como spam)

El correo electrónico que estaba tratando de enviar solo tenía un encabezado Para, un encabezado Asunto y un cuerpo de una línea. No estoy seguro de cómo saber qué encabezados tenía después de que Postfix lo ejecutó a través de los milters, ¿sabes cómo? Aquí está el resultado en /var / log / syslog que muestra cómo Postfix lo procesó y Gmail lo rechazó: log.txt · GitHub