Devin.KR

새 파일의 기본 권한

80분 안팎

학습 목표

umask와 공유 디렉터리의 그룹 상속을 적용해 새 설정 파일의 권한을 검사합니다.

개념

권한은 생성 시점부터 맞춥니다

어제 만든 설정을 640으로 고쳤는데 오늘 생성된 설정은 644인 상황을 다룹니다. 기존 파일 수정만으로는 다음 생성의 권한을 보장하지 못합니다. 설정을 만드는 프로세스의 umask와 공유 디렉터리의 그룹 상속을 정해야 합니다. 이 레슨은 읽기 전용 설정이라는 정책을 새 파일에도 유지하고, 파일 생성·재실행·그룹 관측으로 정책이 지속되는지 확인합니다.

기본 권한 실습은 일반 사용자로 새 ZIP 폴더에서 bash check.sh를 실행합니다. 임시 폴더 안에서만 파일을 생성하며 관리자 계정이나 Linux 사용자 DB를 변경하지 않습니다. 이 맥에서는 640·750·600 생성 권한을 검증하며 setgid 변경은 샌드박스가 거부해 PENDING으로 표시합니다. Linux에서는 2750도 검사하고, 다른 UID의 그룹 상속은 VM에서 추가 확인합니다. 자신의 그룹을 상속한 검사와 bcops가 bcapp으로 파일을 만드는 검사를 같은 증거라고 적지 않습니다.

마스크는 허용 비트를 제거합니다

프로그램이 일반 파일 생성 시 666, 디렉터리 생성 시 777을 요청하는 흔한 경우를 기준으로 봅니다. umask 027은 그룹 쓰기와 기타 권한을 제거해 파일 640, 디렉터리 750을 만듭니다. 파일에서 실행 비트가 원래 요청되지 않았으므로 umask를 느슨하게 해도 실행 권한이 새로 생기는 것은 아닙니다. 요청 모드에 없는 비트를 umask가 추가해 주지 않습니다.

계산은 요청 모드 AND 마스크의 반전입니다. 666에서 023을 산술로 빼서 643이라고 계산하면 틀립니다. 023이 표시한 그룹 쓰기와 기타 쓰기/실행을 제거하면 644입니다. 실제 계산 예제는 Python의 8진수 정수와 비트 연산으로 출력해 확인합니다. 숫자 암기보다 새 파일을 생성하는 코드가 어떤 모드를 요청했는지 알아야 결과를 설명할 수 있습니다.

모든 프로그램이 666을 요청하는 것은 아닙니다. 비밀 자료 생성 도구가 600을 요청하면 umask 027이어도 그룹 읽기가 생기지 않습니다. 기본 ACL이 있는 디렉터리에서는 생성 권한 결정에 ACL도 관여하므로 단순 계산 전제를 확인합니다. 이 실습은 기본 ACL을 설정하지 않은 새 임시 경로에서 진행하며 운영 디렉터리의 ACL 정책을 지우지 않습니다.

프로세스 범위와 기존 파일을 구분합니다

umask는 현재 프로세스에 적용되고 새 자식 프로세스가 이어받습니다. 괄호로 감싼 Bash 서브셸에서 바꾸면 부모 셸은 영향을 받지 않습니다. task.sh는 설정 생성 블록에 027, 가짜 개인키 생성 블록에 077을 지정합니다. 개인 자료 정책이 공유 설정의 정책과 다르다는 사실을 코드의 생성 위치로 표현하고 이후 셸 작업까지 마스크가 새지 않도록 합니다.

기존 파일에 내용을 덮어쓰는 리다이렉션은 파일을 다시 생성하는 경우와 다릅니다. 이미 있는 파일의 모드는 umask 변경으로 소급 수정되지 않습니다. 644인 설정을 027로 덮어써도 640으로 자동 변경된다고 기대하면 안 됩니다. check.sh는 매번 새 임시 루트로 시작하므로 생성 기본값을 검사하며, 기존 잘못된 권한은 별도로 chmod가 필요하다는 점을 결과 해석에 남깁니다.

로그인 셸에서만 umask를 바꿔 놓고 서비스가 같은 값을 사용할 것으로 가정하지 않습니다. 실행 환경이 달라지면 상속한 마스크도 달라질 수 있습니다. 뒤 systemd 모듈에서는 서비스 유닛의 UMask로 정책을 지정합니다. 여기서는 설정을 만드는 Bash 블록 안에 값을 적어 생성 주체와 마스크를 한 위치에서 확인하고 변경 범위를 명확히 합니다.

setgid는 그룹을 이어 주고 쓰기는 열지 않습니다

디렉터리에 setgid를 주면 Linux에서 그 안에 생성된 파일의 그룹을 부모 디렉터리 그룹으로 이어받게 할 수 있습니다. 2750의 앞자리 2가 setgid입니다. 나머지 750은 소유자 전권, 그룹 읽기/탐색, 기타 차단입니다. 새 하위 디렉터리는 setgid도 이어받을 수 있어 파일과 하위 디렉터리의 모드 관측은 구분해 적습니다.

bcops의 기본 그룹이 bcops여도 config를 bcapp 그룹의 setgid 디렉터리로 준비하면 새 설정은 bcapp으로 만들어져 서비스가 읽을 수 있습니다. setgid가 그룹 쓰기를 추가하는 것은 아닙니다. 새 파일에 그룹 쓰기를 줄지 읽기만 줄지는 요청 모드와 umask가 결정합니다. 이 프로젝트에서는 설정 변경자를 bcops로 유지하므로 umask 002로 공동 쓰기를 열지 않습니다.

