権限のないユーザーはFUSEマウントをネストできませんが、root_squashを使用してNFS内でFUSEをマウントできるのはなぜですか?

権限のないユーザーはFUSEマウントをネストできませんが、root_squashを使用してNFS内でFUSEをマウントできるのはなぜですか?
$ 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

fusermountset-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サーバーはデフォルトでthisに設定されていますroot_squash。これは、「root ユーザーはユーザー none と同じアクセス権を持ちます」を意味します。

この2つの状況はなぜ違うのですか?

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が原因で致命的なエラー「Permission Denied」が発生しましたstrace。 2番目は成功しましたが、パスなどの文字列パラメータを表示できませんでした。これら2つのトレースは、致命的なエラーが発生するまで同じ一般的なコードパスを示すようです。これは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

呼び出しはand関数にsetfsuid()あります。drop_privs()restore_privs()ヒューズインストーラ

呼び出しchdir()はという関数にこっそり隠されていますcheck_perm()

結論として

これがNFSで動作するのはなぜですか?答え:NFSはルートではなくUIDに設定されているfsuid項目を確認するためです。fsgid

FUSEがないとFUSEで機能しないのはなぜですかallow_others?答え:FUSEは.UIDではなく「実際の」UIDをチェックするからですfsuid

関連情報