보안

컨테이너 보안 마스터하기: Docker, Kubernetes 취약점 관리부터 런타임 보호까지

강코의 코딩 일기 2026. 4. 24. 09:16
반응형

Docker와 Kubernetes 환경에서 컨테이너 보안을 강화하는 방법을 자세히 알아봅니다. 빌드 단계 취약점 관리부터 런타임 보호 전략까지, 안전한 컨테이너 운영을 위한 필수 가이드를 만나보세요.

안녕하세요! IT/개발 블로그에서 보안 이야기를 나누는 개발자입니다. 요즘 개발 환경에서 컨테이너는 빠질 수 없는 존재가 되었죠. Docker로 이미지를 만들고, Kubernetes로 오케스트레이션하며 개발과 배포의 효율성을 극대화하고 있는데요. 그런데 이렇게 편리하고 강력한 컨테이너 환경, 과연 안전하게 사용하고 계신가요?

컨테이너는 가볍고 유연하지만, 그만큼 새로운 보안 위협에 노출될 수 있다는 점을 간과해서는 안 됩니다. 기존 가상머신(VM)과는 또 다른 접근 방식이 필요한데요. 오늘은 Docker와 Kubernetes 환경에서 컨테이너 보안을 효과적으로 관리하고, 런타임 중 발생할 수 있는 위협으로부터 컨테이너를 보호하는 전략에 대해 자세히 이야기해보려고 합니다. 마치 요리 레시피처럼, 단계별로 차근차근 살펴볼까요?

컨테이너 보안: Docker, Kubernetes 환경에서 취약점 관리 및 런타임 보호 전략 - chain, security, metal, iron, metal chain, chain link, metallic, iron chain, protection, barrier, chain, chain, chain, chain, chain

Image by analogicus on Pixabay

컨테이너 보안, 왜 중요할까요?

컨테이너를 사용하면서 '음, 이건 그냥 앱을 격리하는 가벼운 가상화 아냐?' 하고 생각하실 수도 있어요. 하지만 컨테이너는 호스트 OS의 커널을 공유하기 때문에, 하나의 컨테이너가 뚫리면 전체 시스템에 영향을 미칠 수 있는 잠재적인 위험을 안고 있답니다. 특히 다음과 같은 이유로 컨테이너 보안은 더욱 중요해지고 있어요.

  • 공유 커널의 위험: 컨테이너는 격리되어 있지만, 호스트의 커널을 공유해요. 만약 컨테이너에서 커널 취약점을 악용하는 공격이 성공하면, 호스트 시스템 전체가 위험해질 수 있겠죠.
  • 공급망 공격(Supply Chain Attack): 우리가 사용하는 컨테이너 이미지는 수많은 오픈소스 라이브러리와 베이스 이미지 위에 구축됩니다. 이 과정에서 악성 코드가 삽입되거나 취약한 버전의 라이브러리가 포함될 수 있어요.
  • 짧은 수명 주기와 빠른 배포: 컨테이너는 빠르게 생성되고 파괴됩니다. 전통적인 보안 도구로는 이러한 빠른 변화를 따라잡기 어렵고, 취약점이 있어도 감지하기 전에 사라져 버릴 수도 있어요.
  • 복잡한 오케스트레이션: Kubernetes 같은 오케스트레이션 도구는 컨테이너 배포를 자동화하고 관리하지만, 그만큼 설정 오류나 잘못된 구성으로 인한 보안 취약점이 발생할 가능성도 커집니다.

이러한 특성 때문에 컨테이너 보안은 단순히 방화벽이나 안티바이러스 설치를 넘어선, 다층적인 접근 방식이 필요하답니다.

컨테이너 취약점 관리: 빌드(Build) 단계부터 꼼꼼하게!

보안은 개발 생명주기의 가장 첫 단계부터 시작해야 한다는 'Shift-Left Security' 원칙, 들어보셨나요? 컨테이너 보안도 마찬가지예요. Docker 이미지를 만들고 빌드하는 단계에서부터 취약점을 최소화하는 것이 가장 중요하죠. 나중에 문제가 생겨서 고치는 것보다, 처음부터 잘 만드는 게 훨씬 효율적이거든요.

