繁体   English   中英

在 docker 容器中配置 sendmail

[英]Configure sendmail inside a docker container

我有一个运行 PHP 和 Apache 的docker 容器 主机位于运行 docker 实例的 AWS 实例中。 我无法从 docker 终端发送电子邮件。 有没有办法使用使用 docker 主机配置的 sendmail 从 docker 实例发送电子邮件?

以下命令从主机发送电子邮件,但不从 docker 实例发送电子邮件。 也没有给出错误。

echo "Subject: Testing Email" | cat - text | /usr/lib/sendmail -F abc.pqr@example.com -t abc.pqr@example.com

我所做的是将主机 MTA 配置为侦听docker0并在容器中安装 ssmtp 以将容器中的 sendmail 与主机 MTA 桥接。 在主机上运行 MTA 的原因是系统(严重)错误可以发送到管理员的邮箱。 不在容器中运行 MTA 的原因是它是一个重复的进程,因为主机系统已经运行了 MTA。

在主机上,我使用了后缀。 我们需要做的就是配置 postfix 来监听docker0并接受来自 Docker 容器的传出邮件。 编辑文件/etc/postfix/main.cf并将docker0 IP 地址添加到inet_interfaces以便它接受来自 Docker 容器的连接。 另外,将 Docker 容器的网络地址添加到mynetworks中,以便 Docker 容器可以合法地通过主机上的 postfix 服务器发送邮件。 参考和更多细节

要在容器中使用 sendmail,请安装 ssmtp 并将FromLineOverride设置为允许,并将mailhub设置为/etc/ssmtp/ssmtp.conf中主机的 IP 地址。 您可以将mailhub设置为smtp-server之类的符号,然后使用--add-host选项运行容器,如此Dockerfile所示(使用--add-host smtp-server:your-docker0-address运行它) . 这将在实际使用主机 MTA 发送邮件的容器中配置一个可用的 sendmail。

基于之前的答案,
使用以下命令创建 config/sendmail_config.sh:

#!/bin/sh
# set host in hosts
line=$(head -n 1 /etc/hosts)
line2=$(echo $line | awk '{print $2}')
echo "$line $line2.localdomain" >> /etc/hosts

yum install -y sendmail sendmail-cf m4 \
    && hostname >> /etc/mail/relay-domains \
    && m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf

#remove localhost limit
sed -i -e "s/Port=smtp,Addr=127.0.0.1, Name=MTA/Port=smtp, Name=MTA/g" \
    /etc/mail/sendmail.mc
sendmail -bd

在基于 debian 的容器上将 yum 更改为 apt-get

然后在 Dockerfile 中添加:

RUN sed -i -e "s#;sendmail_path =#sendmail_path = /usr/sbin/sendmail -t -i#g"  \
    /your_path_to/php.ini
COPY ./config/sendmail_config.sh .

我想用我的 php util 发送邮件,这样我就可以把它贴在任何地方,而不必链接到另一个 MTA 容器或主机来完成任务。
我运行 sh sendmail_config.sh,然后运行我的 php util。

您的 Dockerfile 中没有安装 sendmail(或任何其他邮件代理)。 然而,主机显然确实有 sendmail 可用。 “最好的”或最类似 Docker 的解决方案是启动另一个运行 MTA(如postfixexim )的容器,并配置您的应用程序以使用它。

假设在主机上安装并配置了一个邮件服务器!

基于Alpine的 docker 镜像应该有sendmail可执行文件。

简单的解决方案是在主机网络上运行容器:

docker run --rm --net="host" php:fpm-alpine sh -c 'echo "Subject: test" | sendmail -v your@mail.example'

要使用默认网桥运行容器,请将邮件服务器配置为侦听 docker 接口172.17.0.1 ,并允许中继来自 docker 子网172.17.0.0/16的电子邮件。

受影响的 Exim 选项: /etc/exim4/update-exim4.conf.conf

dc_local_interfaces='127.0.0.1 ; ::1 ; 172.17.0.1'
dc_relay_nets='172.17.0.0/16'

重新启动邮件服务器并详细运行容器:)

