권한이 없는 사용자는 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. 이는 "루트 사용자는 사용자 none과 동일한 액세스 권한을 갖습니다"를 의미합니다.

이 두 가지 상황이 왜 다른가요?

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. 두 번째는 성공했지만 경로와 같은 문자열 매개변수를 표시하지 못했습니다. 이 두 추적은 치명적인 오류가 발생할 때까지 동일한 일반 코드 경로를 보여주는 것 같습니다. 이는 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.

관련 정보