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

13

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

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

Qual é a melhor maneira de fazer isso?

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

Eu 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; Eu acho que não adicionou nenhum cabeçalho, então a mensagem foi completamente rejeitada como spam pelo gmail (não era bom o suficiente para ser colocado na minha pasta Spam).

Aqui o conteúdo 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 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
    
por David Grayson 29.09.2014 / 02:32

4 respostas

2

Eu decidi que a maneira como o contêiner enviará e-mails é gravá-los 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, envia-os 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=$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

O Ubuntu usa o upstart, então criei um arquivo chamado /etc/init/mailsender.conf para transformar esse script em um daemon:

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

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

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

    
por 29.09.2014 / 07:19
5

Como você tem uma solução de trabalho, aqui tentarei explicar um comportamento diferente quando fizer um telnet para o postfix (SMTP) e quando usar o sendmail (não-SMTP).

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

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

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

  • O email não-SMTP será processado pela limpeza antes de ser injetado no milter. O Daemon de limpeza foi responsável por adicionar cabeçalhos ausentes: (Resent-) De :, Para :, Mensagem- Id:, e Data: . Portanto, seu e-mail terá o cabeçalho completo quando injetado no OpenDKIM millter, mesmo que o e-mail original tenha um cabeçalho incompleto.

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

    can't determine message sender; accepting
    

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

    
por 29.09.2014 / 16:17
4

Você precisa apontar inet_interfaces para a ponte do docker ( docker0 ) na configuração do postfix localizada no conjunto /etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Mais detalhes de trabalho interno em envio de e-mail-de-docker-através de postfix -instalado-no-host

    
por 20.01.2017 / 09:36
2

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

A resposta do OP (David Grayson) me parece uma reinvenção do spool de e-mail post-up, mas usar esse spool de e-mail parece uma abordagem promissora, então é aqui que eu cheguei.

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

Então, eu sou host de montagem / var / spool / postfix / maildrop e / var / spool / postfix / public. Eu posso obter e-mail entregue para / var / spool / postfix / maildrop no ambiente de host, desde que eu montei o diretório de filas maildrop. Como montei /var/spool/postfix/public , maildrop pode sinalizar pickup para coletar o email da fila. Infelizmente, os uids e gids envolvidos, a menos que eu cuide disso, significando que o pickup no diretório host não pode ler os arquivos de spool, e pior a instalação do postfix atrapalha as permissões no diretório maildrop no ambiente host.

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

Enquanto isso funciona, eu não estou muito feliz em codificar os uids e gids. Isso significa que o mesmo contêiner não pode ser contado para executar o mesmo em todos os lugares. Eu imagino que, se em vez de montar o volume do host, eu o monto de um contêiner que executa o postfix, então ele nunca vai entrar em conflito, e eu só preciso de uma instalação de postfix para obter e-mails de muitos contêineres. Eu defini esses uids e gids em uma imagem de base da qual todos os meus containers herdam.

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

As preocupações com a abordagem msmtp incluem:

  • mais possibilidades de perder e-mails se o relé smtp para o qual ele envia não estiver disponível. Se isso for um retransmissor no mesmo host, a chance de problemas de rede é baixa, mas eu teria que ter cuidado com a reinicialização do contêiner de retransmissão.
  • performance?
  • Se uma grande quantidade de e-mails passar, o e-mail começa a cair?

Em geral, a abordagem do spool postfix compartilhado parece mais provável de ser uma configuração frágil a ser configurada, mas menos provável de falhar no tempo de execução (relé indisponível, portanto, queda de email).

    
por 08.02.2015 / 07:01