2편에서 만든 쉘 스크립트로 점검 결과를 수집했다.
이번엔 그 raw 텍스트를 사람이 읽을 수 있는 보고서로 제작하고, 취약 항목에 대해 조치하였다.
점검 보고서
자동화의 목적
2편의 스크립트를 실행하면 타임스탬프가 붙은 텍스트 파일이 생성된다. 내용은 이렇게 생겼다.
===========================[ D-01 Docker latest patch START ]
1. Docker Version
...
Client : 23 Server : 23
Result : Good
===========================[ D-01 Docker latest patch END ]
===========================[ D-02 /usr/bin/docker audit START ]
1. /usr/bin/docker audit
-w /usr/bin/docker -p rwxa -k docker
Result : Good
===========================[ D-02 Docker daemon audit END ]
...
이 텍스트 파일로는 전체 결과를 한눈에 보기 어렵다. 41개 항목 중 몇 개가 취약인지, 어떤 항목들인지, 위험도 분포는 어떤지 이런 요약 정보가 빠져있다.
보고서 목표는 두 가지였다. 첫번째는 점검 결과를 구조화된 표 형태로 정리하고, 두번째는 취약 항목에 대한 개선 방안을 같이 출력하는 것이다.
보고서 구조
최종 산출물인 상세 보고서는 항목별로 아래 정보를 포함한다.
- 진단 코드 (D-01 ~ D-41)
- 참조 기준 (KISA DO 번호 / CIS sk 번호)
- 진단 항목명
- 취약도 (상/중/하)
- 진단 기준 (양호 조건 / 취약 조건)
- 진단 방법 (사용한 명령어)
- 진단 결과 (실제 출력값 캡처)
- 비고
결과 요약
41개 항목 전체 점검 결과를 집계하면 다음과 같다.
취약 항목: D-03, D-04, D-05, D-06, D-07, D-08, D-21, D-22, D-24, D-25, D-27, D-30, D-31, D-32, D-33, D-35, D-37, D-38, D-39
검토 항목: D-23, D-28
양호 항목: 나머지
취약 항목이 19개로 전체의 46%에 해당한다. 위험도 상 항목 중 audit 설정 관련(D-03~D-07)이 한꺼번에 취약으로 나왔고, 컨테이너 보안 설정 관련(D-21, D-22, D-24, D-25)도 전부 취약이었다.


취약 항목별 개선 방안
Audit 설정 일괄 추가 (D-03~D-07)
# /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
# 적용
sudo systemctl restart auditd
컨테이너 간 네트워크 트래픽 제한 (D-08)
// /etc/docker/daemon.json
{
"icc": false
}
sudo systemctl restart docker
컨테이너 SSH 비활성화 (D-21)
Dockerfile에서 SSH 설치/실행 명령을 제거해야 한다. 이미 SSH가 켜진 컨테이너는 재빌드가 필요하다.
# 잘못된 예 — 컨테이너에 SSH를 띄우는 패턴
RUN apt-get install -y openssh-server
CMD ["/usr/sbin/sshd", "-D"]
# 올바른 접근 — 필요할 때만 exec 사용
# docker exec -it <container_id> /bin/bash
호스트 주요 자원 마운트 금지 (D-22)
docker run 또는 docker-compose.yml에서 볼륨 마운트 경로를 확인하고, /boot, /dev, /etc, /lib, /proc, /sys, /usr는 마운트하지 않도록 한다.
# docker-compose.yml
volumes:
# 절대 하면 안 됨
- /etc:/etc
- /lib:/lib
# 필요한 경로만 명시
- /data/app:/app/data
SSL/TLS 적용 (D-24, D-27)
Docker daemon에 TLS를 적용하려면 인증서를 먼저 생성하고 daemon.json에 설정한다.
// /etc/docker/daemon.json
{
"tls": true,
"tlsverify": true,
"tlscacert": "/etc/docker/certs/ca.pem",
"tlscert": "/etc/docker/certs/server-cert.pem",
"tlskey": "/etc/docker/certs/server-key.pem"
}
root가 아닌 사용자로 컨테이너 실행 (D-31)
# Dockerfile
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
또는 docker run 시 --user 옵션으로 지정한다.
docker run --user 1000:1000 myimage
Docker Content Trust 활성화 (D-32)
# 현재 세션에서 활성화
export DOCKER_CONTENT_TRUST=1
# 전체 시스템에 적용 (Ubuntu)
echo 'export DOCKER_CONTENT_TRUST=1' >> /etc/bash.bashrc
source /etc/bash.bashrc
활성화하면 서명된 이미지만 pull/run할 수 있다. 공식 이미지는 대부분 서명되어 있지만, 사설 레지스트리 이미지는 별도로 서명 설정이 필요하다.
SELinux 보안 옵션 설정 (D-33, D-37)
# daemon 레벨 SELinux 활성화
# /etc/docker/daemon.json
{
"selinux-enabled": true
}
# 컨테이너별 seccomp 프로필 적용
docker run --security-opt seccomp=/path/to/seccomp.json myimage
Dockerfile ADD → COPY 변경 (D-39)
# 취약: ADD는 URL 다운로드, tar 자동 압축 해제 등 사이드 이펙트 있음
ADD https://example.com/file.tar.gz /app/
# 개선: COPY는 로컬 파일 복사만 수행
COPY file.tar.gz /app/
느낀 점
점검 기준 41개를 다 돌리고 나서 느낀 건, 취약 항목들 사이에 패턴이 있다는 거다. audit 설정 누락은 "설치 후 기본 상태"에서 발생하고, 컨테이너 보안 설정 미흡은 "일단 개발해야 되니까 보안 설정 다 꺼놓자"는 패턴에서 발생한다고 생각한다. 사실 나도 프로젝트를 할 때 네트워크 인바운드 처리를 0.0.0.0/0에서 받거나, ssh root 계정 사용 가능 등 개발 단계에서 일부러 보안을 하지 않는 경우가 많은데, 앞으로는 처음부터 보안 설정 위에서 돌아가는 개발을 해야 되겠다고 다짐했다.
더불어 수동 점검이든 스크립트 점검이든, 결국 보고서는 "현재 상태"를 찍는 스냅샷이라고 생각한다. 시간이 지나면 새 컨테이너가 생길 것이고 당연히 설정 또한 바뀔 것이다. 주기적으로 다시 돌려야 한다는 전제가 있어야 의미 있는 점검이 된다고 생각한다.
해당 프로젝트로 해당 기수 K-shield.Jr 프로젝트 3등을 차지했다. 기쁘다!