使用 Stunnel TLS 加密 NFSv4

NFS 客户端和服务器在默认配置中通过明文连接推送文件流量,这与敏感数据不兼容。TLS 可以封装此流量,最终带来协议安全性。在使用云服务提供商的 NFS 工具之前,请检查您的所有 NFS 使用情况,并在必要时进行保护。

网络文件系统 (NFS) 是 UNIX 中最流行的文件共享协议。NFS 历史悠久,早于 Linux,最新的 v4 版本易于防火墙保护,几乎可以提供无缝操作远程文件所需的一切功能,就像它们是本地文件一样。

NFSv4 缺失的最明显功能是原生独立的加密。在没有 Kerberos 的情况下,该协议仅以明文运行,这在现代环境中构成了不可接受的安全风险。NFS 并非唯一存在此缺陷的协议,因为我之前在一篇文章中已经介绍了 明文 SMB。与 SMB 相比,通过 stunnel 的 NFS 在更广泛的操作系统版本上提供更好的加密(如果与现代 OpenSSL 一起使用,则可能是 AES-GCM),并且协议中没有购买付费更新或更新操作系统版本的压力。

NFS 是一种极其常见的 NAS 协议,云存储对其提供广泛支持。虽然 Amazon EC2 支持 明文和加密 NFS,但 Google Cloud 在其 文档化程序 中未提及数据安全,并且最近 Microsoft Azure 和 Oracle Cloud 发起了该协议的重大举措,这引起了人们的怀疑。在不受信任的网络(即使在托管提供商内部)上使用这些功能时,必须假定,如果恶意方对内容稍有兴趣,则他们将捕获、存储和重建易受攻击的流量。幸运的是,通过 stunnel 使用 TLS 加密封装基于 TCP 的 NFS 虽然不明显,但却很简单。

通过 stunnel 隧道传输 NFS 的性能损失出奇地小——通过加密的 NFSv4.2 连接传输 Oracle Linux 安装 ISO 的速度与明文速度的差距在 5% 以内。更令人惊讶的是 fuse-sshfs 的性能,它似乎甚至在传输速度上击败了明文 NFSv4.2。NFS 在可靠性、动态 idmap 和弹性方面仍然优于 sshfs,但 FUSE 和 OpenSSH 提供的性能远超预期。

安装

大多数 NFS 客户端和服务器代码已经存在于 Linux 内核中,包括与 Sun 的原始 v2 和 v3 服务器以及 v4 兼容的实现。运行 NFS 服务器确实需要几个 nfsd 进程,这些进程由小型的 /usr/sbin/rpc.nfsd 二进制文件启动,该二进制文件接受的参数很少,主要作为用户空间占位符在内核中调度文件服务器线程。客户端(TCP 数据流将在此处发出)和服务器都需要 stunnel 二进制文件。某些客户端还需要运行 rpc.portmap 守护进程,但大多数客户端现在可以不用它。

在 Oracle Linux 7.5 及其同类产品(CentOS、Scientific Linux、Red Hat)上,您可以使用以下命令安装实用程序(nfs-utils 软件包可能已安装):


yum install nfs-utils stunnel

Ubuntu 似乎 要求 安装完整的 nfs-kernel-server 才能运行客户端。

如果您希望 NFS 服务在启动时启动,请使用 systemd 通过以下命令启用它们:


systemctl enable rpcbind
systemctl enable nfs-server
systemctl enable nfs-lock
systemctl enable nfs-idmap

您可以使用相应的启动命令启动服务(现在不要启动它们):


systemctl start rpcbind
systemctl start nfs-server
systemctl start nfs-lock
systemctl start nfs-idmap

如果您想允许明文 NFS 通过 TCP 和 UDP 进入服务器,请使用以下命令重新配置防火墙。如果您只想允许通过 stunnel TLS 加密的 NFS 或明文 TCP(但不允许 UDP),请不要运行这些命令:


firewall-cmd --permanent --zone=public --add-service=nfs
firewall-cmd --reload

作为替代方案,如果您要测试通过 TCP 端口 2049 的明文 NFS,请改为运行此命令:


iptables -w -I INPUT -p tcp --dport 2049 --syn -j ACCEPT

iptables 调用在重新启动后将不会保留,并且不允许 UDP 传输,但 firewall-cmd 更改将是持久性的,并提供功能齐全的 NFS 访问。

明文 NFSv4

我应该从承认 NFSv4 协议并非普遍受人称赞开始我的 NFSv4 介绍。虽然 Linux 内核开发人员对 NFS 的总体批评 指出了几个版本中的许多主要缺陷,但 OpenBSD 项目的领导者 Theo de Raadt 对 v4 在 OpenBSD 发行版中的状态发表了 以下评论

NFSv4 对每个人来说都是一个巨大的笑话……NFSv4 不在我们的路线图上。这是一个荒谬的臃肿协议,他们不断地向其中添加 [脏话]。大约十年后,真正开始审计它的人们将会看到它隐藏的所有错误。

NFSv4 团队成员遵循的设计过程与 IPV6 人员所采用的方法相匹配。(就像,一旦犯了一个错误,并且有 4 个人在运行测试代码,那么它就成为了既定事实,并且无法再次更改。)结果是一堆粗制滥造的垃圾。

