포스트

[이제와서 시작하는 Docker 마스터하기 - 실전편 #6] Docker 보안 베스트 프랙티스

[이제와서 시작하는 Docker 마스터하기 - 실전편 #6] Docker 보안 베스트 프랙티스

“이제와서 시작하는 Docker 마스터하기” Docker는 편리하지만 잘못 사용하면 보안 위험이 될 수 있습니다. 이번 편에서는 Docker를 안전하게 사용하기 위한 보안 베스트 프랙티스를 알아보겠습니다.

Docker 보안의 중요성

컨테이너는 프로세스 격리를 제공하지만, 완벽한 보안 경계는 아닙니다. 적절한 보안 조치 없이는 다음과 같은 위험이 있습니다:

주요 보안 위협

위협 유형 설명 영향도 대응 방안
컨테이너 탈출 컨테이너에서 호스트로 접근 🔴 매우 높음 권한 제한, 보안 프로필
권한 상승 일반 사용자가 root 권한 획득 🔴 매우 높음 비특권 사용자 실행
민감정보 노출 시크릿, 비밀번호 유출 🔴 매우 높음 시크릿 관리 도구 사용
악성 이미지 악의적 코드 포함 이미지 🟡 높음 이미지 스캔, 서명 확인
네트워크 공격 컨테이너 간 침투 🟡 높음 네트워크 격리
리소스 고갈 DoS 공격 🟢 중간 리소스 제한 설정

1. 이미지 보안

공식 이미지 사용

1
2
3
4
5
6
7
8
9
# 좋은 예: 공식 이미지
docker pull nginx:latest
docker pull node:16-alpine

# 확인이 필요한 예: 비공식 이미지
docker pull unknown-user/nginx

# 이미지 정보 확인
docker inspect nginx:latest | grep -i "Author\|Created"

이미지 스캔

1
2
3
4
5
6
7
8
9
10
# Docker Scout로 취약점 스캔
docker scout cves nginx:latest

# Trivy로 상세 스캔
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image nginx:latest

# Grype로 스캔
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  anchore/grype:latest nginx:latest

최소 베이스 이미지 사용

1
2
3
4
5
6
7
8
9
# 나쁜 예: 큰 이미지
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nodejs

# 좋은 예: 최소 이미지
FROM node:16-alpine

# 더 좋은 예: distroless
FROM gcr.io/distroless/nodejs:16

2. Dockerfile 보안

비특권 사용자 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 나쁜 예: root로 실행
FROM node:16-alpine
COPY . /app
CMD ["node", "app.js"]

# 좋은 예: 일반 사용자로 실행
FROM node:16-alpine

# 사용자 생성
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

# 작업 디렉토리 설정 및 권한
WORKDIR /app
COPY --chown=nodejs:nodejs . .

# 사용자 전환
USER nodejs

CMD ["node", "app.js"]

민감한 정보 제외

.dockerignore 파일:

1
2
3
4
5
6
7
8
.env
.git
*.pem
*.key
secrets/
credentials/
.aws/
.ssh/

멀티 스테이지 빌드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 빌드 스테이지
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 실행 스테이지 (빌드 도구 제외)
FROM node:16-alpine
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --chown=nodejs:nodejs . .
USER nodejs
CMD ["node", "app.js"]

헬스체크 추가

1
2
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD node healthcheck.js || exit 1

3. 컨테이너 실행 보안

읽기 전용 루트 파일시스템

1
2
3
# 읽기 전용으로 실행
docker run --read-only --tmpfs /tmp \
  --tmpfs /var/run nginx:alpine

권한 제한

1
2
3
4
5
6
7
8
9
# 권한 제거
docker run --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  nginx:alpine

# 보안 옵션
docker run --security-opt=no-new-privileges:true \
  --security-opt=apparmor:docker-default \
  nginx:alpine

리소스 제한

1
2
3
4
5
6
7
# CPU와 메모리 제한
docker run -d \
  --cpus="0.5" \
  --memory="512m" \
  --memory-swap="512m" \
  --pids-limit=100 \
  nginx:alpine

4. 네트워크 보안

네트워크 격리

1
2
3
4
5
6
7
8
# 격리된 네트워크 생성
docker network create --driver bridge isolated_network

# 내부 전용 네트워크
docker network create --internal internal_network

# 컨테이너 연결
docker run -d --network isolated_network --name app myapp

포트 바인딩 제한

1
2
3
4
5
# 나쁜 예: 모든 인터페이스에 바인딩
docker run -p 3306:3306 mysql

# 좋은 예: localhost만 바인딩
docker run -p 127.0.0.1:3306:3306 mysql

5. 시크릿 관리

Docker Secrets (Swarm mode)

1
2
3
4
5
6
7
8
# 시크릿 생성
echo "my-secret-password" | docker secret create db_password -

# 서비스에서 사용
docker service create \
  --name myapp \
  --secret db_password \
  myapp:latest

BuildKit 시크릿

1
2
3
4
5
6
# syntax=docker/dockerfile:1
FROM alpine

# 빌드 시 시크릿 사용
RUN --mount=type=secret,id=mysecret \
  cat /run/secrets/mysecret

빌드:

1
docker build --secret id=mysecret,src=secret.txt .

환경 변수 대신 파일 사용

1
2
# 환경 변수 대신 파일에서 읽기
CMD DB_PASSWORD=$(cat /run/secrets/db_password) node app.js

6. Docker Compose 보안

docker-compose.yml 보안 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: '3.8'

services:
  app:
    image: myapp:latest
    # 보안 옵션
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp
      - /var/run
    # 사용자
    user: "1001:1001"
    # 리소스 제한
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
    # 헬스체크
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

7. 로깅과 모니터링

보안 이벤트 로깅

1
2
3
4
5
6
7
8
# Docker 이벤트 모니터링
docker events --filter type=container

# 로그 드라이버 설정
docker run -d \
  --log-driver=syslog \
  --log-opt syslog-address=tcp://192.168.1.100:514 \
  nginx:alpine

감사 로깅

1
2
3
4
5
6
7
8
9
10
11
// /etc/docker/daemon.json
{
  "log-level": "info",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "experimental": true,
  "metrics-addr": "127.0.0.1:9323"
}

8. 실습: 보안 강화된 웹 애플리케이션

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
FROM node:16-alpine AS builder
WORKDIR /build
COPY package*.json ./
RUN npm ci --only=production

FROM gcr.io/distroless/nodejs:16
WORKDIR /app
COPY --from=builder /build/node_modules ./node_modules
COPY . .
USER nonroot
EXPOSE 3000
CMD ["app.js"]

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
version: '3.8'

services:
  web:
    build: .
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      - NODE_ENV=production
    secrets:
      - db_password
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: true
    tmpfs:
      - /tmp
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    healthcheck:
      test: ["CMD", "node", "healthcheck.js"]
      interval: 30s
      timeout: 3s
      retries: 3

  db:
    image: postgres:13-alpine
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - db_data:/var/lib/postgresql/data
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - FOWNER
      - SETGID
      - SETUID

secrets:
  db_password:
    file: ./secrets/db_password.txt

volumes:
  db_data:

9. 컨테이너 런타임 보안

Seccomp 프로필

1
2
3
4
5
6
7
8
9
10
11
// seccomp-profile.json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": ["accept", "bind", "connect"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

사용:

1
docker run --security-opt seccomp=seccomp-profile.json myapp

AppArmor 프로필

1
2
3
4
5
# AppArmor 프로필 로드
sudo apparmor_parser -r -W /etc/apparmor.d/docker-nginx

# 프로필 적용
docker run --security-opt apparmor=docker-nginx nginx

10. 보안 체크리스트

이미지 보안

  • 공식/검증된 이미지 사용
  • 최소 베이스 이미지 선택
  • 정기적인 이미지 스캔
  • 이미지 서명 확인

Dockerfile 보안

  • 비특권 사용자로 실행
  • 민감한 정보 제외
  • 불필요한 패키지 제거
  • 헬스체크 구현

런타임 보안

  • 읽기 전용 파일시스템
  • 권한 최소화 (cap_drop)
  • 리소스 제한 설정
  • 네트워크 격리

시크릿 관리

  • 하드코딩된 시크릿 제거
  • Docker Secrets 또는 외부 시크릿 관리 도구 사용
  • 환경 변수 노출 최소화

모니터링

  • 로그 수집 및 분석
  • 보안 이벤트 모니터링
  • 정기적인 보안 감사

보안 도구

2025년 최신 보안 도구 비교

도구 유형 주요 기능 장점 단점 가격
Docker Scout 취약점 스캐너 이미지 취약점 분석 Docker 통합 기본 기능만 무료/Pro
Trivy 종합 스캐너 이미지, IaC, 설정 검사 빠른 속도 CLI 중심 무료
Snyk 취약점 스캐너 실시간 모니터링 개발자 친화적 비용 무료/유료
Falco 런타임 보안 실시간 위협 감지 CNCF 프로젝트 복잡한 설정 무료
Aqua Security 엔터프라이즈 전체 라이프사이클 종합 솔루션 높은 비용 유료

보안 도구 통합 워크플로우

graph LR
    A[개발] --> B[빌드]
    B --> C{이미지 스캔}
    C -->|통과| D[레지스트리]
    C -->|실패| E[수정]
    E --> B
    D --> F[배포]
    F --> G[런타임 모니터링]
    G --> H[로그 분석]
    H --> I[보안 대응]

보안 강화 실전 예제

완전히 보안 강화된 웹 애플리케이션 구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# docker-compose-secure.yml
version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile.secure
    image: myapp:secure
    ports:
      - "127.0.0.1:8080:8080"
    security_opt:
      - no-new-privileges:true
      - apparmor=docker-default
      - seccomp=/path/to/seccomp/profile.json
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp:noexec,nosuid,size=100M
      - /var/run:noexec,nosuid,size=50M
    user: "10001:10001"
    environment:
      - NODE_ENV=production
    secrets:
      - db_password
      - api_key
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
    networks:
      - frontend
      - backend

  db:
    image: postgres:15-alpine
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - FOWNER
      - SETGID
      - SETUID
    user: postgres
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
      POSTGRES_DB: secure_app
    secrets:
      - db_password
    volumes:
      - db_data:/var/lib/postgresql/data:Z
    networks:
      - backend
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    file: ./secrets/api_key.txt

volumes:
  db_data:
    driver: local

마무리

Docker 보안은 단일 조치가 아닌 여러 계층의 방어가 필요합니다. 이미지부터 런타임까지 각 단계에서 보안을 고려해야 합니다.

핵심 보안 원칙

  1. 최소 권한 원칙: 필요한 최소한의 권한만 부여
  2. Defense in Depth: 다층 보안 구현
  3. Zero Trust: 모든 것을 검증
  4. 지속적 모니터링: 실시간 위협 감지
  5. 정기적 업데이트: 취약점 패치 적용

다음 편에서는 Docker 로그와 모니터링에 대해 자세히 알아보겠습니다.

다음 편 예고

  • Docker 로깅 전략
  • 중앙 집중식 로그 관리
  • 컨테이너 메트릭 수집
  • 실시간 모니터링 구축

안전하고 관찰 가능한 컨테이너 환경을 만들어봅시다! 🔍

📚 Docker 마스터하기 시리즈

🐳 기초편 (입문자용 - 5편)

  1. Docker란 무엇인가?
  2. Docker 설치 및 환경 설정
  3. 첫 번째 컨테이너 실행하기
  4. Docker 이미지 이해하기
  5. Dockerfile 작성하기

💼 실전편 (중급자용 - 6편)

  1. Docker 네트워크 기초
  2. Docker 볼륨과 데이터 관리
  3. Docker Compose 입문
  4. 멀티 컨테이너 애플리케이션
  5. Docker Hub 활용하기
  6. Docker 보안 베스트 프랙티스 ← 현재 글

🚀 고급편 (전문가용 - 9편)

  1. Docker 로그와 모니터링
  2. Docker로 Node.js 애플리케이션 배포
  3. Docker로 Python 애플리케이션 배포
  4. Docker로 데이터베이스 운영
  5. Docker 이미지 최적화
  6. Docker와 CI/CD
  7. Docker Swarm 기초
  8. 문제 해결과 트러블슈팅
  9. Docker 생태계와 미래
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.