컨테이너 안에서 앱이 root로 돌고 있던 것을 뒤늦게 발견한 이유
문제 발생
보안 점검에서 지적을 받고 확인했습니다.
$ docker exec app whoami
root앱은 파일을 읽고 HTTP 요청만 처리합니다. root가 필요한 이유가 하나도 없었습니다. 게다가 컨테이너가 볼륨으로 마운트한 업로드 디렉터리에 root 소유 파일을 계속 만들고 있어서, 호스트에서 정리하려면 sudo가 필요했습니다.
원인 분석
기본값이 root이기 때문입니다. Dockerfile에 USER가 없으면 RUN, ENTRYPOINT, CMD가 전부 root로 실행됩니다. Docker 문서의 USER 설명 그대로 — 이 명령은 현재 스테이지의 나머지 부분에 대해 기본 사용자와 그룹을 설정합니다. 즉 이 한 줄이 없으면 낮춰지지 않습니다.
Best practices 문서의 권고도 짧습니다 — 서비스가 권한 없이 실행될 수 있다면 USER로 비root 사용자로 전환하십시오.
컨테이너 안의 root가 호스트 root와 같지는 않지만(user namespace를 쓰지 않는 기본 설정에서는 UID가 같습니다), 위험은 실재합니다. 애플리케이션 취약점으로 임의 코드가 실행되면 그 코드는 컨테이너 안에서 무엇이든 할 수 있고, 마운트된 볼륨에도 root 권한으로 접근합니다.
해결 방안
- 전용 사용자를 만들어 전환합니다. 문서가 드는 예와 같은 형태입니다.
RUN groupadd -r app && useradd --no-log-init -r -g app app
USER app
CMD ["node", "server.js"]Node 공식 이미지처럼 이미 node 사용자가 있는 베이스라면 USER node 한 줄이면 됩니다.
- 파일 소유권을 함께 맞춥니다. 복사한 파일이 root 소유면 쓰기가 필요한 경로에서 막힙니다.
COPY --chown=app:app . .-
낮춘 뒤에 필요한 권한이 없는지 확인합니다. 1024 미만 포트 바인딩이 대표적입니다 — 앱은 3000번대로 띄우고 앞단 프록시가 80/443을 담당하게 합니다.
-
sudo를 넣지 않습니다. 문서가 경고하듯 예측하기 어려운 TTY·시그널 전달 동작 때문에 문제를 일으킬 수 있고, 꼭 필요하면 gosu 같은 도구를 권합니다. -
볼륨 권한을 미리 정합니다. 호스트 디렉터리를 마운트한다면 그 UID/GID를 컨테이너 사용자와 맞춰둡니다. 운영에서 뒤늦게 맞추면 이미 만들어진 파일들을 손봐야 합니다.
-
베이스 이미지도 함께 줄입니다. 같은 문서의 권고대로 작은 베이스 이미지는 의존성으로 들어오는 취약점 수를 줄여줍니다 — 비root 실행과 함께 가는 조치입니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.