Invia posta dal contenitore Docker con Postfix dell'host


18

Sto eseguendo un server Ubuntu 14.04 (Linux). Ho installato e configurato Postfix e OpenDKIM molto bene sul server; Posso inviare email a me stesso con comandi come echo hi | sendmail root, e postfix / opendkim aggiungerò intestazioni quali Message-Id, Datee DKIM-Signature, in avanti l'e-mail al mio indirizzo di posta elettronica personale, e tutto funziona alla grande.

Ora vorrei creare un'applicazione che gira in un contenitore Docker e può inviare e-mail con la stessa facilità. In particolare, non voglio preoccuparmi di aggiungere intestazioni come Message-Id, e non voglio fare molta configurazione o installazione di software all'interno del contenitore stesso.

Qual è il modo migliore per farlo?

Esiste un modo per consentire al contenitore di eseguire l' sendmailesecuabile sull'host?

Ho provato a stabilire una connessione a Postfix da un contenitore usando il protocollo SMTP sulla porta 25, ma Postfix sembra trattare i messaggi ricevuti in quel modo in modo diverso; Penso che non abbia aggiunto alcuna intestazione, quindi il messaggio è stato completamente respinto come spam da Gmail (non era nemmeno abbastanza buono per essere inserito nella mia cartella Spam).

Qui il contenuto 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=<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: 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=<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


L'email che stavo cercando di inviare aveva solo Toun'intestazione, Subjectun'intestazione e un corpo di una riga. Non sono sicuro di come dire quali intestazioni ha avuto dopo che Postfix l'ha analizzato, sai come? Ecco l'output in / var / log / syslog che mostra come è stato elaborato da Postfix e rifiutato da Gmail: gist.github.com/DavidEGrayson/fbf65c8290c049a1f262
David Grayson

Risposte:


8

Poiché hai una soluzione funzionante, qui proverò a spiegare comportamenti diversi quando telnet to postfix (SMTP) e quando usi sendmail (non SMTP).

Cordiali saluti, OpenDKIM sarà invocato da postfix con meccanismo Milter . Puoi ottenere alcune informazioni su come attenuare l'implementazione in postfix tramite questa documentazione ufficiale . Qui il diagramma del gancio Milter in postfix.

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

Puoi vedere che sendmail-way (non SMTP) e telnet-way (SMTP) hanno un ordine di elaborazione diverso.

  • L'e-mail non SMTP verrà elaborata mediante pulizia prima di essere iniettata in milter. Daemon Cleanup è stato responsabile per l'aggiunta di intestazioni mancanti: (risentimento) Da :, A :, Message-Id :, e Data: . Pertanto la tua e-mail avrà un'intestazione completa quando iniettata nel mungitore OpenDKIM anche l'e-mail originale aveva un'intestazione incompleta.

  • L'e-mail SMTP verrà iniettata nel mungitore OpenDKIM prima che abbia luogo qualsiasi processo di pulizia. Pertanto, se l'email originale aveva un'intestazione incompleta, opendkim potrebbe rifiutare di firmare l'e-mail. L' intestazione From: era obbligatoria (vedi RFC 6376 ) e se un'e-mail non ce l'ha, OpenDKIM rifiuterà di firmare l'e-mail e ti avviserà

    can't determine message sender; accepting
    

Dato che non utilizzo mai la finestra mobile, non so quale limitazione su sendmail / pickup all'interno del contenitore a. Penso che la soluzione di David Grayson fosse abbastanza sicura da garantire che OpenDKIM firmasse il messaggio.


Quello era illuminante; grazie. Sfortunatamente, non vedo ancora alcuna soluzione migliore della mia attuale soluzione (descritta nella mia risposta).
David Grayson,

Il motivo ovvio è stato risolvere l'app per aggiungere l' From:intestazione nella tua e-mail :)
masegaloeh,

Ma dovrei anche aggiungere cose di Message-Idcui non so molto e che probabilmente mi sbaglierei ... sembra più facile lasciare che il demone cleanup si occupi di quello.
David Grayson,

In realtà, Message-ID non era obbligatorio come diceva RFC 6376 . Per impostazione predefinita, l'intestazione obbligatoria era solo Fromintestazione. Ma, se vuoi generare il tuo ID messaggio, puoi usare consigli come questo Draft IETF
masegaloeh,


