파일은 분명히 있는데 Permission denied가 나오거나, 배포 스크립트가 실행되지 않거나, Nginx에서 403이 뜨는 경우에는 파일 내용보다 권한을 먼저 읽어야 합니다. 이 글은 ls -l에 표시되는 -rwxr-x--- 같은 문자열을 해석하고, 현재 사용자·소유자·그룹·상위 디렉터리 권한까지 확인해서 실제 원인을 좁히는 방법을 다룹니다.
이 글에서 해결할 문제
리눅스 파일 권한 읽는 방법을 찾는 상황은 대부분 단순한 암기 문제가 아닙니다. 운영 중인 VPS나 Ubuntu 서버에서 다음과 같은 문제가 생겼을 때 원인을 판단하기 위해 권한을 읽어야 합니다.
- 스크립트 파일을 실행했는데
Permission denied가 나온다. - 파일은 보이지만 수정하거나 저장할 수 없다.
- Nginx가 파일을 읽지 못해 403 Forbidden이 발생한다.
- Node.js, PM2, systemd 서비스가 실행 중인 계정과 파일 소유자가 맞지 않는다.
- 로그 파일이 생성되지 않거나 앱에서 업로드 디렉터리에 파일을 쓰지 못한다.
서버를 운영하다 보면 처음에는 명령어가 틀렸거나 앱 코드 문제처럼 보이지만, 실제로는 현재 사용자와 파일 소유자, 상위 디렉터리 권한이 맞지 않는 경우가 많습니다. 그래서 권한 문제는 chmod부터 실행하기보다 현재 상태를 읽는 순서가 더 중요합니다.
먼저 확인할 핵심 요약
시간이 없을 때는 아래 5가지만 먼저 확인해도 원인을 상당히 좁힐 수 있습니다.
whoami로 현재 명령을 실행하는 사용자를 확인합니다.id로 현재 사용자가 속한 그룹을 확인합니다.ls -l로 파일의 소유자, 그룹, 권한을 확인합니다.ls -ld또는namei -l로 상위 디렉터리 권한을 확인합니다.- 서비스로 실행 중이면
systemctl status,journalctl에서 실제 실행 계정을 확인합니다.
whoami
id
ls -l /var/www/html/deploy.sh
ls -ld /var /var/www /var/www/html
Ubuntu/Debian 계열에서는 위 명령어를 그대로 사용할 수 있습니다. 일부 최소 설치 환경에서는 namei가 없을 수 있는데, 그 경우에는 ls -ld로 경로를 하나씩 확인하면 됩니다.
권한 문자열은 어떻게 읽는가
ls -l을 실행하면 첫 번째 칸에 10글자짜리 권한 문자열이 나옵니다.
ls -l /var/www/html
-rw-r--r-- 1 www-data www-data 1240 May 11 10:20 index.html
-rwxr-x--- 1 deploy www-data 1824 May 11 10:22 deploy.sh
drwxr-xr-x 2 deploy www-data 4096 May 11 10:30 uploads
예를 들어 -rwxr-x---는 아래처럼 나누어 읽습니다.
| 구간 | 예시 | 의미 |
|---|---|---|
| 1번째 글자 | - |
파일 종류입니다. -는 일반 파일, d는 디렉터리, l은 심볼릭 링크입니다. |
| 2~4번째 | rwx |
소유자 권한입니다. 이 파일을 소유한 사용자에게 적용됩니다. |
| 5~7번째 | r-x |
그룹 권한입니다. 해당 그룹에 속한 사용자에게 적용됩니다. |
| 8~10번째 | --- |
기타 사용자 권한입니다. 소유자도 아니고 그룹에도 속하지 않은 사용자에게 적용됩니다. |
각 문자의 의미는 다음과 같습니다.
r: 읽기 권한입니다. 파일 내용을 열람할 수 있습니다.w: 쓰기 권한입니다. 파일 내용을 수정할 수 있습니다.x: 실행 권한입니다. 스크립트나 바이너리를 직접 실행할 수 있습니다.-: 해당 권한이 없다는 뜻입니다.
따라서 -rwxr-x---는 일반 파일이고, 소유자는 읽기·쓰기·실행이 가능하며, 그룹은 읽기·실행만 가능하고, 기타 사용자는 접근 권한이 없다고 해석합니다.
숫자 권한 755, 644를 문자로 바꿔 읽기
권한을 변경할 때는 chmod 755처럼 숫자로 표현하는 경우가 많습니다. 숫자는 r=4, w=2, x=1을 더해서 계산합니다.
| 숫자 | 문자 권한 | 주로 보이는 상황 |
|---|---|---|
7 |
rwx |
읽기, 쓰기, 실행이 모두 필요할 때 |
6 |
rw- |
읽고 수정은 가능하지만 직접 실행은 필요 없을 때 |
5 |
r-x |
읽고 실행은 가능하지만 수정은 막을 때 |
4 |
r-- |
읽기만 허용할 때 |
0 |
--- |
접근을 허용하지 않을 때 |
755는 소유자 rwx, 그룹 r-x, 기타 사용자 r-x입니다. 644는 소유자 rw-, 그룹 r--, 기타 사용자 r--입니다. 그래서 일반 웹 파일은 644로도 읽히지만, 직접 실행해야 하는 스크립트는 x가 없으면 실행되지 않습니다.
실제 서버 운영 중 헷갈리기 쉬운 지점
권한을 처음 볼 때는 rwx만 확인하기 쉽습니다. 하지만 운영 서버에서는 다음 지점에서 자주 헷갈립니다.
- 내가 로그인한 계정과 서비스를 실행하는 계정이 다를 수 있습니다.
- 파일 권한은 맞아도 상위 디렉터리에 진입 권한이 없으면 접근이 막힙니다.
- 파일 소유자가
root이면 일반 사용자가 수정하지 못할 수 있습니다. - Nginx는 보통
www-data계정으로 파일을 읽기 때문에 내 SSH 계정과 권한 판단이 다를 수 있습니다. - PM2나 systemd로 실행하면 로그인 셸에서 직접 실행할 때와 환경이 다를 수 있습니다.
특히 Node.js 앱을 systemd로 올렸을 때는 터미널에서 직접 실행하면 되는데 서비스로는 실패하는 경우가 있습니다. 처음에는 코드 문제처럼 보이지만, 실제로는 서비스의 User가 파일을 읽거나 실행할 권한이 없는 경우가 있습니다. 이 오류를 본 뒤에는 재시작을 반복하기보다 서비스 계정과 파일 권한을 먼저 확인하는 편이 빠릅니다.
처음 의심하기 쉬운 원인과 실제 원인의 차이
| 겉으로 보이는 증상 | 처음 의심하기 쉬운 원인 | 실제로 확인해야 할 원인 |
|---|---|---|
./deploy.sh 실행 시 Permission denied |
스크립트 문법 오류 | 파일에 실행 권한 x가 있는지, 현재 사용자가 소유자 또는 그룹에 해당하는지 |
| 설정 파일 저장 실패 | 편집기 문제 | 파일 소유자가 root인지, 현재 사용자에게 쓰기 권한이 있는지 |
| Nginx 403 Forbidden | Nginx가 죽었거나 포트 문제 | 웹 루트 경로, index 파일 존재 여부, 파일 및 디렉터리 읽기 권한 |
| 서비스가 실행 직후 종료 | 앱 코드 오류 | systemd 실행 사용자, 작업 디렉터리, 로그 파일 쓰기 권한 |
실제로 자주 막히는 상황 1: 스크립트 파일이 실행되지 않을 때
배포 스크립트를 만들고 실행했는데 아래처럼 실패하는 경우가 많습니다.
./deploy.sh
bash: ./deploy.sh: Permission denied
이때 바로 권한을 바꾸기 전에 현재 상태를 먼저 확인합니다.
whoami
id
ls -l deploy.sh
deploy
uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),33(www-data)
-rw-r--r-- 1 deploy deploy 980 May 11 10:15 deploy.sh
위 출력에서 deploy.sh는 -rw-r--r--입니다. 소유자에게 읽기와 쓰기 권한은 있지만 실행 권한 x가 없습니다. 이 상태에서는 파일 내용이 정상이어도 ./deploy.sh로 직접 실행할 수 없습니다.
수정 예시
현재 사용자가 파일 소유자이고, 이 파일이 실제로 실행해야 하는 스크립트라면 소유자에게만 실행 권한을 추가할 수 있습니다.
chmod u+x deploy.sh
ls -l deploy.sh
./deploy.sh
-rwxr--r-- 1 deploy deploy 980 May 11 10:15 deploy.sh
chmod는 서버의 접근 권한을 바꾸는 명령어입니다. 운영 서버에서는 범위를 넓게 여는 chmod 777을 임시 해결책처럼 사용하지 않는 편이 좋습니다. 어떤 사용자에게 어떤 권한이 필요한지 확인한 뒤 최소 범위로 조정해야 합니다.
실제로 자주 막히는 상황 2: 파일은 있는데 수정이 안 될 때
설정 파일을 수정하려는데 저장이 되지 않거나 Permission denied가 나올 수 있습니다.
whoami
ls -l /etc/myapp/config.json
deploy
-rw-r--r-- 1 root root 512 May 11 10:10 /etc/myapp/config.json
현재 사용자는 deploy인데 파일 소유자는 root입니다. 권한은 -rw-r--r--이므로 소유자인 root만 수정할 수 있고, 일반 사용자는 읽기만 가능합니다.
이 경우 무조건 소유자를 바꾸는 것이 답은 아닙니다. /etc 아래 설정 파일은 root 소유가 정상인 경우가 많습니다. 운영 정책에 따라 sudoedit로 수정하거나, 애플리케이션 전용 설정 파일 위치를 따로 두는 방식이 더 적절할 수 있습니다.
sudoedit /etc/myapp/config.json
sudoedit는 sudo 권한이 있는 사용자에게만 동작합니다. sudo 권한이 없는 서버에서는 관리자에게 권한 정책을 확인해야 합니다. 무리하게 소유자나 권한을 바꾸면 패키지 업데이트나 서비스 동작에 영향을 줄 수 있습니다.
실제로 자주 막히는 상황 3: 상위 디렉터리 권한 때문에 접근이 막힐 때
파일 권한만 보면 읽을 수 있어 보이는데 실제로 접근이 안 되는 경우가 있습니다. 이때는 상위 디렉터리 권한을 봐야 합니다. 디렉터리는 파일과 달리 x 권한이 있어야 그 안으로 진입할 수 있습니다.
ls -l /srv/myapp/public/index.html
ls -ld /srv /srv/myapp /srv/myapp/public
-rw-r--r-- 1 deploy www-data 1240 May 11 10:20 /srv/myapp/public/index.html
drwxr-xr-x 3 root root 4096 May 11 10:00 /srv
drwx------ 5 deploy deploy 4096 May 11 10:05 /srv/myapp
drwxr-xr-x 2 deploy www-data 4096 May 11 10:20 /srv/myapp/public
index.html 자체는 rw-r--r--라서 읽을 수 있어 보입니다. 하지만 /srv/myapp이 drwx------이면 소유자 deploy 외에는 이 디렉터리를 통과할 수 없습니다. Nginx가 www-data 사용자로 실행 중이라면 파일까지 도달하지 못할 수 있습니다.
경로 전체 권한을 한 번에 보고 싶다면 namei -l도 유용합니다.
namei -l /srv/myapp/public/index.html
단, 디렉터리 권한을 바꾸면 해당 경로 아래의 서비스 접근 범위가 달라질 수 있습니다. 웹 루트, 업로드 디렉터리, 애플리케이션 소스 디렉터리는 목적에 따라 필요한 권한이 다르므로 변경 전에 현재 소유자와 실행 사용자를 확인해야 합니다.
원인 분리: 파일, 사용자, 그룹, 경로, 서비스 계정
권한 문제를 빠르게 분리하려면 아래 순서로 봅니다.
- 현재 사용자 확인: 지금 명령을 실행하는 사용자가 누구인지 봅니다.
- 파일 소유자 확인: 파일의 소유자와 그룹이 누구인지 봅니다.
- 권한 문자열 확인: 현재 사용자가 소유자, 그룹, 기타 사용자 중 어디에 해당하는지 판단합니다.
- 상위 디렉터리 확인: 파일까지 도달할 수 있는지 확인합니다.
- 서비스 실행 계정 확인: systemd, Nginx, PM2 등 실제 프로세스의 사용자와 비교합니다.
whoami
id
ls -l /var/www/html/index.html
ls -ld /var /var/www /var/www/html
ps -o user,group,comm -C nginx
Nginx가 실행 중인 환경에서는 보통 워커 프로세스가 www-data 사용자로 동작합니다. 다만 배포판이나 설정에 따라 다를 수 있으므로 ps 출력이나 Nginx 설정을 직접 확인하는 것이 좋습니다.
잘못된 예시: 권한을 넓게 열어서 해결하려는 경우
권한 오류가 나면 아래처럼 모든 사용자에게 읽기·쓰기·실행 권한을 주고 싶어질 수 있습니다.
chmod 777 uploads
하지만 이 방식은 운영 서버에서 권장하기 어렵습니다. 당장은 업로드나 실행이 되는 것처럼 보여도, 다른 사용자나 프로세스가 해당 경로에 파일을 쓰거나 수정할 수 있는 범위가 지나치게 넓어집니다. 특히 웹에서 접근 가능한 디렉터리에 실행 권한까지 넓게 부여하면 보안 위험이 커질 수 있습니다.
더 나은 접근은 필요한 계정과 그룹을 확인한 뒤, 해당 목적에 맞게 최소 권한을 부여하는 것입니다.
수정 예시: 웹 업로드 디렉터리에 필요한 권한만 맞추기
예를 들어 웹 애플리케이션이 /var/www/myapp/uploads에 파일을 저장해야 한다고 가정하겠습니다. 먼저 현재 상태를 확인합니다.
whoami
ls -ld /var/www/myapp /var/www/myapp/uploads
ps -o user,group,comm -C nginx
deploy
drwxr-xr-x 5 deploy www-data 4096 May 11 10:00 /var/www/myapp
drwxr-xr-x 2 deploy www-data 4096 May 11 10:30 /var/www/myapp/uploads
USER GROUP COMMAND
root root nginx
www-data www-data nginx
Nginx 또는 PHP-FPM, 애플리케이션 프로세스가 www-data로 파일을 써야 하는 구조라면 그룹 쓰기 권한이 필요할 수 있습니다. 이때는 전체를 열기보다 해당 디렉터리의 그룹과 권한을 목적에 맞게 조정합니다.
sudo chgrp www-data /var/www/myapp/uploads
sudo chmod 775 /var/www/myapp/uploads
ls -ld /var/www/myapp/uploads
drwxrwxr-x 2 deploy www-data 4096 May 11 10:30 /var/www/myapp/uploads
sudo chgrp와 chmod는 서버 동작에 영향을 줄 수 있습니다. 적용 전에는 해당 디렉터리가 실제로 업로드 전용인지, 소스 코드나 설정 파일이 섞여 있지 않은지 확인해야 합니다. 웹 서버 구성에 따라서는 www-data가 아니라 다른 사용자로 실행될 수 있으므로, 반드시 현재 프로세스 사용자부터 확인해야 합니다.
수정 후 확인 방법
권한을 바꾼 뒤에는 명령이 성공했는지만 보지 말고 실제로 문제가 해결됐는지 확인해야 합니다.
1. 권한 문자열 다시 확인
ls -l deploy.sh
ls -ld /var/www/myapp/uploads
2. 현재 사용자 기준으로 실행 또는 쓰기 테스트
whoami
./deploy.sh
업로드 디렉터리처럼 서비스 계정이 쓰는 경로라면 단순히 SSH 사용자로 테스트하는 것만으로는 부족할 수 있습니다. 실제 웹 요청, 애플리케이션 로그, 서비스 로그를 함께 확인해야 합니다.
3. systemd 서비스 로그 확인
systemctl status myapp.service --no-pager
journalctl -u myapp.service -n 50 --no-pager
서비스가 계속 실패한다면 재시작을 반복하기 전에 로그에서 Permission denied, EACCES, cannot open, failed to write 같은 메시지를 먼저 찾습니다. 화면에 보이는 오류보다 로그가 원인 분리에 더 직접적인 단서를 줄 때가 많습니다.
4. Nginx 403이라면 에러 로그 확인
sudo tail -n 50 /var/log/nginx/error.log
Nginx 설정을 바꿔야 하는 상황이라면 바로 reload하지 말고 먼저 문법 검사를 해야 합니다.
sudo nginx -t
설정 검사가 통과한 경우에만 reload를 검토합니다. 이 글의 주제는 파일 권한 확인이므로, Nginx 설정 변경은 필요한 경우에만 별도로 점검하는 것이 좋습니다.
권한을 읽을 때 자주 놓치는 세부 기준
디렉터리의 x 권한은 실행이 아니라 진입 권한
파일에서 x는 실행 권한이지만, 디렉터리에서 x는 그 디렉터리 안으로 들어가거나 경로를 통과할 수 있는 권한에 가깝습니다. 파일 권한이 맞아도 중간 디렉터리에 x가 없으면 접근이 실패할 수 있습니다.
그룹 권한은 id 출력과 함께 봐야 함
파일 그룹이 www-data라고 해서 현재 사용자가 자동으로 그 권한을 쓰는 것은 아닙니다. 현재 사용자가 그 그룹에 속해 있는지 id로 확인해야 합니다.
id deploy
심볼릭 링크는 링크 대상 권한도 확인해야 함
ls -l에서 맨 앞 글자가 l이면 심볼릭 링크입니다. 링크 자체가 아니라 최종 대상 파일과 디렉터리 권한도 확인해야 합니다.
ls -l /var/www/html/current
readlink -f /var/www/html/current
SELinux, AppArmor 같은 보안 정책은 환경에 따라 별도로 영향을 줄 수 있음
Ubuntu/Debian 서버에서는 AppArmor 정책이 영향을 줄 수 있고, CentOS/RHEL 계열에서는 SELinux가 관련될 수 있습니다. 다만 모든 권한 오류가 보안 정책 때문은 아닙니다. 먼저 일반 파일 권한, 소유자, 그룹, 상위 디렉터리, 서비스 계정을 확인한 뒤에도 설명되지 않을 때 추가로 보는 편이 좋습니다.
재발 방지 체크리스트
- 파일 권한을 바꾸기 전에
whoami,id,ls -l결과를 먼저 확인했는가? - 현재 사용자가 파일의 소유자인지, 그룹에 속하는지, 기타 사용자로 취급되는지 판단했는가?
- 파일뿐 아니라 상위 디렉터리의
x권한까지 확인했는가? - 스크립트 파일에 필요한 실행 권한만 추가했는가?
chmod 777처럼 과도하게 권한을 여는 방식으로 처리하지 않았는가?- Nginx, systemd, PM2 등 실제 서비스를 실행하는 계정을 확인했는가?
- 권한 변경 후
ls -l, 서비스 로그, 실제 요청 테스트로 결과를 확인했는가? - 운영 서버의 설정 파일이나 시스템 디렉터리는 소유자 변경 전에 영향 범위를 확인했는가?
리눅스 파일 권한은 문자열 자체보다 현재 사용자가 어느 권한 구간에 해당하는지 판단하는 것이 핵심입니다. -rwxr-x---를 외우는 것에서 끝내지 말고, 소유자·그룹·기타 사용자 중 어디에 해당하는지, 그리고 상위 경로를 통과할 수 있는지까지 함께 봐야 실제 문제를 줄일 수 있습니다.
FAQ
Q1. 파일 권한이 755인데도 Permission denied가 나오는 이유는 무엇인가요?
755이면 파일 자체는 소유자에게 쓰기·실행, 그룹과 기타 사용자에게 읽기·실행 권한이 있습니다. 그런데도 실패한다면 상위 디렉터리에 진입 권한이 없거나, 파일이 있는 파티션이 실행을 막는 옵션으로 마운트되어 있거나, 서비스 실행 계정이 예상과 다를 수 있습니다. 먼저 whoami, id, ls -ld, namei -l로 경로 전체를 확인하는 것이 좋습니다.
Q2. 스크립트 파일은 무조건 755로 바꾸면 되나요?
항상 그렇지는 않습니다. 혼자 실행하는 관리용 스크립트라면 chmod u+x script.sh처럼 소유자에게만 실행 권한을 주는 것으로 충분할 수 있습니다. 여러 사용자가 실행해야 하는 경우에는 그룹 권한을 설계한 뒤 부여하는 편이 안전합니다. 운영 서버에서는 필요한 대상에게만 최소 권한을 주는 방식이 좋습니다.
Q3. 파일 소유자가 root이면 chown으로 바꿔도 되나요?
파일 위치와 목적에 따라 다릅니다. /etc, /usr, 패키지가 관리하는 경로의 파일은 root 소유가 정상인 경우가 많습니다. 이런 파일의 소유자를 바꾸면 업데이트나 서비스 동작에 문제가 생길 수 있습니다. 변경 전에는 왜 root 소유인지, 어떤 서비스가 읽거나 쓰는지 확인하고, 필요하면 sudoedit이나 전용 설정 경로를 사용하는 것이 안전합니다.