注意
本文档适用于 Ceph 的开发版本。
CephFS Fscrypt
Fscrypt 是文件系统级别的加密实现。这种文件系统加密允许按目录级别进行加密。这使得文件系统可以同时拥有加密和常规非加密部分。Fscrypt 加密会加密文件名和数据内容。
本页将介绍我们 CephFS 实现中的重要关键概念。如果您想查看完整的内核规范,请访问:https://docs.linuxkernel.org.cn/filesystems/fscrypt.html
密钥派生过程
主密钥是在 fscrypt 上下文中派生所有密钥的基石。
当 fscrypt 策略应用于一个空目录时,会使用主密钥和一个随机生成的 nonce 派生一个按目录密钥。这个按目录密钥将用于加密目录名、文件名和符号链接。目录中的每个名称都使用相同的按目录密钥进行加密,以加快元数据操作。
创建每个文件时,它将使用父目录密钥生成一个按文件密钥。这个按文件密钥将用于加密文件的数据内容。文件的数据内容被分解为大小为 4K 的 fscrypt 块。这些块将基于按文件密钥派生出的按块密钥进行加密。
生成文件名
创建新 inode 时,提供的名称使用按目录密钥进行加密。纯文本文件名将被加密。然后将此密文转换为 base64 格式。这是为了确保目录名 (dname) 中没有非法字符。如果 base64 编码的名称长度足够短以适合 dname 条目,它将被存储。如果编码文本的长度超过 NAME_MAX,密文将存储在 alternate_name 字段中。然后密文将被截断为较小的尺寸,以进行 base64 编码并存储在 dname 中。
按块加密密钥
将数据块写入文件时,每个块将以 fscrypt 块大小(4096 字节)的块写入,并具有唯一的密钥。每个按块密钥将从按文件密钥+块编号派生。这意味着写入的每个加密块都将具有唯一的密钥。
探究写入路径
FSCrypt 以 fscrypt 块大小 4096 字节的粒度存储数据。写入文件数据时,数据被分成 4K 块,然后加密成一个 4K 的加密块。如果写入大小小于 4K,它将用零填充至 4K,然后进行加密。
inode 为加密文件保存两个大小值
逻辑大小表示数据的纯文本未加密版本。这存储在名为
ceph.fscrypt.file的虚拟扩展属性中。实际大小是加密大小,即 4K 填充后的大小,存储在 osd 上。
修改现有文件和块时,可能需要读-修改-写工作负载。例如,如果需要部分更新单个块,如图 1 所示,则需要 rmw 工作负载。首先读取块,然后根据 key+blocknum 解密,然后将新数据与纯文本版本合并,最后加密后再写入 osd。
图 1
要确定是否需要 rmw,需要分析写入的偏移量和长度。
如果偏移量未对齐到 fscrypt 块,则需要如上所述的 rmw。这会检查写入开始是否需要读取。
如果 offset+len 未对齐到 fscrypt 块,则此处需要 rmw。这会检查写入结束是否需要读取。
如果写入涉及两个以上的块,则任何相邻的块都不需要读取。这是因为将执行 fscrypt 块的完全覆盖,并且不需要读取任何先前的数据。这种行为如图 2 所示。在这种情况下,只需要读取块 3 和块 5。
图 2
空间放大
在几乎所有情况下,使用加密都会导致空间放大。任何未均匀对齐到 fscrypt 块边界的数据集都会出现这种情况。max_size 配额基于此放大的实际大小。
截断
在截断调用未对齐 fscrypt 块的情况下,它需要在结束块上进行 rmw。由于截断调用由 mds 处理,因此此 rmw 操作部分由 mds 处理。首先,客户端读取最后一个块。然后,如图 3 所示,客户端请求截断 (1),mds 然后直接向 osd 执行写入 (2,3),然后将状态返回给客户端 (4)。
图 3