一、前言

为什么要自己搭建邮件系统?
• 自从小学上信息技术课接触到电子邮件开始,我就对 @ 后面那几个字母充满了疑惑,为什么 @ 后面的几个字母一定要是 qq 、gmail 呢?难道不能自定义吗?直到初三我弄懂了域名这个概念之后,这个谜团才被解开:我们用的是别人的服务器来使用电子邮件服务。自己有域名当然也可以搭建一个啊!由于当时还基本上没有云主机这种东西,而且家里也不让我整天开着电脑(电信有公网 IP ),所以该计划暂时搁置。直到 2015 年,用 163 邮箱的 Apple ID 大规模被盗(很庆幸我逃过了一劫),我这才明白了自己有个邮箱服务器那是多么重要!恰好高考完了,可以折腾一番了。当时用的是傻瓜式的 macOS Server ,啥原理都不懂,点几下就搭建成功了,就这样凑合着用了一年,系统非常稳定,除了停电以及升级系统之外没有重启过(这里赞一下 macOS Server )。macOS Server 最大的缺点就是可以自己定制的功能太少了!现在熟悉了 Linux 的基本操作和服务器的基础知识,是时候该在 Linux 平台上搭建一个了!
• 其实,Linux 上也有傻瓜式邮件系统,iRedMail 就是其中之一。不过我玩 Linux 的目的就是为了学习服务器以及网络知识,还是自己搭建一个才能学到东西。

邮件系统所涉及的模块较多,而且软件又是不断更新的,不同 Linux 发行版的系统环境和默认配置文件也可能不相同,想只看一篇教程然后一次完美的几率太低了。我自己折腾了一个星期,中途也遇到过各种各样的错误,不过好在最后都解决了。所以说,想要自己亲手搭建一个完美的邮件系统,耐心是必不可少的。

我把我搭建的整个过程整理一下,步骤尽量精简,然后写下了这一篇文章,主要目的是想给大家分享下我的学习经验,让新手少走一点弯路,也能给自己增加点印象。所有的步骤(除了域名参数)我在写完之后都有重装系统然后全部重新走一遍验证一次。如果想提高一次成功的几率,建议使用全新安装的系统,然后严格按照我的步骤要求来做。如有错误,请及时留言提出,谢谢!

