Linux 파일 권한: chmod, chown 및 umask

1. 개요

Permission denied 오류는 Linux에 애플리케이션을 배포할 때 가장 흔한 오류 중 하나입니다. 애플리케이션이 로그를 쓸 수 없거나 설정 파일을 읽을 수 없거나, 민감한 파일이 모든 사용자에게 읽힐 수 있습니다.

이 글은 Linux의 파일 권한 모델과 세 가지 주요 도구의 사용법을 설명합니다: chmod (권한 변경), chown (소유자 변경), umask (새 파일의 기본 권한 제어). 이 글을 읽은 후, 모든 파일의 권한을 읽을 수 있고, 필요한 수준의 권한을 부여할 수 있으며, Java에서 POSIX 권한을 조작할 수 있습니다.

2. ls -l로 파일 권한 읽기

예시 디렉토리부터 시작해봅시다:

mkdir -p shop && cd shop
echo "id,total" > order.csv
mkdir reports
ls -l
-rw-r--r-- 1 an dev 9    Oct 13 09:00 order.csv
drwxr-xr-x 2 an dev 4096 Oct 13 09:00 reports

첫 번째 열은 10개의 문자로 이루어져 있습니다:

  • 첫 번째 문자는 파일 유형입니다: -는 일반 파일, d는 디렉토리, l은 심볼릭 링크입니다.
  • 나머지 9개 문자는 3개씩 3개 그룹으로 나뉩니다: user (소유자), group (그룹), other (그 외 사용자).
  • 각 그룹은 r (읽기), w (쓰기), x (실행)로 구성됩니다. -는 해당 권한이 없음을 의미합니다.

3번째와 4번째 열은 소유자(an)와 그룹(dev)입니다. 따라서 order.csv의 경우, an은 읽고 쓸 수 있고, dev 그룹의 멤버는 읽기만 가능하며, 다른 사용자도 읽기만 가능합니다.

8진수 형태로 권한을 보려면 stat을 사용합니다:

stat -c '%a %A %U:%G %n' order.csv reports
644 -rw-r--r-- an:dev order.csv
755 drwxr-xr-x an:dev reports

커널은 순서대로 권한을 확인합니다. 사용자가 소유자이면 user 권한 그룹만 적용됩니다. 소유자가 아니지만 그룹에 속하면 group 권한 그룹만 적용됩니다. 그 외에는 other 권한을 사용합니다. 권한 그룹은 누적되지 않습니다. 따라서 ---r--r-- 권한을 가진 파일은 소유자조차 읽을 수 없습니다. 다른 사람들이 읽을 수 있더라도 말입니다.

3. chmod로 권한 변경

3.1. 기호 형식

기호 형식의 구문은 [ugoa][+-=][rwxX]입니다. 여기서 u는 user, g는 group, o는 other, a는 모두입니다.

chmod g+w order.csv      # 그룹에 쓰기 권한 추가
chmod o-r order.csv      # other의 읽기 권한 제거
chmod u=rw,go= order.csv # user: rw, group과 other: 권한 없음
stat -c '%a %A' order.csv
600 -rw-------

연산자 +와 -는 명시된 권한만 추가하거나 제거하고, 다른 권한은 유지합니다. 연산자 =는 해당 그룹의 모든 권한을 다시 설정합니다. 현재 권한을 알 필요 없이 일부만 수정하려면 기호 형식이 안전합니다.

3.2. 8진수 형식

각 권한에는 값이 있습니다: r = 4, w = 2, x = 1. 값을 더하면 각 그룹마다 한 자리가 나옵니다.

값 권한 사용
7 rwx 디렉토리 또는 스크립트의 소유자
6 rw- 데이터 파일의 소유자
5 r-x 읽기 및 디렉토리 진입, 스크립트 실행
4 r-- 읽기 전용
0 --- 권한 없음

예를 들어 스크립트와 암호가 포함된 파일을 만들어봅시다:

printf '#!/bin/bash\necho "주문 백업 중..."\n' > backup.sh
echo "db.password=s3cret" > secret.properties

chmod 755 backup.sh
chmod 600 secret.properties
./backup.sh
stat -c '%a %n' backup.sh secret.properties
주문 백업 중...
755 backup.sh
600 secret.properties

755는 모든 사용자가 스크립트를 실행할 수 있게 하지만 소유자만 수정할 수 있습니다. 600은 소유자만 민감한 파일을 읽고 쓸 수 있게 합니다. 8진수 형식은 모든 권한을 한 번에 설정하므로 정확한 원하는 상태를 알고 있을 때 적합합니다.

3.3. 재귀적 권한 변경과 대문자 X

일반적인 실수는 다음 명령을 실행하는 것입니다:

chmod -R 755 shop/

이 명령은 order.csv를 포함한 모든 파일에 실행 권한을 부여합니다. 올바른 방법은 대문자 X를 사용하는 것입니다. 이 기호는 디렉토리에만 x를 추가하거나, 이미 어떤 그룹에서 x를 가지고 있는 파일에만 추가합니다:

chmod -R u=rwX,go=rX shop/

