본문 바로가기
빌드노트 검색

빌드노트

파일이 644인데도 Permission denied라면 상위 디렉터리의 x를 본다

파일이 644여도 경로의 디렉터리에 탐색 권한이 없으면 열리지 않는다. 비특권 Linux에서 ls의 이름 조회와 cat의 읽기를 비교하고, 파일 모드를 유지한 채 막힌 디렉터리의 x만 복원하는 판단을 설명한다.

파일 권한이 644인데 특정 사용자에게만 Permission denied가 난다면, 파일까지 가는 디렉터리의 권한을 따로 봐야 한다. 파일 내용을 읽을 권한이 있어도 경로 중 디렉터리 하나를 탐색할 수 없으면 파일을 열 수 없다. 이 경우 바꿀 곳은 파일의 읽기 비트가 아니라 막힌 디렉터리의 탐색 비트다.

644는 소유자에게 읽기·쓰기, 그룹과 그 밖의 사용자에게 읽기를 허용하는 일반 파일 모드다. 이것만으로 파일까지 도달할 수 있다는 뜻은 아니다. 디렉터리의 x는 그 디렉터리 안의 이름을 찾아 경로를 이어 가는 탐색 권한이다. Linux는 경로를 따라가다가 이 권한이 없으면 EACCES, 즉 Permission denied를 반환한다. 파일을 읽기 위해 일반 파일 자체에 실행 비트를 붙일 필요는 없다. Linux path_resolution(7) (새 탭에서 열림)

ls에는 이름이 보이는데 cat은 실패했다

아래는 운영 장애 기록이 아니라 새 폴더와 텍스트 파일로 한 별도 실험이다. Debian 13 컨테이너의 Linux tmpfs에서 UID·GID 10001로 실행했고 권한 검사를 우회하는 capability는 제거했다. Bash 5.2.37, GNU coreutils 9.7, util-linux의 namei 2.41.5를 사용했다.

직접 비교하려면 일반 사용자로 Linux의 Bash를 연다. mktemp, chmod, ls, cat, GNU stat과 util-linux의 namei가 필요하다. Windows 공유 드라이브 대신 Linux 권한을 적용하는 파일시스템에서 실행한다. 다음 명령은 새 임시 폴더를 만들며 이후 상대 경로 명령도 같은 셸에서 이어서 실행한다.

lab=$(mktemp -d)
cd "$lab" || exit 1
mkdir -p gate/inner
chmod 700 gate gate/inner
printf 'directory-search-ok\n' > gate/inner/message.txt
chmod 644 gate/inner/message.txt
stat -c '%a %n' gate gate/inner gate/inner/message.txt
cat gate/inner/message.txt

두 디렉터리는 700, 파일은 644로 표시됐고 cat은 directory-search-ok를 출력했다. 이제 상위 디렉터리인 gate의 소유자 탐색 권한만 뺀다. 아래에서 cat과 namei의 실패는 의도한 비교이므로 오류가 나도 다음 명령을 실행한다.

chmod u-x gate
ls -1 --color=never gate
cat gate/inner/message.txt
namei -l gate/inner/message.txt

실제 출력은 명령 순서대로 다음과 같았다.

inner
cat: gate/inner/message.txt: Permission denied
f: gate/inner/message.txt
drw------- 10001 10001 gate
                        inner - Permission denied

ls -1은 이름 목록을 읽었지만 cat은 그 이름을 따라 파일에 도달하지 못했다. gate에는 읽기 권한이 남아 있고 탐색 권한이 빠져 있었다. 이름이 보인다는 사실과 그 아래 파일을 열 수 있다는 사실은 다르다. 같은 상태에서 상세 정보까지 요청하는 ls -l gate는 inner의 속성을 읽지 못해 물음표와 권한 오류를 출력했다.

