web-dev-qa-db-fra.com

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

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

Maintenant, je voudrais créer une application qui s'exécute dans un conteneur Docker et qui 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 logiciel à 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'exécutable sendmail sur l'hôte?

J'ai essayé de me connecter à 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
19
David Grayson

J'ai décidé que la façon dont le conteneur enverra le courrier est de 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.conf pour 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 mailsender et arrêtez avec stop mailsender. Je peux consulter ses journaux dans /var/log/upstart/mailsender.log, et bien sûr je peux le surveiller en utilisant le fichier PID.

Vous devez créer le /var/mailsend puis rendez-le accessible à partir du conteneur Docker en ajoutant l'argument -v /var/mailsend:/var/mailsend à ton docker run commande.

4
David Grayson

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

Pour info, OpenDKIM sera invoqué par suffixe avec mécanisme Milter . Vous pouvez obtenir des informations sur l'implémentation plus douce de postfix via ceci 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: (Renvoyé-) De:, À:, 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 donner 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.

8
masegaloeh

Vous devez pointer inet_interfaces au pont de docker (docker0) dans la configuration postfixe située dans l'ensemble /etc/postfix/main.cf

inet_interfaces = <docker0_ip>

Plus de détails de travail internes à envoi-e-mail-de-docker-par-postfix-installé-sur-l'hôte

7
Satish Gandham

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.

Je suis donc en train de monter Host/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, maildrop peut signaler à pickup de récupérer 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 Host ne peut pas lire les fichiers de spoule, et pire l'installation postfixe gâche les autorisations sur le répertoire maildrop dans l'environnement Host.

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 sortir 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 serait livré 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. S'il s'agit d'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é).

5
mc0e