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
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.
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.
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
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:
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é).