Postfix + Dovecot 自托管邮箱

最近在服务器上折腾了一下邮箱自托管。使用 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。例:

ini
# main.cf
# postfix 实际会寻找 /etc/postfix/bar.db 文件
foo = hash:/etc/postfix/bar
# /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 端口明文传输。

ini
# 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
# 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,对不同域名使用不同证书

ini
# main.cf
tls_server_sni_maps = hash:/etc/postfix/sni
# /etc/postfix/sni
# 这个文件在 postmap 时要用 -F
domain.name /path/to/chain, /path/to/another/chain
anotherdomain.name /path/to/chain

使用虚拟账户

将不同用户的邮件投递到指定目录。可以创建一个单独的用户来拥有这个目录,然后把 uid 和 gid 写在这里。

ini
# 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
# /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)

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    port = 993
  }
}

10-ssl.conf

启用 tls 并弃用不安全的配置

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:

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_mechanisms = plain

使用 auth-passwdfile.conf.ext 作为 userdb

!include auth-passwdfile.conf.ext

auth-passwdfile.conf.ext

首先用和 /etc/shadow 一样的格式写明各个用户的用户名和密码。密码直接存明文了就(

这里的 uid 和 gid 要与下面 auth-passwdfile.conf.ext 中指定的目录的所有者一致(或者拥有该目录的 rwx 权限)。一般对应上面 postfix 配置时设置的用户(这里)。

user1@domain:{PLAIN}password:1000:1000
user2@domain:{PLAIN}123456:1000:1000

在 auth-passwdfile.conf.ext 中写 shadow 文件的路径:

# 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