Devin.KR

서비스 계정과 파일 접근

80분 안팎

학습 목표

실행 계정의 접근 범위를 제한합니다.

개념

프로세스의 권한은 실행 주체에서 시작합니다

안내판 컨테이너를 시작하는 배포 담당자와 그 안에서 요청을 처리하는 Java 프로세스는 다른 주체입니다. 배포 담당자는 이미지를 선택하고 볼륨을 연결하지만 Java는 연결된 경로에서 파일을 읽고 씁니다. 서비스가 root로 실행되면 파일 권한으로 의도한 차단이 무너질 수 있습니다. 이번 과제는 실제 Linux 컨테이너에서 UID가 10001인지 확인하고 자기 데이터 쓰기는 성공하면서 다른 환경 파일 읽기는 실패하는지 직접 검사합니다. 설정 문자열만 보고 성공이라고 결론내리지 않습니다.

이름보다 UID와 GID를 봅니다

Linux 파일 접근 판정에는 숫자 UID와 그룹 정보가 사용됩니다. 같은 notice 이름을 써도 호스트와 컨테이너에서 숫자가 다르면 소유자가 다를 수 있습니다. 숫자 10001:10001로 실행 계정을 고정하면 실습 경계를 설명하기 쉬워집니다. 이름이 등록되지 않았다고 실행이 root가 되는 것은 아닙니다. docker run으로 id -u를 실행하여 실제 숫자를 확인합니다. 이미지 USER와 실행 옵션의 user가 다르면 실행 시 지정한 값이 영향을 주므로 최종 컨테이너 정보도 함께 확인합니다.

쓰기 허용 디렉터리를 따로 둡니다

자기 데이터 경로 /data는 UID 10001이 소유하고 0700으로 제한합니다. 안내판 내용과 요청 로그는 여기에 생성합니다. 애플리케이션 코드와 다른 환경 설정에 쓰기 권한을 함께 줄 이유는 없습니다. 서비스가 로그를 못 쓰는 문제를 해결하면서 루트 파일시스템 전체의 권한을 바꾸면 변경 범위를 설명할 수 없습니다. 먼저 실패한 정확한 경로를 확인하고 그 디렉터리의 소유자와 접근 비트를 맞춥니다. 기존 볼륨은 이미지 안의 새 디렉터리와 소유권이 다를 수 있어 실제 마운트도 조사합니다.

디렉터리의 실행 비트는 진입 권한입니다

파일에 읽기 비트가 있어도 부모 디렉터리에 진입할 수 없으면 열지 못합니다. /other는 root 소유 0700이고 내부 config는 root 소유 0600입니다. 서비스는 /other/config의 내용이 무엇인지 몰라도 읽기 요청이 실패하는 것을 확인할 수 있습니다. 읽기 거부가 권한 때문인지 파일이 없기 때문인지 구분하기 위해 fixture는 빌드 과정에서 실제로 만듭니다. 경로가 존재하는데 접근하지 못하는 조건을 검사해야 격리를 구현했다고 말할 수 있습니다. 권한 오류 메시지를 내용 출력으로 조사하지 않습니다.

Dockerfile은 생성과 실행을 분리합니다

이미지 빌드의 RUN은 디렉터리를 만들고 소유자를 설정합니다. 마지막 USER는 이후 실행 기본 계정을 정합니다. starter는 USER 0:0으로 남아 있어 UID 검사에서 실패합니다. solution은 10001:10001로 실행하고 /data 쓰기와 /other 읽기 거부가 맞는지 검사합니다. 빌드 준비에 root를 썼다는 사실이 실행 프로세스까지 root여야 한다는 뜻은 아닙니다. 사용자 전환 뒤에 root 소유 경로를 수정하려는 RUN이 있으면 빌드 단계부터 실패하므로 줄 순서도 확인합니다.

독립 레슨 실습은 실제 커널을 검사합니다

cloud-m06-service-identity zip은 작은 Alpine 이미지에서 접근 경계를 확인합니다. 모의 함수가 권한 비트를 계산하는 실습이 아니라 컨테이너에서 echo와 cat을 실행합니다. check.sh는 도구와 daemon을 확인하고 임의의 개인용 이미지 이름을 생성합니다. 실행 컨테이너는 --rm으로 제거하며 검사가 끝나면 이 실행에서 만든 이미지만 지웁니다. 다른 프로젝트의 이미지나 컨테이너를 전역 정리하지 않습니다. 이 작은 실험이 통과한 뒤 미션의 Java 서비스에도 같은 원칙을 연결합니다.

거부 검사는 실패를 성공 조건으로 읽습니다

cat /other/config가 비영 종료하면 이 사례에서는 기대한 거부입니다. 스크립트는 if 조건으로 실패를 받아 읽기가 성공한 경우에만 자신의 실패 코드를 냅니다. set -e만 믿고 cat을 그대로 실행하면 예상한 거부가 전체 스크립트를 중단시켜 결과를 오해하기 쉽습니다. 자기 데이터 echo는 성공해야 하고 다른 환경 cat은 실패해야 하므로 두 검사 결과를 반대로 읽지 않습니다. 명령 stderr에는 permission denied가 있을 수 있지만 공개 fixture이므로 토큰이나 운영 설정을 읽는 명령은 아닙니다.

서비스와 배포 계정의 표를 작성합니다

