# 使用 ZFS 原生加密保护数据

* 原文链接：[Protecting Data with ZFS Native Encryption](https://freebsdfoundation.org/our-work/journal/browser-based-edition/storage-and-filesystems/protecting-data-with-zfs-native-encryption/)
* 作者：Roller Angel

ZFS 原生支持加密数据集，能让你轻松地使用行业标准的密码套件来保护数据。与磁盘的全盘加密相比，将数据集加密的主要优势在于，当未使用数据集时，可以将其卸载，而全盘加密要求在静止状态下加密时，磁盘必须关闭。请记住，ZFS 原生加密有加载和卸载密钥的概念。仅仅卸载加密的数据集是不够的，你还必须卸载与该数据集关联的密钥。如果密钥仍然处于加载状态，数据集可以被挂载并且数据将可用。卸载密钥会使挂载操作失败。加载密钥是挂载数据集的前提。嵌套的子数据集会继承其父数据集的加密密钥，但这并非必须。即使父数据集使用不同的加密设置，也可以使用不同的加密密钥和密码套件。最后，更改密钥就像在数据集上执行 `zfs change-key` 命令一样简单。

这些是开始使用的基础概念。

为新创建的数据集启用加密参数，再设置密钥格式就足够了。如果未指定加密密码套件，则默认使用 aes-256-gcm。默认值可能会随着未来新增密码套件而变化。现有数据集的加密属性是只读的，无法修改未加密的数据集的属性来启用加密。要指定加密属性，你需要了解有哪些参数可用。我建议阅读 `zfsprops` 手册页，你可以输入命令 `man zfsprops` 来查看。我还建议阅读 `zfs-load-key` 的手册页。对于我们的第一个加密数据集，我们将使用默认的密码套件、口令密钥格式，来创建一个名为 `secrets` 的数据集。我使用的是我实验室中创建的 FreeBSD jail 机器，名为 alice。实验室中的所有 jail 都位于名为 `lab` 的 zpool 上。我已将叫 `zroot` 的 zpool 分配给 jail。在 jail 中，我必须使用完整路径 `lab/alice/zroot` 作为 zpool 名称，以便在其中创建数据集。作为对比，我的笔记本电脑上，我可以直接使用我的 zpool 名称并在那里创建数据集。以下是创建加密数据集的命令，适用于 alice jail 和我的笔记本电脑。同其他 ZFS 数据集一样，设置挂载点是一个好主意，但请记住 ZFS 是个分层文件系统，因此不要使用现有路径作为新数据集的挂载点。

alice jail:

```sh
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=/secrets
lab/alice/zroot/secrets
```

我的笔记本：

```sh
zfs create -o encryption=on -o keyformat=passphrase -o mountpoint=/secrets zroot/secrets
```

在运行 `zfs create` 命令后，提示我输入一个足够长的口令。现在，我有了一个已挂载的加密数据集，可以在其中存储需要保护的数据。当数据集处于挂载状态时，我可以像使用其他未加密的数据集一样使用它。当我完成机密数据的添加后，我可以通过输入命令 `zfs unmount -u lab/alice/zroot/secrets` 一次性卸载数据集和密钥。要解密、重新挂载数据，只需运行命令 `zfs mount -l lab/alice/zroot/secrets`。这会提示我输入口令，加载密钥，然后挂载数据集。如果在卸载命令中省略参数 `-u`，只会卸载数据集，密钥仍然会保持加载状态。数据集仍然可以通过 `zfs mount lab/alice/zroot/secrets` 挂载，而无需输入口令。要在数据集已卸载后卸载密钥，我运行 `zfs unload-key lab/alice/zroot/secrets`。现在，之前的挂载命令将失败，因为密钥没有加载，并且我未提供参数 `-l` 来让 ZFS 在挂载之前加载密钥。要加载密钥且允许挂载数据集，我会运行 `zfs load-key lab/alice/zroot/secrets`。系统会提示我输入口令，之前的挂载命令现在会成功，因为密钥已经加载。要检查密钥是否已加载，可以查看数据集的属性。当我运行命令 `zfs list -o name,mountpoint,encryption,keylocation,keyformat,keystatus,encryptionroot lab/alice/zroot/secrets` 时，会显示一些有用的属性。`KEYSTATUS` 列显示为 "available" 时，表示密钥已加载。要查看所有数据集属性，可以使用 `zfs get all lab/alice/zroot/secrets`。

接下来，我在 `secrets` 数据集下创建一个嵌套的数据集，并使用不同的密码套件和密钥格式。这次，我将使用密钥文件而非口令。要创建使用密钥文件的数据集，我首先需要生成密钥并将其存储在文件中。我通过输入命令 `dd if=/dev/urandom bs=32 count=1 of=/media/more-secrets.key` 来完成。由于密钥文件要求长度为 32 字节，因此我使用了 `bs=32`。输出路径选择 `/media`，因为我在此路径下挂载了一个便携式 USB 驱动器，还使用 `dd` 命令生成了密钥文件，再将其直接存储到驱动器上。这样，当我卸载密钥并卸载和移除 USB 驱动器时，密钥文件就不会留在我的机器上。我建议将密钥文件存储在多个 USB 驱动器上，以防某个 USB 驱动器损坏。现在，密钥文件已经生成，我可以通过运行命令 `zfs create -o encryption=aes-256-ccm -o keyformat=raw -o keylocation=file:///media/more-secrets.key -o mountpoint=/secrets/more-secrets lab/alice/zroot/secrets/more-secrets` 来创建使用 AES-256-CCM 密码套件的嵌套数据集。查看这个新数据集的属性时，我可以看到 `ENCROOT` 列设置为 `lab/alice/zroot/secrets/more-secrets`。我可以使用与 `secrets` 数据集相同的方法来卸载和卸载密钥。当我拥有更多数据集和密钥时，我可能想考虑使用 `zfs unload-key -a` 卸载所有密钥，或使用 `zfs unload-key -r lab/alice/zroot/secrets` 来仅卸载 `secrets` 数据集及其所有后代数据集的密钥。而且，如果我想加载 `secrets` 数据集及其所有后代数据集的密钥子集，可以运行 `zfs load-key -r lab/alice/zroot/secrets`。

如果我后来决定 `more-secrets` 数据集中的数据不需要单独的密钥文件，而是希望它继承父数据集 `secrets` 的设置（即从自定义生成的密钥文件切换到之前配置的口令），我只需要运行命令 `zfs change-key -i lab/alice/zroot/secrets/more-secrets`。再次查看属性，注意到 `ENCROOT`、`KEYLOCATION` 和 `KEYFORMAT` 都已经发生了变化。然而，密码套件不会改变，因为密码套件只能在数据集创建时设置。由于 `more-secrets` 是包含在 `secrets` 中的，它将作为卸载 `secrets` 数据集的一部分被卸载。虽然挂载 `secrets` 数据集不会自动挂载 `more-secrets`，但它需要单独挂载。不过，由于它们共享相同的密钥，所以密钥只需要加载一次。要切换回使用密钥文件，我运行命令 `zfs change-key -o keyformat=raw -o keylocation=file:///media/more-secrets.key lab/alice/zroot/secrets/more-secrets`。如果我想永久销毁 `more-secrets` 中的数据，我只需卸载数据集、卸载密钥，并销毁密钥文件及我所做的任何备份副本。现在，数据将无法恢复。然后，我可以运行命令 `zfs destroy lab/alice/zroot/secrets/more-secrets` 来移除该数据集。

最后，我想分享的一点是关于加密数据的备份。如你所见，ZFS 原生加密能轻松地使用加密来保护数据。加密数据集的快照可以以加密形式传输到不受信任的备份服务器上。没有密钥，远程备份服务器就无法挂载该数据集。可以使用 `zfs send` 命令的参数 `--raw` 来实现这一点。有关更多细节，我建议阅读 `zfs-send` 的手册页，以了解其工作原理，然后获取《ZFS Mastery: Advanced ZFS》一书，深入研究具体细节，并学习一系列技巧，以进一步提升你的 ZFS 技能。

希望你喜欢这篇操作指南，并且开始使用 ZFS 文件系统提供的原生加密来保护你的敏感数据。

***

**Roller Angel** 大部分时间都在帮助人们学习如何使用技术实现他们的目标。他是一位热衷于 FreeBSD 系统管理的 Python 爱好者，喜欢学习开源技术（尤其是 FreeBSD 和 Python）解决问题的神奇方法。他坚信人们可以学习所有他们愿意去做的事情。Roller 总是在寻找创造性的解决方案，享受解决问题的挑战。他有很强的学习动机，喜欢探索新想法，并保持技能的敏锐。他喜欢参与研究社区，并分享自己的想法。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.bsdcn.org/qi-kan/20240708-cun-chu-yu-wen-jian-xi-tong/zfs.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
