Vaultwarden Backup 邮件通知排障:代理、认证与容器测试
本文记录一次 Vaultwarden Backup 邮件通知失败的排查过程。最初日志显示 Unexpected EOF on SMTP connection,测试发现 SMTP 流量经过代理时无法收到服务器问候,改为直连后连接恢复;随后又遇到 535 5.7.3 Authentication unsuccessful,进一步确认当前 Outlook 账号不再接受这套基本认证配置。最终,更换为 Serv00 提供 SMTP 服务,并成功收到备份邮件通知。排查中还发现,直接进入容器运行 mail,与通过镜像入口执行邮件测试并不等价。本文分别说明这些问题的现象、验证过程和处理结果。
项目背景
Vaultwarden 介绍
Vaultwarden 是一个用 Rust 语言编写的轻量级、开源的第三方 Bitwarden 服务端实现。
核心特点
• 高度兼容:完全兼容 Bitwarden 的官方客户端(包括浏览器扩展、桌面端、iOS 和 Android 移动端)。
• 极其轻量:资源占用极低(通常只需几十兆内存),一台配置很小的主机或 NAS 就能流畅运行。
• 端到端加密:数据在本地设备加密后才会上传,服务端只存密文,保护隐私安全。
• 免费且功能丰富:支持组织密码共享、两步验证等官方需要付费的部分高级功能。

