在DigitalOcean上搭建自己的邮件系统

在国内Gmail被封的情况下,通过此教程依旧可以使用Gmail的邮件管理服务。(因为邮件其实是从自己服务器发出的嘛)

一封从自己的域名发出的邮件,对于开发者来说,是一份最好的自我介绍。在两位博主的文章以及维基的帮助下,上周在DigitalOcean搭建了自己的邮件系统,中间学到不少东西,特此记录下来。

这篇文章中将会包含以下内容:

那就开始吧。

♠  发送和接收邮件时都发生了什么?

邮件是我自己最喜欢使用的通讯方式,它给我了足够的时间去组织一个良好的回复,并且具有更好的检索功能,可以在需要的时候找到历史的备份,简直就是一个冥想盆。在正式搭建自己的邮件服务之前,对邮件的传输有一个大致的认识会帮助理解后面的许多配置环境。 

上面的图片来自Wikipedia,描述了一封邮件传输过程中要经历的重要节点。MUA (Mail User Agent) 或许是到目前为止最为熟悉的部分。他可以是web-based的,像网页版的Gmail, 也可以是功能完整的桌面客户端,例如Outlook。当一封邮件编辑完成后,它会经由TCP587端口(大多数公司)被发往一个叫做MSA (Mail Submission Agent)的服务器, 由此邮件会被提交到下一站:MTA (Mail Transfer Agent)。 MSA和MTA通常是运行在不同参数配置下的相同的程序,例如我们下面即将配置的Postfix,他们可以是运行在同一台机器上,也可以时运行在不同的机器上。前者主要使用共享文件,后者则需要网络传输。好了,现在你的邮件应该已经到了MTA这一站,接下来即将由此进入I(i)nternet。MTA需要确定收件人的具体位置,这一过程通过DNS (Domain Name System)服务来完成,具体来说是一个叫做MX的DNS记录。

如下就是一条MX记录

# example from DNS and BIND edition 4
peets.mpk.ca.us. IN MX 10 realy.hp.com

该条记录有两个功能,它指明了peets.mpk.ca.us.将使用realy.hp.com作为邮件交换器Mail Exchanger(MX) server,同时还为这个邮件交换器指明了优先级,即10。这个优先级的绝对大小并不重要,重要的是它与其他邮件交换器优先级的相对大小,这个关系将作为邮件路由算法的依据。

回到邮件的发送,现在通过DNS查询,在MTA邮件查明了将发往何处。然后MTA将会通过SMTP协议将邮件转发到该MX服务器。被MX接受的邮件下一步会被转发到MDA (Mail Delivery Agent),通过它邮件将会被分发存往对应用户的邮箱里面。现在邮件的接收者就可以通过邮件管理工具去提取自己的邮件了,邮件提取使用到的协议主要有IMAP (Internet Message Access Protocol) 和 POP3 (Post Office Protocol)。

更多内容, 参考Wikipedia

下面进入正式的配置。

♠  准备工作

为了搭建自己的邮件系统,后面的内容默认你已经具备了以下的条件

有人说会有完全免费的解决方案,但是我依然推荐你购买一台自己的VPS,它不仅可以成为你的邮件服务器,同时还可以host你的博客,搭建私人proxy等等。

♠  DNS配置

首先配置DNS是因为DNS的传播需要花费一定的时间,在那之前别人是找不到你的邮件地址的。我将在DigitalOcean的网页Console中配置我的DNS,如果要使用这项功能,请确保你正在使用DigitalOcean的nameserver,这个配置需要在域名提供商那里完成。下图是我自己的DNS记录:

另外需要注意的是Droplet的名字和你的域名是一致的,这样才能获得一个正确的PTR记录。在DNS传播的同时,继续下面的配置。

♠  转发邮件到配置的邮箱

我们的邮件服务需要使用一款优秀的开源软件来实现,Postfix

在我的机器Ubuntu14.04下使用下面的命令就可以完成安装,使用DEBIAN_FRONTEND=noninteractive将会跳过交互安装的环节,因为Postfix的配置可以之后通过修改配置文件完成。

sudo DEBIAN_FRONTEND=noninteractive apt-get install postfix

安装完成后,修改配置文件/etc/postfix/main.cf

# Host and site name.
myhostname = example.com
mydomain = example.com
myorigin = example.com

#Virtual aliases
virtual_alias_domains = example.com
virtual_alias_maps = hash:/etc/postfix/virtual

myhostname与之前配置的DNS相匹配即可。Virtual Aliases指明了发往virtual alias domains的邮件将被转发至virtual文件定义的邮箱中去,因此下一步编辑/etc/postfix/virtual

#Format: mail@from.address  forward@to.address
#multiple mailboxs can be declared
me@example.com foo@gmail.com

