Postfix + SASL + Courier + MySQL + TLS 邮件服务器搭建笔记

应 @Andy1999 要求搭建邮件服务器,为此折腾了一个多月。寻找到的教程或不完整,或已经过老。最后重装了 3 次 VPS 系统,换了 N 次软件后搭建成功,念到也许有人会需要,特此记录并且分享。

系统环境和所用软件

  • Debian wheezy
  • MySQL
  • Postfix
  • Cyrus-SASL (saslauthd)
  • Courier-Authlib (authdaemond)
  • Courier-IMAP
  • OpenSSL

系统结构

+
    Internet----------------->Receive a mail       Failed-------Reject
                                          \          /
    Internet<---Send----If succeeded    Posifix----Auth----Succeeded----Drop
                             \                       \                    \
    +----SMTP----Postfix----Auth-----saslauthd------MySQL              Maildir/
    |                                                /                  /
    +----IMAP----Courier----Auth----authdaemond-----+                  /
    |                         \                                       / 
  Mail Client             If succeeded-----------------------------Manage
+

准备

假设邮件服务器 IP 是 123.123.123.123 ,用作邮箱的域名是 qaq.cat

  • 把 mta.qaq.cat 解析到 123.123.123.123
  • 把 qaq.cat 的 MX 记录指向 mta.qaq.cat
  • 切换到 root 用户 sudo su -
  • 把系统升级到最新总是没错的 apt-get update && apt-get upgrade

一、 MySQL

apt-get install mysql mysql-server mysql-client
service mysql start

Debian 下安装的时候就会弹框让你设置好 root 密码,一切自动化而不像 RHEL 需要手动执行配置命令

创建数据库、创建用户并授权,这里 mysql 用户是 mail ,数据库名是 maildb ,密码是 mysqlpw 。这里

CREATE DATABASE maildb;
GRANT ALL PRIVILEGES ON maildb.* TO "mail"@"localhost" IDENTIFIED BY "mysqlpw";
FLUSH PRIVILEGES;

另外说一下,如果你需要在邮件系统之外额外增加用户系统(比如 MoeMail )而需要从外部访问数据库,那么就应该把第二句中的 “mail”@”localhost” 换成 “mail”@”%” ,表示允许用户从任意来源访问数据库。

创建数据表:
USE maildb;

CREATE TABLE aliases (
 pkid smallint(3) NOT NULL auto_increment,
 mail varchar(120) NOT NULL default '',
 destination varchar(120) NOT NULL default '',
 enabled tinyint(1) NOT NULL default '1',
 PRIMARY KEY  (pkid),
 UNIQUE KEY mail (mail)
);

CREATE TABLE domains (
 pkid smallint(6) NOT NULL auto_increment,
 domain varchar(120) NOT NULL default '',
 transport varchar(120) NOT NULL default 'virtual:',
 enabled tinyint(1) NOT NULL default '1',
 PRIMARY KEY  (pkid)
);

CREATE TABLE users (
 id varchar(128) NOT NULL default '',
 name varchar(128) NOT NULL default '',
 home varchar(255) NOT NULL default '/var/spool/mail/virtual',
 enabled tinyint(1) NOT NULL default '1',
 change_password tinyint(1) NOT NULL default '1',
 clear varchar(128) NOT NULL default 'changemepls',
 crypt varchar(128) NOT NULL default 'V2VkIE9jdCAyOSAxMzo1MD',
 quota varchar(255) NOT NULL default '',
 PRIMARY KEY  (id),
 UNIQUE KEY id (id)
);

aliases 表是转发规则列表,发到 mail 列中的邮箱的邮件会被转发到 destination 列的邮箱

domains 表是域名列表,发到不在这个表内的域名的邮箱的邮件会被丢弃

users 表是虚拟用户列表,存有邮箱、密码和可用状态等信息。

二、 Postfix

apt-get install postfix postfix-mysql