결과적으로 디렉토리는 755 권한을 가지고, 일반 파일은 644 권한을 가지며, backup.sh는 755 권한을 유지합니다. 더 엄격하게 제어하려면 find를 사용하여 분리할 수 있습니다:

find shop -type d -exec chmod 750 {} +
find shop -type f -exec chmod 640 {} +

4. 디렉토리의 권한은 파일과 다릅니다

디렉토리의 경우 rwx는 다른 의미를 가집니다:

  • r: 내부 항목의 이름 나열 (ls).
  • w: 내부의 항목 생성, 삭제, 이름 변경. 이 권한은 x와 함께 있어야 효과가 있습니다.
  • x: 디렉토리 진입 (cd) 및 이름을 아는 항목에 접근.

놀라운 결과: 파일 삭제는 파일의 권한이 아니라 디렉토리의 권한에 따라 달라집니다.

mkdir inbox && echo "data" > inbox/invoice.txt
chmod 444 inbox/invoice.txt   # 파일 읽기 전용
rm -f inbox/invoice.txt       # inbox에 w 권한이 있으므로 여전히 삭제 가능
ls inbox                      # 비어있음

반대로 디렉토리에서 x 권한을 제거하면 파일이 644 권한을 가지고 있더라도 내부 파일을 읽을 수 없습니다:

echo "data" > inbox/invoice.txt
chmod 644 inbox/invoice.txt
chmod u-x inbox
cat inbox/invoice.txt
cat: inbox/invoice.txt: Permission denied

애플리케이션이 Permission denied를 보고하지만 파일 권한이 올바르게 보이면 경로상의 모든 상위 디렉토리를 확인하세요. namei -l /path/to/file 명령은 각 레벨의 권한을 나열합니다. 계속하기 전에 chmod u+x inbox로 디렉토리 권한을 복원하세요.

5. chown과 chgrp로 소유자 변경

chown은 소유자, 그룹 또는 둘 다를 변경합니다:

sudo chown appuser order.csv          # 소유자만 변경
sudo chown appuser:dev order.csv      # 소유자와 그룹 모두 변경
sudo chown :dev order.csv             # 그룹만 변경
sudo chown -R appuser:appuser /opt/shop

기억해야 할 몇 가지 사항:

  • 오직 root만 소유자를 변경할 수 있습니다. 일반 사용자는 자신의 파일을 다른 사람에게 “양도”할 수 없습니다.
  • 일반 사용자는 자신이 소유한 파일의 그룹을 변경할 수 있지만, 자신이 속한 그룹으로만 변경할 수 있습니다. 사용할 명령은 chgrp dev order.csv입니다.
  • chown -R은 디렉토리 트리를 따릅니다. 심볼릭 링크를 만날 때 기본적으로 링크가 가리키는 파일의 소유자를 변경합니다. 링크 자체의 소유자를 변경하려면 -h를 추가하세요.

배포할 때 일반적인 패턴은 서비스를 별도의 사용자로 실행하고 운영팀은 공유 그룹을 사용하는 것입니다:

sudo chown -R appuser:ops /var/log/shop
sudo chmod -R u=rwX,g=rX,o= /var/log/shop

이 설정으로 서비스는 로그를 쓸 수 있고, ops 그룹은 로그를 읽을 수 있으며, 다른 사용자는 접근할 수 없습니다.

6. umask로 기본 권한 제어

새 파일을 만들 때 프로그램은 일반적으로 파일에 666 권한, 디렉토리에 777 권한을 요청합니다. 그런 다음 커널은 umask에 있는 비트를 끕니다. 결과는:

실제 권한 = 요청 권한 AND (NOT umask)
umask            # 현재 값 보기
umask 027        # 현재 셸에 대해 변경
touch customer.csv && mkdir archive
stat -c '%a %n' customer.csv archive
0022
640 customer.csv
750 archive

umask 027의 경우: 파일은 666에서 비트 027을 빼서 640이 되고, 디렉토리는 777에서 027을 빼서 750이 됩니다. Other는 모든 권한을 잃고, group은 쓰기 권한을 잃습니다.

참고: umask는 비트 마스킹 작업이며, 산술 뺄셈이 아닙니다. umask 033의 경우, 새 파일의 권한은 633이 아니라 644입니다. 그 이유는 x 비트가 원래 666에 없으므로 끌 것이 없기 때문입니다.

일반적인 값들:

umask 파일 디렉토리 사용 시기
022 644 755 대부분의 distro에서 기본값
027 640 750 서버, 그룹 내에서만 공유
077 600 700 민감한 데이터

umask 명령은 현재 프로세스와 자식 프로세스에서만 유효합니다. systemd 서비스에 대해 영구적으로 설정하려면 unit 파일에서 선언합니다:

[Service]
User=appuser
UMask=0027

7. 세 가지 특수 비트: setuid, setgid, sticky

