Nfs

為什麼非特權使用者不能嵌套 FUSE 掛載,但他們可以使用 root_squash 在 NFS 中掛載 FUSE?

  • August 16, 2018
$ 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。這些分佈在年齡上非常相似,但可能存在一些差異。

首先,考慮 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 ...

第一個遇到致命錯誤“Permission denied”,因為 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

strace會話中的 UID 結果是“錯誤的” 。perf trace我們可以在會話中更好地看到 UID 舞蹈部分。(為了便於閱讀,我刪除了最左邊的列)。

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()呼叫在fusermount.cdrop_privs()的andrestore_privs()函式中。

chdir()呼叫被偷偷地隱藏在被呼叫的函式中check_perm()

結論

為什麼這適用於 NFS?答:因為 NFS 會查看已設置為非根 UID的fsuid(and )。fsgid

為什麼這在 FUSE 上不起作用,除非你有allow_others?答:因為 FUSE 檢查的是“真實”的 UID,而不是fsuid.

引用自:https://unix.stackexchange.com/questions/463076