K-Shield Jr 10기 취약점 진단 분반 E-01조 프로젝트

점검 보고서


왜 Docker 취약점을 따로 점검해야 하는가

Docker는 OS 위에 올라가지만, 리눅스 커널을 호스트와 공유한다. 컨테이너가 완전히 격리된 것처럼 보여도, 설정이 잘못되면 컨테이너 탈출(container escape)이 가능하고 호스트 시스템까지 영향을 준다. 일반 서버와 다른 공격 면(attack surface)이 존재하기 때문에, 별도의 진단 체계가 필요하다.

이번 실습에서는 KISA Docker 보안 가이드와 Sk쉴더스 도커 보안 가이드(sk 기준)를 교차 참조하면서 D-01부터 D-41까지 총 41개 항목을 직접 명령어를 실행해 점검했다.

점검 환경은 Ubuntu Linux, Docker 23.x 버전, IP 192.168.253.141이었다.


점검 항목 구조

41개 항목은 크게 네 가지 영역으로 구분된다.

Audit 설정 (D-02 ~ D-07) — Docker daemon, 주요 디렉토리, 서비스 파일들에 auditd 감사 설정이 적용되어 있는지 점검한다. 파일이나 바이너리에 접근/수정이 발생할 때 감사 로그를 남기는 기능이다.

파일 소유권 및 접근 권한 (D-09 ~ D-20) — docker.service, docker.socket, /etc/docker, daemon.json 등 주요 파일의 소유자가 root:root인지, 권한이 기준치(644, 660, 755) 이하인지 확인한다.

컨테이너 런타임 보안 (D-21 ~ D-37) — SSH 비활성화, 호스트 마운트 제한, 권한 제어(privileged, no-new-privileges), SSL/TLS, SELinux, Swarm 설정 등 실행 중인 컨테이너의 보안 설정을 점검한다.

이미지 및 네트워크 (D-38 ~ D-41) — 로그 관리, Dockerfile 설정, 이미지 신뢰성, 네트워크 제어를 점검한다.


주요 취약 항목 정리

41개 항목 중 취약으로 나온 것들만 정리했다.

Audit 설정 미흡 (D-03 ~ D-06)

/usr/bin/docker에 대한 audit은 설정되어 있었지만, /var/lib/docker, /etc/docker, docker.service, docker.socket 는 전부 audit 설정이 없었다.

점검 명령은 이렇다.

# audit 설정 현황 확인
auditctl -l | grep /var/lib/docker
cat /etc/audit/audit.rules | grep /var/lib/docker

# 설정이 없다면 아래를 /etc/audit/rules.d/audit.rules에 추가
-w /var/lib/docker -k docker
-w /etc/docker -k docker
-w /lib/systemd/system/docker.service -k docker
-w /lib/systemd/system/docker.socket -k docker
-w /etc/default/docker -k docker

audit 설정이 없으면 누가 언제 Docker 관련 파일을 수정했는지 추적이 불가능하다. 침해 사고 시 포렌식 자료가 없는 것과 같다.

컨테이너 간 네트워크 트래픽 미제한 (D-08)

bash

docker network ls --quiet | xargs docker network inspect \
  --format "{{ .Name}}: {{ .Options }}"
# 결과: bridge.enable_icc:true

enable_icc:true는 같은 bridge 네트워크 위의 컨테이너들이 서로 자유롭게 통신할 수 있다는 의미다. 격리가 필요한 서비스들이 같은 호스트에 있을 때 문제가 된다. /etc/docker/daemon.json에 아래를 추가해야 한다.

json

{
  "icc": false
}

컨테이너 SSH 활성화 (D-21)

docker ps --quiet
docker exec $INSTANCE_ID ps -el
# SSH 프로세스 확인됨

컨테이너 내부에서 SSH 데몬이 동작하고 있었다. 컨테이너에 SSH를 띄우는 건 안티패턴이다. 접근이 필요하면 docker exec를 써야 한다. SSH가 활성화되면 컨테이너가 사실상 별도의 서버처럼 동작해서 Docker의 격리 모델이 무너진다.

호스트 주요 디렉토리 마운트 (D-22)

docker ps --quiet --all | xargs docker inspect \
  --format 'Volumes={{.Mounts}}'
# /lib 디렉토리 마운트 확인

/lib이 컨테이너에 마운트되어 있었다. /boot, /dev, /etc, /lib, /proc, /sys, /usr 같은 호스트 시스템 디렉토리가 컨테이너에 마운트되면, 컨테이너가 호스트의 핵심 자원에 직접 접근할 수 있다. 컨테이너 탈출의 전형적인 경로다.

SSL/TLS 미적용 (D-24, D-27)

ps -ef | grep dockerd | grep -i "tlsverify"
# 아무것도 반환되지 않음

Docker daemon이 TLS 없이 노출되어 있으면 원격에서 인증 없이 Docker API에 접근 가능하다. --tlsverify, --tlscacert, --tlscert, --tlskey 옵션을 모두 적용해야 한다.

root 권한으로 컨테이너 실행 (D-31)

docker ps --quiet --all | xargs docker inspect \
  --format '{{ .Id }}: User={{ .Config.User }}'
# User= (빈 값 = root)

User 필드가 비어있으면 root로 실행 중이라는 의미다. 컨테이너 내 프로세스가 root이고 컨테이너 탈출이 발생하면, 호스트에서도 root 권한을 가지게 된다. Dockerfile에서 USER 지시어로 비특권 사용자를 지정해야 한다.

Dockerfile에서 ADD 명령어 사용 (D-39)

docker history <Image_ID>
# ADD file:... 확인

ADD는 URL에서 파일을 받거나 tar 아카이브를 자동 압축 해제하는 기능이 있어서 예측하지 못한 파일이 이미지에 포함될 수 있다. 단순 파일 복사는 COPY를 써야 한다.


양호로 확인된 항목들

모든 항목이 취약한 건 아니었다. 파일 소유권 관련 항목(D-09~D-20)은 대부분 양호였다. docker.service, docker.socket, /etc/docker, daemon.json, /var/run/docker.sock 파일들의 소유자가 root:root로 설정되어 있고 접근 권한도 기준치 이하였다.

불필요한 서비스도 대부분 비활성화 상태였다. finger, anonymous FTP, r 계열 서비스, tftp, sendmail, NFS, RPC, NIS 등이 전부 미구동 상태였다. Docker Swarm도 inactive였다(D-26 양호).

/dev에 존재하지 않는 device 파일도 없었고(D-16 양호), .rhostshosts.equiv 파일도 없었다(D-17 양호).


수동 점검의 한계

41개 항목을 한 번 돌리는 데 상당한 시간이 걸렸다. 각 항목마다 명령어를 실행하고, 결과를 해석하고, 양호/취약/검토 중 어느 쪽인지 판단하는 과정이 반복된다. 실수가 생기기 쉽고, 여러 서버에 동일하게 적용하는 건 사실상 불가능하다.

이 한계를 극복하기 위해 쉘 스크립트를 작성해 취약점 점검을 자동화하고, 점검 내용에 대한 보고서를 작성했다. 각각 2편(쉘 스크립트 작성)과 3편(보고서 작성)에서 이어진다.

WBS

점검 기준:

KISA 클라우드 취약점 점검 가이드, SK쉴더스 클라우드 취약점 점검 가이드를 참고하여 우리 팀만의 점검 기준을 수립하였다.


점검 환경: Ubuntu Linux, Docker 23.x