이미지 스캔 및 취약점 분석

컨테이너 이미지는 여러 레이어로 구성되어 있어요. 이 각 레이어에 어떤 소프트웨어 패키지가 포함되어 있고, 그 패키지에 알려진 취약점(CVE)은 없는지 분석하는 과정이 필수적입니다. 다행히 이를 도와주는 강력한 도구들이 많이 나와 있어요.

  • Trivy: 컨테이너 이미지, 파일 시스템, Git 레포지토리 등 다양한 소스에서 취약점을 스캔할 수 있는 인기 있는 오픈소스 도구예요. 가볍고 사용하기 쉬워서 많은 개발팀에서 활용하고 있죠.
  • Clair: CoreOS에서 개발한 오픈소스 컨테이너 취약점 분석 도구입니다. 대규모 환경에서 이미지 레지스트리와 통합되어 지속적으로 이미지를 스캔하는 데 강점을 보여요.
  • Aqua Security, Snyk: 상용 솔루션으로, 더 심층적인 분석 기능과 CI/CD 파이프라인 통합, 정책 기반 제어 등 다양한 엔터프라이즈 기능을 제공합니다.

이런 도구들을 사용해서 이미지를 빌드한 후 반드시 스캔하는 습관을 들이는 게 중요해요. 예를 들어, Trivy를 사용한다면 다음과 같이 간단하게 이미지를 스캔할 수 있습니다.


docker pull alpine:latest
trivy image alpine:latest

이렇게 스캔을 하면 이미지 내부에 어떤 취약점이 있는지, 심각도는 어느 정도인지 상세하게 알려줘요. 이를 바탕으로 취약한 패키지를 제거하거나, 더 보안이 강화된 버전으로 업데이트하는 등의 조치를 취할 수 있습니다.

최소 권한 이미지 사용 및 의존성 관리

컨테이너 이미지의 크기를 줄이는 것은 성능뿐만 아니라 보안에도 큰 도움이 됩니다. 이미지가 작으면 작을수록 포함된 소프트웨어 패키지의 수도 적어지고, 이는 곧 공격 표면(Attack Surface)을 줄이는 효과로 이어지거든요.

  • 경량화된 베이스 이미지 사용: 예를 들어, Ubuntu나 Debian 대신 Alpine Linux 같은 경량화된 베이스 이미지를 사용해 보세요. Alpine은 기본적인 유틸리티만 포함하고 있어 이미지 크기가 매우 작고, 불필요한 패키지가 적어 취약점 발생 가능성도 낮아집니다.
  • 멀티 스테이지 빌드(Multi-Stage Builds): Dockerfile에서 멀티 스테이지 빌드를 활용하면 빌드 시 필요한 도구(컴파일러, 테스트 프레임워크 등)는 첫 번째 스테이지에서만 사용하고, 최종 이미지에는 실제 애플리케이션 실행에 필요한 최소한의 파일만 포함시킬 수 있어요. 이렇게 하면 최종 이미지에 불필요한 의존성이 포함되는 것을 막을 수 있답니다.
  • 불필요한 패키지 제거: 최종 이미지에는 애플리케이션 실행에 필수적인 패키지만 남겨두세요. 셸(shell)이나 디버깅 도구 등은 프로덕션 환경에서는 굳이 필요 없잖아요?

예를 들어, Node.js 애플리케이션의 멀티 스테이지 빌드는 다음과 같이 작성할 수 있습니다.


# Stage 1: Build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install
COPY . .
RUN npm run build

# Stage 2: Run
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package.json ./package.json
EXPOSE 3000
CMD ["node", "dist/main.js"]

이렇게 하면 빌드 스테이지에서 사용된 npm이나 기타 개발 도구들은 최종 이미지에 포함되지 않아서 훨씬 안전하고 가벼운 이미지를 만들 수 있죠.

런타임 보호 전략: 동작 중인 컨테이너를 안전하게!

