FreeBSD 中文社区 2025 第二季度问卷调查
FreeBSD 中文社区(CFC)
VitePress 镜像站QQ 群 787969044视频教程Ⅰ视频教程Ⅱ
  • FreeBSD 从入门到追忆
  • 中文期刊
  • 状态报告
  • 发行说明
  • 手册
  • 网络文章集锦
  • 笔记本支持报告
  • Port 开发者手册
  • 架构手册
  • 开发者手册
  • 中文 man 手册
  • 文章
  • 书籍
  • FreeBSD 中文期刊
  • 编辑日志
  • 2025-123 下游项目
    • FreeBSD 发布工程:新主管上任
    • GhostBSD:从易用到挣扎与重生
    • BSD Now 与将来
    • 字符设备驱动教程(第三部分)
    • 学会走路——连接 GPIO 系统
    • FreeBSD 中对 SYN 段的处理
    • FreeBSD 2024 年秋季峰会
  • 2024-1112 虚拟化
    • 字符设备驱动程序教程(第二部分)
    • 面向 Linux 和 Windows 用户的 bhyve
    • Xen 与 FreeBSD
    • Wifibox:一种嵌入式虚拟化无线路由器
    • 嵌入式 FreeBSD:Fabric——起步阶段
    • DGP:一种新的数据包控制方法
    • 会议报告:我在都柏林的 EuroBSDCon 体验
  • 2024-0910 内核开发
    • 字符设备驱动程序教程
    • VPP 移植到了 FreeBSD:基础用法
    • 利用 Kyua 的 Jail 功能提升 FreeBSD 测试套件的并行效率
    • FreeBSD 上的 Valgrind
    • 嵌入式 FreeBSD:探索 bhyve
    • TCP/IP 历险记:FreeBSD TCP 协议栈中的 Pacing
    • 实用软件:实现无纸化(Paperless)
  • 2024-0708 存储与文件系统
    • FreeBSD 中的 NVMe-oF
    • FreeBSD iSCSI 入门
    • 使用 ZFS 原生加密保护数据
    • 嵌入式 FreeBSD:打造自己的镜像
    • TCP LRO 简介
    • 基于 Samba 的时间机器备份
  • 2024-0506 配置管理对决
    • 基本系统中的 mfsBSD
    • rdist
    • Hashicorp Vault
    • 在 GitHub 上向 FreeBSD 提交 PR
    • 悼念 Mike Karels
    • 2024 年 5-6 月来信
    • 嵌入式 FreeBSD 面包板
    • TCP/IP 历险记:TCP BBLog
    • 实用软件:开发定制 Ansible 模块
  • 2024-0304 开发工作流与集成
    • FreeBSD 内核开发工作流程
    • FreeBSD 与 KDE 持续集成(CI)
    • 更现代的内核调试工具
    • 从零开始的 ZFS 镜像及 makefs -t zfs
    • 提升 Git 使用体验
  • 2024-0102 网络(十周年)
    • FreeBSD 中的 RACK 栈和替代 TCP 栈
    • FreeBSD 14 中有关 TCP 的更新
    • if_ovpn 还是 OpenVPN
    • SR-IOV 已成为 FreeBSD 的重要功能
    • FreeBSD 接口 API(IfAPI)
    • BATMAN:更优的可移动热点网络方式
    • 配置自己的 VPN——基于 FreeBSD、Wireguard、IPv6 和广告拦截
    • 实用软件:使用 Zabbix 监控主机
  • 2023-1112 FreeBSD 14.0
    • LinuxBoot:从 Linux 启动 FreeBSD
    • FreeBSD 容器镜像
    • 现在用 Webhook 触发我
    • 新的 Ports 提交者:oel Bodenmann (jbo@freebsd.org)
  • 2023-0910 Port 与软件包
    • 回忆录:与 Warner Losh(@imp)的访谈
    • 在你自己的仓库中定制 Poudriere 源
    • Wazuh 和 MITRE Caldera 在 FreeBSD Jail 中的使用
    • PEP 517
    • CCCamp 2023 旅行报告
  • 2023-0708 容器与云
    • 在 Firecracker 上的 FreeBSD
    • 使用 pot 和 nomad 管理 Jail
    • 会议报告:C 与 BSD 正如拉丁语与我们——一位神学家的旅程
    • 抒怀之旅:与 Doug Rabson 的访谈
    • 基于 Jail 的广告拦截教程
    • 我们收到的来信
  • 2023-0506 FreeBSD 三十周年纪念特刊
    • CheriBSD 近十多年的历程
    • AArch64:成为 FreeBSD 新的一级架构
    • 岁月如梭:我个人的时间线
    • 安装 FreeBSD 1.0:回顾 30 年前
    • ZFS 是如何进入 FreeBSD 的呢?
    • 我不是来自约克郡的,我保证!
    • 回忆录:采访 David Greenman Lawrence
    • FreeBSD 和早期的 Unix 社区
    • 早期的 FreeBSD 移植
    • FreeBSD 30 周年:成功的秘诀
    • FreeBSD 在日本:回忆之旅与今日之实
  • 2023-0304 嵌入式
    • CheriBSD port 和软件包
    • 让我们来试试 ChatGPT
    • GPU 直通
  • 2023-0102 构建 FreEBSD Web 服务器
    • ZFS 的原子 I/O 与 PostgreSQL
    • 虚拟实验室——BSD 编程研讨会
    • ZFS 简介
    • 会议报告:落基山庆祝女性计算机科学家
    • 进行中的工作/征求反馈:数据包批处理
    • 基金会与 FreeBSD 桌面
  • 2022-1112 可观测性和衡量标准
    • 在 FreeBSD 的 DDB 内核调试器中编写自定义命令
    • DTrace:老式跟踪系统的新扩展
    • 基于证书的 Icinga 监控
    • 活动监控脚本(activitymonitor.sh)
    • 实用 IPv6(第四部分)
    • EuroBSDCon 会议报道
    • 实用 Port:Prometheus 的安装与配置
    • 书评:《用火解决问题:管理老化的计算机系统(并为现代系统保驾护航)》Kill It with Fire: Manage Aging Computer Systems (and Future Proof Modern Ones)
  • 2022-0910 安全性
    • CARP 简介
    • 重构内核加密服务框架
    • PAM 小窍门
    • SSH 小窍门
    • 实用 IPv6(第三部分)
    • 书评:Understanding Software Dynamics(深入理解软件性能——一种动态视角)—— Richard L. Sites 著
    • 访谈:保障 FreeBSD 安全性
    • MCH 2022 会议报告
  • 2022-0708 科研、系统与 FreeBSD
    • 在 FreeBSD 上构建 Loom 框架
    • 教授本科生 Unix 课程
    • FreeBSD 入门研讨会
    • 实用 IPv6(第二部分)
    • 在 2022 年及以后推广 FreeBSD
    • 进行中的工作/征求反馈:Socket 缓冲区
    • FreeBSD 开发者峰会报告
    • 支持 Electromagnetic Field 2022
  • 2022-0506 灾难恢复
    • 使用 FreeBSD 构建高弹性的私有云
    • LLDB 14 —— FreeBSD 新调试器
    • 实用 IPv6(第一部分)
    • 利用 netdump(4) 进行事后内核调试
    • 进行中的工作/征求反馈:FreeBSD 启动性能
    • 实用 Port:在 OpenZFS 上设置 NFSv4 文件服务器
  • 2022-0304 ARM64 是一级架构
    • FreeBSD/ARM64 上的数据科学
    • Pinebook Pro 上的 FreeBSD
    • 嵌入式控制器的 ACPI 支持
    • 进行中的工作/征求反馈:Lumina 桌面征集开发人员
    • 实用 Port:如何设置 Apple 时间机器
  • 2022-0102 软件与系统管理
    • 为 FreeBSD Ports 做贡献
    • 使用 Git 贡献到 FreeBSD Ports
    • CBSD:第一部分——生产环境
    • 将 OpenBSD 的 pf syncookie 代码移植到 FreeBSD 的 pf
    • 进行中的工作/征求反馈:mkjail
    • 《编程智慧:编程鬼才的经验和思考》(The Kollected Kode Vicious)书评
    • 会议报告:EuroBSDCon 2021 我的第一次 EuroBSDCon:一位新组织者的视角
  • 2021-1112 存储
    • 开放通道 SSD
    • 构建 FreeBSD 社区
    • 与完美操作系统同行 27 年
    • 进行中的工作/征求反馈:OccamBSD
    • 通过 iSCSI 导入 ZFS ZIL——不要在工作中这样做——就像我做的那样
  • 2021-0910 FreeBSD 开发
    • FreeBSD 代码审查与 git-arc
    • 如何为 FreeBSD 实现简单的 USB 驱动程序
    • 内核开发技巧
    • 程序员编程杂谈
  • 2021-0708 桌面/无线网
    • 通往 FreeBSD 桌面的直线路径
    • FreeBSD 13 中的人机接口设备 (HID) 支持
    • Panfrost 驱动程序
    • 用 Git 更新 FreeBSD
    • FreeBSD 的新面孔
    • 想给你的桌面加点佐料?
  • 2021-0506 安全
    • 七种提升新安装 FreeBSD 安全性的方法
    • copyinout 框架
    • 使用 TLS 改善 NFS 安全性
    • Capsicum 案例研究:Got
    • 对 Jail 进行安全扫描
  • 2021-0304 FreeBSD 13.0
    • 展望未来
    • FreeBSD 13.0 工具链
    • FreeBSD 13.0 中有新加载器吗?
    • TCP Cubic 准备起飞
    • OpenZFS 中的 Zstandard 压缩
    • 会议报告:FreeBSD 供应商峰会
    • Git 不够吗?
  • 2021-0102 案例研究
    • Tarsnap 的 FreeBSD 集群
    • BALLY WULFF
    • Netflix Open Connect
    • FreeBSD 的新面孔
    • 写作学者的 FreeBSD
    • 在世界之巅
  • 2020-1112 工作流/持续集成(CI)
    • FreeBSD Git 快速入门
    • 使用 syzkaller 进行内核 Fuzzing
    • Mastering Vim Quickly 书评
    • 线上会议实用技巧
    • 在控制台上进行网络监控
  • 2020-0910 贡献与入门
    • 采访:Warner Losh,第 2 部分
    • 代码审查
    • 撰写良好的提交消息
    • 如何在不是程序员的情况下做出贡献——成为 FreeBSD 译者
    • 如何成为文档提交者
    • 谷歌编程之夏
    • 为 FreeBSD 期刊撰写文章
    • 你为什么使用 FreeBSD
    • FreeBSD 的新面孔
  • 2020-0708 基准测试/调优
    • FreeBSD Friday
    • 采访:Warner Losh,第 1 部分
    • 构建和运行开源社区
    • 在 FreeBSD 上轻松搭建我的世界(Minecraft)服务器
    • FreeBSD 的新面孔
  • 2020-0506 网络性能
    • 内核中的 TLS 卸载
    • 访谈:Michael W Lucas
    • FreeBSD 桌面发行版
    • 使用 Poudriere 进行 Port 批量管理
    • FreeBSD 的新面孔
