我正在运行Ubuntu 14.04(Linux)服务器。 我已经在服务器上很好地安装和configuration了Postfix和OpenDKIM ; 我可以使用echo hi | sendmail root等命令向自己发送电子邮件 echo hi | sendmail root ,postfix / opendkim将添加Message-Id , Date和DKIM-Signature等标题,将电子邮件转发到我的个人电子邮件地址,一切正常。
现在我想创build一个在Docker容器中运行的应用程序,并且可以轻松地发送电子邮件。 特别是,我不想担心添加像Message-Id这样的头文件,而且我也不想在容器本身内部进行非常多的configuration或软件安装。
什么是最好的方法来做到这一点?
有没有办法让容器在主机上运行sendmail exectuable?
我尝试使用端口25上的SMTP协议从一个容器连接到Postfix,但是Postfix似乎以不同的方式处理接收到的消息; 我认为它没有添加任何标题,所以邮件被彻底拒绝作为gmail的垃圾邮件(它甚至不够好被放置到我的垃圾邮件文件夹)。
这里的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
因为你有一个工作的解决scheme,在这里我将尝试解释当你telnet到postfix(SMTP)和当你使用sendmail(非SMTP)时的不同行为。
仅供参考,OpenDKIM将由具有Milter机制的postfix调用。 你可以通过这个官方文档得到一些信息在postfix中是如何实现的。 这里是后缀中的milter hook的图。
SMTP-only non-SMTP filters filters ^ | ^ | | v | | Network -> smtpd(8) | | \ | V Network -> qmqpd(8) -> cleanup(8) -> incoming / pickup(8) : Local -> sendmail(1)
您可以看到sendmail-way(非SMTP)和telnet-way(SMTP)具有不同的处理顺序。
非SMTP邮件在注入之前会被清理处理。 清理守护进程负责添加丢失的标题: (Resent-)From:,To:,Message-Id:和Date :。 因此,即使原始电子邮件的标题不完整,您的电子邮件也会在注入OpenDKIM milter时具有完整标题。
在发生任何清理处理之前,SMTP电子邮件将被注入OpenDKIM。 因此,如果您的原始电子邮件标题不完整,那么opendkim可能会拒绝签署电子邮件。 From:头是强制性的(参见RFC 6376 ),如果电子邮件没有,OpenDKIM将拒绝签署电子邮件并给你一个警告
can't determine message sender; accepting
因为我从来没有使用泊坞窗,比我不知道在一个容器内的sendmail /皮卡有什么限制。 我认为大卫·格雷森的解决方法是足够安全的,以确保OpenDKIM签名的消息。
我决定容器发送邮件的方式是把它写到一个特定目录的文件中,这个文件可以作为Docker的“容量”从容器和主机上访问。
我创build了一个名为mailsender.sh的shell脚本,它从指定的目录读取邮件,将它们发送到sendmail,然后删除它们:
#!/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使用了新手,所以我创build了一个名为/etc/init/mailsender.conf的文件,将这个脚本变成一个守护进程:
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
我可以通过start mailsender启动服务,并stop mailsender 。 我可以查看/var/log/upstart/mailsender.log中的日志,当然我可以使用PID文件来监视它。
您需要创build/var/mailsend目录,然后通过将参数-v /var/mailsend:/var/mailsend到您的-v /var/mailsend:/var/mailsend docker run命令,从Docker容器访问它。
这是一个半回答,或者至less是一个半testing,因为我目前正在通过同样的问题。 我希望有人能够帮助充实我错过的东西。
OP(David Grayson)的回答听起来像是重新发明了postdrop mail-spool,但是使用这个邮件服务器听起来像是一个很有前途的方法,所以这里就是我所知道的。
由postfix提供的/ usr / bin / sendmail兼容性接口将邮件传递到postdrop,这是postgrid,允许它将邮件存储到/ var / spool / postfix / maildrop的maildrop队列中。 这应该在docker容器中发生。 后缀的其余部分应该有希望不必在容器中运行。
所以,我主机挂载/ var / spool / postfix / maildrop和/ var / spool / postfix / public。 我可以在主机环境中将邮件发送到/ var / spool / postfix / maildrop,因为我已经挂载了maildrop队列目录。 因为我已经挂载了/var/spool/postfix/public , maildrop可以通过信号pickup来从队列中收集邮件。 不幸的是,除非我处理这些问题,否则uids和gids会涉及到,这意味着主机目录中的拾取无法读取假脱机文件,而且更糟糕的是,后缀安装会混淆主机环境中maildrop目录的权限。
不过,这似乎工作:
$ 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
虽然它起作用了,但我并不满意于编码uid和gid。 这意味着同一个容器不能统计到处都是一样的。 我认为,如果不是从主机挂载卷,而是从运行postfix的容器挂载它,那么它永远不会发生冲突,我只需要一个postfix安装从多个容器中获取邮件。 我将这些uid和gid设置为一个基本的图像,所有的容器都从这个图像inheritance而来。
我想知道这是否是一个很好的方法。 有了这样一个简单的邮件configuration,并且没有在容器上使用守护进程来重新尝试传递,像msmtp这样简单的本地MTA可能更合适。 它将通过TCP传送到同一主机上的一个继电器,在那里发生假脱机。
对msmtp方法的担忧包括:
通常,共享后缀假脱机方法似乎更容易成为脆弱的configuration,但在运行时不太可能失败(中继不可用,因此邮件被丢弃)。
您必须将inet_interfaces指向inet_interfaces bridge( docker0 ),位于/etc/postfix/main.cf中的postfixconfiguration文件中
inet_interfaces = <docker0_ip>
更多的内部工作细节在发送电子邮件从docker到后缀安装在主机上