安装时会要求选择邮箱类型,选 Internet Site ,要求输入域名时留空即可(因为域名会在 MySQL 中定义)

配置系统转发规则、创建虚拟邮箱系统用户、创建邮件文件夹及更改权限:

cp /etc/aliases /etc/postfix/aliases
postalias /etc/postfix/aliases

groupadd --system virtual -g 5000
useradd --system virtual -u 5000 -g 5000

mkdir /var/spool/mail/virtual
chown -R virtual:virtual /var/spool/mail/virtual

Postfix 配置文件 /etc/postfix/main.cf 关键修改位置:

#对于 VPS 而言, relayhost 必须留空,否则发不出邮件
relayhost =

#如果邮件发不出去,每隔 4 小时给用户发邮件提醒
delay_warning_time = 4h

# 不要和邮箱域名一样就行
myhostname = moemail.qaq.cat

# 修改系统转发规则路径
alias_maps = hash:/etc/postfix/aliases
alias_database = hash:/etc/postfix/aliases

# 虚拟用户邮箱设置
virtual_mailbox_base = /var/spool/mail/virtual
virtual_mailbox_maps = mysql:/etc/postfix/mysql_mailbox.cf
virtual_alias_maps = mysql:/etc/postfix/mysql_alias.cf
virtual_mailbox_domains = mysql:/etc/postfix/mysql_domains.cf
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

# 这两项都清空,这些会在 MySQL 中定义
local_recipient_maps =
mydestination =

虚拟用户 MySQL 配置文件 /etc/postfix/mysql_mailbox.cf

user=mail
password=mysqlpw
dbname=maildb
table=users
select_field=CONCAT(SUBSTRING_INDEX(`id`, "@", -1), "/", SUBSTRING_INDEX(`id`, "@", 1), "/")
where_field=id
hosts=127.0.0.1
additional_conditions = and enabled = 1

转发规则 MySQL 配置文件 /etc/postfix/mysql_mailbox.cf

user=mail
password=passwd
dbname=postfix
table=aliases
select_field=destination
where_field=mail
hosts=127.0.0.1
additional_conditions = and enabled = 1

域名 MySQL 配置文件 /etc/postfix/mysql_mailbox.cf

user=mail
password=passwd
dbname=postfix
table=domains
select_field=domain
where_field=domain
hosts=127.0.0.1
additional_conditions = and enabled = 1

修改配置文件拥有者和权限

chown root:postfix /etc/postfix/mysql_*
chmod 0640 /etc/postfix/mysql_*

向 MySQL 添加域名、用户和转发规则

USE maildb;

INSERT INTO domains (domain) VALUES ('localhost'), ('localhost.localdomain');

INSERT INTO aliases (mail,destination) VALUES
 ('postmaster@localhost','root@localhost'),
 ('sysadmin@localhost','root@localhost'),
 ('webmaster@localhost','root@localhost'),
 ('abuse@localhost','root@localhost'),
 ('root@localhost','root@localhost'),
 ('@localhost','root@localhost'),
 ('@localhost.localdomain','@localhost');

INSERT INTO users (id,name,maildir,crypt) VALUES
 ('admin@qaq.cat','root','root/',encrypt('adminpasswd', CONCAT('$5$', MD5(    RAND()))) );

这里添加了几个本地域,一堆转发规则和一个本地域邮箱。

另外我们需要添加 qaq.cat 的域名和邮箱 admin@qaq.cat ,密码是 adminpassword

INSERT INTO domains (domain) VALUES ('qaq.cat');
INSERT INTO users (id,name,maildir,crypt) VALUES ('admin@qaq.cat','Admin','admin/',encrypt('adminpasswd', CONCAT('$5$', MD5(RAND()))) );

三、 Courier-Authlib

安装

apt-get install courier-authdaemon courier-authlib-mysql gamin

authdaemond 配置文件 /etc/courier/authdaemonrc

