前阵子和几位朋友一同建起了一个小社区网站,期间搭建了一个团队服务器用同步代码和收发邮件,搭建邮件服务还是头一回。现在现成的邮件服务器解决方案也是有的,不过还是自己搭建了一遍,熟悉了一下具体流程。这两天在自己的服务器上也部署了,大致记录下:
结构:
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