5

Questa è una mezza risposta, o almeno una metà testata, dal momento che sto lavorando allo stesso problema. Spero che qualcuno possa aiutare a concretizzare quello che mi sono perso.

La risposta dell'OP (David Grayson) mi suona come una reinvenzione dello spool di posta di Postdrop, ma usare lo spool di posta suona come un approccio promettente, quindi ecco dove sono arrivato.

L'interfaccia di compatibilità / usr / bin / sendmail fornita da postfix passa la posta a postdrop, che è sgid postdrop, che consente di archiviare la posta nella coda maildrop in / var / spool / postfix / maildrop. Ciò dovrebbe verificarsi nel contenitore finestra mobile. Si spera che il resto di postfix non debba essere eseguito nel contenitore.

Quindi, sto ospitando mount / var / spool / postfix / maildrop e / var / spool / postfix / public. Posso ricevere la posta consegnata a / var / spool / postfix / maildrop nell'ambiente host, poiché ho montato la directory della coda di maildrop. Perché ho montato /var/spool/postfix/public, maildroppuò segnalare pickupdi raccogliere la posta dalla coda. Sfortunatamente, gli uid e le gid coinvolti a meno che non me ne occupi io, il che significa che il pickup nella directory host non può leggere i file di spool e, peggio ancora, l'installazione postfix incasina le autorizzazioni sulla directory maildrop nell'ambiente host.

Tuttavia, questo sembra funzionare:

$ 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

Mentre funziona, non sono terribilmente felice di scrivere codice su uid e gids. Ciò significa che non è possibile contare lo stesso contenitore per eseguire lo stesso ovunque. Immagino però che se invece di montare il volume dall'host lo monto da un contenitore che esegue Postfix, allora non sarà mai in conflitto, e ho solo bisogno di un'installazione Postfix per estrarre la posta da molti contenitori. Impostarei uid e gid in un'immagine di base da cui ereditano tutti i miei contenitori.

Mi chiedo però se questo sia davvero un buon approccio. Con una configurazione di posta così semplice e nessun demone in uso sul contenitore per riprovare il recapito, un MTA locale più semplice come msmtp potrebbe essere più appropriato. Fornirebbe via TCP a un relay sullo stesso host, dove si verificherebbe lo spooling.

Le preoccupazioni con l'approccio msmtp includono:

  • maggiori possibilità di perdere la posta se l'inoltro smtp a cui invia non è disponibile. Se si tratta di un relè sullo stesso host, la possibilità di problemi di rete è bassa, ma dovrei fare attenzione a come ho riavviato il contenitore di relè.
  • prestazione?
  • Se passa una grande raffica di posta, la posta inizia a cadere?

In generale, l'approccio di spool postfix condiviso sembra più probabile che sia una configurazione fragile da impostare, ma meno probabilità di fallire in fase di esecuzione (inoltro non disponibile, quindi posta eliminata).


4

Ho deciso che il modo in cui il contenitore invierà la posta è di scriverlo in un file in una directory specifica, che sarà accessibile sia dal contenitore che dall'host come "volume" Docker.

Ho creato uno script shell chiamato mailsender.sh che legge i messaggi da una directory specificata, li invia a sendmail e li 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 upstart quindi ho creato un file chiamato /etc/init/mailsender.confper trasformare questo script in un demone:

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

Posso iniziare il servizio con start mailsendere fermarlo con stop mailsender. Posso guardare i suoi log in /var/log/upstart/mailsender.log, e ovviamente posso monitorarlo usando il file PID.

È necessario creare la /var/mailsenddirectory e renderla accessibile dal contenitore Docker aggiungendo l'argomento -v /var/mailsend:/var/mailsendal docker runcomando.


Forse sarà utile qualcosa come mini_sendmail? Viene utilizzato nei contenitori, come un ponte tra l'app isolata dei contenitori e il demone del server sendmail sul sistema host dei contenitori. cyberciti.biz/tips/… acme.com/software/mini_sendmail
Mikl

Se sta inviando l'e-mail a Postfix tramite SMTP, non credo che Postfix pulirà l'email. Forse se avessi un MTA più configurabile (o abbiamo capito come configurare Postfix meglio) funzionerebbe.
David Grayson,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.