为什么你发出去的邮件能进收件箱,却收不到回信
很多站长在服务器上装完 Postfix,用 echo "test" | mail -s "hello" me@example.com 测试,能发出去,就以为邮件系统搞定了。真正的问题往往出在收发链路的另一半:收信。用户用 Outlook、Thunderbird 或者手机自带客户端配置 IMAP 时,报一堆「无法连接到服务器」「认证失败」「文件夹为空」的错误,而你在服务器上束手无策。
本文聚焦一个 Postfix + Dovecot 组合里最容易被忽略的部分:Dovecot 作为 IMAP/POP3 服务端,如何正确配置、如何和 Postfix 打通投递、如何排查客户端连不上的问题。SPF、DKIM、DMARC 那些「发信侧」的配置不在本文范围,网上已经写得够多了;这里只讲「收信侧」这条被你跳过无数次的链路。
一、先厘清角色:谁负责收邮件,谁负责发邮件
在 Linux 邮件体系里,两件事是分开的:
- MTA(邮件传输代理):Postfix。它负责和其他服务器对话,把邮件收进来(25 端口 SMTP)或者发出去。它把收到的邮件写进本地邮件箱,仅此而已。
- MDA(邮件投递代理):Dovecot。它负责提供 IMAP/POP3 协议,让邮件客户端能读取邮件箱里的邮件、管理文件夹、标记已读。它还负责一件事:LDA(本地投递),把 Postfix 收到的邮件按规则塞进用户邮箱目录。
关键点在于:Postfix 默认不知道 Dovecot 的邮箱在哪里。如果你只装了 Postfix,邮件会进入 /var/mail/username 这个 mbox 格式的大文件里,而 Dovecot 默认期望的是 Maildir 格式(一封邮件一个文件)。格式对不上,客户端自然「收不到邮件」。
二、安装与基础配置:让两者用同一种邮箱格式
先安装(Debian/Ubuntu 系):
apt install dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd三个包各有分工:dovecot-core 是核心,dovecot-imapd 提供 IMAP,dovecot-pop3d 提供 POP3,dovecot-lmtpd 提供 LMTP 投递协议(稍后 Postfix 会用到)。
配置目录在 /etc/dovecot/。核心配置文件是 dovecot.conf,但通常只需要改 conf.d/ 下的分片文件。先设置邮箱格式和位置,编辑 /etc/dovecot/conf.d/10-mail.conf:
mail_location = maildir:~/Maildir
# 允许 Postfix 用户投递时带额外参数
mail_privileged_group = mail
# 明确 LMTP 使用的用户名,防止投递权限错乱
first_valid_uid = 1000注意 mail_location 这一行。它决定了 Dovecot 去哪找邮件,也决定了 Postfix 该往哪投。一旦选了 Maildir,Postfix 就必须用支持 Maildir 的投递方式,不能再用默认的 mbox。
三、打通投递:让 Postfix 把邮件交给 Dovecot
有两种方案。第一种是让 Postfix 直接投递到 Maildir 目录,靠虚拟用户或者系统用户目录;第二种(推荐)是用 Dovecot 的 LMTP 服务作为 Postfix 的投递目标,好处是配额、自动创建邮箱目录、sieve 规则等都由 Dovecot 统一管理。
先启用 LMTP。编辑 /etc/dovecot/conf.d/10-master.conf,找到 service lmtp 段,确保监听 UNIX socket:
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}socket 路径必须落在 Postfix 的队列目录下(/var/spool/postfix/private/),因为 Postfix 是 chroot 运行的,它只能看到自己队列目录内的文件。这是新手最容易踩的坑:socket 建在 /var/run/ 下,Postfix 死活连不上,日志只报一个含糊的 connect to private/dovecot-lmtp: No such file or directory。
然后在 /etc/postfix/main.cf 里指定用 LMTP 投递本地邮件:
mailbox_transport = lmtp:unix:private/dovecot-lmtp
mailbox_command =
virtual_transport = lmtp:unix:private/dovecot-lmtpmailbox_command 要留空,否则 Postfix 会尝试用外部命令投递,和 LMTP 冲突。改完重启两个服务:
systemctl restart dovecot
systemctl restart postfix四、认证:为什么客户端一直报「认证失败」
客户端认证失败,九成是 Dovecot 的认证机制没配好。现代客户端一律用明文密码走 TLS,所以要确认 10-auth.conf 允许 plain login:
# 只在 TLS 之后允许明文认证
disable_plaintext_auth = yes
auth_mechanisms = plain login注意 disable_plaintext_auth = yes 的意思是「在非加密连接上禁止明文认证」,这是安全的默认值。但这要求客户端必须先用 STARTTLS 或者直接连 IMAPS(993 端口)。如果你的客户端配置成「无加密」,认证会被直接拒绝,且日志里只写 auth failed,不告诉你是加密的问题。
另外一个高频错误是 auth 服务的权限。编辑 10-master.conf:
service auth {
unix_listener auth-userdb {
mode = 0600
user = dovecot
}
}如果你想让 Postfix 通过 Dovecot 做 SMTP 认证(比如授权用户从外网发信),还要加一个 /var/spool/postfix/private/auth 的 socket。这个 socket 权限错,Postfix 的 SASL 认证就会静默失败。
五、证书:让客户端不再弹「证书不受信任」
用 Let's Encrypt 签发的域名证书,在 10-ssl.conf 里指定:
ssl = required
ssl_cert = ssl = required 会强制所有连接走 TLS,配合上面的 disable_plaintext_auth 才能形成完整的「加密后才能认证」链路。证书里的域名必须和客户端配置的服务器地址一致:客户端填 mail.example.com,证书却只签了 example.com,就会报域名不匹配。
还有一个容易被忽略的点:Dovecot 的证书要能被 dovecot 用户读取。Let's Encrypt 的私钥文件默认是 root:root 600,Dovecot 以 dovecot 用户启动时读不到,报 Permission denied。解决办法是加一个 ssl_key_password(不适用)或者调整组权限,把 dovecot 用户加入能读密钥的组,或者在 deploy hook 里 chmod 640 且 chgrp dovecot。
六、文件夹与订阅:客户端看不到「已发送」「草稿箱」
IMAP 客户端依赖两个特殊文件夹:Sent(已发送)和 Drafts(草稿)。Dovecot 默认不会自动创建它们,需要手工或者配置自动创建。编辑 15-mailboxes.conf:
namespace inbox {
mailbox Drafts {
special_use = \Drafts
auto = subscribe
auto = create
}
mailbox Sent {
special_use = \Sent
auto = subscribe
auto = create
}
mailbox Trash {
special_use = \Trash
auto = subscribe
auto = create
}
mailbox Junk {
special_use = \Junk
auto = create
}
}special_use 里的反斜杠标记是 IMAP 标准里规定的「特殊用途」属性,客户端看到 \Sent 就知道这是已发送箱,会自动同步发出去的邮件。缺了这些标记,客户端只会显示一堆普通文件夹,用户发完信在「已发送」里找不到,就以为发信失败。空邮箱里的文件夹如果没 auto = create,客户端连接时看到的是空白列表,用户以为服务坏了。
七、排查实战:日志在哪里,怎么看
Dovecot 的日志默认走 syslog,一般在 /var/log/mail.log(Debian)或 /var/log/maillog(CentOS)。实时盯:
tail -f /var/log/mail.log | grep -i dovecot几条典型日志的含义要背下来:
Login failed (auth failed, ...)—— 密码错,或者客户端没走加密连接。auth: Error: ... Address already in use—— 认证 socket 冲突,通常是重复启动的进程残留。Error: net_connect_unix(...) failed: Permission denied—— Postfix 读不到 dovecot-lmtp 的 socket,检查属主和 mode。Fatal: service(lmtp) access(...) failed—— Postfix 的 chroot 和 socke t 路径不匹配。
如果日志什么都没写,说明客户端根本没连上:先在服务器本地测 openssl s_client -connect localhost:993,能出证书就说明 Dovecot 在监听;再用 ss -tlnp | grep -E '993|143|110|995' 确认端口打开,最后排查防火墙是不是把 993 挡住了。
ss -tlnp | grep -E ':(993|143|110|995)\b'
# 有输出说明端口在监听七之二、邮箱配额与磁盘水位:别让一个人的附件吃光硬盘
个人服务器硬盘小,某个用户收了一堆带附件的邮件就能把整块盘塞满,导致系统其他服务一起挂。Dovecot 自带配额支持,应在 90-quota.conf 里开启:
plugin {
quota = maildir:User quota
quota_rule = *:storage=1G
quota_rule2 = Trash:storage=+100M
quota_warning = storage=90%% quota-warning 90 %u
}quota_rule2 给废纸篓额外加上 100M 的宽限,避免用户删邮件时因为已经满额而删不掉。quota_warning 在用到 90%% 时触发一个脚本,可以发信提醒或者写日志。注意配置里百分号要写成双百分号 %%,否则会被 Dovecot 当成变量解析而报错。
配额依赖 Dovecot 的 dirsize 或 maildirsize 文件统计。用 Maildir 格式时,Dovecot 会在邮箱目录里维护一个 maildirsize 文件记录用量,这个文件被误删后配额会重新计算,短暂不准属于正常现象。生产上建议再配一道系统层的磁盘水位监控,比如 df -h 超过 85%% 就告警,双保险。
另外要提醒:配额只管 Dovecot 自己的投递,如果 Postfix 直接往 Maildir 目录写(不走 LMTP),配额是绕不过去的。这也是推荐用 LMTP 投递的另一个原因——所有邮件都经过 Dovecot 的配额检查,不会出现「配额显示没满,实际盘已经爆了」的错位。
七之三、邮件夹编码与中文文件夹乱码
中文用户在客户端里创建中文名字的文件夹,常遇到乱码。原因是 IMAP 协议规定文件夹名必须用 modified UTF-7 编码(RFC 3501),而不是直接用 UTF-8。老版本客户端创建的中文文件夹,名字在服务器上存的是 modified UTF-7 串,用终端 ls 看会是一堆 &XXXX- 样子的字符,这不是损坏,而是协议编码。
Dovecot 支持 UTF-8 原生文件夹名,需要在 10-mail.conf 里确认:
mail_location = maildir:~/Maildir
# 新版本 Dovecot 默认支持 UTF8=ACCEPT 扩展
# 老客户端若显示乱码,可在客户端侧设置「使用 UTF-8 编码文件夹名」处理方式:优先升级客户端到支持 UTF8=ACCEPT 的版本(现代 Thunderbird、Outlook 都支持);如果被老客户端绑死,就统一改用英文文件夹名,避免跨客户端同步时反复转码。这个坑不大,但足够让用户以为「邮件服务器坏了」,提前知道能省下不少排查时间。
八、安全收尾:别让收信服务变成爆破靶子
暴露 993/143 端口后,你会立刻开始收到海量认证爆破尝试。几件事必须做:
- 启用 fail2ban 的 dovecot 过滤器:
/etc/fail2ban/jail.d/dovecot.conf里开启[dovecot],enabled = true,maxretry = 3。 - 关掉不需要的协议:只用 IMAP 就别开 POP3,减少攻击面。在
10-master.conf里注释掉不需要的 service。 - 限制并发:
mail_max_userip_connections = 10,防止一个 IP 开一堆连接试探。 - 给认证加上速率限制,或者用
auth_failure_delay = 2s拖慢爆破速度。
做完这些,一个能稳定收发、客户端即开即用、又不容易被爆破的个人邮件服务才算真正搭好。发信进收件箱只是第一步,收信链路顺了,这个邮件系统才算完整。