#只留下 authmysql
authmodulelist="authmysql"

#打开 DEBUG 除错模式
DEBUG_LOGIN=2

authmysql 配置文件 /etc/courier/authmysqlrc

#修改 mysql 连接信息
MYSQL_SERVER        localhost
MYSQL_USERNAME      mail
MYSQL_PASSWORD      mysqlpw
# 注意 port 就是 0 ,不要改成 3306 ,我也不知道为什么
MYSQL_PORT      0
MYSQL_OPT       0
MYSQL_DATABASE      maildb
#下面照着改
MYSQL_USER_TABLE    users
MYSQL_CRYPT_PWFIELD crypt
MYSQL_UID_FIELD     5000
MYSQL_GID_FIELD     5000
MYSQL_LOGIN_FIELD   id
MYSQL_HOME_FIELD    home
MYSQL_NAME_FIELD    name
MYSQL_MAILDIR_FIELD CONCAT(SUBSTRING_INDEX(`id`, "@", -1), "/", SUBSTRING_INDEX(`id`, "@", 1), "/")
MYSQL_WHERE_CLAUSE  enabled=1

重启 authdaemond 服务

service courier-authdaemon restart

测试能否正确认证虚拟用户

authtest admin@qaq.cat adminpasswd

如果输出信息如下就表示认证成功:

Authentication succeeded.

     Authenticated: admin@qaq.cat  (uid 5000, gid 5000)
    Home Directory: /var/spool/mail/virtual
           Maildir: qaq.cat/admin/
             Quota: (none)
Encrypted Password: [加密的密码]
Cleartext Password: [明文密码]
           Options: (none)

否则就检查日志文件 /var/log/mail.log 进行除错

四、 Courier-IMAP

apt-get install courier-base courier-imap courier-imap-ssl courier-ssl

修改 imapd 配置文件 /etc/courier/imapd 使之监听所有 IP

# 把 ADDRESS=0 改成
ADDRESS=0.0.0.0

如果用了 iptables 防火墙,则配置放行规则

-A INPUT -p tcp -m multiport --dport 25,143 -j ACCEPT

25 是 SMTP 端口, 143 是 IMAP 端口

五、测试 Postfix SMTP 和 Courier IMAP

重启所有服务

service mysql restart
service restart
service courier-authdaemon restart
service courier-imap restart
service courier-imap-ssl restart

确定上述服务能监听正确的端口:

netstat -nltp | egrep '25|143|3306'
# 以下是输出内容
tcp   0   0 127.0.0.1:3306    0.0.0.0:*    LISTEN      10970/mysqld    
tcp   0   0 0.0.0.0:143       0.0.0.0:*    LISTEN      13872/couriertcpd
tcp   0   0 0.0.0.0:25        0.0.0.0:*    LISTEN      13116/master

telnet 测试 Postfix SMTP 收发信

telnet mta.qaq.cat 25
Trying 123.123.123.123..
Connected to qaq.cat
220 *************************
EHLO qaq.cat
250-qaq.cat
250-PIPELINING
250-SIZE 10240000
250-ETRN
250-XXXXXXXA
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM: test@example.com
250 2.1.0 Ok
RCPT TO: admin@qaq.cat
250 2.1.5 Ok
data
354 End data with .
this is a tets email
to check postfix installation from telnet
.
250 2.0.0 Ok: queued as 85E663061
quit
221 2.0.0 Bye
Connection closed by foreign host.

RCPT TO: 后面跟目标邮箱,分别测试内部互发(发到 admin@qaq.cat )和外发(比如发到 heygays@gmail.com

检查邮件队列

mailq
# 输出邮件队列为空即表示发信正常
Mail queue is empty

检查 admin@qaq.cat 收信

ls /var/spool/mail/virtual/qaq.cat/admin/new/

如果输出有文件表示收信正常

telnet 测试 IMAP 认证

telnet mta.qaq.cat 143
Trying mta.qaq.cat...
Connected to xxxxxx
Escape character is '^]'.
- OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THRE
AD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS] Courier-IMAP ready. Copyr
ight 1998-2011 Double Precision, Inc.  See COPYING for distribution information.
A01 LOGIN admin@qaq.cat adminpasswd
A01 OK LOGIN Ok.
A20 LOGOUT
- BYE Courier-IMAP server shutting down
A20 OK LOGOUT completed

