Samba 挂载完文件全是 nobody:uid、gid 和 force user 到底改哪边
上周在飞牛 NAS 上开了个 SMB 共享,打算让内网那台 Ubuntu 小主机挂上去做备份目录。挂载本身很顺利,一行命令就上去了,结果脚本一跑就崩:
PermissionError: [Errno 13] Permission denied: '/mnt/nas/bak/2026-09-22.sql'
ls -l 一看,整个目录的文件属主全是 nobody nogroup。我当时下意识以为是 NAS 那边共享权限没放开,跑去后台把「读写权限」开关来回拨了两次,没用。折腾了小半天才想明白——这事儿得先分清是哪一边的属主在生效。
先把现象看准:用 ls -n 而不是 ls -l
ls -l 会把 uid 翻译成用户名,跨机器的时候这个翻译经常骗人。直接看数字:
ls -n /mnt/nas/bak
65534 就是 nobody。我的备份脚本跑在 backup 用户下,uid 1001,写进一个属主是 65534、权限 644 的目录里,自然被拒。
再看一眼挂载参数,问题就清楚了:
mount | grep cifs
# //192.168.1.20/backup on /mnt/nas type cifs (rw,relatime,vers=3.1.1,cache=strict,username=nas,uid=0,...)
我挂载的时候压根没指定 uid / gid,客户端就用了默认值,于是所有文件在 Linux 这边都显示成 nobody。
客户端这一侧:uid、gid、file_mode、dir_mode
cifs 挂载是「无属主映射」的文件系统——SMB 协议传过来的只有 SID,不带 POSIX uid。所以本地显示成谁,完全由挂载选项决定。最直接的修法:
sudo mount -t cifs //192.168.1.20/backup /mnt/nas \
-o username=nas,password=xxxx,vers=3.1.1,\
uid=1001,gid=1001,file_mode=0644,dir_mode=0755
几个点值得留意:
uid/gid写数字比写用户名稳。我试过写uid=backup,在早期内核上翻不出来,直接退化成 nobody。file_mode/dir_mode只在服务端不支持 CIFS Unix 扩展时才生效。如果 NAS 那边开了 unix extensions,权限以服务端为准,这两个参数会被忽略——这也是很多人「明明配了却不生效」的原因。- 别忘
vers=。不写的话内核会先试 SMB1,现在很多 NAS(包括飞牛)默认关了 SMB1,日志里会蹦一句Operation not supported,内核再回退到 3.x。显式写vers=3.1.1能省掉这次无谓的协商。
写进 /etc/fstab 长期挂载时,密码别明文:
# /etc/fstab
//192.168.1.20/backup /mnt/nas cifs credentials=/etc/samba/nas.cred,vers=3.1.1,uid=1001,gid=1001,file_mode=0644,dir_mode=0755,_netdev,nofail 0 0
/etc/samba/nas.cred 内容:
username=nas
password=xxxx
domain=WORKGROUP
这里有个坑我踩过:cred 文件里 username = nas 这种带空格的写法是错的,会被当成用户名的一部分(前面多一个空格),认证必失败。等号两边不要有空格。文件本身 chmod 600,否则 mount 会拒绝加载。
改完先别急着重启,mount -a 试一次,dmesg | tail 看有没有 CIFS 报错,能挂上再重启。
服务端那一侧:force user 才是"统一属主"的正解
客户端改 uid 只是让这台机器看着对。如果同一个共享还要被 Windows、另一台 Linux、Docker 容器同时写,各挂各的 uid,最后 NAS 上存的文件属主会很乱,备份脚本换个机器跑就又挂了。
这种情况应该在服务端收口。ssh 进 NAS(或者直接在 SMB 配置里加),smb.conf 对应共享段:
[backup]
path = /volume1/backup
valid users = nas
read only = no
force user = nas
force group = users
create mask = 0664
directory mask = 0775
force user = nas 的意思是:不管谁连进来,创建文件时一律以 nas 这个本地账号落盘。这样多端写入的属主就统一了,客户端那边只需要保证能读能写,不必强求 uid 一致。
改完用 testparm -s 校验配置有没有语法错,然后 smbcontrol all reload-config 重载,不用重启整个服务。
顺带记两个相关的坑
NFS 的 root_squash。 如果走的是 NFS 而不是 SMB,root 写入后被映射成 nobody 是正常设计,不是 bug。服务端默认开 root_squash,把客户端的 root 压成 nobody。要么在 /etc/exports 里对可信内网段用 no_root_squash(有安全风险,谨慎),要么客户端用非 root 用户写,要么指定 anonuid=1001,anongid=1001。判断方法:
cat /proc/mounts | grep nfs
showmount -e 192.168.1.20
Docker 容器里挂 cifs。 在容器里直接 mount -t cifs 需要 --cap-add SYS_ADMIN --cap-add DAC_READ_SEARCH,而且要 --device /dev/fuse(走 fuse 的话)。我个人的做法是在宿主机挂好,再用 -v /mnt/nas:/data 挂进容器——容器里看到的 uid 就是宿主机那个 uid,只要和容器里跑进程的用户对上就行,比在容器里折腾 mount 省心得多。
排查顺序
下次再遇到类似问题,我按这个顺序走,基本两分钟定位:
df -T确认到底挂没挂上、什么类型mount | grep cifs看挂载选项里有没有 uid/gidls -n看数字属主,别被用户名翻译误导- 服务端的活:
testparm -s看 force user / create mask - 都对了还写不进,再看目录本身的 POSIX 权限和上级目录的 x 位(没 x 位连 cd 都进不去,报错同样是 Permission denied)
说到底就一句话:SMB 挂载的属主是客户端挂载选项决定的,落盘属主是服务端 force user 决定的。以前我一直以为是共享权限开关没打开,其实那玩意儿跟 nobody 半点关系都没有。
作者:phantomxjc
- 点赞
- 收藏
- 关注作者
评论(0)