由 GitBook 提供支持
LogoLogo

FreeBSD 中文社区(CFC) 2025

在本页
  • 实现
  • 移动设备(如笔记本电脑)作为用例
在GitHub上编辑
导出为 PDF
  1. 2021-0506 安全

使用 TLS 改善 NFS 安全性

上一页copyinout 框架下一页Capsicum 案例研究:Got

最后更新于1个月前

  • 原文链接:

  • 作者:RICK MACKLEM

传统上,NFS 提供的安全性非常有限,主要基于客户端的 IP 地址/DNS 主机名,使用 exports(5) 配置。这种方式可以通过 IP 地址欺骗来绕过,且对于没有固定、众所周知的 IP 地址或 DNS 主机名的移动客户端来说根本无法使用。此外,所有数据通常以明文在网络上传输,因此容易被嗅探。

RFC2203 于 1997 年 9 月发布,提供了一种机制,通过使用 GSSAPI 和 Kerberos 机制(通常称为 Kerberized NFS)来缓解至少部分上述问题。当通过“sec=krb5p”(KerberosV 带隐私)使用时,RPC 消息的参数和结果会在网络上传输时进行加密。Kerberos 在用户认证方面表现良好,但在机器认证方面不太方便。与 NFSv3 不同,NFSv4 需要一个“系统主体”,用于维护服务器上的 Open/Byte_range 锁定状态。Kerberos 有一个基于主机的 Kerberos 主体,格式为“host/@REALM”,可以为此创建一个密钥表条目并将其复制到客户端,作为“系统主体”使用。此“实例”应当保护密钥表条目不被另一个客户端使用(如果该条目被泄露)。然而,这使得这种 Kerberos 主体对于没有固定、众所周知的 DNS 主机名的移动客户端来说变得毫无用处。此外,对于“sec=krb5p”,只有 RPC 的数据负载部分是加密的,RPC 头部仍然暴露,导致将加解密工作卸载到专用硬件上变得不切实际。总之,考虑到实施 Kerberos 环境所涉及的管理工作,"sec=krb5p" 并未被广泛采用,并且对于没有固定、众所周知的 DNS 主机名的移动客户端效果不佳。

