[英]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。
假设在主机上安装并配置了一个邮件服务器!
基于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 主机中,修改 /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.