대상 OS: Ubuntu Server 24.04 LTS

--privileged는 컨테이너 운영에서 가장 위험한 지름길 중 하나입니다. “급해서 잠깐”으로 시작해 관성이 되면, 컨테이너 격리 모델이 약해지고 침해 시 피해가 호스트 전체로 번질 수 있습니다.

컨테이너 보안의 목표는 “컨테이너가 뚫려도 호스트는 지킨다”입니다. 그런데 privileged는 그 경계를 스스로 낮추는 선택입니다.

1) privileged가 위험한 이유(핵심 요약)

  • 호스트 디바이스/커널 인터페이스 접근이 크게 늘어남(/dev 및 다양한 커널 기능)
  • Capability가 사실상 풀로 열려 최소 권한 설계가 무력화
  • AppArmor/seccomp 등 기본 완화책이 약해지거나 우회 가능성이 증가
  • 컨테이너 침해가 곧 호스트 침해(또는 심각한 영향)로 이어질 확률이 상승


2) 대안 1: 기본은 cap drop=ALL, 필요한 것만 cap add

대부분의 서비스는 모든 capability가 필요하지 않습니다. 기본을 “전부 제거”로 두고 꼭 필요한 것만 추가하세요.

# 예시: 1024 이하 포트를 열어야 한다면 NET_BIND_SERVICE만 추가
docker run \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  myapp:latest

파일 권한/포트/네트워크 요구사항을 정리해서 capability를 최소로 유지하는 게 포인트입니다.


3) 대안 2: no-new-privileges + seccomp/AppArmor 유지

컨테이너에서 권한 상승을 막는 기본 장치를 켭니다.

docker run \
  --security-opt no-new-privileges:true \
  myapp:latest

Ubuntu에서는 AppArmor가 기본 완화책으로 동작하는 경우가 많습니다. 가능하면 이를 끄지 말고, 필요한 경우 프로파일을 조정하는 쪽이 안전합니다.


4) 대안 3: 파일시스템을 읽기 전용으로 시작 + 필요한 경로만 RW

컨테이너가 내부를 마음대로 바꾸면 웹쉘/백도어/드롭퍼 설치가 쉬워집니다. 읽기 전용을 기본으로 두고, 데이터가 필요한 경로만 쓰기를 허용하세요.

docker run \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  -v /var/lib/myapp:/var/lib/myapp:rw \
  myapp:latest


5) 대안 4: 디바이스는 “전체”가 아니라 “정확히 필요한 것만”

하드웨어 접근이 필요하다고 해서 privileged로 퉁치지 말고, 필요한 디바이스만 제한적으로 전달합니다.

# 예시: 특정 디바이스만 전달
docker run \
  --device /dev/snd \
  myapp:latest


6) (Kubernetes) privileged 대신 securityContext로 설계

Kubernetes에서는 Pod/Container 단위로 최소 권한을 표현할 수 있습니다. 기본은 다음을 우선 검토하세요.

  • runAsNonRoot: true
  • readOnlyRootFilesystem: true
  • allowPrivilegeEscalation: false
  • capabilities: drop: ["ALL"] + 필요한 것만 add


7) 운영 팁: “왜 privileged가 필요했는지” 근거를 남기고 제거 목표를 세우기

  • 티켓/문서에 “필요 기능/커널 요구사항/대안 검토 결과”를 기록
  • 임시 허용이라면 종료 시점(또는 제거 조건)을 명시
  • 가능하면 호스트와의 경계를 줄이는 방향(리버스 프록시, sidecar, managed service)으로 재설계

사례(현장에서 자주 겪는 상황)

  • CI에서 빌드 편의 때문에 privileged를 켰는데, 공급망 공격 시 빌드 노드가 함께 위험해진다.
  • “디버깅용”으로 privileged를 켠 채 운영에 반영되어, 이후 취약점 악용 시 호스트까지 영향이 확대된다.
  • 호스트의 중요한 경로를 볼륨으로 마운트해둔 상태에서 privileged까지 켜져, 침해 시 시스템 파일 변조/자격증명 탈취로 이어진다.

트러블슈팅(증상→원인→해결)

  • 증상: privileged를 끄면 애플리케이션이 “Permission denied”로 실패
    원인: 실제로는 특정 capability/디바이스/마운트만 필요했는데 전체 권한에 의존해버림
    해결: 필요한 기능을 쪼개서 --cap-add, --device, 특정 볼륨 RW 허용으로 최소화(하나씩 추가하며 검증)
  • 증상: 로그/임시파일 기록이 안 되고 오류 발생
    원인: --read-only 적용 후 쓰기 경로 설계가 없음
    해결: 데이터 디렉터리만 RW로 마운트하고, --tmpfs로 임시 경로 제공(용량/옵션 포함)
  • 증상: 80/443 바인딩 실패(권한 문제)
    원인: root가 아니고 NET_BIND_SERVICE capability도 없음
    해결: 리버스 프록시로 포트 종단을 분리하거나, 컨테이너에 --cap-add=NET_BIND_SERVICE만 부여
  • 증상: 특정 시스템 호출/기능이 차단되는 듯한 오류
    원인: seccomp/AppArmor 정책이 해당 동작을 제한
    해결: 정책을 완전히 끄기 전에, 필요한 권한만 예외로 추가하거나 애플리케이션 동작을 표준 패턴으로 수정

- 이 글은 ai가 random적으로 만들어 올리는 글입니다. -