为了改善 NFS 的安全性,已撰写了一份名为“Towards Remote Procedure Call Encryption By Default”的互联网草案,描述了使用传输层安全性(TLS)来加密网络上的 RPC 消息流量,并使用 X.509 证书进行机器认证。由于 TLS 已被广泛采用,已经有了专用的硬件卸载解决方案,更不用说高效的软件实现了。本文描述了 FreeBSD 13 中的 NFS over TLS 实现,并展示了一个针对移动客户端(如笔记本电脑)的示例使用案例。

实现

虽然我将其称为 NFS over TLS,但更准确的说法是 Sun RPC over TLS,因为实现是在内核的 RPC(krpc)中完成的,并且对 NFS 层几乎是透明的。OpenSSL 的库提供了 TLS 和 X.509 证书在用户空间中的全面实现。然而,NFS 是在内核中实现的,将所有 NFS RPC 消息传递到用户地址空间以便由 OpenSSL 库处理似乎不切实际。幸运的是,FreeBSD 13 的内核添加了内核 TLS(KTLS)[TLS Offload in the Kernel,John Baldwin,FreeBSD Journal,2020 年 5 月/6 月 https://issue.freebsdfoundation.org/publication/?m=33057&i=667002&p=12&ver=html5],它在内核中的网络栈内高效地处理 TLS 应用数据记录,包括加密/解密。

这为在 TLS 应用数据记录中封装/加密 RPC 消息并在接收端解密/解封装这些 RPC 消息提供了基本机制。然而,它并不处理非应用数据记录,例如用于 TLS 握手的记录。为了处理非应用数据记录,分别在用户空间实现了 rpc.tlsclntd(8) 和 rpc.tlsservd(8) 用于客户端和服务器。这些守护进程通过自定义的 RPC 从内核接收上行调用,使用 AF_LOCAL 套接字上的 krpc,以类似 gssd(8) 守护进程为 Kerberized NFS 所做的方式来工作。为了处理这些上行调用,守护进程执行 OpenSSL 库调用,承担处理非应用数据记录的重任,包括处理 TLS 握手。守护进程还使用自定义系统调用向内核中的 krpc 注册,以及处理需要将文件描述符与内核中已经存在的套接字关联的特殊情况。

当客户端希望执行 NFS over TLS 时,它会执行一个 STARTTLS 空 RPC。空 RPC 是一个没有参数或结果的 RPC,通常分配为 RPC 编号 0。为了执行 STARTTLS,空 RPC 请求使用一种新的 RPC 凭证类型 AUTH_TLS。对于 FreeBSD 中的 NFS 服务,如果 rpc.tlsservd 正在运行,krpc 会回复一个由八个 ASCII 字符“STARTTLS”组成的凭证验证器。NFS 客户端执行的此 STARTTLS 探测会触发 TLS 握手,从而在用于 RPC 消息传输的 TCP 连接上设置 TLS。

此时服务器的操作序列为:

  • 阻止 TCP 套接字上的 krpc 接收。

  • 发送空 RPC 回复,凭证验证器为“STARTTLS”。

  • 向 rpc.tlsservd 发出握手上行调用。

  • 获取 TCP 套接字的文件描述符。此时 krpc 已拥有客户端 NFS 连接的 TCP 套接字,但没有文件描述符引用。这是实现中的一个较为挑战的部分。我的解决方案是使用守护进程的自定义系统调用将文件描述符与套接字关联。待完成,套接字的关闭工作就交给守护进程,而不是 krpc。

    • 向一个链表中添加一个结构体,按唯一的 64 位引用号作为键关联套接字文件描述符。

    • 调用 SSL_set_fd() 将套接字与 SSL 上下文关联。

    • 调用 SSL_accept() 执行实际的握手。

    • 如果握手成功,调用 BIO_get_ktls_send() 和 BIO_get_ktls_recv() 来检查是否已在套接字上启用 KTLS。如果其中任何一个返回零,握手视为失败。

    • 根据守护进程的命令行选项,验证客户端提供的任何 X.509 证书,并使用证书中指定的任何用户映射来创建 POSIX 用户凭证。

  • 向上行调用 RPC 回复一组标志,指示握手是否成功,是否接收到经过验证的证书,以及是否有根据证书映射的 POSIX 用户凭证(如果有)。回复中还包括套接字的唯一 64 位引用号,以及守护进程的启动日期/时间,以便内核可以在后续的上行调用中引用该套接字。启动日期/时间使得引用号与可能由守护进程的先前或后续实例使用的相同引用号区分开来。

    • 如果握手成功,则标记 krpc 套接字为使用 TLS,并将标志和凭证(如果有)包含在上行调用的回复中。

    • 解锁套接字上的内核 RPC 接收。

如果握手成功,套接字现在应该准备好处理 RPC 消息,KTLS 会在 sosend() 调用下进行应用数据记录的封装/加密,而在 krpc 使用的 soreceive() 调用下进行解密/解封装。

如果非应用数据记录位于套接字接收队列的头部,则为 soreceive() 调用引入了一个新的 MSG_TLSAPPDATA 标志,表示调用应该返回 ENXIO,从而使非应用数据记录保持在套接字接收队列的头部。ENXIO 返回触发了一个上行调用到 rpc.tlsservd 以处理非应用数据记录。内核代码会阻止 krpc 在套接字上的接收,然后执行处理记录的上行调用到守护进程。64 位引用号和守护进程的启动日期/时间作为参数传递上行调用,以便守护进程能够识别正确的套接字。

  • 这个上行调用只做一个 SSL_read(),其长度参数为零。这个调用总是失败,但会处理套接字接收队列头部的非应用数据记录,然后才会失败。

第三次上行调用到守护进程是用来关闭和断开 TCP 套接字的,64 位引用号和守护进程启动日期/时间作为参数传递。

  • 这个上行调用会关闭套接字并将套接字元素从链表中移除。如果还没有做(如通过 SSL_get_shutdown() 所示),这个上行调用还会在关闭套接字之前执行 SSL_shutdown(),以向客户端发送一个 Peer Reset TLS 记录。

虽然上述所有操作都由 krpc 处理,但 NFS 服务器确实使用与 TLS 相关的新标志,这些标志由 krpc 传递给 NFS 服务器,以确定基于以下 exports(5) 选项是否允许该 RPC。这里有三个新的 exports(5) 选项:

tls - 表示客户端必须使用 NFS over TLS,但不要求在 TLS 握手期间向服务器提供任何 X.509 证书。

tlscert - 表示客户端必须使用 TLS,并且必须在 TLS 握手期间提供一个已验证的 X.509 证书。

tlscertuser - 表示客户端必须使用 TLS,必须在 TLS 握手期间提供一个已验证的 X.509 证书,并且该证书必须已成功映射到一个 POSIX 用户凭证()。此映射是从 subjectAltName 中的 otherName 组件找到的登录名生成的,登录名后面附加“@domain”,其中“domain”与服务器使用的域匹配。只有在 rpc.tlsservd 使用 -u/--certuser 命令行选项启动时,才会生成此映射。

如果未指定上述任何 exports(5) 选项,则允许使用 TLS,但不是必需的。

rpc.tlsservd 还有一个命令行选项,指定守护进程要求客户端 IP 地址的反向 DNS(rDNS)名称与客户端 X.509 证书中的 subjectAltName 的“DNS”组件匹配。这类似于 RFC 6125 建议客户端做的操作,用于验证域名应用服务的身份。由于该选项旨在防止客户端 IP 地址欺骗,无法使用 exports(5),因为它是基于客户端的 IP 地址来设置的。因此,该选项指定所有使用 NFS over TLS 的客户端必须满足这一标准,未能满足的将导致握手失败。它是最强的客户端主机身份检查,但要求所有客户端都必须拥有已验证的 X.509 证书,并且证书的 subjectAltName 中的 DNS 组件正确。当指定此选项时,所有客户端还必须具有固定的、已知的 DNS 地址。

客户端守护进程的功能与此类似,但有所不同。与 rpc.tlsservd 不同,rpc.tlscltnd 仅在指定了 -m/--mutualverf 命令行选项时才需要证书。客户端还可以处理存储在不同文件中的多个证书,以防不同的 NFS over TLS 服务器需要不同的证书。

当 NFS 挂载与服务器建立新的 TCP 连接,并且指定了“tls”挂载选项时,krpc 将执行以下操作:

  • 发送带有 AUTH_TLS 类型凭证的 Null RPC 请求。

  • 如果收到带有凭证验证器(由 ASCII 字节“STARTTLS”组成)的 Null RPC 回复:

    • 阻止内核 RPC 在新 TCP 套接字上的接收。

    • 向 rpc.tlsclntd 执行握手上行调用。为了处理握手,rpc.tlsclntd 中的操作为:

      • 为 TCP 套接字获取文件描述符。此时 krpc 已经拥有客户端的 NFS 连接的 TCP 套接字,但没有该套接字的文件描述符引用。通过守护进程的自定义系统调用来完成这项操作,类似于 rpc.tlsservd。

      • 调用 SSL_set_fd() 将套接字与 SSL 上下文关联。

      • 如果守护进程是使用 -m/--mutualverf 命令行选项启动的,则调用 SSL_[ctx_]use_certificate_file()/SSL_[ctx_]use_PrivateKey_file() 在握手期间提供证书。上行调用的参数可以覆盖证书/密钥文件的默认名称。默认名称为“cert.pem”和“certkey.pem”,但可以通过“tlscertname”挂载选项在每个挂载上进行覆盖,以防不同的 NFS 服务器需要不同的证书。

      • 调用 SSL_connect() 执行实际的握手。

      • 如果握手成功,调用 BIO_get_ktls_send() 和 BIO_get_ktls_recv() 检查 KTLS 是否已在套接字上启用。如果其中任何一个返回零,则认为握手失败。如果握手成功:

        • 向套接字文件描述符的链表中添加一个结构,使用唯一的 64 位引用号作为键。

        • 向上行调用 RPC 回复,指示握手成功。回复中包含套接字的唯一 64 位引用号,以及守护进程的启动日期/时间,以便内核在随后的上行调用中可以引用该套接字。 否则:

        • 关闭套接字。

        • 向内核回复失败,这将导致所有后续的 NFS RPC 失败并返回 EACCES。

    • 收到上行调用回复后,krpc 设置标志,指示握手是否成功,并解除对套接字的 krpc 接收阻塞。

对于客户端,如果在指定了“tls”选项的挂载时,STARTTLS Null RPC 或 TLS 握手失败,则所有 RPC 将失败并返回 EACCES。这样做是为了确保在指定了“tls”选项的 NFS 挂载只有在 TLS 成功工作时才会正常运行。

移动设备(如笔记本电脑)作为用例

移动设备,如笔记本电脑,通常可以从任何地方访问互联网,并且没有固定的、已知的 IP 地址。为了允许笔记本电脑从互联网上的任何地方挂载 NFSv4 文件服务器,需要一些合理的安全机制。虽然 NFS over TLS 可以用于 NFSv3 挂载,但从任何地方启用 NFSv3 挂载会比较麻烦,因为 Mount 协议通过 rpcbind 使用动态分配的端口号,而 NFSv4 挂载只使用端口 #2049。因此,本示例将使用 NFSv4 挂载。

可以使用带有隐私保护的 Kerberized NFS 从 FreeBSD 笔记本电脑进行挂载。笔记本电脑用户需要执行以下命令:

# sysctl vfs.usermount=1 - 以 su/root 身份执行
# service gssd onestart - 以 su/root 身份执行
% kinit - 获取用户的 TGT
% mount -t nfs -o sec=krb5p,nfsv4,minorversion=1 nfsv4-server.uoguelph.ca:/ /mnt
 - 以非 root 用户身份执行
 - 使用挂载点
% umount /mnt

通过使用挂载选项“sec=krb5p”,但不使用“gssname”挂载选项,FreeBSD 客户端将使用“用户主体”作为“系统主体”。如果用户的 TGT 在“umount”之前过期,挂载将会失败。

据我所知,这种方法并未广泛采用,可能是因为安装 Kerberos 和维护一个可以从互联网上任何地方访问的 KDC 所需的工作量。

要使用 NFS over TLS 实现这一点,需要为客户端生成一个 X.509 证书。虽然有多种方法可以创建/签署 X.509 证书,但这可以通过站点本地的 CA 和在 FreeBSD 13 中使用 openssl(1) 命令轻松完成。

  • • 为笔记本电脑创建一个由站点本地 CA 签名的证书。使用 openssl(1),可能需要执行以下命令:

    # openssl genrsa -aes256 -out certkey.pem
    # openssl req -new -key certkey.pem -addext "subjectAltName=otherName:1.3.6.1.4.1.2238.1.1.1;UTF8:rmacklem@uoguelph.ca" -out req.pem
    # openssl ca -in req.pem -out cert.pem
  • 将 cert.pem 和 certkey.pem 以某种安全的方式复制到笔记本电脑的 /etc/rpc.tlsclntd 目录中。

  • 启用客户端守护进程,使用该证书。

    • 编辑 /etc/rc.conf 并添加:

      tlsclntd_flags="-m"
    • 由于使用了“-aes256”选项,rpc.tlsclntd 在启动时会询问输入密码短语,因此最好在执行挂载之前手动启动守护进程,而不是在启动时自动启动。

    • 如果希望在启动时启动,请添加以下内容到 /etc/rc.conf:

      tlsclntd_enable="YES"
    • 或者在挂载之前手动启动:

      # service tlsclntd onestart
  • 待笔记本电脑连接到互联网,就可以以 su/root 身份执行挂载:

    # mount -t nfs -o nfsv4,minorversion=1,tls nfsv4-server.uoguelph.ca:/ /mnt

由于客户端提供了由站点本地 CA 签名的证书,服务器可以合理地确认客户端拥有由站点本地 CA 管理员创建的证书。创建客户端私钥时使用的“-aes256”选项迫使 rpc.tlsclntd 在启动时询问输入密码短语。这可以防止笔记本电脑被盗或证书/密钥文件被复制到另一个客户端后轻易的泄露。如果证书要在未经授权的客户端上使用,必须通过某种方式捕获或破解密码短语。

如果由于任何原因笔记本电脑不再允许执行挂载,也可以撤销证书并将其添加到证书吊销列表(CRL)中。

在上述示例中,所有在服务器上执行的 RPC 将使用登录名“rmacklem”在 NFSv4 服务器上的 POSIX 凭证。这避免了笔记本电脑需要与服务器保持统一的 uid 和 gid 空间。它还将因笔记本电脑被入侵而导致的风险限制为“rmacklem”可访问的文件。此可选配置可能与互联网草案不符。该草案的一位共同作者同意根据 TLS 握手期间提供的 X.509 证书将客户端 RPC 凭证映射到特定用户是有用的,事实上他为此创造了“TLS 身份压缩”(TLS Identity Squashing)这个术语。然而,这位作者更倾向于使用数据库将证书的元组映射到“用户”。他认为将“用户”包含在证书中混淆了“机器”与“用户”凭证。作为一个务实主义者,我认为将“用户”包含在证书中只是实现这一目标的一种简单方法。如果这种做法成为首选实践,rpc.tlsclntd 可以修改为使用平面文件/数据库来实现这一点。

以上只是一个示例用例。守护进程的命令行选项支持多种配置,从仅要求 TLS 对传输中的 RPC 消息进行加密,到要求所有客户端提供可验证的 X.509 证书,其中 subjectAltName 的 DNS 组件必须与客户端 IP 主机地址的 rDNS 名称匹配。客户端还可以以 RFC 6125 推荐的方式验证 NFS 服务器的真实性,用于 TLS 域名应用服务。

要在 FreeBSD 13 上设置 NFS over TLS,请参阅:https://people.freebsd.org/~rmacklem/nfs-over-tls-setup.txt

另外,请查阅 rpc.tlsclntd(8)、rpc.tlsservd(8)、exports(5)、ktls(4) 和 mount_nfs(8) 的手册页。


RICK MACKLEM Rick 已经有 30 余年的工作经验,从 1980 年开始,他是计算机科学系的系统管理员,管理着包括 BSD 系统在内的多种系统。当 VAX 11/780 替换为 MicroVAXII 系统时,需要在 4.3BSD 上实现 NFS,因此 Rick 实现了这个功能并将其贡献给了 CSRG。Usenix 论文《Lessons Learned Tuning the 4.3BSD NFS Implementation》描述了作为这项工作的部分内容,第一个在 TCP 上实现的 NFS。Rick 现已退休,继续致力于 FreeBSD 上 NFS 的实现工作。

Using TLS to improve NFS Security