Docker 컨테이너 보안

발생

최근에 Docker를 사용해서 애플리케이션을 배포하는 프로젝트를 진행하고 있다. 개발 환경에서는 문제없이 동작했는데, 프로덕션 환경에 배포할 때 보안에 대한 고민이 생겼다.

컨테이너는 가상머신보다 가볍고 빠르지만, 그만큼 보안 취약점도 있을 수 있다. Docker 컨테이너를 안전하게 사용하기 위한 방법들을 알아보자.

컨테이너 보안의 중요성

컨테이너의 특성

  • 공유 커널: 호스트 OS의 커널을 공유
  • 네임스페이스 격리: 프로세스, 네트워크, 파일시스템 격리
  • cgroups: 리소스 사용량 제한
  • 레이어드 파일시스템: 이미지 레이어 공유

이러한 특성으로 인해 보안 위험이 발생할 수 있다.

주요 보안 위험 요소

1. 취약한 베이스 이미지

# 나쁜 예시
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nodejs

# 좋은 예시
FROM node:18-alpine

2. 루트 권한으로 실행

# 나쁜 예시
USER root
RUN npm install

# 좋은 예시
RUN adduser -D -s /bin/sh appuser
USER appuser
RUN npm install

3. 민감한 정보 노출

# 나쁜 예시
ENV DB_PASSWORD=secret123

# 좋은 예시
ENV DB_PASSWORD_FILE=/run/secrets/db_password

Docker 보안 모범 사례

1. 최소 권한 원칙

비루트 사용자 사용

FROM node:18-alpine

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

# 작업 디렉토리 설정
WORKDIR /app

# 소유권 변경
RUN chown -R nextjs:nodejs /app
USER nextjs

# 애플리케이션 복사 및 실행
COPY --chown=nextjs:nodejs . .
CMD ["npm", "start"]

2. 멀티스테이지 빌드

# 빌드 스테이지
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

# 프로덕션 스테이지
FROM node:18-alpine AS production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs . .
USER nextjs
CMD ["npm", "start"]

3. 이미지 스캔

# Trivy를 사용한 취약점 스캔
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image myapp:latest

4. 시크릿 관리

# docker-compose.yml
version: '3.8'
services:
  app:
    image: myapp:latest
    secrets:
      - db_password
    environment:
      - DB_PASSWORD_FILE=/run/secrets/db_password

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

5. 네트워크 보안

# docker-compose.yml
version: '3.8'
services:
  app:
    image: myapp:latest
    networks:
      - frontend
      - backend
  
  database:
    image: postgres:13
    networks:
      - backend

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

런타임 보안

1. 리소스 제한

# docker-compose.yml
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

2. 읽기 전용 파일시스템

FROM node:18-alpine
WORKDIR /app
COPY . .
RUN chmod -R 755 /app
USER node
CMD ["npm", "start"]
# 읽기 전용으로 실행
docker run --read-only -v /tmp:/tmp myapp:latest

3. 네트워크 정책

# Kubernetes NetworkPolicy 예시
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-network-policy
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 3000

모니터링 및 로깅

1. 컨테이너 로그 관리

# docker-compose.yml
services:
  app:
    image: myapp:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

2. 보안 이벤트 모니터링

# Docker 데몬 로그 확인
journalctl -u docker.service -f

# 컨테이너 프로세스 모니터링
docker stats --no-stream

결론

Docker 컨테이너 보안은 개발부터 배포까지 전 과정에서 고려해야 한다.

주요 원칙:

  1. 최소 권한: 필요한 최소한의 권한만 부여
  2. 최소 공격 표면: 불필요한 패키지나 서비스 제거
  3. 정기적 업데이트: 베이스 이미지와 의존성 정기 업데이트
  4. 모니터링: 지속적인 보안 모니터링

보안은 한 번에 완성되는 것이 아니라 지속적으로 개선해나가는 과정이다. 작은 실수 하나가 큰 보안 사고로 이어질 수 있으니 항상 주의해야 한다.

Day-18


tech