加密 SQLite 邮箱,保护您的隐私
与其他电子邮件服务不同,我们确保始终只有您可以访问您的邮箱。
- 搜索页面
- 目录
前言
太棒了; 我们的电子邮件服务是 100% 开源 通过安全和加密的 SQLite 邮箱注重隐私。
直到我们推出 IMAP 支持,我们使用 MongoDB 来满足持久数据存储需求。
这项技术非常棒,我们今天仍在使用它 - 但为了使用 MongoDB 进行静态加密,您需要使用提供 MongoDB Enterprise 的提供商,例如 Digital Ocean 或 Mongo Atlas - 或支付企业许可证(以及随后必须与销售团队合作(延迟)。
我们的团队在 转发邮件 需要一个开发人员友好、可扩展、可靠且加密的 IMAP 邮箱存储解决方案。作为开源开发人员,使用需要支付许可费才能获得静态加密功能的技术是反对的 我们的原则 – 因此我们从头开始实验、研究并开发了一种新的解决方案来解决这些需求。
我们不使用共享数据库来存储您的邮箱,而是使用您的密码(只有您拥有)单独存储和加密您的邮箱。 我们的电子邮件服务非常安全,如果您忘记密码,您就会丢失邮箱 (并且需要通过脱机备份进行恢复或重新开始)。
继续阅读,我们将深入探讨以下内容 电子邮件服务提供商的比较, 我们的服务如何运作, 我们的技术栈, 和更多。
电子邮件服务提供商比较
我们是唯一一家 100% 开源且注重隐私的电子邮件服务提供商,存储单独加密的 SQLite 邮箱,提供无限的域、别名和用户,并提供出站 SMTP、IMAP 和 POP3 支持:
与其他电子邮件提供商不同,您无需使用转发电子邮件为每个域或别名的存储付费。 存储在您的整个帐户中共享 - 因此,如果您有多个自定义域名且每个域名有多个别名,那么我们是您的完美解决方案。请注意,如果需要,您仍然可以针对每个域或别名强制实施存储限制。
它是如何工作的
-
使用您的电子邮件客户端(例如 Apple Mail、Thunderbird、Gmail 或 Outlook) – 连接到我们的安全 IMAP 使用您的用户名和密码的服务器:
- 您的用户名是您的域名的完整别名,例如
hello@example.com
. - 您的密码是随机生成的,仅在您点击时显示 30 秒 生成密码 从 我的帐户 域 别名。
- 您的用户名是您的域名的完整别名,例如
-
连接后,您的电子邮件客户端将发送 IMAP 协议命令 到我们的 IMAP 服务器以保持您的邮箱同步。这包括编写和存储草稿电子邮件以及您可能执行的其他操作(例如,将电子邮件标记为重要或将电子邮件标记为垃圾邮件/垃圾邮件)。
-
邮件交换服务器(通常称为“MX”服务器)接收新的入站电子邮件并将其存储到您的邮箱中。发生这种情况时,您的电子邮件客户端将收到通知并同步您的邮箱。我们的邮件交换服务器可以将您的电子邮件转发给一个或多个收件人(包括 网络钩子),将您的电子邮件存储在我们的加密 IMAP 存储中, 或两者!
有兴趣了解更多吗?读 如何设置电子邮件转发, 我们的邮件交换服务如何运作,或查看 我们的导游.
-
在幕后,我们的安全电子邮件存储设计通过两种方式来加密您的邮箱,并且只有您可以访问:
-
当您从发件人处收到新邮件时,我们的邮件交换服务器会写入您的个人临时加密邮箱。
sequenceDiagram autonumber actor Sender Sender->>MX: Inbound message received for your alias (e.g. you@yourdomain.com). MX->>SQLite: Message is stored in a temporary mailbox. Note over MX,SQLite: Forwards to other recipients and webhooks configured. MX->>Sender: Success!
-
当您使用电子邮件客户端连接到我们的 IMAP 服务器时,您的密码将在内存中加密并用于读取和写入您的邮箱。只能使用此密码读取和写入您的邮箱。请记住,由于您是唯一拥有此密码的人, 只有你 当您访问邮箱时可以读取和写入您的邮箱。下次您的电子邮件客户端尝试轮询邮件或同步时,您的新邮件将从该临时邮箱传输,并使用您提供的密码存储在您的实际邮箱文件中。请注意,此临时邮箱随后会被清除并删除,以便只有受密码保护的邮箱才会包含邮件。
-
如果您连接到 IMAP(例如使用 Apple Mail 或 Thunderbird 等电子邮件客户端),则我们不需要写入临时磁盘存储。相反,您的内存中加密的 IMAP 密码将被获取并使用。实时地,当一条消息试图传递给您时,我们会向所有 IMAP 服务器发送一个 WebSocket 请求,询问它们是否有一个活动会话供您使用(这是获取部分),然后将其传递给所有 IMAP 服务器。加密的内存密码 – 因此我们不需要写入临时邮箱,我们可以使用您的加密密码写入您的实际加密邮箱。
sequenceDiagram autonumber actor You You->>IMAP: You connect to IMAP server using an email client. IMAP->>SQLite: Transfer message from temporary mailbox to your alias' mailbox. Note over IMAP,SQLite: Your alias' mailbox is only available in-memory using IMAP password. SQLite->>IMAP: Retrieves messages as requested by email client. IMAP->>You: Success!
-
-
加密邮箱的备份 是每天制作的。您还可以随时请求新的备份或从以下位置下载最新的备份 我的帐户 域 别名。如果您决定切换到其他电子邮件服务,那么您可以随时轻松迁移、下载、导出和清除您的邮箱和备份。
技术
数据库
我们探索了其他可能的数据库存储层,但是没有一个像 SQLite 那样满足我们的要求:
数据库 | 静态加密 | 沙盒化 邮箱 | 执照 | 随处使用 |
---|---|---|---|---|
SQLite ⭐ | ✅ 是的 SQLite3多重密码 | ✅ | ✅ 公共领域 | ✅ |
MongoDB | ❌ “仅在 MongoDB Enterprise 中可用” | ❌关系数据库 | ❌ AGPL 和 SSPL-1.0 | ❌ |
rqlite | ❌ 仅网络 | ❌关系数据库 | ✅ MIT | ❌ |
dqlite | ❌ 未经测试且尚未受支持? | ❌ 未经测试且尚未受支持? | ✅ LGPL-3.0-only | ❌ |
PostgreSQL | ✅ 是的 | ❌关系数据库 | ✅ PostgreSQL (如同 BSD 或者 MIT ) | ❌ |
玛丽亚数据库 | ✅ 仅适用于 InnoDB | ❌关系数据库 | ✅ GPLv2 和 BUSL-1.1 | ❌ |
蟑螂数据库 | ❌ 企业专用功能 | ❌关系数据库 | ❌ BUSL-1.1 和别的 | ❌ |
这里有一个 比较几种 SQLite 数据库存储选项的博客文章 在上表中。
安全
我们在任何时候都使用 静态加密 (AES-256), 传输中加密 (TLS), 通过 HTTPS 的 DNS (“DoH”)使用 🍊 柑橘, 和 斯克莱特 (ChaCha20-Poly1305) 邮箱加密。此外,我们使用基于令牌的双因素身份验证(与 SMS 不同,SMS 可能会被怀疑) 中间人攻击)、轮换 SSH 密钥并禁用 root 访问、通过受限 IP 地址独占访问服务器等等。
如果发生 邪恶女仆袭击 或来自第三方供应商的流氓员工, 您的邮箱仍然只能使用您生成的密码打开。请放心,除了我们的 SOC Type 2 兼容服务器提供商 Cloudflare、Digital Ocean 和 Vultr 之外,我们不依赖任何第三方供应商。
我们的目标是拥有尽可能少的 单点故障 尽可能。
邮箱
太棒了; 我们的 IMAP 服务器为您的每个邮箱使用单独加密的 SQLite 数据库。
SQLite 是一个非常流行的 嵌入式数据库 – 它当前正在您的手机和计算机上运行 – 并被几乎所有主要技术所使用.
例如,在我们的加密服务器上有一个 SQLite 数据库邮箱 linux@example.com
, info@example.com
, hello@example.com
等等——每个人一个 .sqlite
数据库文件。我们也不用电子邮件地址命名数据库文件 - 相反,我们使用 BSON ObjectID 和生成的唯一 UUID,它们不会共享邮箱所属的人或它所在的电子邮件地址(例如 353a03f21e534321f5d6e267.sqlite
).
这些数据库中的每一个都使用您的密码(只有您拥有)进行加密 斯克莱特 (ChaCha20-Poly1305)。这意味着您的邮箱是单独加密的、独立的、 沙箱化的和便携式。
我们对 SQLite 进行了以下微调 PRAGMA:
PRAGMA | 目的 |
---|---|
cipher=chacha20 | ChaCha20-Poly1305 SQLite数据库加密。参考 better-sqlite3-multiple-ciphers 在下面 项目 以获得更多洞察力。 |
key="****************" | 这是您解密的内存中唯一密码,该密码通过您的电子邮件客户端的 IMAP 连接传递到我们的服务器。为每个读写会话创建和关闭新的数据库实例(以确保沙箱和隔离)。 |
journal_model=WAL | 预写日志(“WAL") 这提高了性能并允许并发读取访问. |
busy_timeout=5000 | 防止写锁定错误 当其他写入正在进行时. |
synchronous=NORMAL | 提高交易的持久性 无数据损坏风险. |
foreign_keys=ON | 强制执行外键引用(例如从一个表到另一个表的关系)。 默认情况下,SQLite 中未启用此功能,但为了验证和数据完整性,应该启用它。 |
encoding='UTF-8' | 默认编码 用于确保开发人员的理智。 |
所有其他默认值均来自 SQLite,如指定的 官方 PRAGMA 文档.
并发性
太棒了; 我们用
WebSocket
用于并发读取和写入加密的 SQLite 邮箱。
读
您手机上的电子邮件客户端可能会解决 imap.forwardemail.net
到我们的 Digital Ocean IP 地址之一 – 您的桌面客户端可能会从不同的 IP 地址解析出单独的 IP 提供者 共。
无论您的电子邮件客户端连接到哪个 IMAP 服务器,我们都希望该连接能够以 100% 的准确度实时读取您的数据库。这是通过 WebSockets 实现的。
写
写入数据库有点不同 - 因为 SQLite 是嵌入式数据库,并且默认情况下您的邮箱位于单个文件中。
我们探索过诸如 litestream
, rqlite
, 和 dqlite
下面——但是这些都不满足我们的要求。
通过预写日志记录完成写入(“WAL") 启用 – 我们需要确保只有一台服务器(“主服务器”)负责执行此操作。 WAL 极大地加快了并发速度,并允许一名写入者和多名读取者。
主服务器在数据服务器上运行,并已安装包含加密邮箱的卷。从分发的角度来看,您可以考虑背后的所有单独的 IMAP 服务器 imap.forwardemail.net
成为辅助服务器(“辅助”)。
我们与以下人员完成双向沟通 WebSockets:
- 主服务器使用一个实例 WS的
WebSocketServer
服务器。 - 辅助服务器使用一个实例 WS的
WebSocket
包裹着的客户端 websocket 承诺 和 重新连接-websocket。这两个包装器确保WebSocket
重新连接并可以发送和接收特定数据库写入的数据。
备份
太棒了; 每天都会备份您的加密邮箱。您还可以立即请求新的备份或随时下载最新的备份 我的帐户 域 别名。
对于备份,我们只需运行 SQLite VACUUM INTO
在 IMAP 命令处理期间每天执行命令,该命令利用内存中 IMAP 连接中的加密密码。如果未检测到现有备份或者如果 SHA-256 与最近的备份相比,文件的哈希值已更改。
请注意,我们使用 VACUUM INTO
命令而不是内置命令 backup
命令,因为如果在执行期间修改了页面 backup
命令操作,然后它必须重新开始。这 VACUUM INTO
命令将拍摄快照。请参阅以下评论 GitHub 和 黑客新闻 以获得更多洞察力。
另外我们使用 VACUUM INTO
相对于 backup
,因为 backup
命令将使数据库在短时间内保持未加密状态,直到 rekey
被调用(请参阅此 GitHub 评论 以获得洞察力)。
中学将指导小学 WebSocket
连接来执行备份 – 然后主节点将收到执行此操作的命令,随后将:
- 连接到您的加密邮箱。
- 获取写锁。
- 通过运行 WAL 检查点
wal_checkpoint(PASSIVE)
. - 跑过
VACUUM INTO
SQLite 命令。 - 确保可以使用加密密码打开复制的文件(安全/虚拟验证)。
- 将其上传到 Cloudflare R2 进行存储(或您自己的提供商,如果指定)。
请记住,您的邮箱是加密的 – 虽然我们对 WebSocket 通信采取了 IP 限制和其他身份验证措施 – 如果出现不良行为,您可以放心,除非 WebSocket 有效负载有您的 IMAP 密码,否则它无法打开您的数据库。
目前每个邮箱仅存储一个备份,但将来我们可能会提供时间点恢复(“PITR").
搜索
我们的 IMAP 服务器支持 SEARCH
具有复杂查询、正则表达式等的命令。
快速的搜索性能归功于 FTS5 和 sqlite-正则表达式.
我们存储 Date
SQLite 邮箱中的值如下 ISO 8601 字符串通过 Date.prototype.toISOString (使用 UTC 时区进行相等比较以正常运行)。
还存储搜索查询中的所有属性的索引。
项目
下面的表格概述了我们在源代码和开发过程中使用的项目(按字母顺序排序):
项目 | 目的 |
---|---|
安西布尔 | DevOps 自动化平台,用于轻松维护、扩展和管理我们的整个服务器群。 |
布莉 | Node.js 和 JavaScript 的作业调度程序,具有 cron、dates、ms、later 和人性化支持。 |
舱 | 开发人员友好的 JavaScript 和 Node.js 日志库,考虑到安全性和隐私性。 |
小伙子 | Node.js 框架通过 MVC 等为我们的整个架构和工程设计提供支持。 |
MongoDB | 我们用于存储邮箱外部的所有其他数据(例如您的帐户、设置、域和别名配置)的 NoSQL 数据库解决方案。 |
猫鼬 | 我们在整个堆栈中使用 MongoDB 对象文档建模(“ODM”)。我们编写了特殊的帮助程序,让我们可以简单地继续使用 猫鼬与 SQLite 🎉 |
节点.js | Node.js 是开源、跨平台的 JavaScript 运行时环境,它运行我们所有的服务器进程。 |
注意邮件 | 用于发送电子邮件、创建连接等的 Node.js 包。我们是该项目的官方赞助商。 |
雷迪斯 | 用于缓存、发布/订阅通道以及基于 HTTPS 请求的 DNS 的内存数据库。 |
SQLite3多重密码 | SQLite 的加密扩展允许加密整个数据库文件(包括预写日志(“WAL")、日志、回滚、...)。 |
SQLite工作室 | 可视化 SQLite 编辑器(您也可以使用它)来测试、下载和查看开发邮箱。 |
SQLite | 嵌入式数据库层,用于可扩展、独立、快速且有弹性的 IMAP 存储。 |
垃圾邮件扫描器 | Node.js 反垃圾邮件、电子邮件过滤和网络钓鱼防护工具(我们的替代品) 垃圾邮件刺客 和 垃圾邮件). |
柑橘 | 使用 Node.js 通过 HTTPS 进行 DNS 请求并使用 Redis 进行缓存 – 这可确保全局一致性等等。 |
雷鸟 | 我们的开发团队使用这个(也推荐这个)作为 与转发电子邮件一起使用的首选电子邮件客户端. |
UTM | 我们的开发团队使用它为 iOS 和 macOS 创建虚拟机,以便使用我们的 IMAP 和 SMTP 服务器测试不同的电子邮件客户端(并行)。 |
乌班图 | 基于 Linux 的现代开源服务器操作系统为我们所有的基础设施提供支持。 |
野鸭 | IMAP 服务器库 – 请参阅其注释 附件重复数据删除 和 IMAP 协议支持. |
更好的 sqlite3 多重密码 | Node.js 快速而简单的 API 库可通过编程方式与 SQLite3 进行交互。 |
电子邮件模板 | 开发人员友好的电子邮件框架,用于创建、预览和发送自定义电子邮件(例如帐户通知等)。 |
json-sql | 使用 Mongo 风格语法的 SQL 查询生成器。这节省了我们开发团队的时间,因为我们可以使用与数据库无关的方法继续在整个堆栈中以 Mongo 风格进行编写。 它还有助于通过使用查询参数来避免 SQL 注入攻击。 |
knex 模式检查器 | 用于提取有关现有数据库模式信息的 SQL 实用程序。这使我们能够轻松验证所有索引、表、列、约束等是否有效并且是 1:1 他们应该如何。我们甚至编写了自动帮助程序,以便在数据库架构发生更改时添加新列和索引(还提供非常详细的错误警报)。 |
克内克斯 | SQL 查询生成器,我们仅使用它来进行数据库迁移和模式验证 knex-schema-inspector . |
普通话 | 自动的 国际化 支持 Markdown 的短语翻译 谷歌云翻译API. |
MX-连接 | Node.js 包用于解析和建立与 MX 服务器的连接并处理错误。 |
颗粒物 | 具有内置负载均衡器的 Node.js 生产流程管理器(微调的 以达到性能)。 |
smtp服务器 | SMTP 服务器库 – 我们将其用于邮件交换(“MX”)和出站 SMTP 服务器。 |
映射测试 | 用于根据基准和 RFC 规范 IMAP 协议兼容性测试 IMAP 服务器的有用工具。该项目是由 鸽舍 团队(2002 年 7 月开始活跃的开源 IMAP 和 POP3 服务器)。我们使用此工具广泛测试了我们的 IMAP 服务器。 |
您可以找到我们使用的其他项目 我们在 GitHub 上的源代码.
供应商
提供者 | 目的 |
---|---|
云耀斑 | DNS 提供商、运行状况检查、负载均衡器和备份存储使用 Cloudflare R2. |
数字海洋 | 专用服务器托管、SSD 块存储和托管数据库。 |
沃尔特 | 专用服务器托管和 SSD 块存储。 |
想法
原则
转发电子邮件是根据以下原则设计的:
实验
太棒了; 最终,由于性能原因,使用 S3 兼容的对象存储和/或虚拟表在技术上是不可行的,并且由于内存限制而容易出错。
我们已经做了一些实验,最终形成了上面讨论的最终 SQLite 解决方案。
其中之一是尝试使用 克隆 和 SQLite 以及 S3 兼容的存储层。
该实验使我们进一步了解和发现了围绕 rclone、SQLite 和 VFS 用法:
- 如果您启用
--vfs-cache-mode writes
使用 rclone 标记,则读取将正常,但写入将被缓存。- 如果您有多个分布在全球的 IMAP 服务器,那么缓存将在它们之间关闭,除非您有一个写入器和多个侦听器(例如,发布/订阅方法)。
- 这是极其复杂的,增加任何额外的复杂性都会导致更多的单点故障。
- S3 兼容的存储提供商不支持部分文件更改 – 这意味着对文件的任何更改
.sqlite
文件将导致数据库的完全更改和重新上传。 - 其他解决方案如
rsync
存在,但它们并不专注于预写日志(“WAL“)支持 - 所以我们最终审查了 Litestream。幸运的是,我们的加密使用已经加密了 WAL 文件,所以我们不需要依赖 Litestream。然而,我们对 Litestream 的生产用途还没有信心,下面对此有一些说明。 - 使用此选项
--vfs-cache-mode writes
(这 仅有的 使用 SQLite 的方法rclone
对于写入)将尝试在内存中从头开始复制整个数据库 - 处理一个 10 GB 邮箱是可以的,但是处理具有极高存储空间的多个邮箱将导致 IMAP 服务器遇到内存限制,并且ENOMEM
错误、分段错误和数据损坏。
- 如果您尝试使用 SQLite 虚拟表 (例如使用 s3db)为了让数据存在于 S3 兼容的存储层上,那么您将遇到更多问题:
- 我们还探索了使用 sqlite-s3vfs 这在概念和技术上与前面的要点相似(因此具有相同的问题)。一种可能性是使用自定义
sqlite3
构建用加密包装,例如 wxSQLite3 (我们目前在上面的解决方案中使用)通过 编辑安装文件. - 另一种可能的方法是使用 多路复用扩展,但是这有 32 GB 的限制,并且需要复杂的构建和开发。
ALTER TABLE
语句是必需的(因此这完全排除了使用虚拟表)。我们需要ALTER TABLE
为了我们的钩子声明knex-schema-inspector
正常工作 – 这确保数据不会损坏,并且检索到的行可以根据我们的要求转换为有效文档mongoose
模式定义(包括约束、变量类型和任意数据验证)。- 开源社区中几乎所有与 SQLite 相关的 S3 兼容项目都使用 Python(而不是我们 100% 堆栈中使用的 JavaScript)。
- 压缩库如 sqlite-zstd (看 评论)看起来很有希望,但是 可能尚未准备好用于生产使用。相反,对数据类型进行应用程序端压缩,例如
String
,Object
,Map
,Array
,Set
, 和Buffer
将是一种更干净、更简单的方法(并且也更容易迁移,因为我们可以存储Boolean
标志或列 - 甚至使用PRAGMA
user_version=1
用于压缩或user_version=0
不压缩为数据库元数据)。- 幸运的是,我们已经在 IMAP 服务器存储中实现了附件重复数据删除功能,因此具有相同附件的每封邮件都不会保留附件的副本,而是为邮箱(以及外部邮件)中的多个邮件和线程存储单个附件。后续使用参考)。
- Litestream 项目是一个 SQLite 复制和备份解决方案,非常有前途,我们将来很可能会使用它。
- 不要抹黑作者——因为我们十多年来一直热爱他们的工作和对开源的贡献——但是从现实世界的使用来看,似乎有 可能会很头痛 和 使用中潜在的数据丢失.
- 备份恢复需要顺利且简单。使用 MongoDB 等解决方案
mongodump
和mongoexport
不仅繁琐,而且耗时且配置复杂。- SQLite 数据库使这一切变得简单(它是一个文件)。
- 我们想要设计一个解决方案,让用户可以随时拿走他们的邮箱并离开。
- 简单的 Node.js 命令
fs.unlink('mailbox.sqlite'))
并且它会从磁盘存储中永久删除。 - 我们可以类似地使用与 S3 兼容的 API 和 HTTP
DELETE
为用户轻松删除快照和备份。
- 简单的 Node.js 命令
- SQLite 是最简单、最快且最具成本效益的解决方案。
缺乏替代方案
据我们所知,没有其他电子邮件服务是这样设计的,也不是开源的。
我们 认为这可能是由于 现有的电子邮件服务在生产中具有遗留技术 意大利面条代码 🍝.
大多数(如果不是全部)现有电子邮件服务提供商要么是闭源的,要么是标榜开源的, 但实际上只有他们的前端是开源的。
电子邮件中最敏感的部分 (实际存储/IMAP/SMTP交互) 全部在后端(服务器)完成,并且 不是 在前端(客户端).
尝试转发电子邮件
今天就报名吧 https://forwardemail.net! 🚀