一台主机把目录导出给一个 VPN 地址,挂载成功,设计文档里便记成了“已认证”。这条规则真正约束的,只是数据包看起来从哪儿来。至于发起文件操作的人是否真是他声称的人,取决于 NFS 的安全模式;而在这类导出中,多数人根本没有把它从默认值改掉。

一次请求里可能出现三种身份标识

VPN 对端携带的是登记过的身份,例如客户端证书或对端密钥。内层 IP 数据包携带的是源地址。文件操作携带的则是其 NFS 机制所提供的用户凭据。把地址当成三者共同代表的方案,恰恰悄悄假定了本该验证的那一部分。

IdentityWhat it provesWhat it does not
VPN peerWhich peer was accepted by the tunnelWhich user runs on that machine
Inner source addressWhere the packet appears to originateThat only the intended client can use it
NFS credentialWhat the file service believes about the callerAnything, if the flavour only asserts local IDs

把这三行写清楚,就是全部功课。一旦看清每个控制点落在哪一行,关于导出是否安全的大部分争论就不成立了。

地址归属是实现的细节

网关可以给通过认证的对端分配隧道地址,也可以为其授权一个可路由子网。至于实现随后是否拒绝内层源属于其他客户端的数据包,那是该实现自身的性质,值得核实而不是假定。

传统 Community 版OpenVPN的用户态服务器代码,例如可以在数据包经 TUN 设备交付之前就拒绝与其他客户端关联的源地址。这是有实际价值的地址归属检查,但它仍然不是 NFS 用户认证,因为它并没有说明是哪个账户在执行操作。

源地址转换甚至连这一点也会侵蚀。NAT 网关可以把所有客户端表现为同一个地址,此时导出规则就无法区分它背后的人。规则表面上的精确,于是变成由架构图维持的假象。如果导出确实重要,就保持地址显式,而不要让转换器去决定谁算作谁。

信任导出之前,先读安全模式

Red Hat 的 NFS 指南把通过 AUTH_SYS 使用本地 UNIX UID 和 GID 的 sec=sys 与 Kerberos 各模式区分开来。krb5 增加用户认证,krb5i 增加完整性保护,krb5p 则为流量增加隐私保护。

AUTH_SYS 凭据包含调用方 UID 和组 ID 等字段,RFC 5531 明确警告该模式本身不提供任何安全性,而依赖外部保护。在 VPN 内运行它,只是为传输提供了保护,对数据包内部的断言毫无改变。

exportfs -v
showmount -e nfs.example.internal

读客户端列表之前,先读那份输出里的模式。看似受限的导出,可能只受地址限制,而每个调用方仍在声明它想要的任意 UID。

选定模型,然后守住权限

选择适合你实际管理客户端的文件服务认证模型。Kerberos 需要背后的身份与服务部署,所以改一个挂载选项并不等于迁移,在 Kerberos 域、主体和 keytab 存在之前,它只会失败。

无论采用哪种模型,都要保住导出权限和文件系统授权,它们约束着已认证主体能做什么。认证告诉你谁在提问;授权决定答案该不该是肯定的。把两者合并成一个设置,正是迁移最终授予比旧配置更多权限的原因。

把地址规则用在该用的地方

把服务限制在批准的通路和来源上仍然值得,包括 VPN 端点与 NFS 服务器同机时的主机输入策略。本机本地运行的服务并不受转发流量的规则管辖。这一缺口解释了数量惊人的、本应私有的导出。请显式检查本地输入策略,而不要假定隧道规则已经覆盖它。

核实其他接口或做源地址转换的网关不会意外满足该规则,并把它当作建立来源凭证的理由,而不是当作伪造地址即可保证挂载成功的断言。

做三个验收用例,而不是一个。先从已批准客户端以已批准用户身份操作开始,再从从未批准过的对端重试,然后尝试已批准用户仍不被允许的操作。之后再问:如果一台已授权客户端被攻陷会改变什么,因为传输层认证并不会让那台机器上的每个进程都可信。被盗证书和好奇的本地账户是两类不同的问题,需要不同的控制。

相关文章