二、所需软件 & 环境

  • 操作系统:CentOS 7.3.1611 最小安装 (已关闭 SELinux 和防火墙)
  • 应用软件: Postfix 2.10.1 、Dovecot 2.2.10 、MariaDB 5.5.52 、OpenDKIM 2.11.0 、Nginx 1.10.2 、PHP 5.4.16 、Roundcube WebMail 1.3.0
  • 域名(主机名):example.com
  • 公网 IP:1.1.1.1
  • 二级域名mail(这里就是 mail.example.com的数字证书(推荐免费的 Let’s Encrypt)

说明
1、下文的 MySQL 均代表 MariaDB;
2、Nginx 和 PHP 不做详细配置,只确保 Webmail 可以正常使用。

三、原理 & 关系图

下面两张图足以说明一切:

说明
• SMTP 是 Postfix 发件的模块,SMTPD 是 Postfix 收件的模块,请注意区分。

四、安装软件

执行以下命令:

yum -y update && \
yum -y install epel-release && \
yum -y update && \
yum -y install dovecot dovecot-mysql mariadb-server nginx opendkim php-fpm php-mbstring php-mysql php-xml postfix pypolicyd-spf tar wget

若出现密钥警告,按 y 回车即可。

五、配置 MySQL

5.1 初始化 MySQL

5.1.1 启动服务

执行以下命令:

systemctl start mariadb

5.1.2 进行安全设置

执行以下命令:

mysql_secure_installation 

然后根据提示操作:

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MySQL to secure it, we'll need the current
password for the root user.  If you've just installed MySQL, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none): # 回车即可
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MySQL
root user without the proper authorisation.

Set root password? [Y/n] y # 使用密码验证
New password: # 设置 root 密码
Re-enter new password: 
Password updated successfully!
Reloading privilege tables..
 ... Success!

# 后面的问题全部按 y 回车,这里省略。

5.2 邮件系统数据库的创建

注意
• SQL 语句后面要加上分号才能执行。

5.2.1 进入 MySQL 命令行界面

执行以下命令:

mysql -u root -p

然后输入刚才设置的数据库 Root 密码然后按回车。

出现 MariaDB [(none)]> 指示符就说明进入了。

5.2.2 创建一个用户用于读取邮件系统数据库

执行以下 SQL 语句:

CREATE USER 'mail_sys'@'localhost' IDENTIFIED BY 'mail_sys';

5.2.3 创建邮件系统数据库

执行以下 SQL 语句:

CREATE DATABASE mail_sys;

说明
• 这里的“邮件系统数据库”并不存储邮件,只用作域名、用户、别名的验证。邮件默认是存储在 /var/spool/mail 里面的。

5.2.4 为用户授予读取权限

执行以下 SQL 语句:

GRANT SELECT ON mail_sys.* TO 'mail_sys'@'localhost' IDENTIFIED BY 'mail_sys';

5.2.5 刷新权限

执行以下 SQL 语句:

FLUSH PRIVILEGES;

5.2.6 进入邮件系统数据库

执行以下 SQL 语句:

USE mail_sys;

5.2.7 创建域名表

执行以下 SQL 语句:

CREATE TABLE `domains` ( `id` int(20) NOT NULL auto_increment, `name` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.2.8 创建用户表

执行以下 SQL 语句:

CREATE TABLE `users` ( `id` int(20) NOT NULL auto_increment, `domain_id` int(20) NOT NULL, `password` varchar(200) NOT NULL, `email` varchar(200) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.2.9 创建别名表

执行以下 SQL 语句:

CREATE TABLE `aliases` ( `id` int(20) NOT NULL auto_increment, `domain_id` int(20) NOT NULL, `source` varchar(200) NOT NULL, `destination` varchar(200) NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

5.3 往邮件系统数据库里添加或删除域名、用户、别名

说明
1、为了方便以后添加或删除,下面我把 SQL 语句(命令)的格式写出来;
2、索引号从 1 开始依次递增,需手动指定;
3、由于密码需要使用 SQL 语句进行 SHA512 运算来存储散列值,因此图形化数据库管理工具可能无法实现;
4、当添加多行数据时,非末行的行分隔符为逗号,直到末行才使用分号。

5.3.1 添加或删除域名

添加域名

INSERT INTO `mail_sys`.`domains` (`id` ,`name`) 
VALUES ('<域名索引号>', '<域名>');

例如:

MariaDB [mail_sys]> INSERT INTO `mail_sys`.`domains` (`id` ,`name`) 
    -> VALUES ('1', 'example.com');

删除域名

DELETE FROM `mail_sys`.`domains` WHERE `id`='<域名索引号>';

5.3.2 添加或删除用户

添加用户

INSERT INTO `mail_sys`.`users`
(`id`, `domain_id`, `password` , `email`) VALUES
('<用户索引号>', '<域名索引号>', ENCRYPT('<密码>', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '<邮箱地址>'),
('<用户索引号>', '<域名索引号>', ENCRYPT('<密码>', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '<邮箱地址>'),
('<用户索引号>', '<域名索引号>', ENCRYPT('<密码>', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '<邮箱地址>');

例如:

MariaDB [mail_sys]> INSERT INTO `mail_sys`.`users`
    -> (`id`, `domain_id`, `password` , `email`) VALUES
    -> ('1', '1', ENCRYPT('12345678', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '[email protected]'),
    -> ('2', '1', ENCRYPT('password', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '[email protected]'),
    -> ('3', '1', ENCRYPT('11111111', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), '[email protected]');
#以后再继续添加用户名的时候,用户索引就应该从4开始,下同。

删除用户

DELETE FROM `mail_sys`.`users` WHERE `id`='<用户索引号>';

5.3.3 添加或删除别名(可选)

说明
• 别名的意思就比如说:设置 [email protected] 的别名是 [email protected] ,那么其他人往 [email protected] 里发送邮件时,邮件就会到 [email protected] 的邮箱里去。

添加别名

INSERT INTO `mail_sys`.`aliases`
(`id`, `domain_id`, `source`, `destination`) VALUES
('<别名索引号>', '<域名索引号>', '<别名地址>', '<目的地址>'),
('<别名索引号>', '<域名索引号>', '<别名地址>', '<目的地址>'),
('<别名索引号>', '<域名索引号>', '<别名地址>', '<目的地址>');

例如:

MariaDB [mail_sys]> INSERT INTO `mail_sys`.`aliases`
    -> (`id`, `domain_id`, `source`, `destination`) VALUES
    -> ('1', '1', '[email protected]', '[email protected]'),
    -> ('2', '1', '[email protected]', '[email protected]'),
    -> ('3', '1', '[email protected]', '[email protected]');

删除别名

DELETE FROM `mail_sys`.`aliases` WHERE `id`='<别名索引号>';

5.4 检查数据库

5.4.1 检查域名表

执行以下 SQL 语句:

SELECT * FROM mail_sys.domains;

若出现以下格式的输出结果,说明设置正确。

+----+-------------+
| id | name        |
+----+-------------+
|  1 | example.com |
+----+-------------+
1 row in set (0.00 sec)

5.4.2 检查用户表

执行以下 SQL 语句:

SELECT * FROM mail_sys.users;

若出现以下格式的输出结果,说明设置正确。

+----+-----------+------------------------------------------------------------------------------------------------------------+-------------------+
| id | domain_id | password                                                                                                   | email             |
+----+-----------+------------------------------------------------------------------------------------------------------------+-------------------+
|  1 |         1 | $6$afbdd821f68a3f27$QH9yDKslGZMNZjzvBBvMtYXzzclbnNgb1AhmB7lqu6fj6PU04QTgCTvcvPwqsAaW6mJt9kcKPicN0VCQGalg5/ | [email protected] |
|  2 |         1 | $6$a4f819161bd19901$oeDntXEyiY6RiM369ugKZrMfsK6yeV3CG/fhFF4ruPJImLCyzi2hR/PX8f2nBDBRWiMvWv7zWiNv5yEruRsW// | [email protected] |
|  3 |         1 | $6$2a85aaab0ec76f64$KRQ2H8Zgn0YjTzDDnfwqim3mZynZ05iPMZ1GQPw7GNuJApcXuLi5LOmR9yDC6Jh2eAKbhuG4lgHG.I5FdIrf4. | [email protected] |
+----+-----------+------------------------------------------------------------------------------------------------------------+-------------------+
3 rows in set (0.00 sec)

5.4.3 检查别名表(可选)

执行以下 SQL 语句:

SELECT * FROM mail_sys.aliases;

若出现以下格式的输出结果,说明设置正确。

+----+-----------+--------------------+-------------------+
| id | domain_id | source             | destination       |
+----+-----------+--------------------+-------------------+
|  1 |         1 | [email protected] | [email protected] |
|  2 |         1 | [email protected] | [email protected] |
|  3 |         1 | [email protected] | [email protected] |
+----+-----------+--------------------+-------------------+
3 rows in set (0.00 sec)

数据库设置完成。按 Ctrl + D 退出 MySQL 命令行界面。

六、设置邮件系统专用用户

说明
1、所有的邮件用户都映射到系统的同一个真实的用户上;
2、不建议直接使用系统自带的 mail 账户。

6.1 创建邮件系统专用组

执行以下命令:

groupadd -g 2000 mail_sys

6.2 创建邮件系统专用用户

执行以下命令:

useradd -g mail_sys -u 2000 mail_sys -d /var/spool/mail -s /sbin/nologin

6.3 修改邮件目录所有者

执行以下命令:

chown -R mail_sys:mail_sys /var/spool/mail

七、配置 Postfix

7.1 备份原版配置文件

执行以下命令:

cp -r /etc/postfix /etc/postfix.bak

7.2 修改 main.cf

说明
• 该文件配置 Postfix 的全局参数。

执行以下命令:

cat > /etc/postfix/main.cf << EOF

请按实际情况以及注释提示修改以下内容,完成后去除 # 号和后面的注释,然后粘贴到命令行窗口中按回车即可。

mydomain = example.com # 您的域名,需要修改
myhostname = mail.example.com # 您的域名前面加上 mail. 需要修改
mydestination = localhost
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_protocols = all
inet_interfaces = all
smtp_address_preference = ipv4
smtpd_banner = ESMTP
biff = no
append_dot_mydomain = no
readme_directory = no
virtual_transport = lmtp:unix:private/dovecot-lmtp
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
virtual_mailbox_domains = mysql:/etc/postfix/mysql_mailbox_domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql_mailbox_maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql_alias_maps.cf
smtpd_sender_login_maps = mysql:/etc/postfix/mysql_mailbox_maps.cf, mysql:/etc/postfix/mysql_alias_maps.cf
disable_vrfy_command = yes
strict_rfc821_envelopes = yes
smtpd_sender_restrictions = reject_non_fqdn_sender, reject_unknown_sender_domain, reject_sender_login_mismatch
smtpd_recipient_restrictions = reject_non_fqdn_recipient, reject_unknown_recipient_domain, permit_sasl_authenticated, reject_unauth_destination, check_policy_service unix:private/policyd-spf
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
message_size_limit = 102400000
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtpd_tls_cert_file=/etc/pki/tls/certs/cert.pem # mail.example.com 证书文件位置,需要修改
smtpd_tls_key_file=/etc/pki/tls/private/key.pem # mail.example.com 证书私钥文件位置,需要修改
smtpd_tls_session_cache_database = btree:\${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:\${data_directory}/smtp_scache
smtpd_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_ciphers = high
smtpd_tls_ciphers = high
smtpd_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_mandatory_protocols = TLSv1.2, TLSv1.1, !TLSv1, !SSLv2, !SSLv3
smtp_tls_mandatory_ciphers = high
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
smtpd_tls_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
smtp_tls_mandatory_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
smtp_tls_exclude_ciphers = MD5, DES, ADH, RC4, PSD, SRP, 3DES, eNULL, aNULL
tls_preempt_cipherlist = yes
smtpd_tls_received_header = yes
policyd-spf_time_limit = 3600
EOF

7.3 修改 master.cf

说明
• 该文件配置 Postfix 中各模块的参数。

执行以下命令:

cat > /etc/postfix/master.cf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

smtp      inet  n       -       n       -       -       smtpd
submission inet n       -       n       -       -       smtpd
       -o smtpd_tls_security_level=encrypt
smtps     inet  n       -       n       -       -       smtpd
       -o smtpd_tls_wrappermode=yes
pickup    unix  n       -       n       60      1       pickup
cleanup   unix  n       -       n       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       n       1000?   1       tlsmgr
rewrite   unix  -       -       n       -       -       trivial-rewrite
bounce    unix  -       -       n       -       0       bounce
defer     unix  -       -       n       -       0       bounce
trace     unix  -       -       n       -       0       bounce
verify    unix  -       -       n       -       1       verify
flush     unix  n       -       n       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       n       -       -       smtp
relay     unix  -       -       n       -       -       smtp
       -o smtp_helo_timeout=120 -o smtp_connect_timeout=120
showq     unix  n       -       n       -       -       showq
error     unix  -       -       n       -       -       error
retry     unix  -       -       n       -       -       error
discard   unix  -       -       n       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
anvil     unix  -       -       n       -       1       anvil
scache    unix  -       -       n       -       1       scache
policyd-spf    unix  -       n       n       -       0       spawn
       user=mail_sys argv=/usr/libexec/postfix/policyd-spf
EOF

7.4 与 MySQL 对接

7.4.1 指定域名数据表

执行以下命令:

cat > /etc/postfix/mysql_mailbox_domains.cf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

user = mail_sys
password = mail_sys
hosts = localhost
dbname = mail_sys
query = SELECT 1 FROM domains WHERE name='%s'
EOF

7.4.2 指定用户数据表

执行以下命令:

cat > /etc/postfix/mysql_mailbox_maps.cf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

user = mail_sys
password = mail_sys
hosts = localhost
dbname = mail_sys
query = SELECT email FROM users WHERE email='%s'
EOF

7.4.3 指定别名数据表

执行以下命令:

cat > /etc/postfix/mysql_alias_maps.cf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

user = mail_sys
password = mail_sys
hosts = localhost
dbname = mail_sys
query = SELECT destination FROM aliases WHERE source='%s'
EOF

7.5 测试数据库读取

7.5.1 启动 Postfix 服务

执行以下命令:

systemctl start postfix

7.5.2 测试域名数据表的读取

下面的 example.com 请替换为自己设定的域名。

执行以下命令:

postmap -q example.com mysql:/etc/postfix/mysql_mailbox_domains.cf

若返回 1,则说明设置正确。

7.5.3 测试用户数据表的读取

下面的 [email protected] 请替换为自己设定的用户名的其中一个。

执行以下命令:

postmap -q [email protected] mysql:/etc/postfix/mysql_mailbox_maps.cf

若返回用户名,则说明设置正确。

7.5.4 测试别名数据表的读取(可选)

下面的 [email protected] 请替换为自己设定的别名其中一个。

执行以下命令:

postmap -q [email protected] mysql:/etc/postfix/mysql_alias_maps.cf

若返回别名所对应的真实用户名,则说明设置正确。

7.5.5 停止 Postfix 服务

等全部配置完成后再启动。

执行以下命令:

systemctl stop postfix

八、配置 Dovecot

8.1 备份原版配置文件

执行以下命令:

cp -r /etc/dovecot /etc/dovecot.bak

8.2 修改 dovecot.conf

说明
• 该文件配置 Dovecot 的全局参数。

执行以下命令:

cat > /etc/dovecot/dovecot.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

protocols = imap lmtp
dict {
}
!include conf.d/*.conf
!include_try local.conf
EOF

8.3 修改 conf.d/10-mail.conf

说明
• 该文件配置邮箱文件存储的位置和命名空间。

执行以下命令:

cat > /etc/dovecot/conf.d/10-mail.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

namespace inbox {
  inbox = yes
}
first_valid_uid = 1000
mbox_write_locks = fcntl
mail_location = maildir:/var/spool/mail/%d/%n
mail_privileged_group = mail
EOF

8.4 修改 conf.d/15-mailboxes.conf

说明
• 该文件配置邮箱内部的目录结构。

执行以下命令:

cat > /etc/dovecot/conf.d/15-mailboxes.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

namespace inbox {
  mailbox Drafts {
    auto = create
    special_use = \Drafts
  }
  mailbox Trash {
    auto = create
    special_use = \Trash
  }
  mailbox Sent {
    auto = create
    special_use = \Sent
  }
}
EOF

8.5 修改 conf.d/10-auth.conf

说明
• 该文件配置用户身份认证流程。

执行以下命令:

cat > /etc/dovecot/conf.d/10-auth.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

auth_mechanisms = plain login
!include auth-sql.conf.ext
EOF

8.6 修改 conf.d/auth-sql.conf.ext

说明
• 该文件配置数据库认证的参数。

执行以下命令:

cat > /etc/dovecot/conf.d/auth-sql.conf.ext << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=mail_sys gid=mail_sys home=/var/spool/mail/%d/%n
}
EOF

8.7 修改 dovecot-sql.conf.ext

说明
• 该文件配置验证用户名密码所用的数据表以及认证方法。

执行以下命令:

cat > /etc/dovecot/dovecot-sql.conf.ext << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

driver = mysql
connect = host=localhost dbname=mail_sys user=mail_sys password=mail_sys
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM users WHERE email='%u';
EOF

8.8 修改 conf.d/10-ssl.conf

说明
• 该文件配置 SSL 加密参数。

执行以下命令:

cat > /etc/dovecot/conf.d/10-ssl.conf << EOF

请按实际情况以及注释提示修改以下内容,完成后去除 # 号和后面的注释,然后粘贴到命令行窗口中按回车即可。

ssl = required
ssl_cert = </etc/pki/tls/certs/cert.pem # mail.example.com 证书文件位置,需要修改
ssl_key = </etc/pki/tls/private/key.pem # mail.example.com 证书私钥文件位置,需要修改
ssl_protocols = TLSv1.2 TLSv1.1 !TLSv1 !SSLv2 !SSLv3
ssl_cipher_list = ALL:!MD5:!DES:!ADH:!RC4:!PSD:!SRP:!3DES:!eNULL:!aNULL
EOF

8.9 修改 conf.d/10-master.conf

说明
• 该文件配置 Dovecot 中各服务的主要参数。

执行以下命令:

cat > /etc/dovecot/conf.d/10-master.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

service lmtp {
    unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

service imap {

}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

  unix_listener auth-userdb {
    mode = 0600
    user = mail_sys
  }
  user = dovecot
}

service auth-worker {
  user = mail_sys
}
EOF

8.10 修改 conf.d/15-lda.conf

说明
1、该文件配置特定域的 postmaster 邮箱;
2、我自己没有修改这个配置文件。但是根据部分网友反馈,如果不设定 postmaster 邮箱的话会导致收不到邮件,所以还是加上去吧。

执行以下命令:

cat > /etc/dovecot/conf.d/15-lda.conf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

postmaster_address = postmaster@%d

protocol lda {
}
EOF

九、配置 OpenDKIM

9.1 修改 OpenDKIM 配置文件

执行以下命令:

cat > /etc/opendkim.conf << EOF

请按实际情况以及注释提示修改以下内容,完成后去除 # 号和后面的注释,然后粘贴到命令行窗口中按回车即可。

Syslog yes
UMask 002
OversignHeaders From
Socket inet:[email protected]
Domain example.com # 您的域名,需要修改
KeyFile /etc/opendkim/keys/mail.private
Selector mail
RequireSafeKeys no
EOF

9.2 生成私钥

下面的 example.com 请替换成您的域名。

执行以下命令:

opendkim-genkey -D /etc/opendkim/keys/ -d example.com -s mail && \
chown -R opendkim:opendkim /etc/opendkim/keys/

9.3 配置 Postfix 的 main.cf

配置发件增加 DKIM 签名。

执行以下命令:

cat >> /etc/postfix/main.cf << EOF

以下内容直接粘贴到命令行窗口中按回车即可。

milter_protocol = 2
milter_default_action = accept
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = inet:127.0.0.1:8891
EOF

9.4 启动所有服务

执行以下命令:

systemctl start postfix dovecot opendkim

如需开机启动,请执行以下命令:

systemctl enable postfix dovecot opendkim mariadb

十、配置域名记录

以下项目需要在域名服务商或云主机服务商的控制面板中设置。下文的域名服务商以 DNSPOD 为例,其他域名服务商或云主机服务商请自行参阅相关说明文档。

10.1 A 记录的设定

在域名控制面板中添加一条记录,主机记录填 @ ,记录类型选 A 记录值填 1.1.1.1 ,其他保持默认,保存即可。

10.2 MX 记录的设定

在域名控制面板中添加一条记录,主机记录填 @ ,记录类型选 MX ,记录值填 mail.example.com,其他保持默认;
再添加一条记录,主机记录填 mail ,记录类型选 A ,记录值填 1.1.1.1,其他保持默认,保存即可。

10.3 SPF 记录的设定

在域名控制面板中添加一条记录,主机记录填 @ ,记录类型选 TXT ,记录值填 v=spf1 mx -all,其他保持默认,保存即可。

10.4 DMARC 记录的设定

在域名控制面板中添加一条记录,主机记录填 _dmarc ,记录类型选 TXT ,记录值填 v=DMARC1; p=reject,其他保持默认,保存即可。

10.5 DKIM 记录的设定

(以我的密钥文件为例)

执行以下命令:

cat /etc/opendkim/keys/mail.txt 

会出现以下结果:

mail._domainkey IN  TXT ( "v=DKIM1; k=rsa; "
      "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDU5nkxbS36hOa2FCJqotvahTbxb83CvLt5XqV4WAPzJQmfaA1eHyvOz9XiZgE8vWRgP2jJFlL+J4yEroB3YV/8EBjAM8lFTi31DVgRsoHMwH6f3GuLAfcuVofymDfRxHxPzIlm7rgzfWwrGcPrIzt64NLuZG4yusTWp8MTfWZxvQIDAQAB" )  ; ----- DKIM key default for example.com

把括号内的值复制出来,去掉所有引号并整理成一行,形如:

v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDU5nkxbS36hOa2FCJqotvahTbxb83CvLt5XqV4WAPzJQmfaA1eHyvOz9XiZgE8vWRgP2jJFlL+J4yEroB3YV/8EBjAM8lFTi31DVgRsoHMwH6f3GuLAfcuVofymDfRxHxPzIlm7rgzfWwrGcPrIzt64NLuZG4yusTWp8MTfWZxvQIDAQAB

在域名控制面板中添加一条记录,主机记录填 mail._domainkey ,记录类型选 TXT ,记录值填 v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDU5nkxbS36hOa2FCJqotvahTbxb83CvLt5XqV4WAPzJQmfaA1eHyvOz9XiZgE8vWRgP2jJFlL+J4yEroB3YV/8EBjAM8lFTi31DVgRsoHMwH6f3GuLAfcuVofymDfRxHxPzIlm7rgzfWwrGcPrIzt64NLuZG4yusTWp8MTfWZxvQIDAQAB ,其他保持默认,保存即可。

上述五个步骤完成后域名控制面板的效果如下图:

10.6 IP 反向解析

这个……普通家庭宽带用户就别想了,企业宽带和国内云主机用户需要网站通过备案才能设置。如果用国外云主机的话,有些可以支持(我使用的 Vultr 支持)。将服务器的 IP 地址反向解析(PTR 记录)到 MX 域名(这里就是 mail.example.com)可以大幅降低外发邮件被拒收的几率。

十一、配置 Web 邮箱

11.1 下载并安装 Roundcube

11.1.1 下载

执行以下命令:

wget https://github.com/roundcube/roundcubemail/releases/download/1.3.0/roundcubemail-1.3.0-complete.tar.gz

11.1.2 解压 & 安装

执行以下命令:

tar -xf roundcubemail-1.3.0-complete.tar.gz && \
mv roundcubemail-1.3.0 /usr/share/roundcube && \
chown -R apache:apache /usr/share/roundcube

11.2 配置 Nginx 、PHP

11.2.1 配置 Nginx

执行以下命令:

vi /etc/nginx/conf.d/mail.conf

请按实际情况以及注释提示修改以下内容,在命令行窗口按下 i ,将内容直接粘贴到命令行窗口中,再按下 ESC ,最后输入 :wq 按回车。

server {
    listen       80;
    server_name  mail.example.com; # 您的域名前面加上 mail. 需要修改
    return 301 https://$server_name$request_uri;
}

server {
    listen       443 ssl http2;
    server_name  mail.example.com; # 您的域名前面加上 mail. 需要修改
    ssl_certificate "/etc/pki/tls/certs/cert.pem"; # mail.example.com 证书文件位置,需要修改
    ssl_certificate_key "/etc/pki/tls/private/key.pem"; # mail.example.com 证书私钥文件位置,需要修改
    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains";
    location / {
     root         /usr/share/roundcube;
     index        index.php;     
    }   
    location ~ .php$ {
        root         /usr/share/roundcube;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

11.2.2 配置 PHP

设置时区

执行以下命令:

echo "date.timezone = Asia/Shanghai" >> /etc/php.ini 

创建会话文件夹

执行以下命令:

mkdir /var/lib/php/session && \
chown apache:apache /var/lib/php/session

11.3 配置数据库

11.3.1 进入 MySQL 命令行界面

执行以下命令:

mysql -u root -p

然后输入 5.1.2 设置的数据库 Root 密码然后按回车。

出现 MariaDB [(none)]> 指示符就说明进入了。

11.3.2 创建一个用户用于读写 Roundcube 数据库

执行以下 SQL 语句:

CREATE USER 'roundcube'@'localhost' IDENTIFIED BY 'roundcube';

11.3.3 创建 Roundcube 数据库

执行以下 SQL 语句:

CREATE DATABASE roundcube;

11.3.4 为用户授予读写权限

执行以下 SQL 语句:

GRANT ALL ON roundcube.* TO 'roundcube'@'localhost' IDENTIFIED BY 'roundcube';

11.3.5 刷新权限

执行以下 SQL 语句:

FLUSH PRIVILEGES;

数据库设置完成。按 Ctrl + D 退出 MySQL 命令行界面。

11.4 启动服务

执行以下命令:

systemctl start nginx php-fpm

如需开机启动,请执行以下命令:

systemctl enable nginx php-fpm

11.5 配置 Roundcube

打开浏览器,输入 https://mail.example.com/installer/?_step=1 ,回车打开。然后按图片提示进行配置。

配置完成后,关闭浏览器页面。执行以下命令来使安装程序不可用:

chmod -R 000 /usr/share/roundcube/installer/

11.6 登录

打开浏览器,输入 https://mail.example.com ,然后输入您的用户名密码登录。

登录之后,默认只会有一个收件箱,我们再设置一下就完美了!

最终效果如下图:

十二、收发件测试

12.1 收件测试

非常简单,从 QQ 或者 Gmail 给自己的邮箱发一封邮件,如果可以收到,说明外网已经可以连通这台服务器了。

12.2 发件测试

打开浏览器,输入 http://www.mail-tester.com ,回车打开。发一封邮件到它指定的邮箱里。然后过一分钟左右查看下结果,重点检查 SPF 记录DMARC 记录和 DKIM 签名是不是有效的。如果都是有效的,那恭喜您!您的邮件系统已经搭建完了!至于这个得分,由于受 IP 地址可能被拉黑的影响,因此各人的分数有可能不同。如果有 8 分以上的话发出去的邮件基本上就不会被拒收了。如果很不幸 IP 地址被拉黑了,建议换一个 IP 地址。

十三、防火墙的设置(可选)

如果服务器需要部署防火墙,请放行 TCP 25TCP 465TCP 587 以及 TCP 993 这四个端口的传入链接。

以 iptables 为例,配置命令为:

iptables -A INPUT -p tcp -m multiport --dports 25,465,587,993 -j ACCEPT

然后根据需要自行保存防火墙的配置。

十四、不完美的地方 & 总结

  • 之所以标题上的完美打了个双引号,原因之一是本来我计划配置垃圾邮件过滤功能的,但是不知道是不是我设置错误还是网络问题,很多非垃圾邮件都被拒收了,在网上搜索了两天还是无解,所以暂时放弃。(PS :几个月前我用过一次 iRedMail ,里面的垃圾邮件过滤模块也同样导致很多非垃圾邮件被拒收,所以我感觉网络问题大一些。)
  • 此外,这个邮件系统并没有实现使用 Web 界面管理邮件系统(如增减用户)的功能,由于我搭建的邮件系统只有我一个人在用,所以也就不折腾啦。
  • 最后非常感谢能够耐心读完这篇文章的人,希望您有所收获!

十五、参考文献

  1. Email with Postfix, Dovecot, and MySQL
  2. How To Configure a Mail Server Using Postfix, Dovecot, MySQL, and SpamAssassin
  3. Postfix + Dovecot + MySQL 搭建邮件服务器
  4. Ubuntu Postfix Mail Server 設定筆記 (一) MTA (SPF, DKIM, rDNS 及 DMARC)
  5. Debian 8 Server 搭建 Postfix + Dovecot 邮件服务器
  6. Postfix and TLS encryption
  7. Postfix Configuration Parameters
  8. How to install Roundcube
  9. Running Roundcube 0.7.1 On Nginx (LEMP) On Debian Squeeze/Ubuntu 11.10

编辑于 2020-01-03

原作者: https://zhuanlan.zhihu.com/p/28816035