$ 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 서버는 기본적으로 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
.