把 NFS 导出锁到 VPN 地址:它并不能证明什么
导出规则检查的是数据包来自哪里;AUTH_SYS 声明的只是本地 UID,Kerberos 才认证用户。先决定你的文件服务到底需要哪一种。
一台主机把目录导出给一个 VPN 地址,挂载成功,设计文档里便记成了“已认证”。这条规则真正约束的,只是数据包看起来从哪儿来。至于发起文件操作的人是否真是他声称的人,取决于 NFS 的安全模式;而在这类导出中,多数人根本没有把它从默认值改掉。
一次请求里可能出现三种身份标识
VPN 对端携带的是登记过的身份,例如客户端证书或对端密钥。内层 IP 数据包携带的是源地址。文件操作携带的则是其 NFS 机制所提供的用户凭据。把地址当成三者共同代表的方案,恰恰悄悄假定了本该验证的那一部分。
| Identity | What it proves | What it does not |
|---|---|---|
| VPN peer | Which peer was accepted by the tunnel | Which user runs on that machine |
| Inner source address | Where the packet appears to originate | That only the intended client can use it |
| NFS credential | What the file service believes about the caller | Anything, 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 服务器同机时的主机输入策略。本机本地运行的服务并不受转发流量的规则管辖。这一缺口解释了数量惊人的、本应私有的导出。请显式检查本地输入策略,而不要假定隧道规则已经覆盖它。
核实其他接口或做源地址转换的网关不会意外满足该规则,并把它当作建立来源凭证的理由,而不是当作伪造地址即可保证挂载成功的断言。
做三个验收用例,而不是一个。先从已批准客户端以已批准用户身份操作开始,再从从未批准过的对端重试,然后尝试已批准用户仍不被允许的操作。之后再问:如果一台已授权客户端被攻陷会改变什么,因为传输层认证并不会让那台机器上的每个进程都可信。被盗证书和好奇的本地账户是两类不同的问题,需要不同的控制。