NFSv4 客户端标识符¶
本文档解释了 NFSv4 协议如何标识客户端实例,以便在系统重启期间维护文件打开和锁定状态。每个客户端上都维护一个特殊的标识符和主体。这些可以由管理员、站点管理员提供的脚本或 Linux 发行商提供的工具设置。
如果客户端的 NFSv4 标识符及其主体没有仔细选择,则存在风险。
简介¶
NFSv4 协议使用“基于租约的文件锁定”。租约帮助 NFSv4 服务器提供文件锁定保证并管理其资源。
简单来说,NFSv4 服务器为每个 NFSv4 客户端创建一个租约。服务器将每个客户端的文件打开和锁定状态收集在该客户端的租约下。
客户端负责定期续订其租约。在租约保持有效时,持有该租约的服务器保证客户端已创建的文件锁定保持不变。
如果客户端停止续订其租约(例如,如果它崩溃),NFSv4 协议允许服务器在一定时间后删除客户端的打开和锁定状态。当客户端重新启动时,它会向服务器指示与其先前租约关联的打开和锁定状态不再有效,可以立即销毁。
此外,每个 NFSv4 服务器管理一个客户端租约的持久列表。当服务器重新启动并且客户端尝试恢复其状态时,服务器使用此列表来区分服务器重新启动之前持有状态的客户端和发送新的 OPEN 和 LOCK 请求的客户端。这使得文件锁定能够在服务器重启后安全地持续存在。
NFSv4 客户端标识符¶
每个 NFSv4 客户端都向 NFSv4 服务器提供一个标识符,以便它们可以将客户端与其租约相关联。每个客户端的标识符由两个元素组成
co_ownerid: 一个任意但固定的字符串。
启动验证器:一个 64 位实例验证器,使服务器能够区分同一客户端的连续启动时期。
NFSv4.0 规范将这两个项目称为“nfs_client_id4”。NFSv4.1 规范将这两个项目称为“client_owner4”。
NFSv4 服务器将此标识符绑定到客户端呈现它时使用的主体和安全风格。服务器使用此主体来授权客户端发送的后续租约修改操作。实际上,此主体是标识符的第三个元素。
作为呈现给服务器的身份的一部分,一个好的“co_ownerid”字符串具有几个重要的属性
“co_ownerid”字符串在重新启动恢复期间标识客户端,因此该字符串在客户端重新启动后保持不变。
“co_ownerid”字符串帮助服务器区分客户端与其他客户端,因此该字符串是全局唯一的。请注意,没有中央机构分配“co_ownerid”字符串。
由于它经常在网络上以明文形式出现,“co_ownerid”字符串不会泄露有关客户端本身的私有信息。
“co_ownerid”字符串的内容在客户端在重新启动后尝试 NFSv4 挂载之前设置并且不会更改。
NFSv4 协议对“co_ownerid”字符串的大小设置了 1024 字节的限制。
保护 NFSv4 租约状态¶
NFSv4 服务器利用如上所述的“client_owner4”为每个客户端分配唯一的租约。在此方案下,在某些情况下,客户端可能会相互干扰。这被称为“租约窃取”。
如果不同的客户端呈现相同的“co_ownerid”字符串并使用相同的主体(例如,AUTH_SYS 和 UID 0),则服务器无法判断客户端是否相同。每个不同的客户端呈现不同的启动验证器,因此对于服务器来说,它看起来像是一个经常重新启动的客户端。在这种情况下,没有一个客户端可以维护打开或锁定状态。
如果不同的客户端呈现相同的“co_ownerid”字符串并使用不同的主体,则服务器可能会允许第一个客户端正常运行,但拒绝随后具有相同“co_ownerid”字符串的客户端。
如果客户端的“co_ownerid”字符串或主体不稳定,则无法保证服务器或客户端重新启动后的状态恢复。如果客户端意外重新启动,但向服务器呈现不同的“co_ownerid”字符串或主体,则服务器会孤立客户端之前的打开和锁定状态。这会阻止访问锁定的文件,直到服务器删除孤立状态。
如果服务器重新启动并且客户端向服务器呈现更改的“co_ownerid”字符串或主体,则服务器将不允许客户端收回其打开和锁定状态,并且可能会在此期间将这些锁定授予其他客户端。这被称为“锁定窃取”。
租约窃取和锁定窃取增加了拒绝服务的可能性,在极少数情况下甚至会导致数据损坏。
选择合适的客户端标识符¶
默认情况下,Linux NFSv4 客户端实现构造其“co_ownerid”字符串,首先是“Linux NFS”字样,然后是客户端的 UTS 节点名称(顺便说一句,与 AUTH_SYS 凭据中用作“机器名称”的节点名称相同)。在小型部署中,这种构造通常足够。但是,通常情况下,节点名称本身不够唯一,并且可能会意外更改。有问题的情况包括
NFS-root(无盘)客户端,其中本地 DHCP 服务器(或等效服务器)不提供唯一的主机名。
单个 Linux 主机中的“容器”。如果每个容器都有一个单独的网络命名空间,但不使用 UTS 命名空间来提供唯一的主机名,则可能存在多个具有相同主机名的 NFS 客户端实例。
跨多个管理域访问公共 NFS 服务器的客户端。如果未集中分配主机名,则除非主机名中包含域名,否则无法保证唯一性。
Linux 提供了两种机制来为其“co_ownerid”字符串添加唯一性
- nfs.nfs4_unique_id
此模块参数可以通过内核命令行设置任意唯一标识符字符串,或者在加载“nfs”模块时设置。
- /sys/fs/nfs/net/nfs_client/identifier
此虚拟文件自 Linux 5.3 起可用,它对于访问它的网络命名空间是本地的,因此当主机名保持统一时,它可以提供网络命名空间(容器)之间的区分。
请注意,此文件在命名空间创建时为空。如果容器系统可以访问某种类型的每个容器身份,则可以使用该唯一标识符。例如,可以在启动时使用容器的内部标识符形成唯一标识符
- sha256sum /etc/machine-id | awk ‘{print $1}’ \
> /sys/fs/nfs/net/nfs_client/identifier
安全注意事项¶
强烈建议对租约管理操作使用加密安全。
如果未配置带有 Kerberos 的 NFS,则 Linux NFSv4 客户端使用 AUTH_SYS 和 UID 0 作为其客户端身份的主体部分。此配置不仅不安全,而且会增加租约和锁定窃取的风险。但是,它可能是没有本地持久存储的客户端配置的唯一选择。在这种情况下,“co_ownerid”字符串的唯一性和持久性至关重要。
当 Kerberos 密钥表存在于 Linux NFS 客户端上时,客户端会尝试使用该密钥表中的一个主体来标识自己到服务器。“sec=”挂载选项不控制此行为。或者,具有 Kerberos 主体的单用户客户端可以使用该主体代替客户端的主机主体。
为此目的使用 Kerberos 允许客户端和服务器对所有“sec=”设置覆盖的操作使用相同的租约。此外,Linux NFS 客户端使用带有 Kerberos 的 RPCSEC_GSS 安全风格和完整性 QOS 来防止传输中租约修改请求的修改。
补充说明¶
Linux NFSv4 客户端在其访问的每个 NFSv4 服务器上建立一个租约。来自特定服务器的 Linux NFSv4 客户端的 NFSv4 挂载然后共享该租约。
一旦客户端建立打开和锁定状态,NFSv4 协议允许租约状态转换到其他服务器,跟随已迁移的数据。这完全向正在运行的应用程序隐藏了数据迁移。Linux NFSv4 客户端通过向其遇到的所有服务器呈现相同的“client_owner4”来促进状态迁移。
另请参阅¶
nfs(5)
kerberos(7)
NFSv4.0 规范的 RFC 7530
NFSv4.1 规范的 RFC 8881。