vaultwarden-backup 介绍
ttionya/vaultwarden-backup 是一个专为 Vaultwarden 量身定制的开源自动备份工具。它基于 Docker 容器化部署,利用强大的底层传输工具 Rclone,能够安全、高效、自动化地将自建密码库的数据备份并同步到几乎所有主流的云存储或远程系统中。
SMTP 邮件发送失败日志
vaultwarden-backup 可以设置在备份成功或失败时通过 SMTP 发送邮件通知。最开始注意到邮件发送失败时,我并没有马上处理,一方面是因为服务已经稳定运行多年,数据备份正常;另一方面是因为当初启用 Outlook 邮箱 SMTP 的过程有点麻烦,非必要实在不想访问微软的网站。
报错的日志如下:
1 | backup-1 | upload backup file to storage system [BitwardenBackup:/BitwardenBackup] |
当时心里推测的可能原因有两个:
- Outlook 邮箱的
app password可能由于某种安全原因失效了 - 网络流量经过
mihomo代理分发导致了某种问题
事实上,最终的排查结果是两方面的原因都有:
- Outlook 邮箱
SMTP不再支持基本认证,要求使用OAuth2(不排除不同类型,不同个体的 Outlook 账号仍支持基本认证的可能性) - 使用的代理服务器屏蔽
25/465/587端口,防止垃圾邮件
排查过程
最近
Homelab更新告一段落,着手处理这个问题,没想到又是一个大坑,埋了我大半天。Orz……
重置 app password
第一步,重置 app password。
Microsoft 账户 → 安全 → 管理登录方式:
- 确认双重验证已开启(这是使用
app password的前提条件) - 删除现有的
app password(应用密码) 并创建新的
邮件发送测试
第二步,使用新的 app password 测试是否可以发送邮件。
在 vaultwarden-backup 项目的 README_zh.md 中,有一段关于邮件发送测试的说明。
你可以使用下面的命令测试邮件发送功能。我们会增加
-v标志以显示详细信息,你无需在MAIL_SMTP_VARIABLES中重复设置。
1 | docker run --rm -it -e MAIL_SMTP_VARIABLES='<your smtp variables>' ttionya/vaultwarden-backup:latest mail <mail send to> |
因为我的 Vaultwarden 服务正在正常运行,所以我按个人习惯进入 backup 容器进行测试。
1 | docker exec -it vaultwarden-backup-1 bash |
这里我其实踩到了一个深坑,只是无意中 AI 带着我飞过去了。提前剧透,作者的测试命令中的
刚开始我以为容器会根据配置设置好环境变量,但是测试报错,报错的核心信息 sendmail: can't connect to remote host (127.0.0.1): Connection refused 和 20240518 open 至今的 issue #153 提到的报错信息相似。
1 | 1e4cb0d00964:/data# mail another-example.outlook.com |
我的第一反应是怀疑 SMTP 相关设置没有被正确传递。issue #153 的评论区有人有类似的想法:
EDIT: Also want to add this. I think connection refused with 127.0.0.1 means that smtp or mta was not set correctly and is trying to connect to default smtp which is localhost.
测试发现环境变量 MAIL_SMTP_VARIABLES 确实为空,我只当是因为自己使用 .env 导致。
1 | export MAIL_SMTP_VARIABLES=" \ |
但是在手动 export 后重新测试发现仍然报错。
1 | sendmail: can't connect to remote host (127.0.0.1): Connection refused |
SMTP 服务器可达性测试
其实到这里时我有过一瞬间的担心,先想起前段时间更新 Homelab 时卡壳多天的网络问题,不过又想起放下精神洁癖后让 AI 自己突突了 2 小时就解决了。于是我果断让 AI 进行第三步,测试容器和 SMTP 服务器是否可以连接。果不其然,可恶,又有网络问题。AI 排查过程简单处理后如下:
- 确认可以网络可达,建立过连接
1 | Unexpected EOF 说明连接建立过,但 SMTP 对话中途断了。先看容器里的实际情况: |
- 确认是代理节点封了
SMTP端口,还是 Office365 封了代理节点的出口IP。
1 | Office365 :587 8秒无数据 ✗ |
结论是使用的代理服务器屏蔽了 25/465/587 端口,修改 mihomo 规则走 Direct:
- 可以按端口
1 | - DST-PORT,587,DIRECT |
- 也可以按域名
1 | - DOMAIN,smtp.office365.com,DIRECT |
在修复网络问题后,AI 立刻发现了新的报错 535 5.7.3 Authentication unsuccessful。
SMTP 可用性测试
显然,这是微软的响应。第四步,测试 Outlook 邮箱的 SMTP 功能是否可用。在某种程度上这部分是最折磨的,因为 AI 工具介入不便,又因为微软的网站巨卡,还因为敏感操作要反复进行身份验证。
确认并测试:
- 邮箱是否正确
app password是否从正确的账号上生成、复制并被正确粘贴SMTP服务器域名是否从smtp.office365.com变更为smtp-mail.outlook.com- 双重验证是否开启
但报错信息始终不变,而且有点坑的是 535 5.7.3 Authentication unsuccessful 这种模糊的报错信息虽然阻止了攻击者依靠错误信息探测某个账号是否存在、是否启用了什么策略,但是也将账号所有者挡在信息之外。
测试过程中,注意到进入容器内的测试报错始终是
sendmail: can't connect to remote host (127.0.0.1): Connection refused,怀疑两条命令中的
1 | mail: Resolving host smtp.office365.com:587 ... done |
期间,AI 多次建议在 Thunderbird 客户端配一次 SMTP 验证是 s-nail 兼容性问题,密码问题,还是账号基本认证被禁用问题,或者直接更换国内发信服务商,但我还是想挣扎一下。
直到 AI 通过 IMAP 服务器拿到了更具体的响应 Basic authentication is disabled,判断微软确实已经禁用这个账号的基本认证,要求使用 OAuth2;进一步搜索到 n8n Docs声称“微软已移除 Outlook SMTP 的基本认证和 app passwords”;最后根据【转发和 IMAP 页面】没有 SMTP 相关设置判断此路注定不通,我才终于死心,放弃使用 Outlook 作为发信服务商。

