在 Linux 命令行中发送邮件 —— 各种介绍
好长时间都没有真正搞明白 Linux 中是怎么发送邮件的,决定稍微研究一下。
邮件发送简化流程
发送邮件的简化流程是这样的:
发送者的邮件客户端--> 发送者的邮件传送代理 --> 接收者的邮件传送代理 --> 接收者的邮件客户端
概念解释
- 邮件客户端/邮件用户代理(MUA,Mail User Agent)如:Mail、Mailx、Mutt、Mpack、Sendmail、sSMTP
MUA 就是用户使用的客户端,负责编写、查看邮件 - 邮件服务器/邮件传送代理(MTA,Mail Transport Agent)MTA 如:Sendmail、Postfix、sSMTP
接受由 MUA 编写的邮件并发送到对方的 MTA,或者从对方的 MTA 接受邮件并交给 MUA。
MTA 也会检查是本域名还是其他域名的邮件,发往其他域名的邮件称为转寄(relay)。
收到的邮件最后会留在 MTA 中。 - 邮件分发代理(MDA,Mail Deliver Agent)
其实是 MTA 的一部分,分析 MTA 收到的信件数据,来决定这份邮件的取向、如:过滤垃圾邮件、自动回复等。
那要是我的 MUA 和 MTA 不在一起呢?
- 邮件检索代理(MRA,Mail Retrieval Agent)。
MRA 可以通过 POP3、IMAP 协议来远程从 MTA 获取邮件
当然,一个软件可以充当多个角色,比如 Mailx 也可以直接通过 SMTP 协议发送邮件,Sendmail 又能当客户端又能发送邮件。
小知识
SMTP 与 IMAP 和 POP3
- SMTP(Simple Mail Transfer Protocol):用于 MUA 向 MTA 发送邮件,或是 MTA 向 MTA 发送邮件。
是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器。 - POP3(Post Office Protocol 3):用于 MUA 向 MTA 接收邮件。
允许电子邮件客户端下载服务器上的邮件,但是在客户端的操作(如移动邮件、标记已读等),不会反馈到服务器上,比如通过客户端收取了邮箱中的3封邮件并移动到其他文件夹,邮箱服务器上的这些邮件是没有同时被移动的 。POP3 还有一些其他问题,不推荐使用。 - IMAP(Internet Mail Access Protocol):用于 MUA 向 MTA 接收邮件。
而IMAP提供webmail 与电子邮件客户端之间的双向通信,客户端的操作都会反馈到服务器上,对邮件进行的操作,服务器上的邮件也会做相应的动作。
SSL 与 TLS 与 STARTTLS —— 传输过程中的邮件加密
在 IMAP、POP、SMTP 等电子邮件技术开始使用时,SSL/TLS 加密技术还没大规模使用。那时,它们使用 IMAP 143 端口、POP3 110 端口、SMTP 25 端口进行明文传输。
为了安全性,出现了 SMTP-over-SSL (SMTPS),1997 年初 IANA 注册了 smtps 为其分配端口 465,然而 IANA 没有进行征求意见,IETF 也从未签署此方法或正式认可此端口。
历史上,端口 465 曾短暂地注册为“smtps”端口。这种注册毫无意义,因为 SMTP 传输 MX 基础结构无法指定端口,因此始终使用端口 25。因此,该注册被撤销,随后被重新分配给其他服务。
事后看来,“smtps”注册应该被重命名或保留,而不是撤销。不幸的是,一些广泛部署的邮件软件将“smtps”解释为“提交”[RFC6409],并在最终用户在帐户设置期间请求安全性时默认使用该端口进行电子邮件提交。
虽然已经在 587 端口部署了 STARTTLS,但它并没有取代 465 端口上隐式 TLS 提交的部署使用。
Historically, port 465 was briefly registered as the "smtps" port. This registration made no sense, as the SMTP transport MX infrastructure has no way to specify a port, so port 25 is always used. As a result, the registration was revoked and was subsequently reassigned to a different service.
In hindsight, the "smtps" registration should have been renamed or reserved rather than revoked. Unfortunately, some widely deployed mail software interpreted "smtps" as "submissions" [RFC6409] and used that port for email submission by default when an end user requested security during account setup.
Although STARTTLS on port 587 has been deployed, it has not replaced the deployed use of Implicit TLS submission on port 465.
摘自 RFC 8314 (经过机器翻译)
1998 年末当在 STARTTLS 标准化时,IANA 的 smtps 注册被撤销。
因此在描述 IANA 服务注册时,官方写法是 smtps。而在描述网络协议时,官方写法是 SMTPS。
STARTTLS(显式 SSL/TLS)被设定为 SMTPS 标准,它首先在默认端口 587 发送 STARTTLS 命令,告知邮件服务器将通过 TLS 发送 SMTP 流量。如果双方支持加密那么就原地升级为 SSL/TLS 加密连接,如果不支持则进行明文连接,这带来了许多兼容性和安全问题。
由于存在回退机制,容易收到攻击,如今已不太推荐使用。
2017 年 12 月 12 日,IANA 的 465 端口注册已被更新,并相应地使用了特殊的 IESG 批准。
2018 年为了进一步提高安全性,RFC 8314 规定了隐式 SSL/TLS(强制 SSL/TLS),使用三个新端口(IMAP 993 端口、POP3 995 端口、SMTP 465 端口),这些端口从一开始就要求 SSL/TLS 连接。意味着双方一开始就应该使用 SSL/TLS 进行连接,而不需要事先声明或额外命令。这进一步增强了安全性。
于是在 RFC 8314 发布后,端口 465 不再被视为遗留端口并且重新规范了端口 465 的使用,不再是 smtps 端口,这旨在鼓励采用隐式 TLS 而非显式 TLS。
为了保持一致性以及附录 A 中讨论的其他原因,最好随着时间的推移将 MUA 软件使用的核心协议迁移到隐式 TLS。
It is desirable to migrate core protocols used by MUA software to Implicit TLS over time, for consistency as well as for the additional reasons discussed in Appendix A.
摘自 RFC 8314 (经过机器翻译)
因为潜在的安全问题,虽然如今 STARTTLS 虽然没有被淘汰且发布了 MTA-STS 等来增强 587 端口的安全性,但大多数 SMTP 服务器都使用 465 进行隐式 SSL/TLS 连接以获得最佳安全性。
2525 端口又是什么?
2525 是一个真非标准端口,在上述标准端口被屏蔽的情况下,有些电子邮件服务通过端口 2525 提供 SMTP 传输。
它并不是电子邮件的标准端口,也没有与 SMTP 正式关联。
SMTP 端口
端口 | 支持情况 |
25 | 标准端口,不支持加密,支持 STARTTLS |
465 | 标准端口,支持隐式 SSL/TLS |
587 | 标准端口,支持显式 SSL/TLS 和 STARTTLS |
2525 | 非标准备用端口,可能支持隐式 SSL/TLS 和 STARTTLS |
POP3 端口
端口 | 支持情况 |
101 | 不支持加密,支持 STARTTLS,IMAP over STARTTLS |
995 | 支持隐式 SSL/TLS,POP3S (POP3 over SSL/TLS) |
IMAP 端口
端口 | 支持情况 |
143 | 不支持加密,支持 STARTTLS,IMAP over STARTTLS |
993 | 支持隐式 SSL/TLS,IMAPS(IMAP over SSL/TLS) |
警告:尽管 SMTP、POP、SMTP 这些传输协议是支持 SSL/TLS 加密的,但邮件本身是不加密的,这意味着双方的 MTA 将能完整的看到你的邮件内容(如 Gmail、QQ 邮箱、163 邮箱、Outlook 等)。
若要加密邮件本身,请使用 S/MIME 或 E2EE 协议。
实际操作
介绍完了概念,我们来讲讲怎么在 Linux 命令行里面发送邮件,比如 cron 失败提醒、脚本失败提醒……
MUA
首选 mailx,各大发行版应该都有。
替代了老旧的 mail,默认情况下 mailx 会调用 sendmail(不一定真的是 Sendmail 这个软件) 发送邮件,但也可以在配置文件中使其直接使用外部 SMTP 服务器发送邮件。
配置文件位于 /etc/mail.rc
发送邮件
mail -s "邮件主题" root@localhost
这种方法没有办法直接填写邮件正文,但你可以使用重定向符,或者管道来从某个文件输入邮件正文。
也可以用 , 分割多个收件人。或是使用 -a 带上附件。
cat 正文.txt | mail -s "邮件主题" root@localhost, admin@localhost
如果你不想在文件中写入正文,也可以这样:
echo "邮件正文" | mail -s "邮件主题" root@localhost
阅读邮件
直接输入 mail
或 mailx
即可。
使用外部 SMTP 服务器
注意:在系统中有 MTA 时 mailx 会忽略设置的 SMTP 服务器。
在配置文件 /etc/mail.rc
中写入
set from=
set smtp=mail.example.com:465
set smtp-auth-user=
set smtp-auth-password=
set smtp-auth=login
smtp="mail.example.com:465"
指定 SMTP 服务器主机名或 IP 地址和端口号;smtp-auth=login
指定将发生什么类型的 SMTP 身份验证,一般都是 login;smtp-auth-user="user1@example.com"
指定将用于验证的用户名;smtp-auth-password="password"
指定密码。确保您使用的系统是安全的,因为提供了明文密码;
mailx 能自动识别加密方式,一般无需手动指定。
这些信息应该从你的邮件提供者处获取,例如 Gmail 帮助、Outlook 帮助、QQ 邮箱帮助。
如果不想全局指定,也可以在命令行中指定 SMTP 服务器:
mailx -v -s "邮件主题" -S smtp="mail.example.com:465" -S smtp-auth=login -S smtp-auth-user="user1@example.com" -S smtp-auth-password=
更多用法
mailx 的更多用法请参阅:
MTA
真假 MTA
真正的 MTA:Sendmail、Postfix 等
兼容 sendmail 的转发器:sSMTP、Msmtp、eSMTP 等
一般而言我们没必要真的建立一个 MTA,而是提供与 sendmail 兼容的命令来将邮件转发到外部 SMTP 服务器。
Sendmail 和 Postfix 都比较复杂、sSMTP 已经无人维护,目前比较推荐使用 Msmtp。
真假 Sendmail
由于 Sendmail 的历史,所以很多软件会把 MTA 直接叫做 sendmail 而不管实际上的 MTA 是哪一个,因为它们基本都兼容 sendmail 格式。ls -l /usr/sbin/sendmail
可以看到 /usr/sbin/sendmail -> /etc/alternatives/mta
安装 MTA 后再由 /etc/alternatives/mta
指向真正的软件。
虽然有的 MUA 比如 mailx 也能直接使用外部 SMTP 服务器发送邮件,但部分软件还是会直接调用 sendmail,所以设置 MTA 也是有必要的。
安装 Msmtp
我们这里安装 Msmtp,RHEL 系列系统可以在 epel 源中找到此包。
你可以为每个用户设置,或者为整个系统设置,我们这里为整个系统设置。
创建文件 /etc/msmtprc
并写入:
#为以下所有帐户设置默认值
defaults
auth on
tls on
#为 TLS 设置可信 CA 列表。默认是使用系统设置,但是你可以选择你自己的文件。
#tls_trust_file /etc/ssl/certs/ca-bundle.crt
logfile ~/.msmtp.log
#账户1
account account1
tls_starttls off
host smtp.example.com
port 465
from username@example.com
user username
password plain-text-password
#账户2,不需要多个账户就删掉
account account2
tls_starttls off
host smtp.example.com
port 465
from username@example.com
user username
password plain-text-password
#设置默认账户,放在最后
account default : account1
tls_starttls
是否使用 STARTTLS 方式进行加密通讯。host
SMTP 服务器主机名或 IP 地址;port
SMTP 服务器端口号,SSL/TLS 加密一般为 465;from
发件人地址;user
SMTP 服务器用户名;password
SMTP 服务器密码;
这些信息应该从你的邮件提供者处获取,例如 Gmail 帮助、Outlook 帮助、QQ 邮箱帮助。
当然除了明文写入密码,也有这么几种方式可以传入密码:
# 密码方法 1: 将密码加入系统的钥匙圈,让 msmtp 自动获取。使用 Gnome 的 libsecret 来设置钥匙圈密码。
# $ secret-tool store --label=msmtp \
# host smtp.freemail.example \
# service smtp \
# user joe.smith
# 密码方法2:将密码存储在一个加密文件中,并告诉msmtp使用哪个命令来解密。这通常与 GnuPG 一起使用,如本例中。通常 gpg-agent 会询问一次解密密码。
# passwordeval gpg2 --no-tty -q -d ~/.msmtp-password.gpg
# 密码方法 3:将密码直接存储在这个文件中。通常情况下,将密码存储在明文文件中并不是一个好主意。如果你还是这样做了,至少要确保这个文件只能由你自己读取。
# password secret123
#密码方法4:将密码存储在~/.netrc。这种方法可能不再适用了。
#密码方法5:不指定密码。然后Msmtp会提示你输入密码。这意味着当 msmtp 运行时,你需要能够在终端输入。
更改默认 MTA
如果你的系统中有多个 MTA,或是 MTA 没有正确指向 Msmtp,使用此命令可以让你设置默认 MTA:alternatives --config mta
发送邮件
也可以用 Msmtp 发送邮件:
echo "邮件正文" | msmtp user@example.com
除了正文还会发送一些诊断信息,不太推荐使用此方式。
更多用法
更多用法请参阅:
在 Crontab 中使用
Crontab 会默认调用 MTA 将所有输出都发送到 root 邮箱,root 邮箱可以在 /etc/aliases
定义。
但 Crontab 不尊重 Msmtp 中的 SMTP 发送者设置,所以你需要在 cron 文件头部添加:
MAILFROM=
MAILTO=
默认情况下 cron 会发送所有输出,你可以将标准输出重定向到 > /dev/null
以此来仅接受标准错误输出邮件。
* * * * * root bash /opt/shell/error.sh > /dev/null
其他
- 还有域什么的没写……
- 不知道是怎么成功发出去的(图中主机名已被我修改为 Hostname,非实际主机名)
- 更为详细的流程以及搭建过程可以参考鸟哥的文章,但请注意这已经是 10 多年前的文章了:
https://linux.vbird.org/linux_server/centos6/0380mail.php
主要参考
- https://linux.vbird.org/linux_server/centos6/0380mail.php
- https://askubuntu.com/questions/536766/how-to-make-crontab-email-me-with-output
- https://sylvaindurand.org/send-emails-with-msmtp/
- https://www.rennetti.com/cheatsheet/235/using-msmtp-on-linux-centos
- https://www.poftut.com/linux-mail-mailx-commands-tutorial-examples-send-email-command-line/
- https://mailtrap.io/blog/starttls-ssl-tls/
- https://github.com/docker-mailserver/docker-mailserver/issues/1428
- https://www.rfc-editor.org/rfc/rfc8314.html
No Comments