本文档翻译自Documentation/filesystems/nfs/client-identifier.rst,翻译时文件的最新提交是d56b699d76d1 Documentation: Fix typos
,大部分借助于ChatGPT翻译,仅作为我个人的参考,如果你想查阅,建议看英文文档,因为我不确定我记录的中文翻译是否完整和正确。
本文档解释了NFSv4协议如何标识客户端实例,以便在系统重新启动期间维护文件打开和锁定状态。每个客户端都维护一个特殊的标识符和主体。这些可以由管理员、站点管理员提供的脚本或Linux发行版提供的工具进行设置。
如果客户端的NFSv4标识符和其主体选择不当,存在风险。
NFSv4协议使用“基于租约的文件锁定”。租约有助于NFSv4服务器提供文件锁定保证并管理其资源。
简而言之,NFSv4服务器为每个NFSv4客户端创建一个租约。服务器在该租约下收集每个客户端的文件打开和锁定状态。
客户端负责定期更新其租约。在租约保持有效的同时,持有该租约的服务器保证客户端创建的文件锁定仍然有效。
如果客户端停止更新其租约(例如,如果崩溃),NFSv4协议允许服务器在一定时间后移除客户端的打开和锁定状态。当客户端重新启动时,它向服务器指示与其先前租约关联的打开和锁定状态不再有效,可以立即销毁。
此外,每个NFSv4服务器管理一个持久的客户端租约列表。当服务器重新启动并且客户端尝试恢复其状态时,服务器使用此列表来区分在服务器重新启动之前保留状态的客户端和发送新的OPEN和LOCK请求的客户端。这使文件锁定可以安全地持续存在于服务器重新启动之间。
每个NFSv4客户端向NFSv4服务器呈现一个标识符,以便它们可以将客户端与其租约关联起来。每个客户端的标识符包括两个元素:
- co_ownerid: 一个任意但固定的字符串。
- boot verifier: 一个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服务器使用上述的“client_owner4”来分配给每个客户端一个唯一的租约。在此方案下,存在客户端可能相互干扰的情况。这被称为“租约窃取”。
如果不同的客户端呈现相同的“co_ownerid”字符串并使用相同的主体(例如,AUTH_SYS和UID 0),服务器将无法判断这两个客户端是否相同。每个不同的客户端呈现不同的引导验证器,因此对服务器来说,就好像有一个客户端频繁重新引导。在这种情况下,任何一个客户端都无法维持打开或锁定状态。
如果不同的客户端呈现相同的“co_ownerid”字符串并使用不同的主体,服务器可能会允许第一个客户端正常操作,但拒绝具有相同“co_ownerid”字符串的后续客户端。
如果客户端的“co_ownerid”字符串或主体不稳定,则不能保证在服务器或客户端重新启动后可以恢复状态。如果客户端意外重新启动但向服务器呈现不同的“co_ownerid”字符串或主体,则服务器会孤立客户端先前的打开和锁定状态。这会阻止对锁定文件的访问,直到服务器删除孤立的状态。
如果服务器重新启动并且客户端向服务器呈现更改的“co_ownerid”字符串或主体,则服务器将不允许客户端重新获取其打开和锁定状态,并可能在此期间将这些锁定给其他客户端。这被称为“锁窃取”。
租约窃取和锁窃取增加了服务拒绝的潜在风险,甚至在极少数情况下可能导致数据损坏。
默认情况下,Linux NFSv4客户端实现使用以“Linux NFS”开头,后跟客户端的UTS节点名称(与AUTH_SYS凭据中的“机器名称”相同)构造其“co_ownerid”字符串。在小型部署中,此构造通常是足够的。然而,通常情况下,仅使用节点名称可能不足够唯一,并且可能会意外更改。存在问题的情况包括:
- NFS根(无磁盘)客户端,其中本地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”字符串的唯一性和持久性在这种情况下至关重要。
当Linux NFS客户端上存在Kerberos keytab时,客户端会尝试在向服务器标识自己时使用该keytab中的主体之一。 "sec ="挂载选项不控制此行为。或者,具有Kerberos主体的单用户客户端可以在客户端的主机主体的位置使用该主体。
使用Kerberos进行此目的使客户端和服务器能够对所有“sec =”设置覆盖的操作使用相同的租约。此外,Linux NFS客户端使用Kerberos和完整性QOS的RPCSEC_GSS安全性类型,以防止对租约修改请求的传输修改。
Linux NFSv4客户端在访问的每个NFSv4服务器上建立一个租约。然后,来自Linux NFSv4客户端的特定服务器的NFSv4挂载共享该租约。
一旦客户端建立了打开和锁定状态,NFSv4协议就使租约状态可以过渡到其他服务器,遵循已迁移的数据。这完全隐藏了正在运行的应用程序对数据迁移的感知。 Linux NFSv4客户端通过向遇到的所有服务器呈现相同的“client_owner4”来促进状态迁移。
- nfs(5)
- kerberos(7)
- 有关NFSv4.0规范的RFC 7530
- 有关NFSv4.1规范的RFC 8881。