NFS 导出权限的三层结构
NFS 默认没有独立账号体系,服务端只看客户端传来的 UID/GID,再套用本地权限规则。一次访问能否成功取决于三层:导出表是否允许这台客户端挂载、导出选项(rw/ro、squash、sec 等)是否放行这次操作、服务端文件系统上的权限位或 ACL 是否允许这个 UID 读写。任何一层拒绝,客户端看到的就是 Permission denied。
基本信息
| 适用平台 | Linux 服务端(nfs-utils / nfs-kernel-server) |
|---|---|
| 配置文件 | /etc/exports 或 /etc/exports.d/*.exports |
| 生效命令 | exportfs -ra(不中断已有连接) |
| 查看命令 | exportfs -v、showmount -e <server> |
| 权限要求 | root;客户端挂载多数发行版也需 root 或 cap_sys_admin |
典型使用场景
- 一批应用服务器共享同一份数据目录:只允许指定网段挂载,避免内网任意主机都能读写
- 只读分发安装包、镜像、数据集:用 ro 从根上断掉误写和篡改
- 多用户主机共享家目录或工程目录:必须先统一 UID/GID,再谈导出选项
- 容器或临时批量节点挂载共享盘:用 all_squash 把未知身份统一压成 nobody
- 备份与同步任务需要写属主信息:只有受控场景才考虑放宽 squash 策略
exports 写法示例
exports 写法示例
# 只读共享给网段:同步写入,客户端 root 一律映射为匿名
/data/pub 10.0.0.0/24(ro,sync,root_squash,no_subtree_check)
# 读写共享给单台主机:适合 UID 已对齐的应用目录
/data/app app01.example.com(rw,sync,no_subtree_check,root_squash)
# 全量匿名:无论客户端是谁都压成 nobody,适合纯读取的公共仓库
/data/repo *(ro,sync,all_squash,anonuid=65534,anongid=65534)
# 改完必须重载并核对生效选项
exportfs -ra && exportfs -v客户端挂载与验证
客户端挂载与验证
mount -t nfs -o vers=4.2,rsize=1048576,wsize=1048576 10.0.0.10:/data/pub /mnt/pub # NFSv4.2 挂载
mount -t nfs -o vers=3,sync 10.0.0.10:/data/app /mnt/app # 兼容旧客户端
id && ls -ln /mnt/app # 确认服务端识别到的 UID/GID 与预期一致
nfsstat -m # 查看实际协商到的版本与挂载参数
showmount -e 10.0.0.10 # 从客户端侧确认服务端宣告了哪些导出常用导出选项读法
| 选项 | 含义 | 使用要点 |
|---|---|---|
| rw / ro | 可读写 / 只读 | 纯分发场景一律 ro,写权限是共享事故的主要来源 |
| sync / async | 写入是否落盘后再应答 | async 吞吐高但断电有丢数据风险,关键数据用 sync |
| root_squash | 客户端 root 映射为匿名 | 默认且应长期保留 |
| no_root_squash | 客户端 root 保持 root | 等于把服务端 root 交给对端,仅限受控测试 |
| all_squash | 所有用户压成匿名 | 需配合 anonuid/anongid 指定匿名身份 |
| no_subtree_check | 关闭子树一致性检查 | 导出子目录时更稳,现代发行版多为默认 |
| sec=sys / sec=krb5 | 认证方式 | krb5 需 Kerberos 环境;纯 IP 信任即 sec=sys |
| crossmnt | 允许跨越下层挂载点 | 导出父目录又要访问嵌套挂载时使用 |
NFSv4 只用 2049/TCP,不再依赖 rpcbind 与 mountd 的动态端口;它用伪文件系统组织导出根,形如 /export/data,导出路径与 v3 的目录结构不一定一一对应。客户端固定 vers=4.2 可以绕开版本协商带来的端口排查问题。
rw 加 no_root_squash 再配宽网段是典型高危组合:客户端 root 可以直接改写服务端文件的属主与权限位,甚至留下 setuid 程序。确需放宽时,把来源收窄到单机或小型管理网段,并在服务端文件系统权限上做二次限制。
服务端侧排错命令
服务端侧排错命令
exportfs -v # 逐条列出导出与生效选项,确认改动已加载
exportfs -s # 以可直接粘贴回 exports 的格式输出
rpcinfo -p | grep -E 'nfs|mountd' # v3 场景确认端口与服务注册
tail -f /var/log/messages # RHEL 系查看 nfsd 拒绝日志
journalctl -u nfs-server -n 50 # Debian 系查看服务日志先对齐身份再谈选项
NFS 判定读写的依据是 UID/GID 数字,不是用户名。两台主机上都有 dev 账号但 UID 不同(例如 1000 与 1001),共享目录就会按 1001 去匹配服务端权限,结果通常是能挂载、能列目录、写不进文件。因此落地顺序应是先统一账号与 UID,再设计导出选项,最后才调性能参数。