$ mkdir mnt
$ bindfs /tmp mnt
fusermount: option allow_other only allowed if 'user_allow_other' is set in /etc/fuse.conf
$ bindfs --no-allow-other /tmp mnt
$ mkdir /tmp/mnt2
$ bindfs --no-allow-other /tmp mnt/mnt2
fusermount: bad mount point /home/alan/mnt/mnt2: Permission denied
fusermount
失败,因为它以不同的用户身份运行。
$ sudo ls mnt/
ls: cannot open directory 'mnt/': Permission denied
fusermount
是 set-uid root
。这是必需的,因为非特权用户无法使用mount()
系统调用。
$ ls -l $(which fusermount)
-rwsr-xr-x. 1 root root 32848 Feb 7 2018 /usr/bin/fusermount
^ set-uid bit
但是。据报道,FUSE 可以在 NFS 主目录内使用。即使主目录具有模式700
- 只能由拥有的用户访问。 NFS 服务器默认为root_squash
,这意味着“root 用户将具有与用户 nobody 相同的访问权限”。
为什么这两种情况不同?
我正在Fedora 28上进行测试。有关NFS的报告来自Ubuntu 18.04。这些分布在年龄上非常相似,但可能存在一些差异。
答案1
首先,考虑FUSE的实现no_allow_others
。
它要求有效的、真实的和保存的UID(用户ID)都匹配。 (GID 也一样)。这是故意阻止 set-UID 程序访问挂载。
https://github.com/torvalds/linux/blob/v4.18/fs/fuse/dir.c#L1024
调用用户控制的文件系统为文件系统
守护进程提供了对当前进程的类似于 ptrace 的功能。这
意味着文件系统守护进程能够记录执行的确切文件系统操作,并且还可以 以其他不可能的方式控制请求者进程的
行为。
例如,
它可以将操作延迟任意长度的时间,从而允许
针对请求者进行 DoS。
现在让我们追踪一下fusermount
到底做了什么。我们可以尝试看看
strace -f bindfs ...
和
sudo perf trace -o trace.txt -a sleep 2; sleep 1; bindfs ...
第一个遇到致命错误“权限被拒绝”,因为 set-UID root 在strace
.第二个成功,但无法显示路径等字符串参数。我认为这两条跟踪显示了相同的通用代码路径,直到出现致命错误。这意味着我们可以使用strace
结果来填充缺失的字符串参数。
结果中的最后一个调用strace
是:
[pid 30609] mount("/home/alan-sysop/mnt", ".", "fuse", MS_NOSUID|MS_NODEV, "default_permissions,fd=5,rootmod"...) = -1 EPERM (Operation not permitted)
有趣的! "."
表示当前目录。所以fusermount
一定已经在挂载点上运行了......不知何故。此技巧有时可用于访问当前无法使用其绝对路径访问的目录。
如果我们向上滚动,我们可以看到fusermount
确实更改为该目录。它还与一些 UID 相关(和 GID 相关)的系统调用一起跳舞。
[pid 30609] getuid() = 1000
[pid 30609] setfsuid(1000) = 1000
[pid 30609] getgid() = 1000
[pid 30609] setfsgid(1000) = 1000
[pid 30609] openat(AT_FDCWD, "/etc/fuse.conf", O_RDONLY) = 6
...
[pid 30609] lstat("/home/alan-sysop/mnt", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid 30609] getuid() = 1000
[pid 30609] chdir("/home/alan-sysop/mnt") = 0
[pid 30609] lstat(".", {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
[pid 30609] access(".", W_OK) = 0
[pid 30609] getuid() = 1000
[pid 30609] setfsuid(1000) = 1000
[pid 30609] setfsgid(1000) = 1000
会话中的 UID 结果“错误” strace
。我们可以在会议中更好地看到UID舞蹈部分perf trace
。 (为了便于阅读,我删除了最左边的列)。
getuid( ) = 1000
setfsuid(uid: 1000 ) = 0
getgid( ) = 1000
setfsgid(gid: 1000 ) = 1000
openat(dfd: CWD, filename: 0xa428e2bc ) = 6
...
close(fd: 6 ) = 0
lstat(filename: 0xa63882a0, statbuf: 0x7ffe7bd4f6d0 ) = 0
getuid( ) = 1000
chdir(filename: 0xa63882a0 ) = 0
lstat(filename: 0xa428eca5, statbuf: 0x7ffe7bd4f6d0 ) = 0
access(filename: 0xa428eca5, mode: W ) = 0
getuid( ) = 1000
setfsuid( ) = 1000
setfsgid(gid: 1000 ) = 1000
getuid( ) = 1000
调用setfsuid()
位于drop_privs()
和restore_privs()
函数中熔丝安装程序。
该chdir()
调用偷偷地隐藏在名为 的函数中check_perm()
。
结论
为什么这在 NFS 上有效?答案:因为 NFS 会查看已设置为非 root UID 的fsuid
( 和)。fsgid
为什么这在 FUSE 上不起作用,除非你有allow_others
?答案:因为 FUSE 检查“真实”UID,而不是fsuid
.