[Docker] bind mount의 permission denied 문제

1. 문제 상황

Docker에서 bind mount를 사용할 때 가장 많이 만나는 문제가 있다.

바로 permission denied다.

귀찮아서 무시하고 있었는데 드디어 시간을 내서 정리해본다.

결론부터 말하하면 이 문제는 컨테이너의 사용자와 호스트 파일의 소유자가 다르기 때문에 접근 권한이 없을 때 발생한다.


2. UID와 GID

가. 도커의 3단계 권한 흐름

Docker는 아래 3단계를 거쳐 실행된다.

단계역할특징
clientdocker, docker-compose 실행sudo 여부만 결정
docker daemon실제 작업 수행항상 root
container실제 앱 실행user 설정 영향 받음

sudo는 docker daemon에 접근하기 위한 권한일 뿐이다.

sudo docker를 사용하든 docker를 사용하든, 컨테이너 실행이나 bind mount할 디렉터리 생성과 같은 실제 작업에는 영향을 주지 않는다.

이러한 작업은 모두 호스트에서 동작하는 docker daemon이 root 권한으로 수행한다.

반명, 컨테이너 내부에서 실행되는 프로세스는 Dockerfiledocker-compose.ymlUID/GID 설정을 따른다.


나. UID와 GID 설정 우선순위

root (기본값) < Dockerfile USER 설정 < docker-compose user 설정

컨테이너에서 사용할 UID, GID는 위 순서로 덮어쓴다.

즉, docker-compose.yml 설정이 가장 우선된다.

이렇게 결정된 UID, GID가 컨테이너 내부에서 실행의 주체가 된다.


3. 문제의 원인

Linux는 파일 권한을 사용자 이름이 아니라 UID/GID 숫자로 판단한다.

따라서 bind mount로 연결된 호스트 디렉터리에 컨테이너가 접근할 때도,

커널은 컨테이너 안의 이름이 아니라 실제로 실행 중인 프로세스의 UID/GID를 기준으로 권한을 검사한다.

예를 들어 다음과 같은 상황을 가정하자.

  • 컨테이너 프로세스의 UID/GID1000:1000
  • bind mount된 호스트 디렉터리의 소유자는 root:root
  • 권한은 775로 owner만 쓸 수 있는 상태라고 하자.

이때 문제가 발생되는 가정은 다음과 같다.

  1. 컨테이너 내부 프로세스가 bind mount된 경로에 파일 생성 또는 쓰기를 시도한다.
  2. 이 요청은 가상으로 처리되는 것이 아니라, 실제로는 호스트 파일시스템의 해당 경로에 전달된다.
  3. Linux 커널은 요청한 프로세스의 UID/GID와 대상 파일 또는 디렉터리의 소유자·권한 비트(775)를 비교한다.
  4. UID가 다르면 owner 권한은 적용되지 않는다.
  5. 이어서 group 권한, other 권한 순서로 확인한다.
  6. 그 어느 쪽에도 필요한 권한이 없으면 커널은 접근을 거부한다.
  7. 그 결과 애플리케이션에서는 permission denied가 발생한다.

4. 결론

가. 경우의 수

케이스내부 UID폴더 UID실행 방식결과
1rootrootsudo동작 (보안 위험)
2rootroot일반동작 (보안 위험)
3root1000sudo동작 (root 권한)
4root1000일반동작 (root 권한)
51000rootsudo실패 (permission denied)
61000root일반실패 (permission denied)
710001000sudo성공 (주의 필요)
810001000일반완벽

나. root가 컨테이너 내부 실행의 주체라면

1, 2, 3, 4 케이스에 해당한다.

root는 모든 파일에 접근 가능하다.

그래서 대부분의 permission denied 문제는 사라진다.

하지만 이 방식은 보안상 불리하다.

컨테이너는 호스트와 커널을 공유하기 때문에, 취약점이 존재할 경우 커널을 통해 컨테이너 탈출이 발생할 수 있다.

이때 root 권한을 가지고 있다면 일반 사용자보다 탈출 가능성과 피해 범위가 커진다.

편하지만, 그만큼 위험한 방식이다.


다. 일반 사용자가 컨테이너 내부 실행의 주체라면

5, 6, 7, 8 케이스에 해당한다.

이 경우 핵심은 하나다.

컨테이너 프로세스의 UID와 bind mount한 디렉터리의 UID가 일치하는가

  • 일치하면 → 정상 동작 (7, 8 케이스)
  • 일치하지 않으면 → 권한 문제 발생 (5, 6 케이스)

정확히는 UID가 다르면 owner 권한을 사용할 수 없고, group 또는 other 권한으로도 접근할 수 없을 때 permission denied가 발생한다.


라. 문제가 생기지 않으려면

sudo docker를 사용하든 docker를 사용하든 큰 차이는 없다.

핵심은 두 가지다.

bind mount된 디렉터리의 소유자와 컨테이너 내부 UID

컨테이너 내부에서 root를 사용하면 대부분 문제 없이 동작하지만, 보안에 불리하다.

반대로 일반 사용자(보통 1000)를 사용하면 보안에는 유리하지만 권한을 직접 맞춰줘야 한다.

따라서 다음 조건을 만족해야 한다.

bind mount된 디렉터리의 UID와 컨테이너 내부 사용자 UID를 일치시킨다

한 가지 주의할 점이 있다.

bind mount 대상 디렉터리가 존재하지 않으면, Docker는 해당 경로를 자동으로 생성한다.

이 작업은 docker daemon이 root 권한으로 수행하기 때문에, 생성된 디렉터리는 root 소유가 된다.

이 상태에서 내부에서 일반 사용자를 사용하면 권한 문제가 발생한다.

따라서 컨테이너를 실행하기 전에, bind mount에 사용할 디렉터리를 미리 생성하고 소유자를 맞춰야 한다.

이 과정을 생략하면 permission denied 오류를 만나게 된다.


댓글 남기기