认识 NFS 文件共享协议


声明:本文为个人学习笔记,内容不保证完全正确,仅供参考。如有错误,可以的话麻烦提出,谢谢!


一、NFS 简介

NFS(Network File System,网络文件系统)是众多文件共享协议之一,最初是由 Sun Microsystems 公司于 1984 年开发出来的。

由于 NFS 使用起来非常方便,因此在 UNIX 操作系统中被广泛使用。现在,许多非 UNIX 或非类 UNIX 操作系统也对 NFS 提供了支持。

NFS 的配置和使用其实都非常简单,但是它的工作原理和其他的文件共享协议又有所不同,我们有必要了解一下。


二、NFS 的工作原理

NFS 跟其他文件共享协议一样,也是采用了 C/S 架构。

但是,NFS 只提供了基本的文件处理功能,而不提供任何 TCP/IP 数据传输功能。

它需要借助 RPC 协议才能实现 TCP/IP 数据传输功能。

NFS 文件系统对客户端来说是完全透明的,访问 NFS 文件系统上的文件或目录就跟在访问本地的文件或目录一样!

2.1 RPC 简介

RPC(Remote Procedure Call,远程过程调用)是一种网络程序的编程方法,它定义了一种进程间通过网络进行交互通信的机制。

正常情况下,如果客户端要和服务器相互通信,那么客户端和服务器就必须利用系统的套接口函数,编写出一套完整的网络通信协议才能实现。

而如果使用了 RPC,那么服务器和客户端之间只需要相互调用对方提供的 RPC 接口函数(俗称客户桩)即可实现相互通信。函数的参数和返回值就是要传递的信息。

而 RPC 将全权负责网络通信。这样一来,网络程序的设计就变得简单许多,跟编写本地程序一样简单,通信效率也会有所提高。

当调用 RPC 的服务器程序启动时,相关的进程就会向 RPC 发起注册。RPC 就会开启特定的端口来为客户端提供服务。

由于端口号可能是不固定的,因此 RPC 服务器必须提供一种查询服务端口的方法。

RPC 服务器上有一个叫端口映射器的东西,它固定监听 UDP 111 端口。RPC 客户端通过访问这个端口就可以查询到对应服务所使用的端口。

使用 RPC 协议的网络程序在服务器和客户端上都必须借助 RPC 服务才可相互通信。

2.2 NFS 服务器主要进程

2.2.1 rpc.nfsd 进程

NFS 服务的主进程,主要管理客户端是否能够接入 NFS 服务器以及数据的传输。

该进程固定监听 TCP/UDP 2049 端口。

2.2.2 rpc.mountd 进程

管理和维护 NFS 文件系统,根据所设定的权限决定是否允许客户端挂载指定的共享目录。

该进程监听的端口默认是不固定的。

2.2.3 rpc.lockd 进程(可选)

提供文件锁功能,防止多个客户端同时写入一个文件。

该进程监听的端口默认是不固定的。

2.2.4 rpc.statd 进程(可选)

负责检查数据的状态及一致性,需要与 rpc.lockd 配合使用。

该进程监听的端口默认是不固定的。

2.2.5 rpcbind 进程

RPC 的端口映射器进程,监听 UDP 111 端口。

2.3 NFS 服务器和客户端的结构

如图所示。

2.4 NFS 服务器和客户端的通信过程

首先,客户端上的 RPC 程序连接到服务器上的 UDP 111 端口来通过端口映射器查询 NFS 各项服务所使用的端口,然后客户端分别连接 rpc.nfsdrpc.mountd 所在的端口,取得挂载点信息之后将文件系统挂载起来,客户端就可以操作共享文件系统了!

通过 Wireshark 抓包即可观察整个过程。


三、NFS 的特性

3.1 NFS 的安全性

NFS 默认没有加密,且仅依靠 IP 地址或主机名来决定是否允许客户端挂载指定的共享目录。

虽然最新版本的 NFS 支持 Kerberos 身份验证,但是配置起来也比较麻烦,而且我也不知道 Kerberos 是啥玩意……

所以 NFS 一般只用于几台主机在安全的局域网内相互共享文件。

NFS 也可通过 Kerberos 认证和加密(现在我已经把 Kerberos 搞明白了,嘻嘻~)。

如有需要可看下这篇文章:

搭建一个基于 Kerberos 认证的 NFS 服务器

3.2 共享资源的属主、属组和权限

NFS 服务器和客户端通过 UID 和 GID 来识别共享资源的所有者信息。

当客户端挂载 NFS 共享目录时,共享目录中资源的 UID 和 GID 将与服务器上面的保持一致;而客户端会将 UID 和 GID 映射到客户端上所对应的用户名和组名。

举个例子吧,服务器上有名为 server 的用户,其 UID 为 111;客户端上有名为 client 的用户,其 UID 同为 111

