Envoyer du courrier à partir du conteneur Docker avec le suffixe de l'hôte

18

J'utilise un serveur Ubuntu 14.04 (Linux). J'ai installé et configuré Postfix et OpenDKIM très bien sur le serveur; Je peux me envoyer des courriels à des commandes telles que echo hi | sendmail root, et postfix / opendkim ajouterai - têtes tels que Message-Id, Dateet DKIM-Signature, avant l'e - mail à mon adresse e - mail personnelle, et tout fonctionne très bien.

Maintenant, je voudrais créer une application qui s'exécute dans un conteneur Docker et peut envoyer des e-mails avec la même facilité. En particulier, je ne veux pas m'inquiéter d'ajouter des en-têtes comme Message-Id, et je ne veux pas faire beaucoup de configuration ou d'installation de logiciels à l'intérieur du conteneur lui-même.

Quelle est la meilleure façon de procéder?

Existe-t-il un moyen de laisser le conteneur exécuter l' sendmailexectuable sur l'hôte?

J'ai essayé d'établir une connexion à Postfix à partir d'un conteneur en utilisant le protocole SMTP sur le port 25, mais Postfix semble traiter les messages reçus de cette manière différemment; Je pense qu'il n'a ajouté aucun en-tête, le message a donc été rejeté comme spam par gmail (il n'était même pas assez bon pour être placé dans mon dossier Spam).

Voici le contenu du 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
la source
Veuillez poster l'en-tête de votre e-mail (celui qui a été identifié comme spam par GMAIL)
masegaloeh
L'e-mail que j'essayais d'envoyer avait juste un en- Totête, un en- Subjecttête et un corps d'une ligne. Je ne sais pas comment dire quels en-têtes il avait après que Postfix l'ait parcouru les milters, savez-vous comment? Voici la sortie dans / var / log / syslog montrant comment il a été traité par Postfix et refusé par Gmail: gist.github.com/DavidEGrayson/fbf65c8290c049a1f262
David Grayson

Réponses:

8

Parce que vous avez une solution qui fonctionne, j'essaierai ici d'expliquer différents comportements lorsque vous vous connectez telnet à postfix (SMTP) et lorsque vous utilisez sendmail (non-SMTP).

Pour info, OpenDKIM sera invoqué par postfix avec le mécanisme Milter . Vous pouvez obtenir des informations sur l'implémentation plus douce de postfix via cette documentation officielle . Voici le schéma du crochet milter en suffixe.

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

Vous pouvez voir que sendmail-way (non-SMTP) et telnet-way (SMTP) ont un ordre de traitement différent.

  • L'e-mail non SMTP sera traité par nettoyage avant d'être injecté à milter. Démon de nettoyage était responsable de l' ajout des en- têtes manquants: (Resent-) De :, A :, Message-Id :, et Date: . Par conséquent, votre e-mail aura un en-tête complet lorsqu'il sera injecté dans OpenDKIM milter, même l'e-mail d'origine avait un en-tête incomplet.

  • L'e-mail SMTP sera injecté dans OpenDKIM milter avant tout traitement de nettoyage. Par conséquent, si votre e-mail d'origine avait un en-tête incomplet, opendkim peut refuser de signer l'e-mail. L'en - tête From: était obligatoire (voir RFC 6376 ) et si un e-mail ne l'a pas, OpenDKIM refusera de signer l'e-mail et vous donnera un avertissement

    can't determine message sender; accepting
    

Comme je n'utilise jamais docker, je ne sais pas quelle limitation sur sendmail / pickup à l'intérieur d'un conteneur. Je pense que la solution de contournement de David Grayson était suffisamment sûre pour s'assurer qu'OpenDKIM signait le message.

masegaloeh
la source
C'était instructif; Merci. Malheureusement, je ne vois toujours pas de solution meilleure que ma solution actuelle (décrite dans ma réponse).
David Grayson
La raison évidente était de corriger l'application pour ajouter un en- From:tête dans votre e-mail :)
masegaloeh
Mais je devrais aussi ajouter des trucs comme Message-Idje ne sais pas grand-chose et je me tromperais probablement ... il semble plus facile de laisser le démon de nettoyage s'en occuper.
David Grayson
En fait, Message-ID n'était pas obligatoire comme le disait RFC 6376 . Par défaut, l'en-tête obligatoire n'était que l'en- Fromtête. Mais, si vous voulez générer votre propre Message-ID, vous pouvez utiliser une recommandation comme celle-ci IETF Draft
masegaloeh
6

Vous devez pointer inet_interfacesvers docker bridge ( docker0) dans la configuration postfix située sur le set/etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Plus de détails de travail internes lors de l' envoi d'un e-mail à partir de Docker via Postfix installé sur l'hôte

