← 전체 글로 돌아가기

웹 개발

SSH 배포가 성공했는데 새 파일이 읽히지 않았던 umask 실수

배포 로그는 성공인데 애플리케이션이 새 설정 파일을 읽지 못했던 이유를 umask와 실행 사용자 관점에서 점검했습니다.

배포 로그만 보면 성공이었다

서버에 정적 설정 파일을 복사하는 배포가 exit code 0으로 끝났다. 그런데 웹 프로세스를 재시작하자 새 파일만 permission denied가 났다. 기존 파일은 정상이라 애플리케이션 버그로 오해하기 쉬운 상황이었다.

먼저 파일과 프로세스의 사용자를 같은 시점에 확인했다.

id app
ps -o user,group,cmd -C node
stat -c '%A %U:%G %n' /srv/app/config/*

기존 파일은 app:app 소유에 640 권한이었지만 새 파일은 배포 계정 소유에 600이었다. 복사 명령이 성공한 것과 서비스가 읽을 수 있는 것은 별개의 문제였다.

umask가 결과를 바꿨다

배포 셸의 umask는 077이었다. 그래서 임시 파일을 만든 뒤 이동하는 스크립트가 새 파일을 600으로 만들었다. 파일을 이동하면 대상 디렉터리의 기본 권한으로 다시 계산되지 않으므로, 디렉터리 권한만 보고는 놓칠 수 있다.

수정은 배포 마지막에 무조건 640을 덮는 방식이 아니라, 소유자와 그룹을 명시하는 쪽으로 했다.

install -o app -g app -m 0640 rendered/config.json /srv/app/config/config.json
chown app:app /srv/app/config/config.json

비밀값이 들어가는 파일은 그룹 읽기 권한도 허용하지 않고 600을 유지한다. 어떤 파일이 공개 설정이고 어떤 파일이 비밀 설정인지 배포 스크립트에서 구분해야 한다.

재발 방지 체크리스트

  • 서비스의 실제 User와 배포 계정을 따로 확인한다
  • stat으로 권한·소유자·그룹을 배포 전후 비교한다
  • umask를 가정하지 말고 파일 생성 명령에 mode를 명시한다
  • 비밀 파일에는 넓은 권한을 주지 않는다
  • 배포 직후 서비스 사용자로 읽기 테스트를 한다
sudo -u app test -r /srv/app/config/config.json
sudo -u app node -e "require('fs').accessSync('/srv/app/config/config.json')"

기억할 한 가지

권한 문제를 숫자 하나로만 고치면 다음 배포에서 다시 돌아온다. 내가 고정한 기준은 “누가 만들었나”가 아니라 “서비스 사용자가 어떤 경로로 읽어야 하나”를 배포 스크립트와 검증 명령에 함께 적는 것이다.