아무리 이미지를 꼼꼼하게 빌드해도, 컨테이너가 실행되는 동안 발생할 수 있는 위협으로부터 보호하는 것은 또 다른 영역이에요. 런타임 보안은 컨테이너가 예측 불가능한 행동을 하거나, 공격자가 침투했을 때 이를 감지하고 차단하는 데 초점을 맞춥니다.

최소 권한 원칙(Principle of Least Privilege)

이는 보안의 기본 중의 기본인데요. 컨테이너도 마찬가지예요. 컨테이너는 자신이 맡은 작업에 필요한 최소한의 권한만 가져야 합니다. 불필요한 권한은 공격자가 시스템에 침투했을 때 더 큰 피해를 입힐 수 있는 통로가 되거든요.

  • 루트(root) 권한 사용 금지: Docker 컨테이너를 실행할 때 기본적으로는 root 사용자로 실행됩니다. 하지만 `USER` 명령어를 Dockerfile에 추가하거나, `docker run` 명령에 `--user` 옵션을 사용해서 일반 사용자 계정으로 실행하는 것을 강력히 권장합니다.
  • 권한 제한(Capabilities): Docker는 컨테이너에 부여되는 리눅스 커널 권한(capabilities)을 세밀하게 제어할 수 있도록 해줍니다. `NET_RAW`, `SYS_ADMIN` 등 잠재적으로 위험한 권한은 제거하는 것이 좋아요.
  • Kubernetes RBAC(Role-Based Access Control): Kubernetes에서는 RBAC를 통해 사용자나 서비스 어카운트(Service Account)가 클러스터 내에서 어떤 리소스에 접근하고 어떤 작업을 수행할 수 있는지 명확하게 정의할 수 있어요. 꼭 필요한 권한만 부여해서 클러스터 전체의 보안 수준을 높여야 합니다.

예를 들어, Dockerfile에 `USER`를 지정해서 컨테이너가 root가 아닌 다른 사용자로 실행되도록 할 수 있습니다.


FROM alpine:latest
RUN adduser -D myuser
USER myuser
CMD ["echo", "Hello from non-root user!"]

네트워크 보안 및 세그멘테이션

컨테이너 간의 통신, 그리고 외부 네트워크와의 통신은 모두 잠재적인 공격 벡터가 될 수 있어요. 따라서 네트워크 보안을 철저히 하는 것이 중요합니다.

  • Docker 네트워크 드라이버: Docker는 다양한 네트워크 드라이버(bridge, host, overlay 등)를 제공해요. 기본 bridge 네트워크를 사용할 경우, 컨테이너 간에 불필요한 통신이 발생하지 않도록 주의해야 합니다.
  • Kubernetes Network Policies: Kubernetes에서는 Network Policies를 통해 파드(Pod) 간의 네트워크 트래픽을 세밀하게 제어할 수 있어요. 특정 파드만 특정 파드와 통신하도록 허용하거나, 특정 포트만 열어주는 등의 규칙을 설정할 수 있습니다. CalicoCilium 같은 CNI(Container Network Interface) 플러그인을 활용하면 더욱 강력한 네트워크 정책을 적용할 수 있답니다.
  • 방화벽 설정: 호스트 레벨에서 불필요한 포트를 닫고, 컨테이너가 외부로 접근할 수 있는 범위를 제한하는 방화벽 규칙을 설정하는 것도 좋은 방법이에요.

Kubernetes Network Policy 예시를 통해 특정 앱만 백엔드와 통신하도록 제한할 수 있습니다.


apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: backend
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

이 정책은 `app: backend` 레이블을 가진 파드가 `app: frontend` 레이블을 가진 파드로부터 포트 8080으로 들어오는 TCP 트래픽만 허용하도록 합니다. 다른 파드로부터의 접근은 모두 차단되는 거죠.

런타임 모니터링 및 이상 탐지

