#dkim #签名密钥 #邮件服务器 #milter #验证 #消息

bin+lib dkim-milter

DKIM 签名和验证的 Milter

10 个版本

0.2.0-alpha.12024年6月13日
0.1.0 2023年12月27日
0.0.7 2023年10月29日
0.0.5 2023年8月25日
0.0.1 2022年12月22日

84电子邮件

Download history 46/week @ 2024-06-07 40/week @ 2024-06-14 3/week @ 2024-06-28 30/week @ 2024-07-05 67/week @ 2024-07-26 7/week @ 2024-08-02

74 每月下载量

GPL-3.0-or-later

300KB
7K SLoC

DKIM Milter

DKIM Milter 是一个使用 域名密钥识别邮件 (DKIM) 协议签名和验证电子邮件消息的 milter 应用程序。它旨在与具有 milter 功能的 MTA(邮件服务器)如 Postfix 集成。DKIM 由 RFC 6376 指定。

DKIM Milter 基于 viadkim 库。因此,它继承了该库中使用的 DKIM 方法。值得注意的是,viadkim 完全支持国际化电子邮件,包括在 d= 标签中的 Unicode 签名域(RFC 8616)。更实际的是,它继承了 viadkim 在处理大量或大型消息时的性能特征,并与异步范式 milter 实现相结合。

DKIM Milter 尽可能高效地工作。公钥查询是并行进行的。当多个签名使用相同的参数计算正文哈希时,哈希只计算一次,并将结果共享给所有签名。此外,当不需要计算正文哈希时,例如签名已经确定失败,将完全跳过正文处理。另一个例子是处理大型消息:DKIM Milter 以固定大小的块处理消息正文,这意味着即使同时处理数百个每条一两个兆字节的消息,它也不会受到压力;在任何时刻,这些消息正文的 内存使用量永远不会超过几兆字节左右(即,与消息数量相关,而不是其大小)。

DKIM Milter 可以作为 OpenDKIM milter 的简单替代品。归功于该项目,我已经长期使用该项目,并且该项目也启发了这里的一些选择。

安装

DKIM Milter 是一个 Rust 项目。它可以像往常一样使用 Cargo 构建/安装。例如,使用以下命令安装在 crates.io 上发布的最新版本

cargo install --locked dkim-milter

在构建和安装过程中,可以指定选项 --features pre-rfc8301 以将加密算法和密钥使用回滚到 RFC 8301 之前:它启用了对不安全的、历史性的 SHA-1 算法的支持,并允许使用低于 1024 位的长度的 RSA 密钥。不建议使用此功能。

以下各节将讨论,默认的编译内配置文件路径为 /etc/dkim-milter/dkim-milter.conf。在构建DKIM Milter时,可以通过设置环境变量 DKIM_MILTER_CONFIG_FILE 为期望的路径来覆盖此默认路径。

最低支持的Rust版本是1.74.0。

用法

安装后,DKIM Milter可以通过命令行作为 dkim-milter 启动。

配置参数可以在默认配置文件 /etc/dkim-milter/dkim-milter.conf 中设置。必须在该文件中设置强制参数 socket

调用 dkim-milter 将在前台启动milter。向进程发送终止信号或按Control-C以关闭milter。在milter运行时,发送SIGHUP信号以重新加载配置。

DKIM Milter通常作为系统服务设置。使用提供的systemd服务作为起点。请参阅包含的教程文档,了解如何创建系统服务。

支持的签名算法,包括签名和验证,是 rsa-sha256ed25519-sha256。默认情况下,不支持的旧签名算法 rsa-sha1,此类签名的评估结果为 permerror(RFC 8301;但请参阅上面的功能 pre-rfc8301)。

配置

