前阵子和几位朋友一同建起了一个小社区网站,期间搭建了一个团队服务器用同步代码和收发邮件,搭建邮件服务还是头一回。现在现成的邮件服务器解决方案也是有的,不过还是自己搭建了一遍,熟悉了一下具体流程。这两天在自己的服务器上也部署了,大致记录下:
结构:
Postfix 提供 SMTP,submission 服务,并对请求进行过滤,转发。SMTP 是邮件投递的基础。Postfix 各种功能可以添加外部实现,比如这里会将 Virtual Mail 通过 LMTP 转给Dovecot,Email 信息查询部分由 MySQL 提供,通过 pypolicyd-spf 进行过滤,用户验证也由 Dovecot 完成。
Dovecot 负责 IMAP 等服务的请求和用户验证,管理虚拟用户的邮箱内容等,和Postfix通过 LMTP 链接。
使用 MySQL 存储邮箱用户的账户信息,MySQL是可选的存储方式之一。
为了安全起见所有链接均用了加密的方式,普通 STMP,IMAP 链接强制必须使用 STARTTLS 升级为安全链接,STMPS,IMAPS 链接和 HTTPS 一样一开始就为加密链接。
安装Postfix、Dovecot、MySQL、pypolicyd-spf
视不同发行版安装相应的包即可。
配置:
系统:
建立vmail用户,vmail文件夹
useradd -s /sbin/nologin -d /var/vmail -m -r -g mail vmail
chown vmail:mail /var/vmail
chmod o-rwx vmail /var/vmail
grep vmail /etc/passwd | awk -F : '{print $3}'
建立并获得的vmail的uid,稍后会用到。
也可以指定uid建立vmail用户:
useradd -s /sbin/nologin -d /var/vmail -m -r -u <uid> vmail
建立 aliases.db(如果不存在的话),Postfix会用到
touch /etc/alises
newaliases
MySQL:
初次开启的话记得运行一下mysql_secure_installation。
通过 mysql -u root -p 输入密码进入 Mysql,参考以下步骤建表,插入数据。
create user vmail@localhost identified by 'vmailpassword';
create database vmail;
grant select on vmail.* to vmail@localhost identified by 'vmailpassword';
use vmail;
create table domains (name char(30) not null primary key);
create table users (email char(64) not null primary key, password char(128) not null, domain char(30) not null, foreign key (domain) references domains(name));
create table aliases (source char(64) not null primary key, destination char(64) not null, foreign key (destination) references users(email));
insert into domains values('----.com');
insert into users values('[email protected]', ENCRYPT('--password--here--', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))),'----.com');
insert into aliases values ('[email protected]', '[email protected]');
domains表存放virtual domian,user表存放可登陆的用户邮箱和密码。
Postfix 配置:
配置文件:
注意修改前备份,方便恢复和参考。主要修改以下参数:
设置服务器信息,有单个服务器的情况下设置比较简单,domain为服务器域名:
mydestination 不可与 virtual main domain 冲突,否则发往 Dovecot 的邮件会被 Postfix 截下来,一般会找不到用户而 Reject,如果正好有重名的用户的话应该会发往错误的用户。
mydestination = localhost
mydomain = <domain>
myorigin = <domain>
myhostname = <domain>
mynetworks = 127.0.0.0/8
mynetworks_style = host
relay_domains =
TLS证书设置:
smtpd_tls_cert_file = /etc/ssl/private/----crt----
smtpd_tls_key_file = /etc/ssl/private/----key----
smtpd_tls_CApath = /etc/ssl/certs
smtpd_use_tls = yes
SASL支持设置,设置为不兼容老旧客户端:
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous, noactive, nodictionary
smtpd_sasl_local_domain = $mydomain
broken_sasl_auth_clients = no
一些安全强化选项,使用高强度的加密算法,和启用TLS,以及设置空的readme文件夹等。
smtpd_tls_mandatory_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CDC3-SHA, KRB5-DE5, CBC3-SHA
smtpd_tls_mandatory_ciphers = high
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, TLSv1, TLSv1.1, TLSv1.2
smtpd_tls_exclude_ciphers = $smtpd_tls_mandatory_exclude_ciphers
smtpd_tls_ciphers = $smtpd_tls_mandatory_ciphers
smtpd_tls_protocols = $smtpd_tls_mandatory_protocols
smtpd_tls_auth_only = yes
smtpd_tls_dh1024_param_file = /etc/postfix/dh2048.pem
smtpd_tls_dh512_param_file = /etc/postfix/dh512.pem
smtpd_tls_eecdh_grade = ultra
smtpd_tls_security_level = may
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
# outgoing
smtp_tls_mandatory_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CDC3-SHA, KRB5-DE5, CBC3-SHA
smtp_tls_mandatory_ciphers = high
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, TLSv1, TLSv1.1, TLSv1.2
smtp_tls_exclude_ciphers = $smtp_tls_mandatory_exclude_ciphers
smtp_tls_ciphers = $smtp_tls_mandatory_ciphers
smtp_tls_protocols = $smtp_tls_mandatory_protocols
smtp_tls_security_level = may
smtp_tls_note_starttls_offer = yes
smtp_tls_loglevel = 1
tls_random_source = dev:/dev/urandom
tls_preempt_cipherlist = yes
# Log the hostname of a remote SMTP server that offers STARTTLS, when TLS is not already enabled for that server.
# No readme for more security
readme_directory = no
sample_directory = no
生成对应的文件,在Shell中执行以下语句:
openssl dhparam -out /etc/postfix/dh2048.pem 2048
openssl dhparam -out /etc/postfix/dh512.pem 512
如果强制启用TLS的话,将上面的 *_level = may 改为 enforce。不过会导致和一些邮箱不兼容,比如126,163。
一些行为配置,酌情修改:
在找不到收信人的情况下返回永久错误,让发信服务器不再重试投递:
unknown_local_recipient_reject_code = 550
超时和错误次数限制:
# Time before log a delayed warning
delay_warning_time = 4h
# how long to keep message on queue before return as failed.
maximal_queue_lifetime = 7d
# max and min time in seconds between retries if connection failed
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
# how long to wait when servers connect before receiving rest of data
smtp_helo_timeout = 60s
# how many address can be used in one message.
# effective stopper to mass spammers, accidental copy in whole address list
# but may restrict intentional mail shots.
smtpd_recipient_limit = 16
# how many error before back off.
smtpd_soft_error_limit = 3
# how many max errors before blocking it.
smtpd_hard_error_limit = 12
配置和Dovecot的链接参数:
通过lmtp协议连接到dovecot,设置vmail的文件夹,用户等参数。
其中 uid 为 vmail 的 uid,gid 为 mail 的 gid,如果不是12的话酌情修改。(grep mail /etc/group)
virtual_transport = lmtp:unix:private/dovecot-lmtp
address_verify_virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
virtual_mailbox_base = /var/vmail
virtual_uid_maps = static:998
virtual_gid_maps = static:12
其中一些位置可以写死,减少SQL调用,比如只有一个 virtual_mailbox_domain:
virtual_mailbox_domains = ----.com
其中需要三个SQL查询用的配置文件:
/etc/postfix/mysql-virtual-mailbox-domains.cf:
vim mysql-virtual-mailbox-domains.cf
user = vmail
password = vmailpassword
hosts = 127.0.0.1
dbname = vmail
query = SELECT 1 FROM domains WHERE name='%s'
mysql-virtual-mailbox-maps.cf:
vim /etc/postfix/mysql-virtual-mailbox-maps.cf
user = vmail
password = vmailpassword
hosts = 127.0.0.1
dbname = vmail
query = SELECT 1 FROM users WHERE email='%s'
/etc/postfix/mysql-virtual-alias-maps.cf:
vim /etc/postfix/mysql-virtual-alias-maps.cf
user = vmail
password = vmailpassword
hosts = 127.0.0.1
dbname = vmail
query = SELECT destination FROM aliases WHERE source='%s'
最后邮件发送/接受限制,阻止一些简单的垃圾/骚扰邮件:
强制 Client 发送 HELO,启用 REJECT DELAY 和下面的 client 中的 reject_unknown_client_hostname 过滤配合,只让有MX记录/通过验证的用户使用SMTP,禁用VRFY:
smtpd_helo_required = yes
smtpd_delay_reject = yes
disable_vrfy_command = yes
restriction 规则:
tpd_client_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_plaintext_session, reject_unknown_client_hostname, permit
smtpd_relay_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service unix:private/policyd-spf, permit
smtpd_helo_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_helo_hostname, permit
smtpd_sender_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unverified_sender, permit
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service unix:private/policyd-spf, permit
smtpd_data_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_multi_recipient_bounce, permit
smtpd_end_of_data_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, warn_if_reject, reject_multi_recipient_bounce, permit
smtpd_etrn_restrictions = reject
SPF设置:延长SPF验证超时时间:
policy_time_limit = 3600
编辑 /etc/postfix/master.cf 加入以下来开启SPF服务,配合上面的过滤:
policyd-spf unix - n n - 0 spawn
user=tpe argv=/usr/bin/policyd-spf /etc/policyd-spf/policyd-spf.conf
继续编辑 /etc/postfix/master.cf ,开启SMTP服务:
smtp inet n - n - - smtpd
...
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
....
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
....
smpts工作于wraper模式,即不要STARTTLS,直接建立TLS链接,其他参数按照main.cf为默认不修改。
执行:
postfix set-permissions
service postfix start
启动 postfix。
Dovecot:
配置文件,修改前注意备份:
编辑 /etc/dovecot/dovecot.conf,开启 imap lmtp。 imap 提供外部访问,lmtp给postfix用。
protocols = imap lmtp
需要这一行来加载conf.d中的配置文件。
!include conf.d/*.conf
编辑 /etc/dovecot/conf.d/10-mail.conf,指定vmail的文件位置和UID,GID,同时限制Dovecot能使用的GID,UID。如果inbox没有启用的话,取消注释启用。
mail_location = maildir:/var/vmail/%d/%n
mail_uid = vmail
mail_gid = mail
first_valid_uid = 998
last_valid_uid = 998
first_valid_gid = 12
last_valid_gid = 12
编辑: /etc/dovecot/conf.d/10-auth.conf,设置验证:
disable_plaintext_auth = yes
auth_mechanisms = plain login
注释除了sql以外的验证途径,仅保留:
!include auth-sql.conf.ext
编辑 conf.d/auth-sql.conf.ext,配置SQL验证的参数:
密码通过MySQL验证,用户登陆后使用的GID,UID统一使用vmail,mail,文件位置统一为/var/mail/。
passdb {
driver = sql
# Path for SQL configuration file, see example-config/dovecot-sql.conf.ext
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=vmail gid=vmail home=/var/vmail/%d/%n
}
编辑 /etc/dovecot/dovecot-sql.conf.ext,配置查询语句:
driver = mysql
connect = host=localhost dbname=vmail user=vmail password=vmailpassword
default_pass_scheme = SHA512-CRYPT
password_query = \
SELECT email as user, password FROM users WHERE email='%u';
编辑 /etc/dovecot/conf.d/10-logging.conf,显示更多日志:
auth_verbose = yes
编辑 /etc/dovecot/conf.d/10-master.conf,配置 Dovecot 提供的各种服务参数:
LMTP 和 Auth 服务均以 unix socket 形式与 Postfix 链接。worker 进程以 dovecot 身份运行。socket的位置均放在 /var/spool/postfix/private/ 中:
default_internal_user = dovecot
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0666
}
# Create inet listener only if you can't use the above UNIX socket
#inet_listener lmtp {
# Avoid making LMTP visible for the entire internet
#address =
#port =
#}
}
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Users that have
# full permissions to this socket are able to get a list of all usernames and
# get the results of everyone's userdb lookups.
#
# The default 0666 mode allows anyone to connect to the socket, but the
# userdb lookups will succeed only if the userdb returns an "uid" field that
# matches the caller process's UID. Also if caller's uid or gid matches the
# socket's uid or gid the lookup succeeds. Anything else causes a failure.
#
# To give the caller full permissions to lookup all users, set the mode to
# something else than 0666 and Dovecot lets the kernel enforce the
# permissions (e.g. 0777 allows everyone full permissions).
unix_listener auth-userdb {
mode = 0666
#user =
#group =
}
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0666
}
# Auth process is run as this user.
user = $default_internal_user
}
default_internal_user = dovecot
service auth-worker {
# Auth worker process is run as root by default, so that it can access
# /etc/shadow. If this isn't necessary, the user should be changed to
# $default_internal_user.
user = $default_internal_user
}
编辑 /etc/dovecot/conf.d/10-ssl.conf,管理IMAP服务的链接安全性:
ssl = required
ssl_cert = </etc/ssl/private/<cert_file>
ssl_key = </etc/ssl/private/<key_file>
ssl_dh_parameters_length = 2048
ssl_protocols = !SSLv2 !SSLv3 TLSv1 TLSv1.1 TLSv1.2
ssl_cipher_list = DEFAULT:!EXPORT:!LOW:!MEDIUM:!MD5
ssl_prefer_server_ciphers = yes
其中将dh key长度设置为了2048,启动dovecot 后 dovecot 会去生成新的 ssl-parameters,如果设备性能不高的话会花费很长时间。如果ssl-parameters出现问题可以删除旧的 ssl-parameters (/var/lib/ssl-parameters.dat) 之后重启dovecot。
编辑 15-lda.conf,配置LMTP参数:
postmaster_address = [email protected]
hostname = --domain.com--
防火墙:
使用Iptables的话,需要打开以下端口:
参考设置(没启用POP3):
#imap/imaps
-A INPUT -p tcp -m tcp --dport 143 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp -m tcp --dport 993 -m conntrack --ctstate NEW -j ACCEPT
#smtp/smpts
-A INPUT -p tcp -m tcp --dport 25 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -p tcp -m tcp --dport 465 -m conntrack --ctstate NEW -j ACCEPT
重新加载iptables,postfix,dovecot,之后服务器应该就可以用了。
配置域名:
首先要有个域名,添加MX记录,SPF记录,MX记录用于在接收邮件的时候解析,这样其他邮件服务器可以知道给你投递邮件的话投递到什么地方,SPF记录用来判断一个IP是否有权利以此域名的名义发送邮件,防止别人冒充你。
MX记录为服务器IP即可。
TXT记录可以参考:
v=spf1 mx ~all
意为允许MX记录中的IP地址使用你的域名发信,其他地址均不能以你的名义发信。
完成:
邮件服务器建好了,启动服务,之后可以用Thunderbird之类的客户端登陆。
TODO:
反垃圾,Docker,性能,Header checker