docker run --rm --hostname="your-domain.example" php:fpm-alpine sh -c 'echo "Subject: test" | sendmail -v your@mail.example -S 172.17.0.1'

我自己想出了一个办法,虽然不是最优雅的解决方案。 我在 docker 中配置了 sendmail,以便通过主机的 ip 中继请求。 将以下行添加到文件“/etc/mail/access

Connect:<host_ip_here>          RELAY

此外,在主机和 docker 中,注释掉文件“/etc/mail/sendmail.mc”中的以下行,将其添加为前缀“dnl #”并添加后缀为“dnl”。

DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')

我将主机 ip 作为环境变量传递给 docker 容器,以便它是可配置的。 现在 docker 的 sendmail 将通过主机中继它的 sendmail 的 smtp 请求。

在 /etc/hosts 中向 Docker 主机名添加一个完全限定的域名对我来说是诀窍:

{YourDockerIP} {YouDockerHostName}.localdomain {YouDockerHostName}

对我来说,它看起来像这样:

172.17.0.25 77f5a7ae8606.localdomain 77f5a7ae8606

您还可以使用此 bash 脚本自动更新此行:

#!/bin/bash
line=$(head -n 1 /etc/hosts | awk '{printf "%s %s.localdomain %s", $1, $2, $2}')
sed -e "1 s/^.*$/${line}/g" /etc/hosts > hosts
# with sed -i, it actually performs a rename of /etc/hosts, but docker does not
# allow that, so we have to use a temp file and copy it to overwrite /etc/hosts
cp hosts /etc/hosts
rm hosts

参考: http ://hjk41.azurewebsites.net/2015/09/25/using-sendmail-inside-docker/

编辑:请参阅 xuhdev 答案以获取更多信息以及如何设置邮件转发器。 我的答案可用于在主机上设置 sendmail 而不是 postfix。

编辑#2:添加防火墙规则以允许来自 docker 的 smtp 流量

