自建一个将邮件列表转换为 RSS 的服务

common.Created 3410 common.chars / common.mor

common.oldpost


不图折腾的用户也可以直接去用 Kill the Newsletter! 搭建的服务

最近用着 Miniflux 做 RSS 拉取/阅读器很愉快,于是希望将一些邮件列表转换为 RSS。如果收件箱里总是一堆订阅邮件的话,来自个人的通信邮件很容易淹没在其中,因此萌生了让它们成为 RSS 的想法。

现成的项目的话有 leafac 的 Kill the Newsletter!。这是一个用 JavaScript 写的单文件项目,也有搭建公共服务供不想 self-host 的用户使用。不过这个项目有几个不够我用的地方:

  • 大部分的提交似乎都没有 commit message,对一个 repo 而言看起来不是很好的预兆。
  • 不能自定义邮件别名。考虑到它的主要用例是公共(而非个人专用)服务,这个特点其实也容易理解:只要知道了邮件别名,就可以访问相应邮件地址收到的邮件。利用这个特性,知道邮件别名的其它用户可以控制相应邮件地址的订阅,甚至获取付费人的支付信息。

刚好最近 Pop 也写了个类似的东西。不同于 Kill the Newsletter!,这个项目的主要用例是给个人使用,所以对邮件别名没有特别的限制,只要收件人的域名对上,就会收录到 RSS 中,同时也提供网页端渲染的列表与显示。前段时间加上了 Basic Auth,所以也可以防止其它人随意访问。大多数 RSS 客户端应该也是支持 Basic Auth 的,所以对 RSS 客户端没有影响。

我则是基于这个项目进行了一些修改,添加了针对单个收件地址收取的邮件生成 RSS 的功能。

不过这都不是正题。本篇文章的主题是,在拿到一个可以接收明文 SMTP 协议的端口之后,如何把它优雅(?)地暴露到公网上。

配置 nginx 处理 SMTP

nginx 有一些处理邮件的模块。如果编译命令中使用了 --with-mail,编译出来的 nginx 就会包含这些模块(可以通过 nginx -V 查看)。这些模块可以处理一些 IMAP/SMTP/POP3 协议上基本的认证流程,然后将请求交由相应的后端处理,也可以配置 SSL 和 STARTTLS。

Kill the Newsletter! 后端用的是 nodemailer 的 SMTP server,那个 server 可以配置 SSL/STARTTLS(虽说不知道相应的配置有没有暴露出来);但是 Pop 这个 mail-list-rss 利用了一个实现得不太完整的基础 Rust SMTP server 实现,其中不自带对 SSL/STARTTLS 的支持。因此我们需要使用像是 nginx 之类的服务来处理这个。

基本配置可以参考 nginx 文档中的例子:

mail {
    server_name mail.example.com;
    auth_http 127.0.0.1:8825;
    smtp_auth none;
    proxy_pass_error_message on;
    xclient off;

    starttls on;
    ssl_certificate     /path/to/cert.cer;
    ssl_certificate_key /path/to/key.key;
    ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_session_cache   shared:MozSSL:10m;
    ssl_session_timeout 10m;

    server {
        listen     25;
        protocol   smtp;
    }
}
nginx

mailnginx.conf 配置的一级区块,所以不能放在 sites-enabled。这里另外提及几点:

  • mail-list-rss 用到的实现中不支持 XCLIENT 指令,记得把它关掉。
  • server_name 和证书需要匹配。这个域名不一定要是邮箱接收邮件的域名,而应该是 MX 记录结果中指向的域名。
    • 考虑一封发给 hi@example.com 的邮件,而 example.comMX 记录指向 smtp-01.example.com,那么 server_name 应该是 smtp-01.example.com,而 SSL 证书也应该是这个域名(而非 example.com)的证书。
  • 读者可能会发现这里完全没有关于后端服务器的配置,这个我们马上讲。

关于 auth_http

上面没有提及怎么配置后端服务器,因为 nginx 需要另外的方法来动态指定后端,那就是 auth_http。nginx 会给 auth_http 指定的服务发送关于此次请求的信息,包括用户名、密码、访问的协议、用户 IP 等,而服务返回的响应将会告诉 nginx 是将它引导到某个端点,还是拒绝此次访问。具体可以参见 ngx_mail_auth_http_module 的文档。这里由于我们的服务配置比较简单,并且 SMTP 服务也只用于接收而不是发送邮件,就不需要各种复杂的身份验证了,直接返回后端 SMTP 端点就好:

HTTP/1.0 200 OK
Auth-Status: OK
Auth-Server: smtp.mail-list-rss.local
Auth-Port: 10000
http

于是写了个简单的 HTTP 服务器处理这个 于是就让 nginx 来处理这个吧:

server {
        listen 127.0.0.1:8825;
        location / {
                add_header "Auth-Status" "OK";
                add_header "Auth-Server" "smtp.mail-list-rss.local";
                add_header "Auth-Port" "10000";
                return 200;
        }
}
nginx

最后...

记得 nginx -t 检查配置以及 nginx -s reload 重新加载配置。


common.Comments

common.LIKE this

common.Webmentions

common.webmention_response_0