Connection closed by foreign host.

六、 SMTP SASL 安全认证

安装 Courier-SASL

apt-get install libsasl2-2 libsasl2-modules libsasl2-modules-sql libgsasl7 libauthen-sasl-cyrus-perl sasl2-bin libpam-mysql

允许 postfix 访问 SASL 认证

adduser postfix sasl
mkdir -p /var/spool/postfix/var/run/saslauthd

强制 Postfix 进行 SASL 认证后再发信,把以下内容添加到 /etc/postfix/main.cf 末尾

smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = no
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =

并且修改以下两项

smtpd_sender_restrictions = permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, permit

照着修改 /etc/default/saslauthd

START=yes
DESC="SASL Authentication Daemon"
NAME="saslauthd"
MECHANISMS="pam"
MECH_OPTIONS=""
THREADS=5
OPTIONS="-r -c -m /var/spool/postfix/var/run/saslauthd"

让 Postfix 从 MySQL 进行 SASL 认证,创建 /etc/postfix/sasl/smtpd.conf

pwcheck_method: saslauthd
mech_list: plain login cram-md5 digest-md5
log_level: 7
allow_plaintext: true
auxprop_plugin: sql
sql_engine: mysql
sql_hostnames: 127.0.0.1
sql_user: mail
sql_passwd: mysqlpw
sql_database: maildb
sql_select: select crypt from users where id='%u@%r' and enabled = 1

创建或修改 pam SMTP 配置文件 /etc/pam.d/smtp 使之连接 MySQL

auth required pam_mysql.so user=mail passwd=mysqlpw host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1
account sufficient pam_mysql.so user=mail passwd=mysqlpw host=127.0.0.1 db=maildb table=users usercolumn=id passwdcolumn=crypt crypt=1

重启服务

service saslauthd restart
service postfix restart

配置到这里,一个邮件服务器已经可以使用了。如果需要让他变得更安全,你还需要配置 TLS 加密

七、 Postfix TLS SMTP

生成密钥对

cd /etc/postfix
openssl req -new -outform PEM -out postfix.cert -newkey rsa:2048 -nodes -keyout postfix.key -keyform PEM -days 1825 -x509

输入国家名代码(中国是 CN 大写)和城市、组织 /公司名、管理员邮箱等信息

为确保安全,密钥的有效期是 1825 天,即 5 年, 5 年后需要重新生成密钥。 1825 也可以改成其他数字。

在 /etc/postfix/main.cf 中修改或新建以下设置项

smtp_tls_security_level = may
smtpd_tls_security_level = may
smtp_tls_note_starttls_offer = yes
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
smtpd_tls_cert_file=/etc/postfix/postfix.cert
smtpd_tls_key_file=/etc/postfix/postfix.key

注释掉原有的这两项:

# smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
# smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

在 /etc/postfix/master.cf 中,取消 smtps 段的注释并作如下修改

smtps     inet  n       -       -       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_tls_auth_only=yes
  -o smtpd_sasl_tls_security_options=noanonymous

注意 -o 行的缩进是两个空格

八、 Courier TLS IMAP

生成密钥,有效期同样是 1825 天

cd /etc/courier
openssl req -x509 -newkey rsa:2048 -keyout imapd.pem -out imapd.pem -nodes -days 1825

修改 /etc/courier/imapd-ssl 中以下的几项