很多时候,一个人的垃圾是另一个人的财富。虽然 Theo de Raadt 是一位伟大的梦想家,而且我们使用 OpenSSH 要归功于他,但 NFSv4 是最容易通过 stunnel TLS 运行的 NFS 实现。

NFSv3 及更早版本是“无状态”文件服务器——服务器仅记录读取和写入操作,并且不保留有关客户端使用情况的状态。NFS 广泛使用 Sun ONC RPC(开放网络连接远程过程调用),它由 rpc.portmap 守护进程与几个其他支持进程协调,以实现文件锁定、状态报告、崩溃恢复和 ID 映射——这些是在单独端口上运行的独立服务器进程,它们维护与文件服务器分开的客户端状态信息。2008 年的一个讨论主题中 提出了 在 v3 及更低版本上使用 stunnel 的问题,并且其中一位主题参与者 提到了 他撰写的关于该主题的一份文档,该文档此后已被存档。隧道传输 v3 的过程非常复杂。

NFSv4 将这些有状态活动引入到主协议中,使用它的客户端不需要连接到旧的 v3 lockdstatd 或任何其他单独的 RPC 服务。本地 rpc.idmapd 是维护正确的属主关系和权限所必需的,但 idmapd 不需要超出 v4 客户端维护的 TCP 连接已提供的通道的远程网络连接。

NFS 最初在端口 2049 上通过 UDP(不可靠数据报协议)运行,期望本地网络上的数据包丢失不会严重干扰 NFS 流量。当高流量导致数据包丢失时,基于 UDP 的 NFS 可能会并且确实会受到严重影响。NFSv3 添加了通过 TCP(传输控制协议)运行的能力,并且由于 TCP 传输对不利条件的容忍度更高,因此端口 2049 上的 TCP 传输是 Linux 中的默认设置。在某些使用场景中,UDP 更有效(有关详细信息,请参阅 man 5 nfs),但 UDP 不适用于 stunnel,因此我在此处不讨论它。

让我们首先配置一个目录,使其由 NFS 服务器提供给客户端。使用以下命令在服务器机器上创建并填充目录:


mkdir /home/share

chmod 777 /home/share

cp /etc/services /etc/nsswitch.conf /etc/hosts /home/share

编辑 /etc/exports 文件,以便为客户端的 IP 地址提供读/写共享:


/home/share 5.6.7.8(fsid=0,rw)

fsid 对于 NFSv4 挂载非常有用,并在 man exports 手册页中进行了解释:“对于 NFSv4,有一个特殊的 文件系统,它是所有导出的文件系统[的]根目录。这由 fsid=root 或 fsid=0 指定,两者含义完全相同。” 建立根 fsid 将使您的导出工作更顺畅。

为了教学目的,定义一个小的 shell 函数,并使用它来检查 rpc 进程。在确认没有已知的 NFS 程序正在运行后,启动 NFS 服务器,然后观察还启动了什么:


# function pps { typeset a IFS=\| ; ps ax | while read a
do case $a in *$1*|+([!0-9])) echo $a;; esac; done }

# pps rpc
  PID TTY      STAT   TIME COMMAND
  598 ?        S<     0:00 [rpciod]

# systemctl start nfs-server

# pps rpc
  PID TTY      STAT   TIME COMMAND
  598 ?        S<     0:00 [rpciod]
15120 ?        Ss     0:00 /usr/sbin/rpc.statd --no-notify
15131 ?        Ss     0:00 /usr/sbin/rpc.idmapd
15143 ?        Ss     0:00 /sbin/rpcbind -w
15158 ?        Ss     0:00 /usr/sbin/rpc.mountd

很明显,v3 相关的守护进程是由 Oracle Linux 7 下的主文件服务器单元启动的。不要对它们的存在感到惊讶。

在客户端上,您可以向 /etc/fstab 文件添加一个条目,定义远程挂载——它必须包含服务器的主机名或 IP 地址以及(供以后使用)TCP 端口号:


1.2.3.4:/ /home/share nfs noauto,vers=4.2,proto=tcp,port=2049 0 0

上面的 fstab 条目将允许您挂载服务器,前提是任何和所有防火墙都允许流量,并且它们可以互相 ping 通。


# mount /home/share

# ls -l /home/share
total 664
-rw-r--r--. 1 root root    158 May 16 11:34 hosts
-rw-r--r--. 1 root root   1746 May 16 11:34 nsswitch.conf
-rw-r--r--. 1 root root 670293 May 16 11:34 services

# cp /etc/yum.conf /home/share

# ls -l /home/share
total 668
-rw-r--r--. 1 root      root         158 May 16 11:34 hosts
-rw-r--r--. 1 root      root        1746 May 16 11:34 nsswitch.conf
-rw-r--r--. 1 root      root      670293 May 16 11:34 services
-rw-r--r--. 1 nfsnobody nfsnobody    841 May 16 12:02 yum.conf

上面的 nfsnobody 是“root squash”的一个示例,其中服务器将客户端 root 帐户的活动转换为非特权用户。有几种类型的 squash,它们通常是意想不到的意外。