那么,UID 为 111 的共享资源在服务器上的属主是 server;在客户端上的属主则是 client

GID 和属组,以及 ACL 中的用户和组同理,很好理解吧。

如果 UID 或 GID 没有对应的用户或组时,那么用户名或组名将直接以 UID 或 GID 表示。

再举个例子,客户端上有名为 client 的用户,其 UID 为 222;而服务端上没有 UID 为 222 的用户。

那么,UID 为 222 的共享资源在服务器上的属主将直接用 222 表示;在客户端上的属主则是 client

NFS 服务器与客户端上共享资源的权限及 ACL 信息(若支持)将保持一致。


四、操作环境

  • 服务器操作系统:CentOS 7.4.1708 最小安装
  • 客户端操作系统:CentOS 7.4.1708 最小安装

五、NFS 服务器的配置

5.1 安装 NFS 服务器

yum -y install nfs-utils

5.2 配置 NFS 共享目录

请使用 vim 之类的文本编辑器打开 /etc/exports

在该文件中,一行表示一个共享目录。请按以下格式进行修改:

共享目录路径   允许的 IP 或主机名 1(选项 1,选项 2,...)  允许的 IP 或主机名 2(选项 1,选项 2,...)   ...
选项 功能
ro 以只读方式共享(默认)
rw 以读写方式共享
sync 客户端写入的数据立即写入到磁盘中去(默认)
async 客户端写入的数据先保存到内存中再写入到磁盘中去
root_squash 把 root 用户映射为匿名用户(默认)
no_root_squash 允许使用 root 用户
all_squash 把所有用户映射为匿名用户
anonuid=<UID> 指定匿名用户的 UID
anongid=<GID> 指定匿名用户的 GID
insecure 允许客户端使用大于 1024 的端口连接

修改完成后记得保存文件。

例如

允许任意主机以匿名身份读写 /tmp 目录;允许 10.0.0.1root 的身份读写 /root 目录;允许 10.0.0.2root 的身份读取 /root 目录。

配置文件应为:

/tmp *(rw,all_squash)
/root 10.0.0.1(rw,no_root_squash) 10.0.0.2(ro,no_root_squash)

5.3 启动 NFS 服务

systemctl start nfs

如需开机自启动,请执行:

systemctl enable nfs

5.4 更新 NFS 配置

如果修改了 NFS 配置文件,我们可以通过以下命令直接使新的配置文件生效而不用中断服务。

exportfs -rv

systemctl reload nfs

5.5 固定 NFS 端口 & 防火墙的设定(可选)

如果 NFS 服务器上开启了防火墙,那么就必须把 NFS 服务器使用的端口固定下来,而不能使用 RPC 随机分配的端口。

请执行以下命令:

echo "MOUNTD_PORT=52101" >> /etc/sysconfig/nfs 
echo "STATD_PORT=52102" >> /etc/sysconfig/nfs
echo "LOCKD_TCPPORT=52103" >> /etc/sysconfig/nfs
echo "LOCKD_UDPPORT=52104" >> /etc/sysconfig/nfs

然后重启 NFS 服务,防火墙配置打开 TCP/UDP 111 2049 52101 52102 52103 52104 端口。


六、NFS 客户端的配置

6.1 安装 NFS 实用工具

yum -y install nfs-utils

6.2 查看服务器的共享目录

命令用法

showmount -e <服务器 IP 或主机名>

例如

查看上面服务器共享出来的目录(IP 为 10.0.0.101)。

showmount -e 10.0.0.101

执行结果:

Export list for 10.0.0.101:
/tmp  *
/root 10.0.0.2,10.0.0.1

6.3 挂载 NFS 服务器上的共享目录

命令用法

mount.nfs <服务器 IP 或主机名>:<共享路径> <挂载点> [-o <选项>]

挂载选项大部分与普通文件系统相同,下面只列出一些 NFS 文件系统的特色选项(一般都不会用到)。

选项 功能
bg 在后台执行挂载任务(网络不稳定时使用)
soft 在服务器无响应时将会提示出错而不是不断尝试挂载
tcp 强制使用 TCP 连接
rsize=<字节数> 读取块大小
wsize=<字节数> 写入块大小

例如

挂载上面服务器共享出来的 /tmp 目录到本机的 /mnt 上,并验证结果。

mount.nfs 10.0.0.101:/tmp /mnt/
mount | grep nfs

执行结果:

10.0.0.101:/tmp on /mnt type nfs4 (rw,relatime,vers=4.1,rsize=131072,wsize=131072,namlen=255,hard,proto=tcp,port=0,timeo=600,retrans=2,sec=sys,clientaddr=10.0.0.128,local_lock=none,addr=10.0.0.101)

这样就挂载成功了。

发表评论