Enviar e-mail do contêiner Docker com o Postfix do host

Estou executando um servidor Ubuntu 14.04 (Linux). Eu instalei e configurei Sufixo e OpenDKIM muito bem no servidor; posso enviar e-mails para mim mesmo com comandos como echo hi | sendmail root, e postfix / opendkim adicionará cabeçalhos como Message-Id, Date, e DKIM-Signature, encaminhar o e-mail para o meu endereço de E-mail pessoal, e tudo funciona muito bem.

Agora eu gostaria de criar um aplicativo que é executado em um Encaixe recipiente e pode enviar e-mails com a mesma facilidade. Em particular, não quero me preocupar em adicionar cabeçalhos como Message-Id, e eu não quero fazer muita configuração ou instalação de software dentro do próprio contêiner.

Qual é a melhor maneira de fazer isso?

Existe alguma maneira de deixar o contêiner executar o sendmail exectuable no anfitrião?

Tentei fazer uma conexão com o Postfix a partir de um contêiner usando o protocolo SMTP na porta 25, mas o Postfix parece tratar as mensagens recebidas dessa maneira de maneira diferente; acho que não adicionou nenhum cabeçalho, então a mensagem foi totalmente rejeitada como spam pelo gmail (nem era bom o suficiente para ser colocado na minha pasta de Spam).

Aqui o conteúdo 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

Você tem que apontar inet_interfaces para docker bridge (docker0) na configuração postfix localizada em set /etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Mais detalhes de trabalho internos em envio-email-from-docker-through-postfix-installed-on-the-host

Porque você tem uma solução de trabalho, aqui vou tentar explicar o comportamento diferente quando você telnet para postfix (SMTP) e quando você usa sendmail (não-SMTP).

Para sua informação, o OpenDKIM será invocado pelo postfix com Mecanismo Milter. Você pode obter algumas informações sobre como a implementação do milter no postfix por meio disso documentação oficial. Aqui o diagrama do gancho milter no postfix.

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

Você pode ver que sendmail-way (não-SMTP) e telnet-way (SMTP) tem ordem de processamento diferente.

  • O e-mail não SMTP será processado pela limpeza antes de injetado no milter. Daemon de limpeza foi responsável por adicionar cabeçalhos ausentes: (Ressentir -) De:, Para:, Message-Id:, e Data:. Portanto, seu e-mail terá cabeçalho completo quando injetado no opendkim milter, mesmo o e-mail original tinha cabeçalho incompleto.

  • O e-mail SMTP será injetado no opendkim milter antes que qualquer processamento de limpeza ocorra. Portanto, se o seu e-mail original tiver um cabeçalho incompleto, o opendkim poderá se recusar a assinar o e-mail. O De: o cabeçalho era obrigatório (ver RFC 6376) e se um e-mail não tiver, o OpenDKIM se recusará a assinar o e-mail e lhe dará um aviso

      can't determine message sender; accepting

Como nunca uso o docker, não sei qual limitação no sendmail / pickup dentro do contêiner A. Acho que a solução alternativa de David Grayson foi segura o suficiente para garantir que o OpenDKIM assinasse a mensagem.

Decidi que a maneira como o contêiner enviará o e-mail é gravá-lo em um arquivo em um diretório específico, que será acessível a partir do contêiner e do host como um "volume"do Docker.

Eu fiz um script de shell chamado mailsender.sh que lê e-mails de um diretório especificado, os envia para o sendmail e os exclui:

#!/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 então eu criei um arquivo chamado /etc/init/mailsender.conf para transformar este script em um daemon:

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

Posso iniciar o serviço com start mailsender e pare com isso stop mailsender. Eu posso olhar para seus logs em /var/log/upstart/mailsender.log, e claro que posso monitorá-lo usando o arquivo PID.

Você precisa criar o /var/mailsend diretório e, em seguida, torná-lo acessível a partir do contêiner Docker, adicionando o argumento -v /var/mailsend:/var/mailsend para o seu docker run comando.

Esta é uma meia resposta, ou pelo menos uma metade testada, já que atualmente estou trabalhando com o mesmo problema. Espero que alguém possa ajudar a concretizar o que perdi.

A resposta do OP (David Grayson) soa para mim como uma reinvenção do postdrop mail-spool, mas usar esse mail spool soa como uma abordagem promissora, então aqui é onde eu cheguei.

A interface de compatibilidade /usr / bin / sendmail fornecida pelo postfix passa o correio para o postdrop, que é o sgid postdrop, permitindo que ele armazene o correio na fila do maildrop em / var / spool/postfix / maildrop. Isso deve ocorrer no contêiner do docker. O resto do postfix espero que não precise ser executado no contêiner.

Então, estou montando o host / var / spool / postfix / maildrop e/var/spool/postfix / public. Posso receber o correio entregue em / var / spool / postfix / maildrop no ambiente host, pois montei o diretório da fila do maildrop. Porque eu tenho montado /var/spool/postfix/public, maildrop pode sinalizar pickup para coletar o correio da fila. Infelizmente, os uids e gids envolvidos, a menos que eu cuide disso, o que significa que o captador no diretório host não pode ler os arquivos spool e, pior, a instalação do postfix atrapalha as permissões no diretório maildrop no ambiente host.

Ainda assim, isso 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

Embora funcione, não estou muito feliz com a codificação difícil dos uids e gids. Isso significa que o mesmo contêiner não pode ser contado para executar o mesmo em todos os lugares. Eu acho que se em vez de montar o volume do host eu montá-lo a partir de um contêiner que executa o postfix, ele nunca entrará em conflito, e eu só preciso de uma instalação do postfix para receber e-mails de muitos contêineres. Eu definiria esses uids e gids em uma imagem base da qual todos os meus contêineres herdam.

Eu me pergunto se esta é realmente uma boa abordagem. Com uma configuração de E-mail tão simples e nenhum daemon em uso no contêiner para tentar novamente a entrega, um MTA local mais simples como o msmtp pode ser mais apropriado. Ele entregaria via TCP para um relé no mesmo host, onde o spooling ocorreria.

As preocupações com a abordagem msmtp incluem:

  • mais possibilidade de perder e-mails se o relé smtp para o qual ele envia não estiver disponível. Se for um relé no mesmo host, a chance de problemas de rede é baixa, mas eu teria que ter cuidado com a forma como reiniciei o contêiner de relé.
  • desempenho?
  • Se uma grande explosão de correio passar, o correio começa a cair?

Em geral, a abordagem de spool postfix compartilhada parece mais provável que seja uma configuração frágil para configurar, mas menos provável de falhar em tempo de execução (retransmissão indisponível, então o correio caiu).

Por favor, publique o cabeçalho do seu e-mail (aquele que erroneamente identificado como spam pelo GMAIL)

O e-mail que eu estava tentando enviar tinha apenas um cabeçalho para, cabeçalho assunto e um corpo de uma linha. Não tenho certeza de como dizer quais cabeçalhos ele tinha depois que o Postfix o executou nos milters, você sabe como? Aqui está a saída em / var / log / syslog mostrando como ele foi processado pelo Postfix e recusado pelo Gmail: log.txt · GitHub