
failfs는 도달하는 모든 작업을 EOPNOTSUPP 오류로 실패시키는 커널 내부 파일시스템이다. 영구적으로 비어 있는 nullfs와 달리, failfs는 “여기서는 아무것도 지원되지 않는다”는 뜻이다. 사용자 공간에서 마운트할 수 없고, 그 위에 다른 파일시스템을 마운트할 수도 없으며, 복제할 수도 없다. 진입 방법은 fchdir(2)와 fchroot(2)가 인식하는 파일 디스크립터 센티널 FD_FAILFS_ROOT뿐이다.
failfs를 통과하는 경로 탐색은 .을 포함해 각 구성 요소를 파싱하기도 전에 EOPNOTSUPP 오류로 실패한다. O_PATH를 사용해도 경로 조회로 루트를 열 수 없다.
작업 디렉터리가 failfs에 있는 프로세스는 AT_FDCWD 기준 상대 경로 조회가 모두 실패한다. 프로세스 루트에서 도달할 수 없는 다른 작업 디렉터리와 마찬가지로, getcwd(2) 시스템 호출은 (unreachable)이 앞에 붙은 경로를 반환한다.
프로세스의 루트 디렉터리가 failfs에 있으면 동적 링크 바이너리의 인터프리터와 절대 심볼릭 링크를 포함한 모든 절대 경로 조회가 실패한다. 따라서 exec도 실패한다. 명시적으로 연 디렉터리 파일 디스크립터를 기준으로 하는 조회는 계속 작동한다. 이는 RESOLVE_BENEATH와 동등한 fs_struct 수준의 제한이다. 프로세스는 모든 경로 조회의 기준으로 자신이 명시적으로 보유한 파일 디스크립터를 사용해야 한다.
fchroot(FD_FAILFS_ROOT, 0)을 호출하려면 호출자의 사용자 네임스페이스에서 CAP_SYS_CHROOT 권한이 필요하다. 이는 chroot(2)와 같은 조건이다. 권한이 없는 호출자도 다음 조건을 모두 충족하면 진입할 수 있다.
no_new_privs가 설정돼 있어야 한다. 일반 마운트에 있는 setuid 바이너리는 상속된 디렉터리 파일 디스크립터를 통해 계속 접근할 수 있다. 사용할 수 없는 루트 디렉터리로 해당 바이너리를 실행하면 전형적인 혼란된 대리인(confused deputy) 문제가 발생할 수 있다.- 호출자가 이미 chroot 상태가 아니어야 한다. 루트 디렉터리는
..해석 범위를 제한한다. failfs 루트는 실제 마운트 트리를 따라 올라가 도달할 수 없으므로, chroot된 작업의 루트를 failfs로 바꾸면openat(fd, "..")를 통해 chroot에서 벗어날 수 있다. - 호출자가
fs_struct를 공유하지 않아야 한다.no_new_privs는 호출 스레드에서 확인하지만 루트 디렉터리는fs_struct에 있다.CLONE_FS를 사용해 공유하는 다른 스레드에no_new_privs가 설정되지 않은 상태라면, 그 스레드가 failfs 루트에서 setuid 바이너리를 실행할 수 있다. 이를 막기 위해 진입 시fs->users == 1이어야 한다. 이는setns(2)가 마운트 및 사용자 네임스페이스에 적용하는 제한과 같다.
현재 구현에서는 failfs에서 빠져나오기 어렵다. 다만 이는 현재 구현의 특성일 뿐 보장된 인터페이스는 아니며, 나중에 제한이 완화될 수 있다.
현재 failfs에 진입한 프로세스는 chroot된 것으로 간주된다. 따라서 CAP_SYS_CHROOT를 다시 얻기 위해 사용자 네임스페이스를 만들 수 없으며, chroot(2)나 fchroot(2)로 나오려면 CAP_SYS_CHROOT가 필요하다. 지금 가능한 다른 방법은 마운트 네임스페이스 파일 디스크립터를 사용한 setns(2)다. 이 방법에는 대상 마운트 네임스페이스에 대한 CAP_SYS_ADMIN과 호출자의 사용자 네임스페이스에서 CAP_SYS_CHROOT, CAP_SYS_ADMIN이 필요하며, 루트 디렉터리와 작업 디렉터리가 모두 초기화된다. 그러한 파일 디스크립터를 보유하지 않고 seccomp로 *chdir()·*chroot()·setns()를 제한한 프로세스는 현재 failfs에서 나올 수 없다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요