使用下面的命令使得Postfix识别virtual文件,

sudo postmap /etc/postfix/virtual

接下来重启Postfix服务,

sudo service postfix restart
sudo postfix reload

接下来就可以测试了,发一封邮件去virtual文件里定义的邮箱,然后去对应的Gmail查看。不出意外,那封邮件应该已经在那里了。 如果没有的话,可以检查/var/log/mail.log/var/log/mail.err看出现了什么问题,很有可能是DNS还没有更新完成,稍加等候在尝试。我的DNS感觉很快就更新完成了,不知道是不是和在香港有关。

邮件转发完成后,进去邮件发送的部分。

♠  邮件的发送

这一部分会比之前的部分麻烦一点,我们需要把我们的服务器配置成为一个relay服务器,原因是我希望继续使用Gmail的管理界面,但是邮件的发送人又需要是我自己的邮箱,那么这封邮件就需要由Google先发送到我的邮件服务器,然后在进行转发。Gmail和我们的转发服务器之间的交流是受加密保护的,因此这里使用到了TLS。有关TLS是如何运作的,我推荐一下的几篇文章,看过之后会对这套系统有一个认识

使用Cyrus SASL来完成认证

首先安装所需的库

sudo apt-get install sasl2-bin libsasl2-modules

在第一篇博客中,作者指出,我们需要让Gmail通过一组用户名/密码来登陆我们的邮件转发服务器,而不是一个Open Relay,因此首先建立远程认证的用户

sudo saslpasswd2 -c -u example.com smtp

上面的命令会建立一个名为smtp的用户,用户名可以随意选择。完成后,在/etc下会出现一个保存用户名和密码的文件sasldb2

~$ ls -l /etc/sasldb2
-r-------- 1 postfix root 12288 Dec 12 05:01 /etc/sasldb2

可以通过下面的命令在验证用户是否创建成功:

sudo sasldblistusers2

然后为这个文件设置合适的权限

sudo chmod 400 /etc/sasldb2
sudo chown postfix /etc/sasldb2

修改配置文件/etc/postfix/sasl/smtpd.conf

# /etc/postfix/sasl/smtpd.conf
sasl_pwcheck_method: auxprop
auxprop_plugin: sasldb
mech_list: PLAIN LOGIN CRAM-MD5 DIGEST-MD5 NTLM
log_level: 7

生成认证所需的公钥密钥

  1. 生成所需的秘钥(记住所输入的密码)

    cd
    openssl genrsa -des3 -out example.com.key 2048

  2. 生成SSH Key (private key)和Certificate Signing Request (csr)文件

    openssl req -new -key example.com.key -out example.com.csr
    除了不要忘记这里输入的密码外,注意两点: [1]在Common Name那里输入你的域名地址(与/etc/postfix/main.cf中的myhostname同) [2]不用输入Challenge Password

  3. 生成Self-signed的Certifacte

    openssl x509 -req -days 3650 -in example.csr -signkey example.com.key -out example.com.crt

    x509 -req: 指明使用的CSR管理系统是X.509

  4. 移除生成的秘钥上的密码 对于邮件系统这样的守护程序,在机器遇到意外重启后,我们希望在无人值守的情况下恢复工作,所以不可能每次都人为输入密码。

    openssl rsa -in example.com.key -out example.com.key.nopass
    mv example.com.key.nopass example.com.key

  5. 生成pem文件

    openssl req -new -x509 -extensions v3_ca -keyout cakey.pem -out cacert.pem -days 3650
    与第二步类似,注意填写正确的Common Name

  6. 设置合适的权限,并安装Certificate

    chmod 600 example.com.key
    chmod 600 cakey.pem
    mv example.com.key /etc/ssl/private/
    mv example.com.crt /etc/ssl/certs/
    mv cakey.pem /etc/ssl/private/
    mv cacert.pem /etc/ssl/certs/

完成认证相关的步骤,修改Postfix配置文件

#Those are commands, contents in quotes will be put in /etc/postfic/main.cf accordingly
postconf -e 'smtpdusetls = yes'
postconf -e 'smtpdtlsauthonly = no'
postconf -e 'smtpdtlskeyfile = /etc/ssl/private/example.com.key'
postconf -e 'smtpdtlscertfile = /etc/ssl/certs/example.com.crt'
postconf -e 'smtpdtlsCAfile = /etc/ssl/certs/cacert.pem'
postconf -e 'tlsrandom_source = dev:/dev/urandom'

配置Postfix使之支持Gmail邮件转发,编辑/etc/postfix/master.cf, 打开如下内容,注意submission那一行的第三个选项,也就是chroot设置位 n

submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=may
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

完成后重启Postfix服务