SSLPORT=993
SSLADDRESS=0.0.0.0
IMAP_TLS_REQUIRED=1
TLS_CERTFILE=/etc/courier/imapd.pem
TLS_TRUSTCERTS=/etc/ssl/certs

SSLADDRESS=0.0.0.0 表示绑定所有 IP ,接受所有来源的连接

如果 IMAP_TLS_REQUIRED 是 1 ,那么客户端只能通过 993 端口连接加密的 IMAP ,明文传输的 143 端口将拒绝接受连接

如果用了 iptables 防火墙,则配置放行规则:

-A INPUT -p tcp -m multiport --dport 587,465,993 -j ACCEPT

587, 465 是 SMTP TLS 端口, 993 是 IMAP TLS 端口

九、测试 SMTP 和 IMAP 安全连接

重启所有服务

service mysql restart
service postfix restart
service courier-authdaemon restart
service courier-imap restart
service courier-imap-ssl restart
service saslauthd restart

查看端口是否被正确监听

netstat -nltp | egrep '25|587|465|143|993|3306'
#以下是输出内容
tcp   0   0 127.0.0.1:3306  0.0.0.0:*     LISTEN    18177/mysqld    
tcp   0   0 0.0.0.0:587     0.0.0.0:*     LISTEN    20534/master    
tcp   0   0 0.0.0.0:143     0.0.0.0:*     LISTEN    17434/couriertcpd
tcp   0   0 0.0.0.0:465     0.0.0.0:*     LISTEN    20534/master    
tcp   0   0 0.0.0.0:25      0.0.0.0:*     LISTEN    20534/master    
tcp   0   0 0.0.0.0:993     0.0.0.0:*     LISTEN    20710/couriertcpd

这里我在测试的时候,不知道为什么, Postfix 只监听 465 而不监听 587 。这两个端口的区别在于: 465 是 SSL/TLS 协议,整个连接周期内数据都是加密的;而 587 是 STARTTLS ,在客户端请求 STARTTLS 命令后才开始加密传输。

SSL/TLS SMTP 连接测试

openssl s_client -connect mta.qaq.cat:465

CONNECTED(00000003)
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = MoeNet, OU = MoeMail, CN = MoeMail, emailAddress = admin@qaq.cat
verify error:num=18:self signed certificate
verify return:1
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = MoeNet, OU = MoeMail, CN = MoeMail, emailAddress = admin@qaq.cat
verify return:1
---
证书信息略去
---
220 moemail.qaq.cat ESMTP Postfix (Debian/GNU)
HELO qq.com
250 moemail.qaq.cat
AUTH LOGIN
334 VXNlcm5hbWU6
(这里输入 base64 编码的邮箱)
334 UGFzc3dvcmQ6
(base64 编码的密码)
235 2.7.0 Authentication successful
QUIT
221 2.0.0 Bye
closed

SSL/TLS IMAP 链接测试

openssl s_client -connect mta.qaq.cat:993

CONNECTED(00000003)
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = MoeNet, OU = MoeMail, CN = MoeMail, emailAddress = admin@qaq.cat
verify error:num=18:self signed certificate
verify return:1
depth=0 C = CN, ST = Shanghai, L = Shanghai, O = MoeNet, OU = MoeMail, CN = MoeMail, emailAddress = admin@qaq.cat
verify return:1
---
证书信息略去
---
- OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT THREAD=REFERENCES SORT QUOTA IDLE AUTH=PLAIN ACL ACL2=UNION] Courier-IMAP ready. Copyright 1998-2011 Double Precision, Inc.  See COPYING for distribution information.
A01 LOGIN admin@qaq.cat adminpasswd
A01 OK LOGIN Ok.
A20 LOGOUT
- BYE Courier-IMAP server shutting down
A20 OK LOGOUT completed
closed

十、最后的清理工作

编辑 /etc/courier/authdaemonrc 关闭 DEBUG ,以免 log 泄露重要信息

DEBUG_LOGIN=0

Enjoy it!

via: v2ex.com