컨테이너 용어 사전
보안USER · privileged · capability · docker.sock

컨테이너 보안

기본이 root다. --privileged와 docker 소켓 마운트는 사실상 호스트 권한을 주는 것이다.

루트로 돌리지 않는다

RUN addgroup -g 10001 app && adduser -u 10001 -G app -D app
USER 10001:10001

기본은 root 다 — 명시하지 않으면 컨테이너 안 프로세스가 root 로 돈다

왜 위험한가

  • 컨테이너 탈출 취약점이 나오면 호스트에서도 root 다
    • (User 네임스페이스를 켜지 않았다면 UID 가 같다)
  • 볼륨 마운트한 호스트 파일을 마음대로 고칠 수 있다
  • 컨테이너 안에서 패키지를 설치하거나 시스템을 바꿀 수 있다

이름이 아니라 '숫자 UID' 로 지정한다 쿠버네티스의 runAsNonRoot 검증이 숫자를 요구한다 이름만 있으면 판정할 수 없다

  • 1024 미만 포트는 root 만 bind 할 수 있다
  • 컨테이너 안에서 8080 을 쓰고 밖에서 -p 80:8080 으로 매핑한다

읽기 전용 루트

docker run --read-only --tmpfs /tmp myapp

침투해도 바이너리를 심거나 설정을 바꿀 수 없다 쓰기가 필요한 곳만 tmpfs·볼륨으로 연다

어디에 쓰는지 파악해야 하므로 도입에 품이 든다

  • docker diff 로 조사하면 목록이 나온다

권한을 좁힌다

docker run \
  --cap-drop=ALL --cap-add=NET_BIND_SERVICE \
  --security-opt=no-new-privileges \
  --read-only \
  myapp
  • capability — root 권한을 쪼갠 것. 전부 끄고 필요한 것만 켠다
  • no-new-privileges — setuid 로 권한이 올라가는 것을 막는다
  • seccomp — 허용할 시스템 콜 목록. 기본 프로파일이 적용된다

--privileged 는 사실상 호스트 권한이다

'조금 더 많은 권한' 이 아니다

  • 모든 capability
  • seccomp·AppArmor 해제
  • 호스트 장치 접근

컨테이너 탈출이 어렵지 않아진다 필요한 이유를 설명할 수 없다면 쓰지 않는다 필요하면 그 능력 하나만 --cap-add 로 준다

도커 소켓 마운트는 곧 호스트 장악이다

-v /var/run/docker.sock:/var/run/docker.sock

이 한 줄이면 그 컨테이너는 호스트에 아무 컨테이너나 띄울 수 있다

  • --privileged 로 호스트 루트를 마운트한 컨테이너를 만들면 끝이다

CI 러너·모니터링 도구에서 흔히 보이는 설정이다 대안 — 소켓 프록시로 필요한 API 만 노출 · rootless 모드

  • DinD 대신 Kaniko·Buildah 로 빌드

비밀을 이미지에 넣지 않는다

남는 경로

  • COPY 로 넣은 설정 파일 (.env · 키 파일)

  • ARG 로 넘긴 값 → docker history 에 명령이 남는다

  • ENV → docker inspect 로 그대로 보인다

  • 앞 레이어에 넣고 뒤에서 지운 파일 → 레이어에 남아 있다

  • 빌드 시점 — RUN --mount=type=secret

  • 런타임 — 환경변수 주입 · 시크릿 매니저 · 파일 마운트

확인

  • docker history --no-trunc myapp:1.0
  • docker save myapp:1.0 | tar -tv | head -50

런타임 위협 탐지

빌드 시점 방어(스캔·설정)만으로는 실행 중 이상을 못 잡는다

  • 예상 못 한 프로세스 실행 (컨테이너 안에서 셸이 떴다)
  • 예상 못 한 아웃바운드 연결
  • 민감 파일 접근

Falco 같은 도구가 시스템 콜을 관찰해 규칙 위반을 알린다 eBPF 기반이라 애플리케이션 변경 없이 붙는다

"셸이 없는 이미지" 와 조합하면 강력하다 — 셸이 뜨는 것 자체가 이상 신호가 된다

최소 권한 체크리스트

□ USER 를 숫자 UID 로 지정했다 □ --read-only + 필요한 곳만 tmpfs □ --cap-drop=ALL 후 필요한 것만 add □ --security-opt=no-new-privileges □ --privileged 를 쓰지 않는다 □ docker.sock 을 마운트하지 않는다 □ 호스트 경로를 rw 로 마운트하지 않는다 □ 네트워크·PID 네임스페이스를 호스트와 공유하지 않는다

하나씩 켜면서 애플리케이션이 도는지 확인하는 순서로 도입한다 한꺼번에 적용하면 무엇 때문에 깨졌는지 못 찾는다

면접 함정

  • "컨테이너는 격리돼 있으니 root여도 된다" → 탈출 시 호스트 root다.
  • "docker.sock은 읽기 전용으로 주면 안전하다" → API로 무엇이든 만들 수 있다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 이미지 크기와 보안 — 같은 문제의 두 얼굴