기본 9비트 외에 Linux에는 세 가지 특수 비트가 있습니다. 이들은 8진수 형식 앞의 네 번째 자리로 작성됩니다:

  • setuid (4): 프로그램이 소유자의 권한으로 실행됩니다. 예를 들어 /usr/bin/passwd는 -rwsr-xr-x로 표시됩니다.
  • setgid (2): 디렉토리에 설정하면 내부에 생성된 새 파일이 디렉토리의 그룹을 상속받습니다. 이 비트는 공유 작업 디렉토리에 매우 유용합니다.
  • sticky (1): 디렉토리에 설정하면 파일의 소유자만 파일을 삭제할 수 있습니다. /tmp는 이 비트를 사용하고 drwxrwxrwt로 표시됩니다.

그룹 dev용 공유 디렉토리를 만드는 예:

sudo mkdir /srv/shared && sudo chown :dev /srv/shared
sudo chmod 2770 /srv/shared
stat -c '%a %A' /srv/shared
2770 drwxrws---

참고: 위험을 완전히 이해하지 못한다면 자신이 작성한 스크립트나 파일에 setuid를 할당하지 마세요. 결함이 있는 setuid root 프로그램은 권한 상승의 전형적인 경로입니다.

8. Java에서 POSIX 권한 조작

Java NIO는 PosixFilePermissions를 통해 POSIX 권한을 지원합니다. 다음 클래스는 민감한 파일을 쓰고 소유자만 읽을 수 있도록 보장합니다:

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;

public class SecretWriter {

    public Path writeSecret(Path dir, String content) throws IOException {
        Path file = dir.resolve("secret.properties");
        Files.writeString(file, content);
        Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rw-------"));
        return file;
    }
}

fromString("rw-------")는 기호 문자열을 Set<PosixFilePermission>으로 변환합니다. 그런 다음 setPosixFilePermissions는 chmod 600 명령처럼 작동합니다.

PosixFilePermissions.asFileAttribute(...)를 Files.createFile에 전달하려고 할 수 있습니다. 이 방법도 umask가 적용되므로 open() 시스템 호출과 정확히 같습니다. 민감한 파일의 경우 생성 후 setPosixFilePermissions를 호출하여 결과를 확인합니다. 더 안전한 방법은 처음부터 엄격한 권한으로 파일을 생성하여 다른 사람이 파일을 읽을 수 있는 기간이 없도록 하는 것입니다.

JUnit 5 및 AssertJ를 사용한 테스트:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

class SecretWriterTest {

    @TempDir
    Path tempDir;

    @Test
    void givenSecret_whenWritten_thenOnlyOwnerCanReadAndWrite() throws Exception {
        assumeTrue(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"));

        Path file = new SecretWriter().writeSecret(tempDir, "db.password=s3cret");

        String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(file));
        assertThat(perms).isEqualTo("rw-------");
        assertThat(Files.readString(file)).contains("db.password");
    }
}

assumeTrue는 posix 속성 뷰가 없는 Windows에서 테스트를 건너뜁니다. 이 라인이 없으면 테스트는 UnsupportedOperationException을 던집니다.

9. 일반적인 오류

  • “빠른 수정”을 위해 chmod 777 사용. 이 방법은 머신의 모든 사용자에게 쓰기 권한을 엽니다. 프로세스가 어떤 사용자로 실행되는지 확인하고 (ps -o user= -p <pid>), 해당 사용자 또는 그룹에만 권한을 부여하세요.
  • 디렉토리 트리에서 chmod -R 755 실행. 이 명령은 모든 파일을 실행 파일로 만듭니다. 3.3절에서처럼 대문자 X 또는 find를 사용하세요.
  • 파일의 권한만 확인. namei -l을 사용하여 상위 디렉토리도 확인해야 합니다.
  • sudo 실행 후 소유자 변경 잊음. sudo로 생성된 파일은 root에 속하므로 일반 사용자로 실행되는 애플리케이션은 쓸 수 없습니다. 바로 후에 chown을 실행하세요.
  • Docker에서 볼륨 마운트. 컨테이너 내부의 UID는 호스트의 UID와 다를 수 있습니다. 커널은 사용자 이름이 아니라 UID 번호로 권한을 확인합니다.
  • SSH가 키를 거부. 권한이 너무 넓으면 SSH는 ~/.ssh/authorized_keys 또는 개인 키를 무시합니다. ~/.ssh 디렉토리는 700 권한이 필요하고 개인 키는 600 권한이 필요합니다.

10. 결론

Linux의 권한 모델은 3개의 그룹(user, group, other)과 3개의 권한(r, w, x)으로 구성됩니다. 이러한 권한은 파일과 디렉토리 간에 다른 의미를 가집니다.

chmod를 사용하여 권한을 변경합니다: 일부만 수정할 때는 기호 형식을, 전체를 설정할 때는 8진수 형식을 사용합니다. chown과 chgrp을 사용하여 파일을 올바른 사용자와 그룹에 할당합니다. umask를 사용하여 새 파일이 처음부터 안전한 권한으로 생성되도록 합니다.

일관된 원칙은 필요한 최소 권한을 부여하는 것입니다. 서비스를 별도의 사용자로 실행하고, 그룹을 통해 공유하며, 민감한 모든 항목에 대해 other를 0으로 설정합니다.

linuxchmodchownumask보안