아무리 철저하게 준비해도 예상치 못한 공격은 언제든 발생할 수 있어요. 이때 중요한 것이 런타임 모니터링입니다. 컨테이너가 실행되는 동안 비정상적인 활동을 감지하고, 이에 대한 경고를 보내거나 자동으로 대응하는 시스템을 구축하는 것이 필요해요.

  • Falco: Sysdig에서 개발한 오픈소스 런타임 보안 도구로, 리눅스 커널 이벤트(시스템 호출 등)를 기반으로 컨테이너 내 비정상적인 활동을 탐지합니다. 예를 들어, 웹 서버 컨테이너에서 셸이 실행되거나, /etc/passwd 파일이 수정되는 등의 행위를 감지하여 경고를 보낼 수 있죠.
  • Sysdig Secure, Aqua Security: 상용 솔루션들은 Falco와 같은 기능을 넘어 더 정교한 정책 기반의 런타임 보호, 취약점 관리, 컴플라이언스 체크 등을 제공해요.
  • 로그 및 메트릭 수집: 컨테이너의 로그와 성능 메트릭을 중앙 집중식으로 수집하고 분석하는 시스템(ELK 스택, Prometheus + Grafana 등)을 구축하여 보안 이벤트를 빠르게 식별할 수 있도록 합니다.

Falco는 "컨테이너에서 예상치 못한 셸 실행"과 같은 규칙을 정의하여 잠재적인 침입 시도를 감지할 수 있답니다.


# Falco rule example
- rule: Shell in container
  desc: A shell was spawned in a container
  condition: >
    spawned_process and container and proc.name in ("sh", "bash", "csh", "ksh", "zsh", "ash")
    and not user.name in (root_users) # Exclude expected root processes if any
  output: >
    Shell spawned in container (user=%user.name container=%container.name
    image=%container.image.repository:%container.image.tag shell=%proc.name
    parent=%proc.pname cmdline=%proc.cmdline)
  priority: WARNING
  tags: [shell, container]

이런 규칙을 통해 컨테이너 내부에서 비정상적으로 셸이 실행될 때 즉시 알림을 받을 수 있는 거죠.

컨테이너 보안: Docker, Kubernetes 환경에서 취약점 관리 및 런타임 보호 전략 - door, lock, blue door, rusted, rusty lock, rusty padlock, padlock, closed, rusty, entrance, wooden door, old, wooden, metal, antique, locked, protected, blue wood, door, door, door, door, door, lock, lock, lock, lock, closed, closed, closed

Image by haalkab on Pixabay

Kubernetes 환경 보안 강화: 오케스트레이션의 힘을 빌려!

Kubernetes는 컨테이너를 오케스트레이션하는 강력한 도구이지만, 그만큼 복잡하고 잘못 설정할 경우 큰 보안 구멍이 될 수 있습니다. Kubernetes 자체의 보안 기능을 적극적으로 활용하는 것이 핵심이에요.

Pod Security Standards (PSS) 적용

Kubernetes는 파드(Pod)의 보안을 강화하기 위해 Pod Security Standards (PSS)를 제공합니다. 이는 파드에 적용할 수 있는 보안 정책의 집합인데요.

  • Privileged: 제한 없음 (가장 취약)
  • Baseline: 잘 알려진 권한 에스컬레이션 방지 (일반적인 워크로드에 적합)
  • Restricted: 보안 강화를 위한 엄격한 제한 (최고 수준의 보안)

이러한 표준을 네임스페이스에 적용하여 해당 네임스페이스에서 생성되는 모든 파드가 특정 보안 기준을 만족하도록 강제할 수 있어요. 예를 들어, `Restricted` 프로파일을 적용하면 파드는 루트 사용자로 실행될 수 없으며, 특정 권한이 자동으로 제거됩니다.

네임스페이스에 `restricted` 프로파일을 적용하는 예시입니다.


apiVersion: v1
kind: Namespace
metadata:
  name: my-secure-namespace
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/audit: restricted

이렇게 설정하면 `my-secure-namespace`에 배포되는 파드들은 `restricted` 정책을 따르지 않으면 생성을 거부하거나 경고를 받게 됩니다.

시크릿 관리

데이터베이스 접속 정보, API 키, 인증서 등 민감한 정보는 시크릿(Secret)으로 관리해야 합니다. 일반적인 환경 변수나 설정 파일에 저장하는 것은 매우 위험하죠.

  • Kubernetes Secrets: Kubernetes는 기본적으로 Secrets 리소스를 제공합니다. 이 리소스를 사용하면 민감한 정보를 안전하게 저장하고, 파드에 볼륨으로 마운트하거나 환경 변수로 주입할 수 있어요. 하지만 Kubernetes Secrets는 기본적으로 Base64 인코딩만 되어 있을 뿐, 암호화되어 저장되는 것은 아니라는 점을 기억해야 합니다.
  • 외부 시크릿 관리 도구: 더 강력한 보안을 위해서는 HashiCorp Vault, AWS Secrets Manager, Azure Key Vault 같은 외부 시크릿 관리 도구와 Kubernetes를 연동하는 것이 좋습니다. 이 도구들은 시크릿을 암호화하여 저장하고, 접근 제어 및 감사 기능을 제공하여 보안을 한층 더 강화할 수 있거든요.

API 서버 보안 및 인증/인가

Kubernetes API 서버는 클러스터의 모든 작업을 제어하는 핵심적인 구성 요소예요. 따라서 API 서버에 대한 접근을 철저히 제어하는 것이 매우 중요합니다.

  • 인증(Authentication): API 서버에 접근하는 모든 사용자나 서비스는 반드시 인증되어야 합니다. 클라이언트 인증서, 베어러 토큰, OpenID Connect(OIDC) 등의 다양한 인증 방식을 활용할 수 있어요.
  • 인가(Authorization): 인증된 사용자나 서비스라도 모든 작업을 수행할 수 있는 것은 아닙니다. RBAC(Role-Based Access Control)를 사용하여 특정 사용자나 서비스 어카운트가 어떤 리소스에 대해 어떤 작업을 수행할 수 있는지 세밀하게 정의해야 합니다.
  • 감사 로깅(Audit Logging): API 서버에서 발생하는 모든 요청에 대해 감사 로그를 활성화하고, 이를 중앙 집중식으로 수집하고 분석하여 잠재적인 보안 위협을 감지할 수 있도록 합니다.

CI/CD 파이프라인에 보안 통합

앞서 말씀드렸던 Shift-Left SecurityCI/CD 파이프라인에 보안을 통합하는 것을 의미해요. 개발 초기 단계부터 보안을 고려하고, 자동화된 방식으로 보안 검사를 수행하는 것이죠.

  • 정적 코드 분석(SAST): 코드 작성 단계에서부터 잠재적인 보안 취약점을 발견할 수 있도록 정적 코드 분석 도구를 CI/CD 파이프라인에 통합합니다.
  • 컨테이너 이미지 스캔 자동화: Docker 이미지가 빌드될 때마다 자동으로 TrivyClair 같은 도구를 사용해서 취약점을 스캔하고, 심각한 취약점이 발견되면 빌드를 실패시키는 등의 정책을 적용할 수 있어요.
  • 배포 전 정책 강제(Policy Enforcement): Kubernetes에 배포되기 전에 Open Policy Agent (OPA)와 같은 도구를 사용하여 미리 정의된 보안 정책(예: 루트 권한으로 실행되는 파드 금지, 특정 이미지 레지스트리만 허용 등)을 검사하고 위반 시 배포를 차단할 수 있습니다.

이렇게 CI/CD 파이프라인에 보안을 통합하면 개발자가 코드를 푸시하는 순간부터 최종 배포까지 모든 단계에서 보안이 자동으로 검증되므로, 수동으로 처리하는 것보다 훨씬 빠르고 정확하게 보안 문제를 해결할 수 있습니다.

컨테이너 보안: Docker, Kubernetes 환경에서 취약점 관리 및 런타임 보호 전략 - padlock, lock, chain, key, security, protection, safety, access, locked, link, crime, steel, privacy, secure, criminal, shackle, danger, thief, theft, vulnerable, restrain, break-in, protect, strong, padlock, padlock, lock, lock, lock, lock, lock, chain, crime, privacy, privacy, thief, thief, theft, strong

Image by stevepb on Pixabay

Docker와 Kubernetes 보안 전략 비교

DockerKubernetes는 컨테이너 환경에서 각기 다른 계층에서 보안을 담당합니다. 이를 비교해 보면 각자의 역할과 중요성을 더 명확히 이해할 수 있을 거예요.

보안 영역 Docker (단일 컨테이너/호스트) Kubernetes (클러스터/오케스트레이션)
이미지 취약점 관리 Docker 이미지 빌드 시 경량 베이스 이미지 사용, 멀티 스테이지 빌드, 이미지 스캔 도구(Trivy 등) 활용 CI/CD 파이프라인에 이미지 스캔 통합, 이미지 레지스트리 보안(권한 제어, 서명된 이미지 사용)
런타임 보호 컨테이너 최소 권한 실행(non-root user), Docker Capabilties 관리, AppArmor/SELinux 정책 적용 Pod Security Standards (PSS) 적용, 컨테이너 런타임 보안 도구(Falco 등) 통합, SecurityContext 설정
네트워크 보안 Docker 네트워크 드라이버 선택, 호스트 방화벽 규칙 적용, 컨테이너 포트 노출 최소화 Kubernetes Network Policies 활용, CNI 플러그인(Calico, Cilium)을 통한 고급 네트워크 제어, Service Mesh 통합
시크릿 관리 Docker Swarm Secret 기능 활용 (단일 호스트 환경에서는 제한적) Kubernetes Secrets 리소스 사용, 외부 시크릿 관리 도구(Vault 등) 연동, Secret 암호화
접근 제어 및 인증 Docker Daemon 접근 제어, Host OS 사용자/권한 관리 Kubernetes RBAC, Service Account, API 서버 인증/인가, 클러스터 사용자 인증

보시는 것처럼 Docker는 주로 개별 컨테이너와 호스트 레벨의 보안에 집중하는 반면, Kubernetes는 클러스터 전체의 오케스트레이션과 리소스 관점에서의 보안을 강화하는 데 초점을 맞추고 있어요. 이 둘은 상호 보완적인 관계를 가지며, 함께 사용될 때 가장 강력한 보안 시너지를 낼 수 있답니다.

마무리하며: 지속적인 관심과 노력이 중요해요!

오늘은 컨테이너 보안, 특히 Docker와 Kubernetes 환경에서 취약점 관리와 런타임 보호 전략에 대해 자세히 알아봤는데요. 어떠셨나요? 컨테이너 환경의 보안은 단순히 한 번 설정하고 끝나는 것이 아니라, 끊임없이 변화하는 위협에 맞춰 지속적인 관심과 노력이 필요한 영역이라는 점을 꼭 기억해 주세요.

빌드 단계에서의 이미지 보안, 런타임 중의 행위 모니터링, 그리고 Kubernetes 오케스트레이션의 강력한 보안 기능 활용까지, 이 모든 것이 유기적으로 연결될 때 비로소 안전한 컨테이너 환경을 구축할 수 있답니다. 오늘 알려드린 내용들이 여러분의 컨테이너 환경을 더욱 튼튼하게 만드는 데 도움이 되기를 바랍니다.

혹시 여러분은 어떤 컨테이너 보안 전략을 사용하고 계신가요? 또는 오늘 내용 중에서 궁금한 점이나 더 깊이 다뤄보고 싶은 주제가 있다면 언제든지 댓글로 남겨주세요! 함께 이야기 나누면 더 좋은 보안 노하우를 얻을 수 있을 거예요. 다음에 또 유익한 글로 찾아뵙겠습니다!

📌 함께 읽으면 좋은 글

  • [보안] OWASP Top 10으로 배우는 웹 취약점 분석과 실전 방어 전략
  • [보안] CI/CD 파이프라인 보안 강화: SAST/DAST 통합 전략과 효과
  • [AI 머신러닝] LLM RAG 시스템 구축: 벡터 데이터베이스와 임베딩 모델 실전 가이드

이 글이 도움이 되셨다면 공감(♥)댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.

반응형