/엔지니어/Docker / 컨테이너/컨테이너 보안 심화 — 이미지 스캔 · 런타임 보안
Docker / 컨테이너고급UbuntuDebianCentOSRHEL컨테이너보안TrivySeccomp

컨테이너 보안 심화 — 이미지 스캔 · 런타임 보안 · 최소 권한 원칙

Trivy 이미지 취약점 스캔, Seccomp·AppArmor 런타임 격리, 루트리스 컨테이너, Docker Content Trust까지 — 프로덕션 컨테이너 보안의 모든 레이어를 다룹니다.

컨테이너 보안의 4개 레이어

컨테이너 보안은 이미지 → 런타임 → 오케스트레이터 → 네트워크 4개 레이어를 모두 잠가야 합니다. 하나라도 열려 있으면 전체가 노출됩니다.


1. 이미지 보안 — Trivy 취약점 스캔

Trivy 설치 및 기본 스캔

Bash
# 설치 (Ubuntu/Debian)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# 이미지 스캔
trivy image nginx:latest

# CRITICAL/HIGH만 표시
trivy image --severity CRITICAL,HIGH nginx:latest

# JSON 출력 (CI 파이프라인 연동)
trivy image --format json --output result.json nginx:latest

# 로컬 Dockerfile 기반 스캔 (빌드 전)
trivy config Dockerfile

# 파일시스템 스캔 (코드 저장소)
trivy fs --severity HIGH,CRITICAL .

GitHub Actions CI 통합

YAML
# .github/workflows/security.yml
name: Container Security Scan

on: [push, pull_request]

jobs:
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t app:${{ github.sha }} .

      - name: Run Trivy scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: app:${{ github.sha }}
          format: sarif
          output: trivy-results.sarif
          severity: CRITICAL,HIGH
          exit-code: 1          # CRITICAL 발견 시 빌드 실패

      - name: Upload to GitHub Security
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: trivy-results.sarif

2. 최소 권한 Dockerfile

Dockerfile
# ❌ 안티패턴
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl python3
COPY . /app
CMD ["python3", "/app/server.py"]
# root로 실행, 불필요한 패키지 포함

# ✅ 프로덕션 패턴
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.12-slim
# 전용 비특권 사용자 생성
RUN groupadd -r appuser && useradd -r -g appuser -s /sbin/nologin appuser

WORKDIR /app
COPY --from=builder /install /usr/local
COPY --chown=appuser:appuser src/ .

# 쓰기 권한 제거
RUN chmod -R 555 /app

USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8080/healthz || exit 1
CMD ["python3", "server.py"]

이미지 크기 최소화

Dockerfile
# Distroless: 쉘·패키지매니저 없는 최소 이미지
FROM gcr.io/distroless/python3-debian12
COPY --from=builder /app /app
WORKDIR /app
USER nonroot:nonroot
CMD ["server.py"]

# Alpine 기반 (Go 바이너리)
FROM golang:1.22-alpine AS builder
RUN apk add --no-cache git
WORKDIR /app
COPY go.* .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o server .

FROM scratch               # 빈 이미지: 쉘, 라이브러리 전혀 없음
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534           # nobody
ENTRYPOINT ["/server"]

3. 런타임 보안 — Seccomp 프로파일

Seccomp은 컨테이너가 호출할 수 있는 시스템 콜을 허용 목록 방식으로 제한합니다.

JSON
// seccomp-profile.json (최소 허용 프로파일)
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": [
        "read", "write", "open", "close", "stat", "fstat",
        "mmap", "mprotect", "munmap", "brk",
        "rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
        "ioctl", "access", "pipe", "select", "sched_yield",
        "mremap", "msync", "mincore", "madvise",
        "dup", "dup2", "pause", "nanosleep", "getitimer",
        "alarm", "setitimer", "getpid", "sendfile",
        "socket", "connect", "accept", "sendto", "recvfrom",
        "sendmsg", "recvmsg", "shutdown", "bind", "listen",
        "getsockname", "getpeername", "socketpair",
        "setsockopt", "getsockopt", "clone", "fork",
        "execve", "exit", "wait4", "kill", "uname",
        "fcntl", "flock", "fsync", "fdatasync", "truncate",
        "ftruncate", "getcwd", "chdir", "rename", "mkdir",
        "rmdir", "unlink", "symlink", "readlink", "chmod",
        "chown", "umask", "gettimeofday", "getrlimit",
        "getrusage", "sysinfo", "times", "getuid", "getgid",
        "setuid", "setgid", "geteuid", "getegid",
        "setpgid", "getppid", "getpgrp", "setsid",
        "getgroups", "arch_prctl", "futex", "set_tid_address",
        "restart_syscall", "exit_group", "epoll_wait",
        "epoll_ctl", "tgkill", "openat", "newfstatat",
        "set_robust_list", "get_robust_list", "epoll_create1",
        "accept4", "eventfd2", "pipe2", "prlimit64"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}
