跳转到主要内容

在 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 端口进行明文传输。

为了安全性,出现了 SMTPS (SMTP-over-SSL), 最初使用的加密协议是 STARTTLS(显式 SSL/TLS),它首先在默认明文端口发送 STARTLS 命令,如果双方支持加密那么就移动到 587 端口进行 SSL/TLS 加密连接,如果不支持则进行明文连接,这带来了许多兼容性和安全问题。

为了进一步提高安全性,出现了强制 SSL/TLS(隐式 SSL/TLS),使用三个新端口(IMAP 993、POP3 995、SMTP 465),这些端口从一开始就要求 SSL/TLS 连接。意味着双方一开始就应该使用 SSL/TLS 进行连接,而不需要事先声明。

如今 STARTTLS 已经淘汰,大多数 SMTP 服务器都使用 465 或 587 端口进行隐式 SSL/TLS 连接。

那么 587 端口又是什么?

最初 IANA 为 SMTPS 分配了配了 SMTP 端口 465,然而 IANA 没有进行征求意见,因此 IETF 从未签署此方法或正式认可此端口。
然后,发送加密邮件的其他方法(例如 STARTTLS)变得流行,因此 IETF 最终将端口 587 定为 SMTPS 标准端口,同时支持 STARTTLS 和隐式 SSL/TLS,IANA 也同意了这一更改。

如今大部分服务商仍然支持 465 端口,虽然已经不推荐使用,但使用 465 端口可以避免一些兼容性问题。

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


阅读邮件

直接输入 mailmailx 即可。


使用外部 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 源中找到此包。

你可以为每个用户设置,或者为整个系统设置,我们这里为整个系统设置。

  • 每用户配置示例:/usr/share/doc/msmtp/msmtprc-user.example,实际文件位于 ~/.msmtprc
  • 系统用户配置示例:/usr/share/doc/msmtp/msmtprc-system.example,实际文件位于 /etc/msmtprc

创建文件 /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,非实际主机名)

image.png


主要参考