我做了一个与 tarun mittal 类似的设置,如下所示:

  • 在 docker 应用程序中,我已将其设置为使用 smtp 服务器和 docker0 接口 (172.17.42.1) 的 ip
  • 在 docker 主机中,修改 /etc/mail/sendmail.mc 以包括监听 docker0 接口(与 tarun 答案中的所有接口相反 - 添加粗体行)

    DAEMON_OPTIONS(`Family=inet, Name=MTA-v4, Port=smtp, Addr=127.0.0.1')dnl

    DAEMON_OPTIONS(`Family=inet, Name=MTA-v4, Port=smtp, Addr=172.17.42.1')​​dnl

  • 允许从 docker 容器访问防火墙中的 docker 接口 iptables -I INPUT -s 172.17.0.0/24 -d 172.17.42.1 -dport 25 -j ACCEPT

  • 在 /etc/mail/access 我最后添加了允许所有 docker 实例发送电子邮件和makemap hash /etc/mail/access < /etc/mail/access编译数据库

    /// 已编辑 - 在下面使用,因为 Docker 将在多次重建后转到 172.17.1.X

    /// 旧 - 连接:172.17.0 继电器

    连接:172.17 继电器

  • 最后重启 sendmail - service sendmail restart

我也在这个问题上苦苦挣扎。 从我在 docker 容器中运行的 python 代码,我需要使用在主机上运行的 postfix(SMTP 服务器)临时发送电子邮件。 在尝试了一大堆事情之后,结果证明是一个简单的解决方案是带有 --net="host" 的 docker run 命令。 注意:在所有情况下,这可能不是一个好的解决方案,因为这样的容器将与 docker 主机共享网络堆栈,并且从容器的角度来看, localhost(或127.0.0.1 )将引用 docker 主机。 这是我在 Ubuntu 主机上所做的: docker run -it --net="host" Ubuntu /bin/bash这给了我容器外壳。 然后我在这个容器中安装了 python: apt-get update && apt-get -y install supervisor python-pip

然后我启动了 python 解释器并触发了以下代码行:

import smtplib
from email.mime.text import MIMEText

fromaddr = 'testemail.example.com'
toaddr = 'youremail.example.com'

msg = MIMEText('Sample email from python/docker container')
msg['from'] = fromaddr
msg['subject'] = 'Subject of python email'
msg['to'] = toaddr

server = smtplib.SMTP('localhost')
server.set_debuglevel(1)
server.sendmail(fromaddr, toaddr, msg.as_string())
server.quit

我还发现 [this][1] 是一本有用的读物​​。

根据Sendmail 手册,我们可以使用sendmail -bd命令来运行服务。 但它以零状态存在。 所以...

# In your Dockerfile
RUN yum install -y sendmail
# Run mail listener
RUN sendmail -bd

CMD sleep infinity
# or run your command

你也可以使用supervisord。 我使用这个配置:

[program:sendmail]
command=/usr/sbin/sendmail -bd
autostart=true
autorestart=false
numprocs=1
startretries=0

并测试它:

$ echo test123 | sendmail your@example.com

不要忘记检查垃圾邮件文件夹。

我使用安装了 postfix 的图像。 我稍微需要调整配置,因为默认配置(至少在 Centos 8 中)不起作用,因为在容器内部,“localhost”是未知的(先验)。 所以我只是在安装 postfix 后注释掉 Dockerfile 中的一个配置行:

RUN dnf install -y postfix \
    && sed -i "s/inet_interfaces = localhost/#inet_interfaces = localhost/" /etc/postfix/main.cf

然后我使用以下命令启动容器:

       --mount type=bind,source=/var/spool/postfix,target=/var/spool/postfix

现在,每当我在容器中使用sendmail时,它都会将电子邮件写入var/spool/post-fix 然后邮件被主机接收,主机将其发送出去。

这样,我不需要在我的容器中启动(或配置)任何进一步的进程(除了后缀配置的一个小修复)。

您必须在位于 set /etc/postfix/main.cf 的 post fix 配置中将 inet_interfaces 指向 docker bridge (docker0)

inet_interfaces =

通过主机上安装的后缀从 docker 发送电子邮件的更多内部工作细节

注意:使用 ifconfig 命令获取 docker 网桥地址

对我来说,只需在我的 docker run 命令上添加 --network=host 标志就可以了。 它应该使用主机发送电子邮件。

我知道这个问题已经以几种不同的方式得到了回答,但我将包括我的扩展。 一种方法是创建一个到主机的 SSH 隧道,如果您在让邮件客户端直接使用 ssmtp 连接时遇到问题。

如果您尝试的一切都失败了,这更像是一种破解。

您可以创建一个脚本,该脚本连接到在端口 25 上运行的 SSH 隧道,方法是使用您正在编码的任何语言的套接字,如果 ssmtp 不工作或存在证书问题。 但请确保您的容器和在端口 25 上运行的机器不会暴露于远程连接。 因为您将使用本地计算机作为中继服务器。 这意味着您的脚本不必验证它是您主机上的用户。 您应该允许的唯一远程连接是 docker0 连接,它被添加到 /etc/postfix/main.cf (参见上面的 inet_interfaces 解决方案)。

该脚本可以相当容易地编写。 只需查找如何使用 telnet 发送邮件即可了解 smtp 的握手协议。

如果端口 25 对公众关闭(建议保持此端口关闭),请使用 ssh 隧道“ssh user@yourdomain.club -L ​​25:localhost:25”,这与您将使用的 ssh 隧道相同访问容器内的端口 25。 您必须将 open-ssh 添加到 Dockerfile 中,因为大多数轻量级 linux 版本都不包含 ssh。 您也可以使用 net cat 而不是 open-ssh(ssh 命令),默认情况下可能会出现一些发行版。 此外,当您在程序中使用套接字进行连接时,请务必将 \r\n 添加到脚本中的每一行。 \r 代表返回,\n 代表换行。

您将 1 创建隧道 2 连接到脚本内的隧道 3 从脚本发送邮件命令 4 断开隧道

如果隧道意外关闭,隧道将不得不自动打开并重新打开。 此外,在创建 SSH 隧道时,创建 SSH 密钥或使用 expect -c bash 脚本输入密码。 在我看来,将 SSH 密钥添加到 Docker 容器会更好,因为期望 -c 脚本在等待密码提示时往往会稍有延迟。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM