我正在运行 Ubuntu 14.04 (Linux) 服务器。我已经安装并配置了后缀和OpenDKIM在服务器上运行非常顺畅;我可以使用诸如 之类的命令向自己发送电子邮件,并且 postfix/opendkim 将添加、和echo hi | sendmail root
之类的标题,并将电子邮件转发到我的个人电子邮件地址,一切都运行良好。Message-Id
Date
DKIM-Signature
现在我想创建一个在Docker容器,可以同样轻松地发送电子邮件。特别是,我不想担心添加这样的标头Message-Id
,也不想在容器内部进行太多配置或软件安装。
做这个的最好方式是什么?
有什么办法可以让容器运行sendmail
主机上的可执行文件吗?
我尝试使用端口 25 上的 SMTP 协议从容器连接到 Postfix,但 Postfix 似乎以不同的方式处理以这种方式收到的消息;我认为它没有添加任何标头,因此该消息被 gmail 直接拒绝为垃圾邮件(它甚至不够好,不能放入我的垃圾邮件文件夹)。
以下是邮件日志内容
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
答案1
您必须在位于设置的 postfix 配置中指向inet_interfaces
docker bridge ( )docker0
/etc/postfix/main.cf
inet_interfaces = <docker0_ip>
更多内部工作细节请访问 通过主机上安装的 Postfix 从 Docker 发送电子邮件
答案2
因为您有一个可行的解决方案,所以在这里我将尝试解释当您通过 telnet 连接到 postfix(SMTP)和使用 sendmail(非 SMTP)时的不同行为。
仅供参考,OpenDKIM 将通过 postfix 调用米尔特机制。您可以通过此获得有关如何在 postfix 中实现 milter 的一些信息官方文档。下面是 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方式(非SMTP)和telnet方式(SMTP)的处理顺序有所不同。
非 SMTP 电子邮件在注入邮件过滤器之前将经过清理处理。清理守护进程负责添加缺失的标题:(重新发送)发件人:,收件人:,消息 ID:,和日期:。因此,即使原始电子邮件的标头不完整,当您的电子邮件注入 OpenDKIM 邮件过滤器时,它将具有完整的标头。
在进行任何清理处理之前,SMTP 电子邮件将注入 OpenDKIM 邮件过滤器。因此,如果您的原始电子邮件标头不完整,则 opendkim 可能会拒绝签署该电子邮件。从:标头是必需的(请参阅RFC 6376),如果电子邮件没有该信息,OpenDKIM 将拒绝签署该电子邮件并向您发出警告
can't determine message sender; accepting
由于我从未使用过 docker,所以我不知道容器内的 sendmail/pickup 有什么限制。我认为 David Grayson 的解决方法足够安全,可以确保 OpenDKIM 签署消息。
答案3
我决定容器发送邮件的方式是将其写入特定目录中的文件中,该文件可作为 Docker“卷”从容器和主机访问。
我编写了一个名为 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 使用 upstart,因此我创建了一个名为的文件/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 文件来监视它。
您需要创建/var/mailsend
目录,然后通过将参数添加-v /var/mailsend:/var/mailsend
到docker run
命令中使 Docker 容器可以访问该目录。
答案4
我将其用作msmtp-mta
被动中继(不需要运行守护进程)。容器中的每个用户都~/.msmtprc
可以使用 sendmail 命令、mail 命令发送电子邮件,在 php 邮件功能中也可以正常工作。不需要任何配置和更改。也许它会对你有所帮助。
$ cat ~/.msmtprc
defaults
port 25
account gw
from [email protected]
host host.docker.internal
port 25
auth off
account default : gw
当然,你必须在--add-host=host.docker.internal:host-gateway
Linux 和 Mac 上运行 docker。
主机上默认配置了 Postfix,并进行了以下更改main.cf
:
mynetworks = 127.0.0.0/8 172.17.0.0/16
# ipv6 [::ffff:127.0.0.0]/104 [::1]/128
inet_interfaces = 127.0.0.1 172.17.0.1
当然,它的工作方式与容器中的 postfix 类似。