以下是(已停止维护且不受支持的)Oracle Linux 5 的一个示例,其中所有权限都被 squash:


# ll /some/share
total 44604
-rwxr-xr-x   1 nobody nobody  1638192 Jul 28  2016 7za.16.02
-rw-r--r--   1 nobody nobody    57280 Oct 18  2017 fuse-sshfs-2.4-1.el5.i386.rpm
-rwxr--r--   1 nobody nobody   233066 May  2  2017 Oracle_LMS_Collection_Tool.zip

发生这种情况是因为必须在 /etc/idmapd.conf 文件中指定 idmap “域”。默认情况下,NFS 域是从完全限定域名 (FQDN) 中删除主机名前缀获得的。如果两个服务器位于不同的 DNS 域中,则它们的 NFSv4 挂载将始终被完全 squash。要纠正此问题,请手动指定 NFS 域:


# service rpcidmapd stop
Stopping RPC idmapd:                                       [  OK  ]

# grep ^Domain /etc/idmapd.conf
Domain = master_nfs_domain.yourco.com

# service rpcidmapd start
Starting RPC idmapd:                                       [  OK  ]

# umount /some/share
# mount /some/share

# ls -l /some/share
total 44604
-rwxr-xr-x   1 cfisher grp     1638192 Jul 28  2016 7za.16.02
-rw-r--r--   1 cfisher grp       57280 Oct 18  2017 fuse-sshfs-2.4-1.el5.i386.rpm
-rwxr--r--   1 root    root     233066 May  2  2017 Oracle_LMS_Collection_Tool.zip

请注意,NFSv3 及更低版本不是这样工作的。默认情况下,数值用户和组 ID 在没有 idmap 访问权限的普通挂载上会被保留。虽然维护 uid/gid 同步仍然很重要,但 NFSv4 不再允许数值映射,因此不要对过度的 squash 感到惊讶。

较旧的 Linux 内核对 NFSv4 挂载使用略有不同的 fstab 语法。在 Oracle Linux 5 下,请注意下面(已弃用的)nfs4 挂载类型以及缺少 vers 选项:


server:/ /share nfs4 noauto,proto=tcp,port=2049 0 0

在结束本节之前,我想回到客户端上的 fstab 条目:


1.2.3.4:/ /home/share nfs noauto,vers=4.2,proto=tcp,port=2049 0 0

vers=4.2 请求最新版本的 NFS 协议,如果服务器上不可用,则会失败。如果您使用的是较旧的服务器,请降低此版本。客户端主要负责确定连接的协议版本和功能设置(尽管服务器可以在 /etc/nfs.conf 和 /etc/sysconfig/nfs 中全局启用/禁用特定的 NFS 版本和某些功能)。

上面的 noauto 可防止由于无法访问的 NFS 服务器而导致的启动延迟,因为它默认情况下不会在启动时挂载它们。我的建议是始终使用 noauto,以避免启动时卡在“NFS 服务器未响应”上。有一个“后台挂载”选项,它很有用,但我更喜欢 root 的(Vixie)crontab 中的重新启动条目,这保证了 NFS 不会干扰获取登录名或以其他方式启动本地服务。您可以使用以下 crontab 条目来实现此目的:


@reboot /sbin/mount /home/share

更合适的方法是将所有自定义启动项放入一个脚本中,然后将该脚本添加为重新启动条目。请务必将您的 NFS 挂载放在最后,按首选项和对延迟的容忍度排序(您可以将特别有问题的挂载作为后台进程启动)。

有些人表达了对来自各种来源的 NFS 自动挂载器的喜爱。我的挂载点不多,不足以证明维护多个客户端自动挂载配置是合理的,因此我在此处省略了此类讨论。幸运的是,如果您按照此处的说明进行操作,那么您即将进行的 stunnel 修改应该与大多数自动挂载器兼容。

通过 Stunnel 的 TLS 上的 NFSv4

在 TCP 连接离开客户端之前对其进行拦截将需要一个本地端点的端口。还必须在服务器上选择一个新的端口用于 TLS 服务。为了参考,以下端口似乎与 NFS 相关:


# egrep -i '([^a-z]nfs|nfs[^a-z])' /etc/services

nfs             2049/tcp  nfsd shilp # Network File System
nfs             2049/udp  nfsd shilp # Network File System
nfs             2049/sctp nfsd shilp # Network File System
picknfs         1598/tcp             # picknfs
picknfs         1598/udp             # picknfs
3d-nfsd         2323/tcp             # 3d-nfsd
3d-nfsd         2323/udp             # 3d-nfsd
mediacntrlnfsd  2363/tcp             # Media Central NFSD
mediacntrlnfsd  2363/udp             # Media Central NFSD
winfs           5009/tcp             # Microsoft Windows Filesystem
winfs           5009/udp             # Microsoft Windows Filesystem
enfs            5233/tcp             # Etinnae Network File Service
mountd          20048/tcp            # NFS mount protocol
mountd          20048/udp            # NFS mount protocol
nfsrdma         20049/tcp            # (NFS) over RDMA
nfsrdma         20049/udp            # (NFS) over RDMA
nfsrdma         20049/sctp           # (NFS) over RDMA

