最近在服务器上折腾了一下邮箱自托管。使用 postfix 作为 MTA&MDA 收信,dovecot 作为 imap server。因为 vps 会拦截目标端口25的出站流量,所以发信使用第三方服务了(smtp2go)。服务器上也没有发信需求,所以 postfix 完全没有配发信。
postfix 和 dovecot 通过 Arch Linux 官方仓库安装。别的安装方式的默认配置文件的位置可能不同。
Postfix 配置
目标:只收不发;多个域名上的多个虚拟用户,无对应 unix 用户。
基本知识
配置文件
- main.cf: 默认配置
- master.cf: 每个 service 的配置,会覆盖 main.cf 中的默认配置
hash
main.cf 中可以用 hash:/path/to/file 来指定加载 hash。例:
1 2 3
| # main.cf # postfix 实际会寻找 /etc/postfix/bar.db 文件 foo = hash:/etc/postfix/bar
|
1 2 3
| # /etc/postfix/bar key1 value1, value2 key2 value3
|
用 postmap [-F] bar
来生成 bar.db
。 -F
会将 value 视为文件路径列表,将文件内容用 base64 编码、连接。
基本配置
- main.cf
- myhostname: 互联网域名(比如对 ip reverse DNS 得到的域名)
- 其余默认(因为不考虑发信)
smtpd 开启 starttls
据说强制 tls 是不推荐的配置。
以下配置在开启 587 端口 starttls 的同时保留 25 端口明文传输。
1 2 3 4 5 6 7 8 9 10 11
| # main.cf
# tls 相关日志 smtpd_tls_loglevel = 1 # 声明支持 StartTLS smtpd_tls_security_level = may smtpd_tls_protocols = >=TLSv1.2 smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA smtpd_tls_dh1024_param_file = /etc/postfix/dhparams.pem # 默认使用的 chain,密钥+证书 smtpd_tls_chain_files = /etc/postfix/cert/chain.pem
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| # master.cf # 以下服务的端口是 postfix 默认配置
# 25 端口 smtp inet n - n - - smtpd
# 587 端口 声明 STARTTLS 并且只允许 STARTTLS submission inet n - n - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_tls_auth_only=yes -o milter_macro_daemon_name=ORIGINATING
|
开启 sni,对不同域名使用不同证书
1 2
| # mail.cf tls_server_sni_maps = hash:/etc/postfix/sni
|
1 2 3 4
| # /etc/postfix/sni # 这个文件在 postmap 时要用 -F domain.name /path/to/chain, /path/to/another/chain anotherdomain.name /path/to/chain
|
使用虚拟账户
将不同用户的邮件投递到指定目录。可以创建一个单独的用户来拥有这个目录,然后把 uid 和 gid 写在这里。
1 2 3 4 5 6
| # main.cf virtual_mailbox_domains = domain1, domain2 virtual_mailbox_base = /base/path/to/mail/dir virtual_mailbox_maps = hash:/etc/postfix/vmailbox virtual_uid_maps = static:1000 virtual_gid_maps = static:1000
|
1 2 3 4 5 6
| # /etc/postfix/vmailbox # 这里路径结尾有/,表明使用 Maildir 风格存储邮件(Maildir 是 dovecot 的默认方式) # 这些路径在下面 dovecot 的配置中会被用到 user1@domain1 domain1/user1/ user2@domain1 domain1/user2/ user1@domain2 domain2/user1/
|
Dovecot
dovecot.conf
protocols
: 只启用 imap
10-master.cf
可以设置自定义 port。设置为0则关闭对应服务(可以以此来关闭 imap 只保留 imaps)
1 2 3 4 5 6 7 8
| service imap-login { inet_listener imap { port = 0 } inet_listener imaps { port = 993 } }
|
10-ssl.conf
启用 tls 并弃用不安全的配置
1 2 3 4 5 6 7
| ssl = required ssl_cert = </path/to/cert ssl_key = </path/to/key ssl_dh = </path/to/dhparam ssl_min_protocol = TLSv1.2 ssl_cipher_list=ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA ssl_prefer_server_ciphers = yes
|
sni:
1 2 3 4 5 6 7 8
| local_name imap.example.org { ssl_cert = </path/to/imap.example.org/cert ssl_key = </path/to/imap.example.org/key } local_name imap.example2.org { ssl_cert = </path/to/imap.example2.org/cert ssl_key = </path/to/imap.example2.org/key }
|
10-auth.conf
密码由 tls 保护所以此处保留默认的明文认证
使用 auth-passwdfile.conf.ext 作为 userdb
1
| !include auth-passwdfile.conf.ext
|
auth-passwdfile.conf.ext
首先用和 /etc/shadow 一样的格式写明各个用户的用户名和密码。密码直接存明文了就(
这里的 uid 和 gid 要与下面 auth-passwdfile.conf.ext 中指定的目录的所有者一致(或者拥有该目录的 rwx 权限)。一般对应上面 postfix 配置时设置的用户(这里)。
1 2
| user1@domain:{PLAIN}password:1000:1000 user2@domain:{PLAIN}123456:1000:1000
|
在 auth-passwdfile.conf.ext 中写 shadow 文件的路径:
1 2 3 4 5 6 7 8 9 10 11 12
| # auth-passwdfile.conf.ext mail_location = maildir:/path/to/virtual/mailbox/%d/%n
passdb { driver = passwd-file args = username_format=%n@%d /path/to/shadow/file }
userdb { driver = passwd-file args = username_format=%n@%d /path/to/shadow/file }
|
杂项
使用 openssl 验证 starttls
如验证 smtp 的 starttls:
openssl s_client -servername xxx -connect xxx:port -starttls smtp