默认配置文件是 /etc/dkim-milter/dkim-milter.conf。包含的man页 dkim-milter.conf(5) 作为参考文档。(您可以通过将文件路径传递给 man 来在不安装的情况下查看man页: man ./dkim-milter.conf.5

请参阅包含的 示例配置,了解一组配置文件可能的样子。

有关DKIM Milter入门的动手实践介绍,请参阅包含的 教程文档

设计

DKIM Milter配置由主要配置文件 dkim-milter.conf 以及提供额外表格配置的辅助文件或数据源组成。

主要配置文件包含全局设置。对于签名,配置从指定的数据源读取。

可以通过进一步数据源中指定的 overrides 覆盖全局设置以针对所选输入。覆盖可以应用于连接的网络地址、接收者(在 RCPT TO: SMTP命令)和发送者(在 SenderFrom 标题)。

例如,可以使用 recipient_overrides 参数指定某些消息接收者的配置覆盖。这允许全局禁用生成的签名中 l= 标签的使用,但仅对某些接收者启用。

这种设计,主要配置的参数可以通过一定的粒度进行覆盖,应该足够灵活,以满足许多配置需求。

签名-验证决策

对于传递给DKIM Milter的所有消息,消息是否应进行验证或签名的决策如下。

如果消息来自一个 可信源 并由匹配配置的 签名发送者 提交,则该消息将被签名。如果消息来自不受信任的源,则将其验证。换句话说,来自可信源的消息是授权的或符合签名的资格;它不符合验证的资格。

可信源是指来自 trusted_networks(默认:回环地址)中的IP地址的连接,或者如果设置了 trust_authenticated_senders(默认:是),则是经过身份验证的发送者。

消息的 发起者 来自消息的 Sender 头(如果存在),否则来自消息的 From 头。(通常,Sender 不存在,因此发起者将来自 From;然而,如果 From 包含多个邮箱,则必须根据RFC 5322包含 Sender,因此发起者将来自 Sender。)

签名发送者 是为它们设置了签名密钥和签名配置的发送者(域名或电子邮件地址)。它们在参数 signing_senders 指定的数据源中进行配置。

操作模式(仅签名、仅验证或根据上述程序自动进行)也可以通过 mode 参数进行配置。

签名发送者

签名配置通过两个指向表格文件(或其他数据源,见下一节)的配置参数进行设置。这些参数是 signing_senderssigning_keys

signing_senders = </path/to/signing_senders_file
signing_keys = </path/to/signing_keys_file

配置签名的关键是 签名发送者 表(参数 signing_senders)。此表将发送者电子邮件地址链接到具体的签名配置

# Sender expression   Domain        Selector   Signing key name
example.org           example.org   sel1       key1
.example.org          example.org   sel2       key2

发送者表达式 example.org 匹配该域名的发送者(me@example.org)。发送者表达式 .example.org 匹配该域名的发送者和子域名(me@subdomain.example.org)。注意:每个匹配的发送者表达式都会为消息生成额外的DKIM签名。在上面的例子中,来自 me@example.org 的消息使用两个密钥进行签名,因为两个发送者表达式都匹配该地址。(多重签名主要用于使用Ed25519和RSA密钥的双重签名。)

签名发送者 表的第四列中命名的密钥列在 签名密钥 表(参数 signing_keys)中列出

# Key name   Key source
key1         </path/to/signing_key1_pem_file
key2         </path/to/signing_key2_pem_file

密钥源必须是文件系统数据源(即,以 <file: 前缀的路径)指向一个PKCS#8 PEM文件。签名密钥类型(RSA或Ed25519)会自动检测。

可以在可选的第五列中指定额外的每个签名(即,每个发送者表达式匹配)配置覆盖,在 signing_senders 文件中。

本节余下的部分简要介绍了某些附加功能。在域名列中,单个点 . 会复制匹配的发送者地址的域名。下面列表中的两个条目是等效的,两者都生成标签 d=example.com

# Sender expression   Domain        ...
example.com           example.com   ...
example.com           .             ...

签名发送者表也是配置 签名标识符 的地方,即生成的签名中的 i= 标签:默认情况下,签名不包括签名标识符;在域名列中使用 @ 字符可以启用签名标识符。

# Sender expression   Domain/Identity   ...

example.com           @example.com      ...
example.com           @.                ...
# both => d=example.com, i=@example.com

# Double dot separates i= subdomain from d= domain.
mail.example.com      @mail..example.com
# => d=example.com, i=@mail.example.com

# The local-part before the @ may be included literally.
example.com           user@example.com
# => d=example.com, i=user@example.com

# A dot before the @ copies the sender’s local-part into the i= tag.
example.com           .@example.com
# => d=example.com, i=user@example.com

数据源

上面介绍了几种类似表格的文件。事实上,这些都是 数据源 的更一般概念的一部分。某些配置参数引用表格数据 - 一系列条目 - 这些将由特定的数据存储提供。

目前有三个数据源可用:<slurp:,一次性从文件系统中读取数据并保存在内存中;file:,每次需要时从文件系统中重新读取数据;(如果启用)sqlite:,从 SQLite 数据库中读取数据。

内存文件系统数据源<slurp: 前缀的文件路径表示。

示例

signing_keys = </path/to/signing-keys

当使用此数据源时,数据会在启动时被积极读取和验证,并在内存中保持不变,直到终止(或重新加载)。查找是在内存中进行的,操作期间不会访问文件系统。

实时文件系统数据源file: 前缀的文件路径表示。

示例

signing_keys = file:/path/to/signing-keys

使用此数据源时,数据仅在需要时读取,并不保存在内存中。文件更改将立即生效,无需重新加载或重启。

文件系统数据源有哪些用途?您可以仅使用 <。整个配置将在启动时被积极读取,然后仅存在于内存中。如果您在所有地方都使用 file:,则配置将在需要时读取,即文件系统被视为数据库,更改将自动并立即应用。另一种选择是,在整个过程中使用 <,但使用 file: 为签名密钥文件本身:然后所有配置都保存在内存中,但(敏感的)密钥材料仅在签名时临时读取到内存中,使用后丢弃。

SQLite 数据源sqlite: 前缀的 SQLite 数据库 URI 表示。此数据源仅在构建/安装时通过选项 --features sqlite 可用。如果需要,可以通过附加 # 后跟表名来自定义表名。

示例

signing_keys = sqlite://mail-config.db#dkim_signing_keys

数据库模式在其他地方有文档说明。

将来,可以添加更多数据源(SQL、LDAP、…)。

密钥设置

严格来说,密钥管理不在 DKIM Milter 的范围内。然而,有一个配套工具 dkimdo,您可以使用它手动进行密钥设置。以下是一个简要介绍。

对于签名,DKIM Milter 从 PKCS#8 PEM 格式的文件中读取 签名密钥(私钥)。这种格式可以通过其起始行 -----BEGIN PRIVATE KEY----- 来识别。

使用以下命令分别生成 RSA 2048 位或 Ed25519 私钥文件 private.pem

dkimdo genkey --out-file private.pem rsa
dkimdo genkey --out-file private.pem ed25519

这些命令为 RSA 或 Ed25519 密钥类型创建签名密钥文件。每个命令还会将相应的 DKIM 公钥记录 输出到标准错误流。例如,对于 RSA,打印的记录看起来像以下这样

v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCA...

每个签名密钥的公钥记录必须作为TXT记录在DNS中公布,域名格式为:<选择器>._domainkey.<域名>。具体操作方式取决于DNS软件和/或DNS服务提供商。

以下是一个示例,展示了如何使用dig工具在DNS中查找以这种方式生成的公钥记录。注意选择器ed25519.2022和域名gluet.ch

dig +short ed25519.2022._domainkey.gluet.ch txt
"v=DKIM1; k=ed25519; p=7mOZGVMZF55bgonwHLfOzwlU+UAat5//VJEugD3fyz0="

(上述Ed25519公钥记录可以放在单个文本字符串中。较大的RSA记录通常分散在几个文本字符串中。此类大型TXT记录的设置取决于DNS软件和/或DNS服务提供商。)

还可以使用dkimdo query查询公钥记录。还可以使用dkimdo keyinfo从现有的签名密钥生成公钥记录。

请注意,dkimdo的输出并非定制或神奇,您也可以使用OpenSSL项目的标准openssl工具生成密钥材料。

许可证

版权所有 © 2022–2024 David Bürgin

本程序是自由软件:您可以在自由软件基金会发布的GNU通用公共许可证条款下重新分发和/或修改它,许可证版本为3,或(根据您的选择)许可证的任何较新版本。

本程序是在希望它将是有用的希望下分发的,但没有任何保证;甚至没有关于其商业性或针对特定目的的适用性的暗示保证。有关详细信息,请参阅GNU通用公共许可证。

您应该已经收到了GNU通用公共许可证副本。如果没有,请参阅https://www.gnu.org/licenses/

依赖项

~15–32MB
~542K SLoC