nano 저장 권한 거부 해결 방법
nano에서 Permission denied 오류가 발생하는 4가지 원인을 정리했습니다. 파일 소유권, 상위 디렉터리 권한, 읽기 전용 파일 시스템, 컨테이너 내 UID 문제를 순서대로 점검하여 문제를 해결하십시오. 데이터 손실 없이 안전하게 파일을 저장하는 방법도 함께 안내합니다.
nano에서 파일을 저장할 수 없는 이유
nano에서 파일을 저장할 수 없는 이유는 다음 네 가지 중 하나입니다. 파일의 소유자가 아니거나, 상위 디렉터리에서 nano의 작업을 허용하지 않거나, 파일 시스템이 읽기 전용이거나 공간이 부족하거나, 혹은 다른 사용자 ID로 실행 중인 컨테이너 내부에 있기 때문입니다. 앞의 두 가지는 권한 문제이며, 뒤의 두 가지는 그렇지 않습니다. 이 순서대로 확인하십시오. 첫 번째 이유가 대부분의 사례를 차지하며, 명령 한 번으로 확인이 가능하고 해결 방법 또한 sudoedit가 sudo nano보다 적절하기 때문입니다.
편집기가 열려 있는 동안에는 데이터가 손실되지 않습니다. 텍스트는 메모리에 남아 있으므로 파일을 열어둔 채로 소유권이 있는 경로에 버퍼를 저장한 뒤, 나중에 해당 위치로 옮길 수 있습니다. 이 탈출구는 본 가이드의 마지막 부분에서 다룹니다.
권한을 변경하기 전에 다음 점검을 수행하십시오
각 명령어를 편집 중인 실제 경로에 적용하십시오. 이 명령어들은 서로 다른 정보를 제공하므로, 무언가를 수정하기 전에 모두 실행해야 합니다. 어떤 점검에서 실패가 발생하는지 모르는 상태에서 권한을 변경하면, 보통 기존 문제에 더해 새로운 문제가 추가로 발생합니다.
id
ls -l /etc/nginx/nginx.conf
ls -ld /etc/nginx
namei -l /etc/nginx/nginx.conf
findmnt -no SOURCE,FSTYPE,OPTIONS -T /etc/nginx/nginx.conf
df -h /etc/nginx
df -i /etc/nginxid는 현재 사용자의 ID와 그룹 ID를 출력합니다. ls -l은 파일 자체의 소유자, 그룹, 권한 비트를 보여줍니다. ls -ld은 해당 파일을 포함하는 디렉터리에 대해 동일한 정보를 보여주며, 이는 파일과는 별개의 확인 사항입니다. namei -l은 경로의 모든 구성 요소를 따라가며 각 부분의 소유자와 권한을 나열하므로, 두 가지 질문에 대한 답을 한 번에 확인할 수 있습니다. findmnt는 해당 경로의 파일 시스템 이름과 마운트 옵션을 보여줍니다. df -h은 여유 공간을 보고하며, df -i은 여유 inode를 보고합니다. inode는 디스크 공간과는 별도로 고갈될 수 있습니다. 만약 권한 문자열이 익숙하지 않다면, ls -l이 출력하는 권한 문자열을 읽는 방법부터 확인하십시오.
원인 1: 파일 소유자가 root이며 사용자가 root가 아님
읽기 권한과 쓰기 권한은 별개이며, /etc 하위의 대부분 파일은 누구나 읽을 수 있습니다. 이것이 nano가 파일을 열어 내용을 보여주고 자유롭게 타이핑할 수 있게 하는 이유입니다. 이 과정 중 어느 것도 디스크에 영향을 주지 않습니다. 거부 메시지는 저장 시점에 발생하며, 이때 커널은 사용자의 ID 및 그룹 ID와 파일의 소유자, 그룹, 기타 권한 비트를 비교합니다. nano는 커널이 전달한 내용을 그대로 보여주는 것이므로, 어떤 nano 옵션을 사용해도 결과는 바뀌지 않습니다.
id 및 ls -l를 함께 사용하면 이 문제를 해결할 수 있습니다. 파일의 소유자가 root이고 사용자가 root가 아니며, 기타 권한 비트에 쓰기 권한이 부여되어 있지 않기 때문입니다. Ctrl-O를 다시 누르는 것은 도움이 되지 않습니다.
root 소유 파일을 편집할 때 sudoedit을 사용해야 하는 이유
SUDO_EDITOR=nano sudoedit /etc/nginx/nginx.confsudo는 파일을 임시로 복사하여 소유자를 사용자 본인으로 변경한 뒤, 해당 복사본을 일반 사용자 권한으로 nano에서 실행합니다. 편집기가 종료되면 root 권한으로 결과물을 원본 위치에 덮어씁니다. 즉, 편집기 자체가 root 권한으로 실행되지 않습니다. sudo -e는 같은 명령어를 다른 이름으로 부르는 것입니다. 편집기는 SUDO_EDITOR, VISUAL, EDITOR 순서로 결정되므로, 셸 프로필에 export EDITOR=nano를 설정하면 어디서든 기본 편집기로 사용할 수 있습니다. sudoers 설정에서 env_editor 플래그가 비활성화되어 있다면 해당 환경 변수는 무시되며, 대신 sudoers의 editor 설정에 따라 편집기가 선택됩니다.
sudo nano를 사용하면 파일을 저장할 때 문제가 발생합니다. 세션이 유지되는 동안 대화형 편집기 전체에 파일 시스템에 대한 root 권한이 부여되기 때문입니다. 따라서 저장 프롬프트에서 경로를 잘못 입력하면 root 권한으로 다른 시스템 파일을 덮어쓰게 될 위험이 있습니다. 필요한 단계에서만 sudo를 호출하는 일반 사용자로 작업하는 습관을 들이는 것이 좋으며, 설정 파일을 편집할 때 이 습관을 실천하는 방법이 바로 sudoedit입니다.
sudoedit에는 사용자들을 놀라게 하는 두 가지 규칙이 있습니다. 첫째, 심볼릭 링크 편집을 거부합니다. 둘째, 사용자가 쓰기 권한을 가진 디렉터리 내부의 파일은 root가 아닌 이상 편집을 거부합니다. 두 번째 규칙은 디렉터리에 쓰기 권한이 있는 누군가가 편집기가 열려 있는 동안 파일을 교체할 수 있기 때문에 존재합니다. 두 동작 모두 sudoers의 기본값(sudoedit_follow off, sudoedit_checkdir on)입니다. 아직 존재하지 않는 파일은 새로 생성됩니다.
원인 2: 상위 디렉터리가 실제로 제어하는 것
다른 편집기를 위한 조언에서는 편집기가 새 파일을 작성한 뒤 기존 파일 위로 이름을 바꾸는 방식으로 저장하는 경우가 많으므로, 디렉터리에 쓰기 권한이 필요하다고 설명합니다. nano는 그런 방식으로 작동하지 않습니다. nano는 지정한 파일을 열어 그 파일에 직접 내용을 쓰므로, 이미 존재하는 파일의 경우 디렉터리의 쓰기 비트를 확인하지 않습니다.
그럼에도 디렉터리는 다른 사항들을 결정하며, 이것이 체크리스트에 ls -ld가 포함된 이유입니다.
- 아직 존재하지 않는 파일을 생성하려면 디렉터리에 쓰기 및 실행 권한이 필요합니다. 새로운 이름을 추가해야 하기 때문입니다. umask는 새 파일이 시작될 때의 권한을 결정합니다.
- 파일에 접근하려면 경로상의 모든 디렉터리에 실행 권한(검색 권한이라고도 함)이 있어야 합니다. 권한이 없는 디렉터리가 하나라도 있으면 그 아래의 모든 파일에 접근할 수 없으며,
namei -l을 통해 어느 디렉터리가 문제인지 확인할 수 있습니다. - 백업 기능을 사용하거나 파일 잠금을 켠 상태로 저장하면 원본 파일 옆에 두 번째 파일이 생성되므로, 이러한 기능은 쓰기 가능한 디렉터리를 필요로 합니다. 백업은
-B옵션 또는 nanorc의set backup설정이며, 잠금은-G또는set locking입니다. 사용자나 배포판에서 활성화하지 않았다면 기본적으로 꺼져 있습니다.
디렉터리 권한은 시스템의 다른 곳에서도 동일한 비중을 가집니다. SSH 서버는 홈 디렉터리나 .ssh 디렉터리에 다른 사용자가 쓰기 권한을 가질 경우 키를 거부하며, 이는 로그인 시 SSH가 키를 거부하는 흔한 원인 중 하나입니다.
nano는 이미 존재하는 파일에 내용을 쓰기 때문에 파일은 inode를 유지합니다. inode는 이름 뒤에 숨겨진 디스크상의 실제 식별자입니다. 파일을 열어둔 프로세스는 계속해서 해당 파일을 추적하며, 컨테이너에 바인드 마운트된 단일 파일도 정상적으로 작동합니다. 파일을 교체하는 방식으로 저장하는 편집기는 마운트를 끊어버립니다. 마운트는 이름이 아닌 inode를 따라가기 때문입니다.
원인 3: 파일 시스템이 읽기 전용이거나 공간이 부족함
findmnt 옵션에 ro이 포함되어 있다면 쓰기 작업은 처음부터 성공할 수 없습니다. 파일 시스템이 /etc/fstab를 통해 그렇게 마운트되었거나 읽기 전용 바인드 마운트가 적용된 경우, 혹은 디스크 오류로 인해 커널이 읽기 전용으로 다시 마운트한 경우입니다. 후자는 심각한 상황입니다. sudo dmesg -T | tail -50에는 다시 마운트하게 된 입출력 및 파일 시스템 오류가 기록되어 있으며, 해결 방법은 마운트 해제 후 파일 시스템을 검사하는 것입니다. VPS 환경이라면 제공업체의 복구 콘솔로 부팅해야 합니다.
파일 시스템이 가득 차면 다른 이유로 쓰기 작업이 실패합니다. df -h은 일반적인 경우를 다룹니다. df -i은 흔히 놓치는 경우를 다룹니다. inode는 파일 시스템 생성 시 고정된 풀에서 할당되므로, 아주 작은 파일이 많으면 df -h상으로는 기가바이트 단위의 여유 공간이 보여도 inode가 모두 소진될 수 있습니다. 공간이 부족한데 원인을 알 수 없다면 df와 du의 디스크 사용량 불일치를 참고하여 삭제되었으나 여전히 열려 있는 파일이 있는지 확인하십시오.
이와 관련하여 혼란스러운 증상 하나를 설명합니다. ext4는 파일 시스템 생성 시 루트 사용자를 위해 블록의 일부를 예약해 둡니다. 따라서 일반 사용자의 쓰기가 거부되어도 루트 사용자는 계속 쓸 수 있습니다. 이때 sudo를 실행하면 문제가 해결된 것처럼 보이지만, 디스크가 나머지 공간까지 모두 차버리면서 더 심각한 형태로 문제가 재발합니다.
nano는 파일 내용을 쓰기 전에 기존 파일을 잘라내기(truncate) 때문에, 쓰기 도중 공간이 부족해지면 파일이 이전보다 짧아질 수 있습니다. 파일 시스템 용량이 거의 찼을 때 설정을 편집하려면 먼저 해당 파일을 복사해 두십시오. sudo cp -a /etc/nginx/nginx.conf /root/nginx.conf.bak을 사용하면 복사본의 소유자, 그룹, 권한이 그대로 유지됩니다.
원인 4: 컨테이너 내부에서 바인드 마운트된 파일을 수정하는 경우
파일 소유권은 숫자로 관리됩니다. 커널은 사용자 ID를 저장하며, 사용자가 보는 이름은 조회 시점에 어떤 /etc/passwd을 사용하는지에 따라 달라집니다. 따라서 하나의 파일이 호스트에서는 특정 이름으로 보이고, 컨테이너 내부에서는 다른 이름이나 단순한 숫자로 보일 수 있습니다. 이름을 비교하지 말고 숫자를 비교하십시오. 컨테이너 내부에서 id -u를 실행하고 파일에 대해 ls -ln을 실행하여 확인합니다.
바인드 마운트된 파일은 호스트에서의 소유권을 그대로 유지합니다. 호스트 파일의 소유자가 현재 사용자이고 컨테이너 프로세스가 다른 사용자로 실행 중이라면, 컨테이너 내부에서의 쓰기 작업은 거부됩니다. 또한 컨테이너 내부에서 sudo를 실행해도 호스트의 파일 소유자는 변경되지 않습니다. 이 문제는 호스트에서 컨테이너가 실행되는 ID로 소유자를 변경하거나, 파일 소유자와 동일한 ID로 컨테이너를 실행하여 해결하십시오. linuxserver.io와 같은 프로젝트에서 제공하는 이미지는 프로세스가 실행될 사용자를 지정하는 PUID 및 PGID 변수를 제공합니다.
알아두어야 할 두 가지 컨테이너 사례가 더 있습니다. :ro 옵션으로 읽기 전용으로 마운트되었거나 --read-only 옵션으로 시작된 컨테이너는 소유권과 관계없이 쓰기를 거부하며, 컨테이너 내부에서 cat /proc/mounts을 실행하면 해당 플래그가 표시됩니다. 루트 권한이 없는 Podman의 경우, 사용자 네임스페이스가 컨테이너의 사용자 ID를 호스트의 특정 ID 범위로 매핑합니다. 따라서 컨테이너 내부에서 root 소유로 보이는 파일이 호스트 외부에서는 일반 사용자 계정의 소유가 됩니다.
수정 작업이 성공한 것처럼 보이지만 사라지는 경우도 있습니다. 마운트 지점이 아닌 경로의 파일을 컨테이너 내부에서 수정하면 해당 변경 사항은 컨테이너의 쓰기 가능 레이어에 저장되며, 컨테이너가 재생성될 때 해당 레이어는 삭제됩니다. 변경 사항을 영구적으로 유지하려면 마운트의 호스트 측에서 파일을 수정하거나 이미지 빌드 과정에 반영하십시오.
탈출구: 소유권이 있는 경로에 저장하기
편집기 내부에서 권한을 상승시키려 하지 마십시오. Ctrl-O를 누르고 프롬프트에 나타난 경로를 지운 뒤, /home/you/nginx.conf.new와 같이 홈 디렉터리 하위의 경로를 입력하고 Enter를 누르십시오. 그 후 Ctrl-X를 눌러 종료합니다. 이제 작업 내용이 디스크에 저장되었으며 소유권은 사용자에게 있습니다. 나머지는 일반적인 파일 복사 작업입니다.
sudo cp /home/you/nginx.conf.new /etc/nginx/nginx.conf
sudo nginx -t여기서는 mv 대신 cp를 사용하십시오. cp은 기존 파일을 그대로 둔 채 내용을 덮어쓰므로, 파일의 소유자, 그룹, 권한이 유지됩니다. 동일한 파일 시스템에서 mv를 사용하면 기존 파일이 사용자의 파일로 교체됩니다. 이 경우 /etc에 있는 설정 파일의 소유자가 사용자 계정으로 바뀌게 되며, 이는 곧 해결해야 할 또 다른 권한 문제로 이어집니다.
무엇인가를 다시 불러오기 전에 해당 파일을 관리하는 도구로 결과를 확인하십시오. sudo nginx -t는 Nginx 설정을 구문 분석하며, sudo sshd -t는 SSH 서버 설정을 구문 분석합니다. 두 파일은 이 전체 과정을 대신 수행해 주는 전용 편집기를 가지고 있습니다. /etc/sudoers을 위한 sudo visudo과 사용자의 cron 작업을 위한 crontab -e이 그것입니다. 각 편집기는 임시 복사본을 수정하고 구문을 검사한 뒤, 구문 분석이 성공할 때만 파일을 설치합니다.
FAQ
시스템 파일을 편집할 때 sudo nano와 sudoedit 중 무엇을 사용해야 합니까?
sudoedit를 사용하십시오. 이 명령은 파일을 사용자 소유의 임시 복사본으로 만든 뒤 사용자의 권한으로 편집기를 실행합니다. 편집기가 종료되면 root 권한으로 결과를 다시 기록하므로, 편집기 자체가 root 권한을 가질 필요가 없습니다. nano를 선택하려면 SUDO_EDITOR, VISUAL 또는 EDITOR를 nano으로 설정하십시오. sudo nano를 사용할 수도 있지만, 이 방식은 세션 동안 대화형 편집기에 시스템의 모든 경로에 대한 root 접근 권한을 부여합니다. 따라서 저장 프롬프트에서 파일 이름을 한 글자라도 잘못 입력하면 시스템 파일이 손상될 수 있습니다.
nano로 파일을 저장하려면 디렉터리에 쓰기 권한이 있어야 합니까?
이미 존재하는 파일이라면 그렇지 않습니다. nano는 파일 자체에 직접 쓰기를 수행하므로, 커널은 파일의 쓰기 비트와 해당 경로에 있는 모든 디렉터리의 실행 비트를 확인합니다. 디렉터리의 쓰기 비트가 중요한 경우는 파일이 아직 존재하지 않아 새로운 이름을 생성해야 할 때, 그리고 백업이나 파일 잠금 기능이 켜져 있어 원본 옆에 두 번째 파일을 생성해야 할 때입니다.
소유자 정보가 정확하고 디스크 용량도 충분합니다. 무엇이 쓰기를 방해할 수 있습니까?
네 가지 원인이 있습니다. 첫째, 파일 시스템이 읽기 전용으로 마운트되었을 수 있으며, 이는 findmnt -no OPTIONS -T /etc/nginx/nginx.conf로 확인할 수 있습니다. 둘째, 파일에 변경 불가능(immutable) 속성이 설정되었을 수 있습니다. 이는 lsattr으로 확인하고 sudo chattr -i로 제거할 수 있으며, 이 속성이 설정되어 있으면 root조차 파일을 수정할 수 없습니다. 셋째, 여유 공간은 남아있지만 inode 풀이 고갈되었을 수 있으며, 이는 df -i로 확인합니다. 넷째, 권한 비트상으로는 허용되더라도 SELinux나 AppArmor가 쓰기를 거부할 수 있으며, 이때는 감사 로그(audit log)에 해당 경로에 대한 거부 기록이 남습니다.
파일이 전혀 저장되지 않을 때는 변경 사항을 어디에 두어야 합니까?
Ctrl-O를 누른 뒤 홈 디렉터리 아래나 사용자가 쓰기 권한을 가진 다른 경로 등 본인 소유의 경로를 지정하십시오. 버퍼는 여전히 메모리에 있으므로 입력한 내용은 사라지지 않습니다. 이후 sudo cp를 사용하여 저장한 파일을 원래 위치로 복사하십시오. 이 명령은 원본 파일의 소유자와 권한을 유지합니다. 그 후 서비스를 다시 로드하기 전에 해당 서비스의 자체 테스트 명령으로 설정을 검증하십시오.
Docker 컨테이너 내부에서 수정한 내용이 왜 사라집니까?
해당 경로가 마운트 지점이 아닐 경우, 수정 사항은 컨테이너의 쓰기 가능 계층(writable layer)에 저장되며 컨테이너가 교체될 때 해당 계층은 폐기됩니다. 바인드 마운트나 볼륨의 호스트 측 파일을 편집하거나, 이미지 빌드 과정에 해당 파일을 포함시키십시오. 경로가 바인드 마운트인데도 저장이 거부된다면, 컨테이너 내부의 id -u 결과와 ls -ln에서 확인한 숫자 형태의 소유자 정보를 비교해 보십시오. 파일은 호스트의 소유권을 유지하므로, 컨테이너 내부 프로세스가 이를 일치시켜야 합니다.