빌드노트
파일이 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 정책, 네트워크 파일시스템은 실험하지 않았다. 각 디렉터리의 탐색과 파일 읽기가 모두 허용된 것처럼 보이는데도 거절된다면, 모드를 계속 넓히기 전에 그 추가 조건을 확인할 차례다.