本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2022年03月15日 统计字数: 5362字 阅读时间: 11分钟阅读 本文链接: https://soulteary.com/2022/03/15/build-a-lightweight-mail-gateway-for-homelab-using-docker.html ----- # 使用 Docker 搭建适用于 HomeLab 的轻量邮件网关 本篇文章将介绍如何使用 Docker 快速搭建一个适用于 HomeLab 和开发阶段使用的邮件网关,用来快速聚合各种软件的通知消息。当然,你也可以用它来快速验证各种软件中的邮件配置是否正确。 如果你熟悉 Docker 的话,大概十分钟,你将会拥有一套完全属于自己的邮件通知聚合服务,而这个服务,只需要 20MB 左右的内存消耗,非常轻量。 ## 写在前面 最近在整理家里的部署的软件和服务,这些服务多数都拥有“邮件通知”的能力,并会在必要的时候,使用“发送邮件”的方式通知用户一些必要的信息,比如:任务执行完毕、敏感操作、根据计划任务跑完的数据统计摘要等。 以往部署这些软件的时候,在邮件通知功能配置上,我们的选择无非是三种:注册一个真实的邮箱,使用我们自己已经在用的邮箱账号,关闭邮件通知功能。 **当软件比较少的时候,不论选择那种方案,都是可以的,因为我们的一次性操作和维护成本都比较低**。 但当我们部署了越来越多的软件和服务之后,关闭邮件通知属于“鸵鸟行为”,是不推荐的;在不能100%确定软件可靠性的前提下,所有软件共享一个邮箱账号,显然是不安全的;**最可靠的方案,便是为为每一个软件配置不同的邮箱账号**。 而如果为每一个软件都配置独立的邮箱账号,维护邮箱账号的时间成本,将会变得不可忽视,因为你永远不知道什么时候、哪一个邮箱账号会有问题,以及在什么时候你会漏掉重要的应用消息。 所以,我开始寻找一个适用于个人或者小团队的、私有化部署的邮件网关方案,降低账号的维护成本和经济成本,以及尽可能减少不必要的公网数据交换。 ## 软件选型 为了解决上面的问题,一般可以选择两类软件方案:邮局类软件、邮件测试网关。 我们先来聊聊邮局类应用。 ### 邮局类软件应用 邮局类软件,顾名思义,和我们日常使用的 GMail、Outlook、QQ 邮箱、163 邮箱等等。在 GitHub 上,我们也可以找到不少优秀的邮局软件应用,比如下面这些: - (11.4k Stars)[https://github.com/postalserver/postal](https://github.com/postalserver/postal) - (10.9k Stars)[https://github.com/mail-in-a-box/mailinabox](https://github.com/mail-in-a-box/mailinabox) - 可以比较简单的和 NextCloud Mail 一起使用(625 Stars)[https://github.com/nextcloud/mail](https://github.com/nextcloud/mail) - (8.7k Stars) [https://github.com/docker-mailserver/docker-mailserver](https://github.com/docker-mailserver/docker-mailserver) - (4.9k Stars)[https://github.com/mailcow/mailcow-dockerized](https://github.com/mailcow/mailcow-dockerized) - (3.5k Stars)[https://github.com/Mailu/Mailu](https://github.com/Mailu/Mailu) - (2.1k Stars)[https://github.com/modoboa/modoboa](https://github.com/modoboa/modoboa) 上面的开源方案都可以作为我们日常使用的云服务、知名邮件厂商的替代方案使用。 但是通常情况下,这类软件会包含非常多的组件和能力,比如:Web 界面、多账户支持、多种邮局聚合、各种邮件协议支持、邮件推送、垃圾邮件审查、邮件防火墙、各种复杂的邮件相关的 DNS 支持等等。 随着软件功能的丰富完善,软件运行过程中的资源消耗和使用中的功能复杂度自然也就上去了,加上这几个头部的项目,技术选型多是 Ruby、Python,资源使用自然更是“雪上加霜”。 考虑到我不需要多用户支持,并且我希望我的应用始终是**轻量可靠的**。所以,我将目光转向了:测试网关类应用。 ### 邮件测试网关类应用 坦白说,能够符合我前文中提到的大部分需求,并具备比较低的资源占用的项目并不多。如果再限制能够快速进行功能验证(跑起来看效果)的项目,那就更屈指可数啦: - (5.6k Stars)[https://github.com/sj26/mailcatcher](https://github.com/sj26/mailcatcher) - (3.2k Stars)[https://github.com/maildev/maildev](https://github.com/maildev/maildev) 在简单使用之后,我选择了以第二个项目,将它作为代码基进行二次开发。毕竟基于在以往项目中的经验,相比较 Ruby 的性能和效率,我对 Node 更有信心。 如果你等不及验证效果,可以跳过下面的小节,直接阅读文章的 “使用 Docker 进行快速体验”部分。 ## 基于 MailDev 进行二次开发 从项目当前出现的问题和社区里的反馈里,我们可以看到几个比较明显的问题: 1. 软件文档和官方镜像似乎“对不上号”,一些代码中的依赖配置项也是有问题的,会导致软件无法正常使用。 [issue #376](https://github.com/maildev/maildev/pull/376)、[issue list](https://github.com/maildev/maildev/issues) 2. 作者官宣弃坑,后来者做了 fork 版本,但仅仅是解决了一些基础问题。[issue #335](https://github.com/maildev/maildev/issues/335) 3. 软件依赖和运行时都过于陈旧,依赖的 lib 的版本缺乏有效管理,NPM 子依赖中不少依赖都已经被废弃或者存在安全隐患。 4. 使用更可靠的 Markdown 和 HTML 互相转化方案,对内容进行安全的标签过滤。 所以,我花了一些时间,针对原来的代码做了一些调整: - 升级了 Node Runtime 到 v16 TLS。 - 将各种基础依赖升级到可靠版本,解决各种安全问题。 - 重新构建可用的 Docker 容器版本。 ![针对 MailDev 进行细节调整](https://attachment.soulteary.com/2022/03/15/maildev-changes.jpg) 如果你好奇到底改了哪些内容的话,可以看这里的提交记录:[https://github.com/maildev/maildev/compare/master...soulteary:master](https://github.com/maildev/maildev/compare/master...soulteary:master) 接下来,我们来看看如何通过容器快速使用这个“邮件工具”吧。 ## 使用 Docker 快速体验邮件网关 如果我们想启动一个“邮件网关”,可以直接使用“一句话”的容器命令来解决战斗: ```bash docker run -p 1080:1080 -p 1025:1025 soulteary/maildev ``` 当命令执行完毕,我们将能够看到类似下面的日志输出: ```bash MailDev using directory /tmp/maildev-1 MailDev webapp running at http://0.0.0.0:1080 MailDev SMTP Server running at 0.0.0.0:1025 ``` 接着在浏览器中打开 `http://0.0.0.0:1080`,就能看到下图一样的收件箱界面了。 ![MailDev 的欢迎界面](https://attachment.soulteary.com/2022/03/15/maildev-welcome.jpg) 如果我们需要测试邮件聚合功能是否能够正常工作,**只需要使用邮件客户端、配置任意用户名和密码**,向 `0.0.0.0:1025` 端口发送邮件,就能够看到效果啦。 还记得上文中需要配置不同账号的问题吗?是不是很轻松的就解决啦?甚至你还可以配置邮件转发真实邮箱、限制只接收某些账号的邮件消息。 ### 使用 Node.js 快速验证服务功能 相比较使用客户端,我更喜欢使用代码来做快速验证。 这里为了方便描述,我使用 Node.js 写了一个非常简单的发信脚本: ```js 'use strict' const nodemailer = require('nodemailer') async function main () { const { user, pass } = await nodemailer.createTestAccount() let transporter = nodemailer.createTransport({ host: '0.0.0.0', port: 1025, auth: { type: 'login', user, pass } }) // send mail with defined transport object let info = await transporter.sendMail({ from: '\'Fred Foo 👻\' ', // sender address to: 'bar@example.com, baz@example.com', // list of receivers subject: 'Hello ✔', // Subject line text: 'Hello world?', // plain text body html: 'Hello world?' // html body }) console.log('Message sent: %s', info.messageId) // Message sent: // Preview only available when sending through an Ethereal account console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info)) // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou... } main().catch(console.error) ``` 将上面的代码保存为 `example-sendmail.js`,接着执行 `node example-sendmail.js`,顺利的话,我们将看到类似下面的日志输出: ```js Message sent: Preview URL: false ` ``` 接着,在浏览器中打开 `http://0.0.0.0:1080`,我们将看到 MailDev 的界面中多了一份意料之中的“邮件”,邮件正文正是我们上面写代码中的内容。 ![收到来信的 MailDev](https://attachment.soulteary.com/2022/03/15/maildev-new-mail.jpg) 在不进行额外的代码调整之前,我们多重复几次上面的发信操作,就可以模拟出日常学习和工作中各种应用的邮件通知发送场景。 此时,MailDev 的列表中就会实时展示新到的“邮件”了。 ![MailDev 的邮件列表](https://attachment.soulteary.com/2022/03/15/maildev-mail-list.jpg) ### 使用 Docker-Compose 启动服务 为了方便我的老读者们,让大家能够一起偷懒,按照惯例,我提供一个简单的容器编排配置文件: ```yaml version: '3' services: maildev: image: soulteary/maildev restart: always environment: - TZ=Asia/Shanghai - MAILDEV_WEB_PORT=1080 - MAILDEV_SMTP_PORT=1025 ports: - "1080:1080" - "1025:1025" ``` 将上面的内容保存为 `docker-compose.yml`,接着使用 `docker-compose up -d` 启动应用,和上文提到的一样,我们就能够在浏览器中访问 `http://localhost:1080` 来浏览和管理“邮件内容”,并通过 `1025` 端口来进行邮件汇聚操作啦。 ## 最后 和之前提到过的其他的项目一样,接下来我将持续改进这个项目。短时间内,我希望它能够更好的支持 WebHook、并和一些消息推送软件进行打通,更好的支持我的 HomeLab 场景。 如果你对这个项目感兴趣、又比较“心急”的话,可以访问项目源代码:[https://github.com/soulteary/maildev](https://github.com/soulteary/maildev) 进行 DIY。当然,也欢迎你在项目 issue 中留下你对这个项目的建议和想法。 --EOF