Bash
# Seccomp 프로파일 적용
docker run --security-opt seccomp=seccomp-profile.json my-app

# 기본 Docker 프로파일로 실행 (권장)
docker run --security-opt seccomp=default my-app

# 프로파일 없이 실행하면 위험한 syscall 허용됨
# 절대 --security-opt seccomp=unconfined 사용하지 마세요

4. AppArmor 프로파일 적용

Bash
# AppArmor 프로파일 생성
sudo tee /etc/apparmor.d/docker-app << 'EOF'
#include <tunables/global>

profile docker-app flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  network inet tcp,
  network inet udp,

  file,
  deny /proc/sys/kernel/** w,
  deny /sys/firmware/** rwklx,

  # 특정 민감 경로 차단
  deny /etc/passwd w,
  deny /etc/shadow rwklx,
  deny /root/** rwklx,
}
EOF

sudo apparmor_parser -r /etc/apparmor.d/docker-app

# Docker 컨테이너에 AppArmor 프로파일 적용
docker run --security-opt apparmor=docker-app my-app

5. 루트리스(Rootless) Docker

루트리스 모드에서는 Docker 데몬 자체가 일반 사용자 권한으로 실행됩니다.

Bash
# 루트리스 Docker 설치
curl -fsSL https://get.docker.com/rootless | sh

# 환경 변수 설정 (~/.bashrc에 추가)
export PATH=/home/$USER/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock

# 부팅 시 자동 시작
systemctl --user enable docker
sudo loginctl enable-linger $USER

# 확인
docker info | grep "rootless"

6. Docker Content Trust — 이미지 서명 검증

Bash
# Content Trust 활성화 (서명된 이미지만 pull/push 허용)
export DOCKER_CONTENT_TRUST=1

# 이미지 서명 후 push
docker trust key generate my-signing-key
docker trust signer add --key my-signing-key.pub myuser my-registry.com/myapp
docker push my-registry.com/myapp:1.0   # 자동으로 서명

# 서명 정보 확인
docker trust inspect --pretty my-registry.com/myapp:1.0

# 서명되지 않은 이미지는 pull 거부됨
docker pull my-registry.com/myapp:untagged   # Error: no trust data

7. 런타임 이상 탐지 — Falco

Bash
# Falco 설치
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" | sudo tee /etc/apt/sources.list.d/falcosecurity.list
sudo apt update && sudo apt install -y falco
YAML
# /etc/falco/falco_rules.local.yaml — 커스텀 탐지 룰
- rule: Container Shell Spawned
  desc: 실행 중인 컨테이너에서 쉘이 시작됨 (잠재적 침해 신호)
  condition: >
    spawned_process and container and
    proc.name in (bash, sh, zsh, ksh) and
    not proc.pname in (containerd-shim, runc)
  output: >
    Shell spawned in container (user=%user.name cmd=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: WARNING
  tags: [container, shell, mitre_execution]

- rule: Write to /etc in Container
  desc: 컨테이너 내 /etc 디렉터리 쓰기 시도
  condition: >
    open_write and container and
    fd.name startswith /etc
  output: >
    Write to /etc in container (file=%fd.name container=%container.name)
  priority: ERROR
Bash
sudo systemctl enable --now falco

# 실시간 알림 확인
sudo journalctl -u falco -f
#컨테이너보안#Trivy#Seccomp#AppArmor#rootless#Docker#보안
편집 안내 · Editorial Note

이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.