서비스는 /data 쓰기, 자기 비밀 경로 읽기만 필요하고 다른 환경 설정 읽기와 비밀 수정은 필요하지 않습니다. 배포 계정은 자기 프로젝트의 계획과 적용, 검증 이미지 선택, 비밀 교체를 담당합니다. 권한 표에는 허용 이유와 거부를 증명하는 명령을 넣습니다. 누가 실행하는 명령인지 생략하면 docker exec를 수행한 사람과 그 안의 UID를 혼동하게 됩니다. 컨테이너 안의 파일 격리와 Docker daemon에 접근하는 호스트 사용자의 권한은 별도의 경계라고 표시합니다.

Docker daemon 접근은 좁은 배포 권한과 다릅니다

개인 Docker daemon을 제어하는 계정은 컨테이너에 임의의 호스트 경로를 마운트할 수 있는 등 넓은 권한을 가질 수 있습니다. 이번 실습은 호스트 배포 계정 사이의 완전한 격리를 구현하지 않습니다. 코드에 project 라벨을 적었다고 다른 사용자의 Docker API 호출을 차단한 것은 아닙니다. 실무에서는 승인된 배포 경로와 daemon 접근 계정을 별도로 제한하고 감사합니다. 이 레슨에서는 표와 실행 기록에 남은 경계를 밝히고 서비스 프로세스의 파일 접근 제한만 검증 결과로 주장합니다.

볼륨 권한은 기존 데이터를 지우지 않고 조사합니다

앞 모듈의 전용 볼륨에는 안내판 데이터가 이어집니다. 새 이미지에서 chown을 했더라도 이미 만들어진 볼륨의 내용과 소유권은 그대로 남을 수 있습니다. permission denied가 나면 container mount의 대상 볼륨 이름, 현재 실행 UID, 파일 소유 숫자를 비교합니다. 문제 해결을 위해 볼륨을 삭제하고 재생성하면 권한 오류는 사라져도 프로젝트의 보존 조건이 깨집니다. 교정이 필요할 때는 데이터를 먼저 보존하고 정확한 자기 볼륨의 특정 경로만 바꾸는 절차를 검토합니다.

흔한 chmod 777 수정은 테스트를 약하게 만듭니다

서비스가 못 쓰는 디렉터리를 0777로 넓히면 모든 사용자에게 쓰기를 줍니다. 원하는 결과는 특정 UID의 쓰기이지 누구나 쓰기가 아닙니다. /other의 읽기 거부도 권한을 넓히면 실패해야 합니다. 신입의 리뷰에서는 명령 한 줄이 성공한 사실보다 어떤 주체가 추가로 접근 가능해졌는지 설명하게 합니다. 계정 이름을 바꾸는 것만으로 소유자가 바뀌었다고 가정하는 수정도 피합니다. chown의 자세한 옵션은 서재로 보내고 여기서는 숫자 주체와 실제 접근 결과를 연결합니다.

실행 결과와 외부 대기를 구분합니다

Docker는 작성 환경에서 실행하지 못하므로 단계 출력에는 성공 로그를 미리 채우지 않습니다. 학습자는 check.sh에서 UID 검사, 자기 쓰기, 다른 환경 거부 세 조건을 확인합니다. daemon 연결 오류는 파일 접근 실패가 아니라 실습 환경이 준비되지 않은 상태입니다. docker info가 성공한 뒤에 접근 검사를 시작해야 그 차이를 알 수 있습니다. 미션에서 최종 컨테이너 inspect와 HTTP 결과까지 연결한 증거를 제출하며 문서의 USER 한 줄만으로 실제 프로세스 권한을 증명하지 않습니다.

따라하기

권한 비트 해석

소유자만 접근 가능한 디렉터리 비트를 계산합니다.

import stat
print(stat.filemode(stat.S_IFDIR | 0o700))
print(oct(0o700 & 0o077))

실행 결과

drwx------
0o0

starter UID 검사

압축 해제한 루트에서 실행합니다. starter는 UID 검사에서 실패합니다. Docker 연결 오류라면 먼저 개인 daemon을 준비합니다.

bash check.sh

실행 계정 수정

Dockerfile 마지막 줄을 바꾸고 다시 검사합니다. 실제 UID 10001, 자기 데이터 쓰기 성공, 다른 환경 읽기 실패를 확인합니다.

USER 10001:10001

미션 권한 표 연결

미션 permissions.md에 배포 명령을 실행하는 호스트 계정과 컨테이너 UID를 구분하여 적습니다. 실제 볼륨 이름과 접근 거부 검사 명령을 연결합니다.

확인 문제

실습

Dockerfile의 실행 계정을 고칩니다. bash check.sh가 실제 id -u와 파일 접근을 검사합니다. /other의 권한을 넓히거나 fixture를 삭제하여 회피하지 않습니다. Docker 실행 검증은 외부 대기입니다.

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

실행 명령

bash check.sh

기대 결과

실제 UID 10001·자기 데이터 쓰기 성공·다른 환경 읽기 거부를 모두 검사합니다.

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

더 읽기

면접 질문

  • 서비스 계정과 배포 계정의 권한을 어떻게 구분하나요?
  • 컨테이너의 파일 접근 오류에서 UID와 볼륨 소유권을 어떻게 조사하나요?