项目中许多地方都需要邮件通知功能。以开发工具为例,譬如,Gogs在版本库变化时可以通知团队成员、Redmine在项目(任务)进度变化时也有类似的功能,更进一步地,程序上线后将会配置相应的JVM监控,以便于发生线上故障时(程序抛出非预期的异常等)及时通知开发者处理。

但是,有几个问题需要留意。首先是如何发送邮件?自己搭建一套完整的邮件服务?真的这样做的话,可以以类似mail@mydomain.com的名义发送邮件,看上去是逼格满满,可就算不考虑这个操作的复杂性,在这个垃圾邮件泛滥的时代,十有八九这种自己发送的邮件会面临各种限制。再者,更现实一点,这些程序都运行在内网主机上,如何向外部发送邮件呢?

其实,使用邮件中继转发(relay)服务就可以完全解决上面这些所谓的“问题”。邮件中继转发,简单地说,就是邮件的最终递送仍旧由外部SMTP服务完成,163、QQ、Gmail什么的都可以,他们不是更专业吗?而我们的邮件服务器只是扮演一个代理的作用而已。

肩负邮件中继转发这一使命的,也就是本文的主角——Postfix。想要详细学习了解的同学可以戳这里)。在本例中,我们的程序将要发送的邮件交给Postfix,然后就没有然后了,完全不知道,也不需要知道邮件最后怎么样了。而收到邮件的Postfix,就像我们在邮件客户端(Foxmail、Thunderbird等)里点击“发送”按钮一般,将邮件交由配置好的外部SMTP服务。

下面直接介绍开始配置的过程。

安装Postfix

1
apt install postfix

安装过程中有3次同用户的交互。

首先是询问你邮件服务器类型,网上大部分资料,选择的都是Internet site,但其实,纯粹用作邮件中继转发,选择Satellite system就可以了。

然后是设置邮件域名,这里Postfix默认填入了主机名,因为不是直接通过我们的主机对外发送邮件,这一步的意义似乎没有网上教程说的那么邪乎,还要配置DNS的MX记录的。

最后是设置Postfix将收到的邮件中继转发给谁,也就是外部SMTP服务器的地址,本例中我们使用阿里云邮,按照它的说明,这里应该填入[smtp.aliyun.com]:465。
注意:如果第一步选择了Internet site,那么这一步就不会出现了,可以在安装完成后直接去修改配置文件。

配置main.cf

Postfix的主要配置文件为main.cf和master.cf,其中后者一般可以直接保持默认,配置邮件中继转发服务仅需要修改前者,vim打开这个文件,找到其中这几行(没有的话就自己加上),并且修改等号后面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
inet_interfaces = all
inet_protocols = ipv4
mynetworks_style = subnet
mynetworks = 127.0.0.0/8, 192.168.207.0/24
smtpd_use_tls = no
relayhost = [smtp.****.com]:465
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_use_tls = no
smtp_tls_wrappermode = yes
smtp_tls_security_level = encrypt

可能这个地方又和网上的教程不太一致了,但也是博主自己反复踩坑、修改文件后得出的一组能够正常工作的配置。

inet_interfaces:配置Postfix监听的网卡,常见的选项包括all和loopback-only,当然也可以指定具体的IP+端口号,但官方的建议是这个选项直接保持默认,因为还有许多别的途径可以满足类似的需求。

inet_protocols:配置Postfix使用的网络协议,毕竟在我国ipv6只用在教育网,直接把这项配置成ipv4还可以免去诸如::1这样的IP地址出现。

mynetworks:配置Postfix可信任的客户端,本例中采用了CIDR写法,对来源是本机,或者本机所在局域网(192.168.207.0网段)的邮件才予以中转。这个和mynetworks_style是相呼应的,一般设置这个即可。

smtpd_use_tls:这个的缺省值是yes,并且还配置了证书,但估计就是证书的问题,发送邮件时报了一个关于证书的错误。考虑到我们搭建的是close relay且仅在内部使用,对安全性要求没那么高,直接关掉这个选项了事。

relayhost:这个应该在安装的第三步已经配置过了,当时没配的,这里补上吧。

smtp_sasl打头的3个选项:分别用于配置外部smtp服务是否需要授权?授权信息保存在哪?有哪些授权限制?答案是当然需要授权、授权信息保存在/etc/postfix/sasl_passwd中(后面会单独配置)、不允许匿名登录。

smtp_use_tls:这个的缺省值也是yes,按照我所使用的外部smtp服务方的说明,应该是要启用这个选项了,但是一旦启用就提示connection time out,也不像是证书又惹祸了,知道原因的同学可以说一声呀。

smtp_tls打头的2个选项:这最后2个原本我是没有配置的,网上的教程似乎也没提及,但是配置成功、发送邮件时有个警告,建议我加上,那就加上吧……

配置sasl_passwd

前面提到过,关于外部SMTP服务授权相关的信息保存在sasl_passwd中,按照官方的建议,我们创建/etc/postfix/sasl_passwd文件,并在其中填入

1
$relayhost username:password

注意:这个relayhost和main.cf中的必须一致(所以我给加了个$符),毕竟要建立映射关系的。我在这里踩了两次坑,第一次是两个地方配置不一致,第二次是密码居然输错了……报错前者是“需要认证”,后者是“认证失败”,因此耽搁了一点时间。

然后转换成Postfix需要的hash格式(生成sasl_passwd.db文件)

1
postmap /etc/postfix/sasl_passwd

注意:我们将邮箱密码明文保存在sasl_passwd中了,出于安全性考虑,建议将这个文件的权限设置成只有root用户可读。

测试一下

至此,Postfix的配置就完成了,重启之。嗯,其实Postfix提供了重新载入配置文件的方法,很多时候并不需要重启

1
postfix reload

莫忘了把我们的程序中的SMTP服务器填写成“Postfix服务器地址:25”,然后发送一封测试邮件。瞅一眼日志,发送成功!

1
2
Jan 10 16:32:02 ubuntu-server postfix/smtp[3946]: 9D6E381168: to=<****@163.com>, relay=smtp.****.com[*.*.*.*]:465, delay=0.5, delays=0/0/0.25/0.25, dsn=2.0.0, status=sent (250 Data Ok: queued as freedom)
Jan 10 16:32:02 ubuntu-server postfix/qmgr[3942]: 9D6E381168: removed

P.S.日志位于/var/log/mail.log中,Postfix设计的原则就是Linux已经有的,绝不重复造轮子,所以……

参考资料

How to Set Up a Mail Relay with Postfix and Mailgun on Ubuntu 16.04
Configure Postfix to use Gmail as a Mail Relay
Configure Postfix to Use Gmail SMTP on Ubuntu
Configure Postfix to Send Mail Using an External SMTP Server