Satish Gandham
la source
Merci pour le lien! La partie pertinente pour moi était d'ajouter quelque chose comme 172.17.0.0/16à mynetworksdans /etc/postfix/main.cfet service postfix restart.
JSchirrmacher
5

Ceci est une demi-réponse, ou au moins une demi-testée, car je travaille actuellement sur le même problème. J'espère que quelqu'un pourra aider à étoffer ce que j'ai manqué.

La réponse de l'OP (David Grayson) me semble être une réinvention de la bobine de courrier de postdrop, mais l'utilisation de cette bobine de courrier semble être une approche prometteuse, alors voici où j'en suis arrivé.

L'interface de compatibilité / usr / bin / sendmail fournie par postfix transmet le courrier à postdrop, qui est sgid postdrop, lui permettant de stocker le courrier dans la file d'attente maildrop à / var / spool / postfix / maildrop. Cela devrait se produire dans le conteneur Docker. Espérons que le reste de postfix ne doive pas s'exécuter dans le conteneur.

Donc, je suis l'hôte de montage / var / spool / postfix / maildrop et / var / spool / postfix / public. Je peux recevoir du courrier dans / var / spool / postfix / maildrop dans l'environnement hôte, car j'ai monté le répertoire de la file d'attente maildrop. Parce que j'ai monté /var/spool/postfix/public, maildroppeut signaler pickupde recueillir le courrier de la file d'attente. Malheureusement, les uids et les gids impliqués sauf si je m'occupe de cela, ce qui signifie que la collecte dans le répertoire hôte ne peut pas lire les fichiers spoule, et pire l'installation postfixe gâche les autorisations sur le répertoire maildrop dans l'environnement hôte.

Pourtant, cela semble fonctionner:

$ 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

Bien que cela fonctionne, je ne suis pas très content de coder en dur les uids et les gids. Cela signifie que le même conteneur ne peut pas être compté pour fonctionner de la même manière partout. Je pense cependant que si au lieu de monter le volume à partir de l'hôte, je le monte à partir d'un conteneur qui exécute postfix, alors il ne sera jamais en conflit, et je n'ai besoin que d'une seule installation postfix pour extraire le courrier de nombreux conteneurs. Je mettrais ces uids et gids dans une image de base dont tous mes conteneurs héritent.

Je me demande cependant si c'est vraiment une bonne approche. Avec une configuration de messagerie aussi simple et aucun démon utilisé sur le conteneur pour réessayer la distribution, un MTA local plus simple comme msmtp pourrait être plus approprié. Il livrerait via TCP à un relais sur le même hôte, où la mise en file d'attente se produirait.

Les préoccupations concernant l'approche msmtp comprennent:

  • plus de possibilité de perdre du courrier si le relais smtp auquel il est envoyé n'est pas disponible. Si c'est un relais sur le même hôte, le risque de problèmes de réseau est faible, mais je dois faire attention à la façon dont j'ai redémarré le conteneur de relais.
  • performance?
  • Si une grande quantité de courrier passe, le courrier commence-t-il à être abandonné?

En général, l'approche du spool postfix partagé semble plus susceptible d'être une configuration fragile à installer, mais moins susceptible d'échouer au moment de l'exécution (relais indisponible, donc courrier abandonné).

mc0e
la source
4

J'ai décidé que la façon dont le conteneur enverra le courrier consiste à l'écrire dans un fichier dans un répertoire particulier, qui sera accessible à la fois depuis le conteneur et l'hôte en tant que "volume" Docker.

J'ai créé un script shell appelé mailsender.sh qui lit les mails d'un répertoire spécifié, les envoie à sendmail, puis les supprime:

#!/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 utilise upstart, j'ai donc créé un fichier nommé /etc/init/mailsender.confpour transformer ce script en démon:

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

Je peux démarrer le service avec start mailsenderet l'arrêter avec stop mailsender. Je peux regarder ses journaux /var/log/upstart/mailsender.loget bien sûr, je peux le surveiller en utilisant le fichier PID.

Vous devez créer le /var/mailsendrépertoire, puis le rendre accessible à partir du conteneur Docker en ajoutant l'argument -v /var/mailsend:/var/mailsendà votre docker runcommande.

David Grayson
la source
Peut-être que quelque chose comme mini_sendmail sera utile? Il est utilisé dans les conteneurs, comme un pont entre l'application isolée des conteneurs et le démon du serveur sendmail sur le système hôte des conteneurs. cyberciti.biz/tips/… acme.com/software/mini_sendmail
Mikl
S'il envoie l'e-mail à Postfix via SMTP, je ne pense pas que Postfix nettoiera l'e-mail. Peut-être que si vous aviez un MTA qui était plus configurable (ou si nous avons compris comment mieux configurer Postfix), cela fonctionnerait.
David Grayson