为了便于读取 netstat,我在服务器上打开端口 2363,并将客户端挂载重定向到其本地端口 2323。您可能不希望您的 NFS 流量容易被识别——如果是这样,请选择不相关的端口。

至少,stunnel TLS 服务器必须提供密钥对。我实际上生成并分发一个有效期为十年的单个自签名密钥对到服务器和所有客户端,这将充当“本地协议密钥”,所有成员都必须提供并验证所有连接参与者的正确性。在这里,我生成一个示例密钥:


$ openssl req -newkey rsa:4096 -x509 -days 3650 -nodes \
  -out nfs-tls.pem -keyout nfs-tls.pem
Generating a 4096 bit RSA private key
.................................................++
...................................++
writing new private key to 'nfs-tls.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:US
State or Province Name (full name) []:IL
Locality Name (eg, city) [Default City]:Chicago
Organization Name (eg, company) [Default Company Ltd]:NFS-TLS
Organizational Unit Name (eg, section) []:CHI
Common Name (eg, your name or your server's hostname) []:nfs-tls
Email Address []:foo@bar.org

上面的命令生成类似于以下输出的密钥。将您的文件移动到 /etc/stunnel 目录,并将其设置为 root 的 400 只读权限。不要复制以下内容;它仅用于演示目的——您必须生成自己的密钥


# f=nfs-tls.pem; cat $f ; chmod 400 $f ; mv $f /etc/stunnel
-----BEGIN PRIVATE KEY-----
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
NqwdAo0hNa3SsNtiM4Z3TM0RzDLw6fw=
-----END PRIVATE KEY-----

-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----

在文件服务器上,为 localhost 添加相同共享的导出。设置 insecure 选项,这将允许来自 1024 以上客户端端口的连接(我稍后会讨论此选项的后果)。如果您想删除明文导出,请确保客户端已先卸载:


$ cat /etc/exports

/home/share 5.6.7.8(fsid=0,ro)
/home/share 127.0.0.1(fsid=0,ro,insecure)

运行以下命令以激活到 localhost 的共享:


exportfs -a

在端口 2363 上添加一个 inetd 样式的套接字激活单元,以启动 stunnel,超时时间为十分钟:


$ cat /etc/systemd/system/MC-nfsd.socket

[Unit]
Description=NFS over stunnel/TLS server

[Socket]
ListenStream=2363
Accept=yes
TimeoutSec=600

[Install]
WantedBy=sockets.target

配置套接字以使用您稍后将定义的设置文件启动 stunnel:


$ cat /etc/systemd/system/MC-nfsd@.service

[Unit]
Description=NFS over stunnel/TLS server

[Service]
ExecStart=-/bin/stunnel /etc/stunnel/MC-nfsd.conf
StandardInput=socket

使用以下命令启动套接字并启用它以在启动时自动启动:


systemctl start MC-nfsd.socket
systemctl enable MC-nfsd.socket

打开端口 2363 以允许加密的 NFS 通过您的防火墙:


iptables -w -I INPUT -p tcp --dport 2363 --syn -j ACCEPT

为 NFS 服务器创建以下 stunnel 控制文件:


$ cat /etc/stunnel/MC-nfsd.conf

#GLOBAL#######################################################

TIMEOUTidle     =       600
renegotiation   =       no
        FIPS    =       no
        options =       NO_SSLv2
        options =       NO_SSLv3
        options =       SINGLE_DH_USE
        options =       SINGLE_ECDH_USE
        options =       CIPHER_SERVER_PREFERENCE
        syslog  =       yes
        debug   =       0
        setuid  =       nobody
        setgid  =       nobody
        chroot  =       /var/empty/stunnel

        libwrap =       yes
        service =       MC-nfsd
        ; cd /var/empty; mkdir -p stunnel/etc; cd stunnel/etc;
        ; echo 'MC-nfsd: ALL EXCEPT 5.6.7.8' >> hosts.deny;
        ; chcon -t stunnel_etc_t hosts.deny

        curve   =       secp521r1
; https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
↪ciphers=ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+
↪AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS

#CREDENTIALS##################################################

        verify  =       4
        CAfile  =       /etc/stunnel/nfs-tls.pem
        cert    =       /etc/stunnel/nfs-tls.pem

#ROLE#########################################################

        connect =       127.0.0.1:2049

创建 chroot() 目录,stunnel 将在其中删除权限:


# mkdir /var/empty/stunnel

尝试本地明文套接字连接到端口 2363;stunnel 配置问题将在此处显示:


# nc localhost 2363
Clients allowed=500
stunnel 4.56 on x86_64-redhat-linux-gnu platform
Compiled/running with OpenSSL 1.0.1e-fips 11 Feb 2013
Threading:PTHREAD Sockets:POLL,IPv6 SSL:ENGINE,OCSP,FIPS
 ↪Auth:LIBWRAP
Reading configuration from file /etc/stunnel/MC-nfsd.conf
FIPS mode is disabled
Compression not enabled
Snagged 64 random bytes from /dev/urandom
PRNG seeded successfully
Initializing inetd mode configuration
Certificate: /etc/stunnel/nfs-tls.pem
Error reading certificate file: /etc/stunnel/nfs-tls.pem
error queue: 140DC002: error:140DC002:SSL
 routines:SSL_CTX_use_certificate_chain_file:system lib
error queue: 20074002: error:20074002:BIO
 routines:FILE_CTRL:system lib
SSL_CTX_use_certificate_chain_file: 200100D:
 error:0200100D:system library:fopen:Permission denied
Service [MC-nfsd]: Failed to initialize SSL context
str_stats: 11 block(s), 355 data byte(s), 638 control byte(s)

在这种情况下,SELinux 已启用,并且密钥的类型阻止 stunnel 读取它。需要 chcon 命令来解决此问题:


# cd /etc/stunnel

# ls -lZ
-rw-r--r--. root root XXX:XXX:stunnel_etc_t:s0 MC-nfsd.conf
-r--------. root root XXX:XXX:user_home_t:s0 nfs-tls.pem

# chcon -t stunnel_etc_t nfs-tls.pem

# ls -lZ
-rw-r--r--. root root XXX:XXX:stunnel_etc_t:s0 MC-nfsd.conf
-r--------. root root XXX:XXX:stunnel_etc_t:s0 nfs-tls.pem

当您可以运行 netcat 而不出错时,您就可以转移到客户端了。在 NFS 客户端上添加 inetd 样式的套接字激活单元:


$ cat /etc/systemd/system/3d-nfsd.socket

[Unit]
Description=NFS over stunnel/TLS client

[Socket]
ListenStream=2323
Accept=yes
TimeoutSec=300

[Install]
WantedBy=sockets.target

配置套接字以使用您稍后将定义的设置文件启动 stunnel:


$ cat /etc/systemd/system/3d-nfsd@.service

[Unit]
Description=NFS over stunnel/TLS client

[Service]
ExecStart=-/bin/stunnel /etc/stunnel/3d-nfsd.conf
StandardInput=socket

为 NFS 客户端创建 stunnel 控制文件:


$ cat /etc/stunnel/3d-nfsd.conf

#GLOBAL#######################################################

sslVersion      =       TLSv1.2
TIMEOUTidle     =       600
renegotiation   =       no
        FIPS    =       no
        options =       NO_SSLv2
        options =       NO_SSLv3
        options =       SINGLE_DH_USE
        options =       SINGLE_ECDH_USE
        options =       CIPHER_SERVER_PREFERENCE
        syslog  =       yes
        debug   =       0
        setuid  =       nobody
        setgid  =       nobody
        chroot  =       /var/empty/stunnel

        libwrap =       yes
        service =       3d-nfsd
        ; cd /var/empty; mkdir -p stunnel/etc; cd stunnel/etc;
        ; echo '3d-nfsd: ALL EXCEPT 127.0.0.1' >> hosts.deny;
        ; chcon -t stunnel_etc_t hosts.deny

        curve   =       secp521r1
; https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
↪ciphers=ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:
↪ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS

#CREDENTIALS##################################################

        verify  =       4
        CAfile  =       /etc/stunnel/nfs-tls.pem
        cert    =       /etc/stunnel/nfs-tls.pem

#ROLE#########################################################

        client  =       yes
        connect =       nfs-server.yourco.com:2363

注意:我之前曾使用 IP 地址 1.2.3.4 引用服务器,但上面是 nfs-server.yourco.com——使用您喜欢的任何形式的主机名。

最新的 Ubuntu 配备了“stunnel4”,它实际上是 stunnel 版本 5.44。它不使用 NO_SSLv2SINGLE_*_USE 选项中的任何一个运行(您必须删除它们),并且组“nogroup”应该在那里用于上面的 setgid 选项。

修改 /home/share 的 fstab 条目以连接到本地 stunnel:


$ grep share /etc/fstab
localhost:/ /home/share nfs noauto,vers=4.2,proto=tcp,port=2323 0 0

挂载卷,并检查 stunnel 进程,然后检查活动网络连接:


# mount /home/share

# pps stun
  PID TTY      STAT   TIME COMMAND
 5870 ?        Ss     0:00 /bin/stunnel /etc/stunnel/3d-nfsd.conf

# netstat -ap | grep nfsd
tcp        0      0 localhost:860        localhost:3d-nfsd
 ↪ESTABLISHED -
tcp        0      0 squib:48804          192.168.:mediacntrlnfsd
 ↪ESTABLISHED 5870/stunnel
tcp6       0      0 [::]:3d-nfsd         [::]:*
 ↪LISTEN      1/init
tcp6       0      0 localhost:3d-nfsd    localhost:860
 ↪ESTABLISHED 1/init

# ls -l /home/share/
total 676
-rw-r--r-- 1 root    root       158 May 21 18:58 hosts
-rw-rw-r-- 1 cfisher cfisher   5359 May 21 19:22 nfs-tls.pem
-rw-r--r-- 1 root    root      1760 May 21 18:58 nsswitch.conf
-rw-r--r-- 1 nobody  nogroup   1921 May 21 19:17 passwd
-rw-r--r-- 1 root    root    670293 May 21 18:58 services

另请检查服务器的 stunnel 进程和网络状态:


# pps stun
  PID TTY    STAT   TIME COMMAND
16282 ?      Ss     0:00 /bin/stunnel /etc/stunnel/MC-nfsd.conf

# netstat -ap | grep nfsd
tcp6       0      0 [::]:mediacntrlnfsd     [::]:*
 ↪LISTEN      1/systemd
tcp6       0      0 192.168.:mediacntrlnfsd 192.168.0.24:48824
 ↪ESTABLISHED 1/systemd

squash 权限可能会记录在您的 syslog 中:


rpc.idmapd[4321]: nss_getpwnam: name 'cfisher@yourhost'
  does not map into domain 'localdomain'

要解决此问题,您需要在 /etc/idmapd.conf 中手动设置域。

NFS 客户端上的一个主要问题是任何本地用户都能够通过 SSH 或其他端口转发工具连接到 NFS 端点。他们可以将此转发到他们选择的服务器(并在他们的控制之下),以挂载和操作远程文件服务器。客户端上的任何本地用户都能够:


# telnet localhost 2323
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

Connection closed by foreign host.

连接到端点的能力授予了控制它的能力。

没有原生的 stunnel 选项来限制客户端对特权端口的访问,但您可以编写自己的包装器来限制此访问——它调用 exec() 函数以在验证传入端口是否为特权端口后启动 stunnel,并将活动文件描述符传递给替换进程。要启用此包装器,请放置以下文件:


# cat /bin/pstunnel.c

#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>


int main(int argc, char *argv[], char *envp[])
{
 struct sockaddr_storage addr;
 socklen_t len = sizeof addr;
 int port = 65535, bad = 0;

 if(getpeername(fileno(stdin), (struct sockaddr *) &addr, &len)) bad = 1;
 else if(addr.ss_family == AF_INET) //IPv4
 {
  struct sockaddr_in *s = (struct sockaddr_in *) &addr;
  port = ntohs(s->sin_port);
 }
 else if(addr.ss_family == AF_INET6) //IPv6
 {
  struct sockaddr_in6 *s = (struct sockaddr_in6 *) &addr;
  port = ntohs(s->sin6_port);
 }
 else bad = 1;

 if(!bad && port < IPPORT_RESERVED) execve("/bin/stunnel", argv, envp);
 else printf("Nope.\n");
}

使用以下命令编译特权包装器:


# cd /bin

# cc -s -O2 -DFORTIFY_SOURCE=2 -Wall -o pstunnel pstunnel.c

修改套接字单元文件以调用特权包装器:


# cat /etc/systemd/system/3d-nfsd@.service
[Unit]
Description=NFS over stunnel/TLS client

[Service]
ExecStart=-/bin/pstunnel /etc/stunnel/3d-nfsd.conf
StandardInput=socket

然后重新加载 systemd 以识别修改后的单元:


# systemctl daemon-reload

来自非特权客户端的连接现在被阻止,但挂载请求仍然会通过:


# telnet localhost 2323
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Nope.
Connection closed by foreign host.

# mount /home/share

# pps stun
  PID TTY      STAT   TIME COMMAND
 2483 ?        Ss     0:00 /bin/pstunnel /etc/stunnel/3d-nfsd.conf

# umount /home/share

请注意,argv[0] 将保留包装器的名称。

您可能需要调整您的包装器,而不是简单地打印“Nope”,以触发未授权用户滥用您的端点的通知——这是一件非常严肃的事情。

pstunnel.c 包装器在 Oracle Linux 5 下无法按预期工作。任何活动的 NFS 挂载都将被 netstat 报告为源自特权客户端端口,但在移动到 xinetd 中的特权包装器后,挂载尝试将失败。观察到的解决方法是在没有包装器的情况下挂载,将 xinetd 配置切换到 pstunnel,然后允许 stunnel 超时到期,导致生成新的 stunnel 以服务于现有连接以强制执行特权端口。似乎此问题的原因是在建立挂载时初步的非特权客户端连接(可能是 nfsconf 中的 STATD_OUTGOING_PORT 参数是罪魁祸首)。即使 Oracle Linux 5 已停止维护,此解决方法也可能在其他操作系统上很有用,因此我在此处将其包括在内。

如果您所在的系统未通过防火墙阻止对 2323 端点的远程连接,则应使用客户端 stunnel controlfile 中记录的 libwrap 功能来限制对 localhost 的访问。libwrap 功能在服务器上不太有用,因为必须先提供 RSA 密钥对才能允许访问。

请注意,Microsoft Windows 有可用的 NFS 客户端,但该平台不遵守对 1024 以下特权端口的限制——任何 Windows 用户都可以从这些受限端口发起连接,因此低端口过滤将不是一种有效的安全控制。如果您将 NFS 卷导出到 Windows 客户端,则必须信任所有客户端的用户。

另请注意,NFS 服务器上的 insecure 选项将允许本地用户在那里进行类似的恶作剧。Linux iptables 有一个可以锁定到 root 的所有者匹配模块,它可能能够类似地保护服务器的易受攻击的端口 2049。如果您无法保护 NFS 服务器免受用户建立颠覆性本地连接的影响,则不应在其上拥有任何不受信任的本地用户。

最后,请注意,stunnel 控制文件中的以下套接字选项可能对 NFS 非常有用:


socket  =       a:TCP_NODELAY=1
socket  =       a:SO_KEEPALIVE=1

NODELAY 选项禁用 Nagle 算法,这以(可能)发送“微小数据包”为代价防止 NFS 流量中的延迟——stunnel 不会等待希望发送完整的数据包,这应该使对少量数据的操作更具响应性。如果您将不断交换大量数据,则此选项可能没有那么有用。

NFSv4 具有深度文件锁定和“委派”,客户端可以从服务器“检出”文件以无限期使用。如果另一个客户端请求该文件,服务器必须能够联系客户端以取消委派并获取当前内容,如果 stunnel 连接关闭,则不会发生这种情况。如果/当客户端有服务器活动时,它可以自动重启连接,但反之则不然,这可能会影响锁定和委派。虽然服务器可以使用命令 echo '0' > /proc/sys/fs/leases-enable 全局禁用委派,但 KEEPALIVE 选项可能是一个有用的替代方案,并留给读者作为研究主题。

性能基准

对于那些真正关心数据安全的人来说,性能是无关紧要的;敏感信息不能通过明文连接传输。尽管如此,了解为加密开销必须付出的代价仍然很重要,因此我进行了一些涉及 NFSv4 的简单测试,以明确说明性能损失。

Linux 曾经有一个完全在用户空间中实现的 NFS 服务器,但为了提高性能,它被移动到 Linux 2.2 内核中(仍然有一个在积极开发中的 用户空间 NFS 服务器,它对于特定应用程序很有用,特别是 FUSE)。我曾预计强制在每一端为 stunnel 返回用户空间会带来严重的速度损失,但影响远小于预期。

我的测试是在两台运行最新版本 Oracle Unbreakable Enterprise Kernel v4 (UEK) 的 HP DL360 G9 服务器上进行的。该测试涉及在明文 NFS 和 TLS 下将 Oracle Linux 7.5 安装 ISO 的副本推送到服务器。

我尝试在通过 NFS 发送任何数据之前清除客户端和服务器上的缓存:


# sync && echo 3 > /proc/sys/vm/drop_caches

我从服务器中删除了先前测试中的任何 ISO 副本:


# rm /home/share/V975367-01.iso
rm: remove regular file '/home/share/V975367-01.iso' y

然后,我在客户端验证了 Oracle 提供的 sha256 ISO 哈希,以努力将 ISO 的内容放入客户端的缓冲区缓存中:


# tail -1 sha256
D0CC4493DB10C2A49084F872083ED9ED6A09CC065064C009734712B9EF357886
 ↪V975367-01.iso

# sha256sum -c < sha256
V975367-01.iso: OK

此时,我通过明文 NFSv4.2 连接挂载了服务器:


# tail -1 /etc/fstab
1.2.3.4:/ /home/share nfs noauto,vers=4.2,proto=tcp,port=2049 0 0

# mount /home/share

然后,我运行了三次复制迭代,并在每次运行之间清除缓存:


# time cp V975367-01.iso /home/share

real    0m39.697s
user    0m0.005s
sys     0m2.173s


# time cp V975367-01.iso /home/share

real    0m39.927s
user    0m0.005s
sys     0m2.159s

# time cp V975367-01.iso /home/share

real    0m39.489s
user    0m0.001s
sys     0m2.218s

通过明文连接移动 ISO 的平均挂钟时间为 39.70 秒。然后,我重新配置为使用 stunnel:


# tail -1 /etc/fstab
localhost:/ /home/share nfs noauto,vers=4.2,proto=tcp,port=2323 0 0

# mount /home/share

并再次运行测试:


# time cp V975367-01.iso /home/share

real    0m39.476s
user    0m0.002s
sys     0m2.265s

# time cp V975367-01.iso /home/share

real    0m40.376s
user    0m0.005s
sys     0m2.189s


# time cp V975367-01.iso /home/share

real    0m41.971s
user    0m0.001s
sys     0m2.894s

加密连接的平均时间为 40.61 秒,差异为 2.2%(几乎不算高昂的代价)。

DL380 服务器的 CPU 实现了 AES-NI 原生机器指令,这可能提高了性能。将 stunnel 配置为高日志记录(设置 debug=debug),报告的密码套件为 ECDHE-RSA-AES256-GCM-SHA384。没有 OpenSSL 识别的 AES-NI 的系统性能不会这么好。

我还使用来自 EPEL 存储库的 fuse-sshfs 测试了此活动。我卸载了 NFS,安装了 RPM,然后重新连接到远程目标:


# sshfs cfisher@1.2.3.4:/home/share /home/share
The authenticity of host '1.2.3.4 (1.2.3.4)' can't be established.
ECDSA key fingerprint is
 ↪4c:90:f8:48:2e:03:f5:31:30:c1:73:a3:5e:da:42:d3.
Are you sure you want to continue connecting (yes/no)? yes
cfisher@1.2.3.4's password:

然后,我重新运行了测试:


# time cp V975367-01.iso /home/share

real    0m38.727s
user    0m0.039s
sys     0m4.733s


# time cp V975367-01.iso /home/share

real    0m39.498s
user    0m0.035s
sys     0m4.751s


# time cp V975367-01.iso /home/share

real    0m39.536s
user    0m0.030s
sys     0m4.763s

sshfs 的平均时间为 39.25 秒,比通过 stunnel 的 NFSv4 快 3.3%。已经有 其他测试 表明 NFS 更快,但我没有看到这种行为,尽管此测试可能未在足够严格的条件下执行足够的活动来揭示差异。

尽管存在任何性能差异,但在几种情况下,NFS 优于 sshfs。支持更多文件系统功能(例如 FAQ 中提到的 df 命令),NFS 实现动态 id 映射(sshfs 仅接受静态映射),并且无论是否使用 stunnel,NFS 客户端都会自动重启断开的 TCP 连接,从而允许在不利的网络条件下可靠地维护长期挂载。OpenSSH 是一种专注于交互式使用的工具;客户端并非旨在像 stunnel 那样在 inetd 之外运行,出于这些原因,stunnel 更适合基本的自动化服务。

结论

在 NFSv4 开发的几十年中,令人惊讶的是,在协议中大量涌入新功能的情况下,简单的对称密码被忽略了。2016 年 11 月在 RFC 7862 中发布的 4.2 版本足够新,以至于作者们痛苦地意识到了明文流量的滥用。这种遗漏可能是故意的,并且应立即在受支持产品的所有 NFS 版本上追溯安装常用的 AEAD 套件(即 AES-GCM 和/或 ChaCha20-Poly1305)。

sec=krb5p 选项将在 Kerberos 域中加密 NFSv4 流量,但需要此基础架构在托管环境中是不合适的,并且通常远非有用。对对称密码学的基本访问不应该也不应该强制要求如此庞大的包袱。

越来越明显的是,我们不能信任我们的网络。思科 再次被发现在其产品中存在硬编码的后门。在我撰写本文时,FBI 咨询 正在生效,要求 重启家庭和小型办公室路由设备,原因是未知向量恶意软件渗透。互联网充斥着受感染的设备,因为我们没有修补运行此基础设施的软件。假设存在妥协并加密所有流量已成为唯一合理的立场。

虽然反对 telnet 的运动可能已经基本获胜,但 Linux 和更广泛的 UNIX 社区仍然存在盲目性领域。NFS 早该受到保护,并且令人反感的是,甚至有必要使用 stunnel 这样的变通方法。敏感数据不应与未知来源共享。在协议和内核架构师将此铭记于心之前,请使用 stunnel 封装您的 NFS。

附录

另一种加密 NFS 流量的方法 最近 已提交以包含到 Linux 内核中。Wireguard 是一种 VPN 实现,它力求最小的代码占用空间(仅 4,000 行),以简化审计以确保实现的正确性。

Wireguard 的加密有意限制为 DJB 的 Curve25519 和 ChaCha20-Poly1305。文档 阐述 “WireGuard 在密码学方面有自己的主张。它有意缺乏密码和协议的灵活性。如果在底层原语中发现漏洞,则所有端点都将被要求更新。” 系统对交换 25519 公钥,这些公钥记录在简单的配置文件中(与我们希望在 NFS 挂载的 /etc/fstab 中看到的方式完全相同)。对于那些担心 Shor 算法在功能强大的量子计算机上运行的人来说,“WireGuard 还支持一种模式,其中任何一对对等方都可能在彼此之间预先共享一个 256 位对称加密密钥”(以可能牺牲前向保密性为代价)。

加密后,Wireguard 在传输前将所有流量转换为 UDP。由于所有处理都在内核内部进行,因此性能非常好,据报道甚至在具有原生 AES 机器指令的系统上都击败了 OpenVPN。从理论上讲,通过 Wireguard 连接返回 telnet 和 FTP 是安全的。

Wireguard 不是也不能成为 NFS 安全的完整解决方案。虽然 macOS、Windows 和其他平台也 存在 实现,但 stunnel 将更易于移植到更广泛的 POSIX 社区,因为它完全在用户空间中运行。由于多种原因,许多人会更喜欢 stunnel 的 TCP 传输而不是 UDP Wireguard 流量。有些人可能还更喜欢 TLS 的密码学灵活性,尤其是与 stunnel 的 chroot() 功能结合使用时。

尽管如此,令人鼓舞的是 Curve25519 和 ChaCha20-Poly1305 很可能在 Linux 内核中发挥关键作用。也许 NFSv4.3 RFC 将强制要求它们,并将这些工具强制推入商业 UNIX 和其他 NFS 实现者的内核中。

Wireguard 和 stunnel 很可能成为互补的安全工具。每种工具都可以服务于另一种工具无法进入的市场,并且两者都可能在不久的将来在 NFS 安全中发挥一定作用。

免责声明

本文中表达的观点和意见是作者的观点和意见,不一定反映 Linux Journal 的观点和意见。

Charles Fisher 拥有爱荷华大学电气工程学位,目前在一家财富 500 强矿业和制造公司担任系统和数据库管理员。

加载 Disqus 评论