실습 task.sh는 shared 디렉터리를 2750으로 설정하고 027 서브셸에서 new.conf와 new-dir을 생성합니다. check.sh는 new.conf 640, 새 디렉터리의 접근 비트 750, 부모와 자식 GID 일치를 검사합니다. private.key는 가짜 문자열만 담고 600이어야 합니다. 실제 인증키나 운영 토큰이 필요하지 않으며 예제 파일명을 비밀 관리의 완성으로 해석하지 않습니다.

생성 실패를 관측할 때의 순서

새 파일의 그룹이 예상과 다르면 부모 디렉터리 GID와 setgid부터 확인합니다. ls -ld의 s 표시만 보지 말고 숫자 모드와 GID를 함께 관찰합니다. 그룹이 맞지만 서비스 읽기가 안 되면 새 파일 모드와 부모 경로의 탐색 권한을 확인합니다. setgid를 켰다는 사실만으로 읽기나 쓰기를 허용했다고 보고하면 문제를 다시 만들 수 있습니다.

check.sh의 config mode 실패는 새 파일이 640이 아니라는 뜻입니다. umask가 해당 생성 블록에 적용됐는지 확인하며 022와 027의 차이를 설명합니다. shared setgid 실패는 0750만 지정한 경우처럼 상속 비트가 없는 상태입니다. 검증기를 고치기보다 task.sh의 정책을 수정하고 다시 실행합니다. 공백 경로는 변수를 인용했는지, 빈 인자는 파일 작업 전에 거부하는지도 확인합니다.

완료 기록에는 기존 파일 권한과 새 생성 권한을 분리해 씁니다. 두 번 실행해 설정 내용이 중복되지 않는지도 확인합니다. VM에서 bcops가 만든 파일의 그룹이 bcapp인지 추가 관찰하면 운영 경계와 생성 정책을 연결할 수 있습니다. umask 비트 계산과 기본 ACL 예외는 Linux umask 문서에서 확인했으며 전체 셸 설정 방식은 더 읽기에서 이어갑니다.

생성 마스크 기준: Linux umask(2).

따라하기

마스크 비트 계산

파일 요청 모드가 666일 때 023과 027의 차이를 실제 Python 계산으로 확인합니다.

for mask in (0o023, 0o027):
    print(f'mask={mask:03o} file={0o666 & ~mask:03o} directory={0o777 & ~mask:03o}')

실행 결과

mask=023 file=644 directory=754
mask=027 file=640 directory=750

생성 결과와 기존 파일 비교

새 임시 폴더에서 Python 프로세스의 마스크를 지정합니다. 새 파일은 640이며 chmod 뒤 마스크를 바꿔 덮어써도 기존 644는 유지됩니다. 폴더와 마스크는 종료 전에 정리·복구합니다.

import os, stat, tempfile
from pathlib import Path
old = os.umask(0o027)
try:
    with tempfile.TemporaryDirectory() as root:
        p = Path(root) / 'app.conf'
        p.write_text('service=lab-web\n')
        print(f'new={stat.S_IMODE(p.stat().st_mode):03o}')
        p.chmod(0o644)
        os.umask(0o077)
        p.write_text('service=lab-web\n')
        print(f'existing={stat.S_IMODE(p.stat().st_mode):03o}')
finally:
    os.umask(old)

실행 결과

new=640
existing=644

생성 정책 실습 검사

defaults ZIP에서 task.sh를 수정합니다. check.sh는 새 임시 폴더에 실제 파일을 만들며 출력은 이 맥에서 solution으로 실행한 결과이며 setgid는 VM 확인이 남아 있습니다. starter 구현 전에는 검사 실패가 정상입니다.

bash check.sh

실행 결과

setgid=PENDING Linux VM
PASS defaults: config 640, directory 750, private 600, group, content
PASS defaults boundaries: repeat, spaced path, empty root

다른 기본 그룹에서 상속 관찰

Linux VM에서 관리자 준비 후 수행합니다. config 소유자는 bcops, 그룹은 bcapp으로 두고 2750을 적용합니다. bcops로 새 파일을 생성했을 때 그룹은 bcapp, 모드는 640인지 확인합니다. output은 VM 미실행으로 비워 둡니다. 검사 뒤 생성 파일을 지웁니다.

sudo chmod 2750 /srv/bootcamp-m02/lab-root/config
sudo -u bcops bash -c 'umask 027; printf "service=lab-web\n" > "$1/config/generated.conf"' _ /srv/bootcamp-m02/lab-root
sudo -u bcops stat -c "%a %G %n" /srv/bootcamp-m02/lab-root/config/generated.conf
sudo -u bcops rm /srv/bootcamp-m02/lab-root/config/generated.conf

확인 문제

실습

task.sh에 설정 생성 umask를 적용하고 Linux에서는 공유 디렉터리에 setgid를 설정합니다. macOS에서는 umask와 공백·반복·빈 인자 사례를 검사하며 setgid는 PENDING으로 남습니다. VM 단계에서 bcapp 상속을 추가 확인합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

macOS: setgid=PENDING Linux VM, PASS defaults 및 PASS defaults boundaries; Linux: setgid=checked와 두 PASS 줄

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 파일 권한 때문에 서비스가 실패하는 상황을 설명합니다.