namei -l은 경로 구성요소의 모드·소유자·그룹을 보여 준다. 위 출력은 gate의 x가 없어서 다음 이름인 inner에서 진행이 막힌 것을 보여 준다. 막힌 지점 아래의 파일 모드까지 읽어 낸 결과는 아니다. util-linux namei(1) (새 탭에서 열림)

파일을 그대로 두고 막힌 폴더만 바꾼다

이 실험에서는 실행 사용자와 gate 소유자가 같으므로 소유자 비트인 u를 고쳤다.

chmod u+x gate
cat gate/inner/message.txt
stat -c '%a %n' gate gate/inner gate/inner/message.txt

cat은 다시 directory-search-ok를 출력했다. 두 디렉터리는 700, 파일은 여전히 644였다. chmod u+x는 소유자의 실행·탐색 비트만 추가한다. 파일과 하위 폴더 전체의 모드를 다시 지정할 이유가 없었다. GNU chmod(1)의 기호 모드 (새 탭에서 열림)

이어 gate를 700으로 둔 채 gate/inner의 x만 제거했을 때도 파일 읽기가 실패했다. 그때는 namei가 inner의 drw-------를 보여 준 뒤 message.txt에서 막혔다. inner에 u+x를 복원하자 읽혔다. 바로 위 폴더 하나만 확인해서는 더 위에 있는 장벽을 놓칠 수 있고, 앞의 장벽을 풀고 나면 그 아래의 장벽이 드러날 수도 있다.

읽기와 탐색을 반대로 나눠 본 결과도 있다. gate를 100으로 바꾸면 소유자의 탐색 권한만 남는다. 이때 ls -1 gate는 목록을 읽지 못해 실패했지만, 경로를 이미 알고 있는 cat gate/inner/message.txt는 성공했다. 목록 조회가 필요 없는 접근에 디렉터리 읽기 권한까지 늘릴 필요는 없다. 디렉터리의 r을 빼는 것만으로 이미 경로를 아는 사용자의 파일 읽기까지 막히지도 않는다.

내 경로에서는 어느 사용자의 비트가 적용되는가

실제 문제를 확인할 때는 실패하는 사용자로 id를 실행해 UID와 그룹 목록을 보고, namei -l에 문제 파일의 절대 경로를 전달한다. 관리자 셸에서 성공한 결과로 서비스 계정의 성공을 대신 판단하지 않는다. 서비스라면 그 프로세스가 실제로 사용하는 UID와 그룹, 보이는 경로를 확인해야 한다.

일반 모드 비트에서는 소유자이면 u, 소유자가 아니고 파일의 그룹에 속하면 g, 둘 다 아니면 o가 적용된다. 소유자 비트가 막혔다고 다른 사용자 비트로 재시도하지 않는다. 파일과 각 디렉터리의 소유자가 다를 수 있으므로 이 선택을 경로 구성요소마다 한다. 경로 권한의 사용자·그룹 선택 규칙 (새 탭에서 열림)

따라서 예제의 u+x를 다른 계정의 접근 문제에 그대로 복사하면 해결되지 않을 수 있다. 공유 그룹을 통해 접근시키려는 경로라면 그 그룹과 디렉터리의 그룹이 맞는지, 그룹의 x가 빠졌는지를 먼저 확인한다. o+x는 특정 사용자 한 명을 지정하는 변경이 아니므로 필요한 접근 범위를 대신할 수 없다. 막힌 디렉터리와 허용하려는 사용자 범위를 확인한 뒤 그 비트만 바꾸고, 같은 사용자로 파일 읽기를 다시 확인한다.

이번 비교는 같은 비특권 UID가 소유한 새 디렉터리에서 일반 모드의 탐색 비트만 바꾼 결과다. 다른 계정·보조 그룹 조합, 확장 ACL, SELinux·AppArmor 정책, 네트워크 파일시스템은 실험하지 않았다. 각 디렉터리의 탐색과 파일 읽기가 모두 허용된 것처럼 보이는데도 거절된다면, 모드를 계속 넓히기 전에 그 추가 조건을 확인할 차례다.

← 전체 글 보기