更换发信服务商
最终,我参考SMTP on Serv00(仅需域名使用基于serv00的SMTP服务)选择使用 Serv00 作为发信服务商,测试成功发送邮件通知。 相信选择 QQ 或者 163 邮箱也同样可以。
mail 不是 mail
命令 docker run …… mail …… 中的 mail 和进入容器后运行的 mail 不是一个东西,如果没有注意,后者的测试结果将会一直是 sendmail: can't connect to remote host (127.0.0.1): Connection refused。
惯性思维
如果你要连接容器中的 Redis 服务,你可能会使用一个用完就删的临时容器:
1 | docker run -it --rm redis redis-cli -h <你的Redis服务IP或主机名> -p 6379 |
你还可能会想直接进入已经存在的 Redis 容器内部使用自带的 redis-cli:
1 | docker exec -it <你的Redis容器名或ID> redis-cli |
所以,当我看到文档中提供的邮件发送测试方法:
1 | docker run --rm -it -e MAIL_SMTP_VARIABLES='<your smtp variables>' ttionya/vaultwarden-backup:latest mail <mail send to> |
我以为效果等同于:
1 | docker exec -it vaultwarden-backup sh |
参数 vs 命令
在执行 docker run 时,Dockerfile 中配置的 ENTRYPOINT 脚本会作为容器启动时的第一个主进程(PID 1)优先运行。
- 如果
Dockerfile中同时有ENTRYPOINT和CMD,CMD的内容会被当作参数传递给ENTRYPOINT脚本。 - 如果在
docker run后面追加了命令参数(例如docker run <image> echo hello),这些参数会追加到ENTRYPOINT后面,并覆盖掉Dockerfile中的CMD。
查看项目源码,scripts/entrypoint.sh 中有以下一段代码:
1 | . /app/includes.sh |
而 send_mail 方法定义在 scripts/includes.sh 中:
1 | function send_mail() { |
因此,如果想要进入容器直接进行测试,必须正确使用 mail 命令。比如:
1 | mail -S smtp-use-starttls \ |
.env 和容器内环境变量
另外,之所以刚开始测试时容器内环境变量 MAIL_SMTP_VARIABLES 为空,是因为在 Docker Compose 中,项目目录下的 .env 文件和容器内的环境变量是两个不同的概念,它们之间不会自动划等号。.env 不会自动注入容器,必须通过以下两种方式“桥接”到容器内:
通过 environment 显式引用
在 docker-compose.yml 中使用 ${VAR_NAME} 语法,把 .env 里的值显式传给容器的 environment 节点:
1 | services: |
转换链路:.env 文件 ➔ Compose 解析并替换 docker-compose.yml 中的 ${DATABASE_HOST} ➔ 启动容器时通过 -e 注入到容器运行时环境。
通过 env_file 整体加载
如果某个服务需要加载整个文件,可以在 docker-compose.yml 中通过 env_file 指定:
1 | services: |
转换链路:Compose 读取指定的 .env 文件,在创建容器时将其中的 KEY=VALUE 批量注入容器环境。
总结和反思
总结
最终排查出的问题有两方面:
- Outlook 邮箱
SMTP不再支持基本认证,要求使用OAuth2(不排除不同类型,不同个体的 Outlook 账号仍支持基本认证的可能性)。文档参见POP, IMAP, and SMTP settings for Outlook.com。 - 使用的代理服务器屏蔽
25/465/587端口。可见,透明代理也不是一劳永逸。
排查有三个主要方向:
- 确认发信服务商支持
SMTP及其使用方式 - 报错信息为
sendmail: can't connect to remote host (127.0.0.1): Connection refused,极有可能是SMTP相关设置没有正确传递给最终的二进制可执行程序mail。 - 报错信息为
Unexpected EOF on SMTP connection,检查网络是否走代理以及代理服务器是否屏蔽25/465/587端口。
反思
- 为了网络畅通必须引入代理,但有时候会产生一些奇怪的问题,你往往很难定位到底是哪个环节出了问题。但在 AI 能够“触达和上手”之后,排查和测试变得容易非常多,它比半吊子的自己更知道如何从更底层获取信息并发现关键点。
- 马后炮地说,如果直接换 QQ 或 163 邮箱,好像一切自然迎刃而解,但避开了坑 vs 趟过了坑,各有好处。
- 善游者溺,自以为已经熟悉的地方也可能有自己不了解的坑。
- 几年前 Outlook 邮箱
SMTP要求必须开启双重验证、使用app password而不能直接使用主密码。几年后 Outlook 禁用了基本认证,要求使用 OAuth2。年年坑不同。不过难道微软完全放弃那些不支持OAuth2的场景了? Docker常常用,但常用的知识其实很少,很多时间一长大部分就模糊了,而且有些关键点,如果不是踩过坑,可能根本留意不到。
- 几年前 Outlook 邮箱