Docker 컨테이너의 non-root 전환과 파일 권한
서비스 컨테이너를 non-root 사용자로 실행하도록 바꾼 작업입니다. 사내 표준 베이스 이미지에 있는 실행 사용자를 적용하고, 파일 권한과 외부 파일을 가져오는 명령도 함께 정리했습니다. 먼저 적용한 서비스의 변경을 다른 서비스에서도 참고했습니다.
실행 사용자만 바꾸면 끝날 것 같지만, 앱이 어떤 파일을 읽고 어디에 쓰는지까지 맞춰야 했습니다. root일 때는 드러나지 않던 권한 의존성을 Dockerfile에서 확인하는 작업이기도 했습니다.
실행 사용자와 빌드 단계
Dockerfile의 USER는 이후 RUN과 컨테이너 실행 시의 CMD·ENTRYPOINT에 사용할 사용자를 지정합니다. 베이스 이미지가 이미 사용자를 정했을 수도 있으므로, 현재 Dockerfile에 USER가 없다는 이유만으로 항상 root라고 단정할 수는 없습니다. 실행 시 별도로 사용자를 지정하는 설정도 확인해야 합니다.
패키지 설치처럼 관리자 권한이 필요한 준비는 먼저 마치고, 애플리케이션은 일반 사용자로 실행하도록 구분했습니다. 이때 COPY의 파일 소유권은 앞선 USER와 별개이므로 --chown이나 별도 권한 설정을 명시해야 합니다.
다음은 내부 이미지를 사용한 실제 Dockerfile 대신, 같은 구분을 보여 주는 예제입니다.
FROM eclipse-temurin:21-jre
USER root
RUN groupadd --gid 10001 app \
&& useradd --no-log-init --uid 10001 --gid app --no-create-home app \
&& install -d -o root -g root -m 0755 /app \
&& install -d -o app -g app -m 0750 /app/data
WORKDIR /app
COPY --chown=root:root build/libs/service.jar /app/service.jar
RUN chmod 0644 /app/service.jar
USER app:app
ENTRYPOINT ["java", "-jar", "/app/service.jar"]이 예제의 UID·GID는 설명용 값이며 대상 이미지에서 충돌하지 않아야 합니다. 앱은 JAR를 읽고 /app/data에는 쓸 수 있지만, root 소유의 /app과 JAR를 직접 변경할 권한은 주지 않았습니다. 실제 앱의 저장 경로와 마운트 설정은 별도로 맞춰야 합니다.
소유권과 파일 모드
당시에는 일반 파일에 644, 실행 스크립트에 755를 적용하며 불필요한 권한을 줄였습니다. JAR는 Java가 읽는 파일이므로 실행 비트가 필요하지 않습니다.
다만 644라는 숫자만으로 앱의 파일 변경을 막을 수는 없습니다. 644는 소유자에게 쓰기를 허용합니다. 앱이 JAR의 소유자라면 여전히 수정할 수 있고, 부모 디렉터리에 쓰기 권한이 있으면 파일을 지우거나 교체할 수도 있습니다.
그래서 실행 파일·설정 파일과 앱이 생성하는 데이터를 구분해야 합니다. 읽기만 필요한 파일은 앱이 수정하지 못하도록 소유권과 상위 경로를 함께 확인하고, 쓰기가 필요한 경로에만 권한을 줍니다. 비밀 설정처럼 모든 사용자에게 읽기를 허용하면 안 되는 파일도 같은 모드로 일괄 처리하지 않습니다.
외부 파일 다운로드
이 작업에서는 외부 파일을 가져오는 ADD를 wget 명령으로 바꾸기도 했습니다. 다운로드와 파일 권한 설정을 한 흐름에서 확인하려는 변경이었습니다.
그렇다고 URL을 사용하는 ADD가 실패를 전혀 감지하지 못하거나, wget으로 바꾸기만 하면 안전해지는 것은 아닙니다. 필요한 도구가 이미지에 있는지, 실패가 빌드에 전달되는지, 받은 파일이 기대한 버전과 내용인지도 확인해야 합니다. 다운로드 도구의 선택과 파일 검증은 별개의 항목입니다.
다른 서비스에 적용할 때의 확인
먼저 적용한 Dockerfile은 다음 서비스의 출발점으로 쓸 수 있었습니다. 다만 앱마다 로그·임시 파일·데이터 경로가 달라, 같은 파일을 복사하는 것으로 확인을 끝낼 수는 없었습니다.
이미지의 기본 사용자와 실제 실행 UID를 확인하고, 그 사용자로 앱을 시작했을 때 필요한 파일을 읽고 쓸 수 있는지 봐야 합니다. 볼륨을 마운트하면 이미지 안에서 설정한 소유권과 다른 파일이 보일 수 있다는 점도 확인 대상입니다.
non-root 실행은 앱에 처음부터 부여하는 권한을 줄이는 조치입니다. capability나 마운트, 특권 컨테이너 설정까지 자동으로 바꾸지는 않습니다. 사용자 전환과 런타임 권한을 함께 확인해야 변경의 효과를 판단할 수 있습니다.