sudo service postfix restart
sudo postfix reload

这时可以发现在587端口已经有人在监听了:

$ sudo netstat -antu --program | grep 587
tcp        0      0 0.0.0.0:587             0.0.0.0:*               LISTEN      1257/master
tcp6       0      0 :::587                  :::*                    LISTEN      1257/master
$ sudo ps aux | grep 1257
root      1257  0.0  0.1  25344  1700 ?        Ss   Dec14   0:02 /usr/lib/postfix/master

Gmail端的配置

在Gmail的Setting中找到Accounts and Import,其中有一项Add another email address you own,点开后进行认证

如果一切正确,你会收到一封验证邮件。否则的话,查看log文件查看问题。

配置成功后在可以把自己邮箱设置为默认发送邮箱,这样就完成了邮件发送部分的配置。

♠  防止被识别为垃圾邮件

完成这一部分的配置后,你会发现经由你发出的邮件头中出现了新的内容,如下:

同时在这个非常友好的测试网站中也能取得高分:

还记得我们将服务器配置成了一台帮助Gmail进行转发的MTA吗?是的,整个互联网中充满了这样的转发服务器,他们代表着发送者进行邮件的转发,我们已经配置了SASL验证来避免我们的relay服务器被其他人使用,这是好的。但是我们的邮箱依然有可能被别人伪造来进行钓鱼攻击(phishing),因此我们需要采取必要的措施允许收件人验证邮件的确由我发出,这里使用到了DKIM

如果你还记得在配置公钥密钥时候的那几篇文章,理解DKIM就会方便很多。我们使用我们的秘钥加密邮件(header以及contents),然后将加密后的值保存在一个DKIM-Signature结构中附加在Mail Header中,DKIM是独立于SMTP的,邮件最后会通过管理DKIM的软件所监听的端口进行签名,亦即插入DKIM-Signature记录,如果我们在Gmail中查看邮件的具体信息(下拉菜单中使用show original),我们可以清楚的看到这条记录位于邮件头中(下图倒数第二条):

Delivered-To: yifan.yang9@gmail.com
Received: by 10.140.97.199 with SMTP id m65csp161044qge;
        Sun, 14 Dec 2014 06:15:27 -0800 (PST)
X-Received: by 10.236.70.70 with SMTP id o46mr18757082yhd.191.1418566527120;
        Sun, 14 Dec 2014 06:15:27 -0800 (PST)
Return-Path: 
Received: from legato.ninja (legato.ninja. [104.236.3.63])
        by mx.google.com with ESMTP id i66si2938819yhq.145.2014.12.14.06.15.26
        for ;
        Sun, 14 Dec 2014 06:15:26 -0800 (PST)
Received-SPF: pass (google.com: domain of me@legato.ninja designates 104.236.3.63 as permitted sender) client-ip=104.236.3.63;
Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of me@legato.ninja designates 104.236.3.63 as permitted sender) smtp.mail=me@legato.ninja;
       dkim=pass header.i=@legato.ninja
Received: from mail-ig0-f176.google.com (mail-ig0-f176.google.com [209.85.213.176])
    by legato.ninja (Postfix) with ESMTPSA id F20BC144E39
    for ; Sun, 14 Dec 2014 09:14:55 -0500 (EST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=legato.ninja; s=mail;
    t=1418566496; bh=qiS/yt1TWHlEjtVeGqJOO42mPKrn6L0OMAupfmIZoGw=;
    h=Date:Subject:From:To:From;
    b=ESgslZ7IFbRx36ssnZJxb5FAPFFxb9IjxGv5sgO4K+80hil3B/T+665Su8AaO6agM
     A0aG2bf0BGw2mI/682SpMZ1lpwjaMLQS4M0bRhxXSqYRcoAkP6KhbK7TRaeQ6HsXbi
     igix2Jh31PSU7rdhUGo7CXW1C+6RMumQM2vH7k9Q=
Received: by mail-ig0-f176.google.com with SMTP id l13so4021635iga.15
        for ; Sun, 14 Dec 2014 06:14:55 -0800 (PST)

其中 b字段记录了由发送方加密的内容。收件人则会通过一个DNS请求来获得公钥进行解密,具体的步骤是通过 s字段的selector以及 d字段的domain来发起DNS(TXT)请求,而应答中会包含公钥。然后进行内容解密,查实,来确认这封邮件的确从认证域名发出。

具体的配置我建议参考这篇组织良好的文章,但是需要注意的是该文章中的DNS TXT记录部分设置有误,使用FQDN时不能忘记最后的dot,可参考这篇文章对DNS的设置。

至此,邮件服务搭建完成.

Reference#1: Season of Code by cji,
Reference#2: Mark's BLog

Cheers,
@stevenyfy

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.