Skip to content
Published on

보안 취약점 조치 플레이북: OS 패키지 · 컨테이너 이미지 · 런타임 하드닝

Authors
  • Name
    Twitter

들어가며

보안 스캐너가 CVE 목록을 쏟아내면 어디서부터 손대야 할지 막막해진다. 중요한 것은 모든 취약점을 즉시 고치는 것이 아니라, 리스크를 기준으로 우선순위를 매기고, 체계적으로 조치하고, 재발을 방지하는 프로세스를 갖추는 것이다.

이 글에서는 OS 패키지 취약점 패치, 컨테이너 이미지 보안, 런타임 하드닝을 아우르는 실전 플레이북을 제시한다.


1. 취약점 대응 프로세스 개요

┌─────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  발견    │ → │  분류    │ → │  조치    │ → │  검증    │ → │  보고    │
Scanning │    │ Triage   │    │ Remediate│Verify   │    │ Report└─────────┘    └──────────┘    └──────────┘    └──────────┘    └──────────┘

대응 SLA (권장)

심각도CVSS 점수조치 기한예시
Critical9.0~10.024~72시간RCE, 인증 우회
High7.0~8.97일권한 상승, 정보 유출
Medium4.0~6.930일XSS, 서비스 거부
Low0.1~3.990일 또는 다음 정기 패치정보 노출 (제한적)

핵심 원칙: CVSS 점수만으로 판단하지 말고, Exploit 가능 여부(EPSS), 인터넷 노출 여부, 영향받는 자산의 중요도를 종합 판단한다.


2. OS 패키지 취약점 관리

2.1 취약점 스캔

# RHEL / Rocky - 보안 업데이트 확인
dnf updateinfo list security
dnf updateinfo list --sec-severity=Critical
dnf updateinfo info RHSA-2026:1234

# Ubuntu - 보안 업데이트 확인
apt update
apt list --upgradable 2>/dev/null | grep -i security

# 설치된 패키지의 CVE 확인 (Trivy)
trivy rootfs --severity CRITICAL,HIGH /

# OpenSCAP 스캔 (컴플라이언스)
oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis \
  --results results.xml \
  --report report.html \
  /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

2.2 패치 적용 절차

#!/usr/bin/env bash
# patch-os.sh - OS 보안 패치 적용 스크립트
set -euo pipefail

LOG_FILE="/var/log/security-patch-$(date +%Y%m%d).log"
SNAPSHOT_NAME="pre-patch-$(date +%Y%m%d-%H%M%S)"

log() { printf '[%s] %s\n' "$(date +%T)" "$1" | tee -a "$LOG_FILE"; }

# 1단계: 스냅샷/백업
log "=== 패치 시작 ==="
log "스냅샷 생성: $SNAPSHOT_NAME"
# LVM 스냅샷 (해당 시)
# lvcreate -L 10G -s -n "$SNAPSHOT_NAME" /dev/vg0/root

# 2단계: 현재 상태 기록
rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > /tmp/packages-before.txt

# 3단계: 보안 업데이트만 적용
log "보안 업데이트 적용 중..."
if command -v dnf &>/dev/null; then
  dnf update --security -y 2>&1 | tee -a "$LOG_FILE"
elif command -v apt-get &>/dev/null; then
  apt-get update
  apt-get upgrade -y -o Dpkg::Options::="--force-confold" 2>&1 | tee -a "$LOG_FILE"
fi

# 4단계: 변경된 패키지 목록
rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > /tmp/packages-after.txt
diff /tmp/packages-before.txt /tmp/packages-after.txt > /tmp/packages-diff.txt || true
log "변경된 패키지:"
cat /tmp/packages-diff.txt | tee -a "$LOG_FILE"

# 5단계: 서비스 재시작 필요 여부 확인
if command -v needs-restarting &>/dev/null; then
  log "재시작 필요 서비스:"
  needs-restarting -s 2>&1 | tee -a "$LOG_FILE"
  if needs-restarting -r 2>&1 | grep -q "Reboot is required"; then
    log "WARNING: 커널 업데이트로 리부팅 필요"
  fi
fi

log "=== 패치 완료 ==="

2.3 자동 보안 업데이트 설정

RHEL / Rocky

# dnf-automatic 설치·설정
dnf install -y dnf-automatic

# /etc/dnf/automatic.conf
# [commands]
# apply_updates = yes
# upgrade_type = security  # 보안 업데이트만

systemctl enable --now dnf-automatic-install.timer

Ubuntu

# unattended-upgrades 설정
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

# /etc/apt/apt.conf.d/50unattended-upgrades
# Unattended-Upgrade::Allowed-Origins {
#   "${distro_id}:${distro_codename}-security";
# };
# Unattended-Upgrade::Automatic-Reboot "false";
# Unattended-Upgrade::Mail "admin@example.com";

2.4 패치 관리 체크리스트

  • 보안 업데이트 알림 채널 구독 (RHSA, USN, CVE 메일링 리스트)
  • 주간 취약점 스캔 자동화
  • 스테이징 → 프로덕션 패치 파이프라인 구축
  • 패치 적용 전 스냅샷/백업 필수
  • 패치 후 서비스 헬스체크 자동화
  • 커널 업데이트 시 재부팅 일정 수립
  • 패치 이력·감사 로그 보존 (최소 1년)

3. 컨테이너 이미지 보안

3.1 이미지 스캔 도구 비교

도구라이선스특징CI/CD 통합
TrivyOSS (Apache-2.0)OS + 언어 패키지 + IaC + SecretGitHub Actions, GitLab CI
GrypeOSS (Apache-2.0)SBOM 기반 스캔CLI 중심
Snyk Container상용 (무료 플랜)개발자 친화적 Fix PR대부분 CI
Prisma Cloud상용전체 CNAPP엔터프라이즈 CI
AWS ECR ScanningAWS 포함Inspector 기반AWS 네이티브

3.2 Trivy 실전 사용

# 이미지 스캔 (Critical + High만)
trivy image --severity CRITICAL,HIGH myapp:latest

# SBOM 생성
trivy image --format spdx-json -o sbom.json myapp:latest

# 취약점이 있으면 빌드 실패 (CI용)
trivy image --exit-code 1 --severity CRITICAL myapp:latest

# .trivyignore - 허용된 취약점 (예외 처리)
# CVE-2024-XXXX  # 해당 코드 경로 미사용, 2026-04-01까지 예외

3.3 안전한 Dockerfile 작성

# 1. 최소 베이스 이미지 사용
FROM cgr.dev/chainguard/python:latest-dev AS builder
# 또는 distroless, Alpine, UBI-micro

# 2. 빌드 의존성과 런타임 분리 (멀티스테이지)
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
COPY . .

FROM cgr.dev/chainguard/python:latest
WORKDIR /app
COPY --from=builder /install /usr/local
COPY --from=builder /app .

# 3. non-root 사용자 (Chainguard은 기본 non-root)
# 일반 이미지의 경우:
# RUN addgroup -S app && adduser -S app -G app
# USER app

# 4. 불필요한 패키지 제거, 캐시 정리
# (멀티스테이지로 해결)

# 5. 건강 검사
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]

ENTRYPOINT ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0"]

3.4 베이스 이미지 선택 가이드

이미지 유형크기취약점 수패키지 매니저추천 용도
ubuntu:24.04~78MB중간aptbash범용
alpine:3.20~7MB낮음apkash경량 서비스
distroless~20MB매우 낮음없음없음프로덕션 런타임
chainguard~15MB거의 0apk (dev만)없음(dev만)보안 중시
ubi-micro~35MB낮음없음없음RHEL 호환 필요
scratch0MB0없음없음Go 바이너리

3.5 CI/CD 파이프라인 통합

# GitHub Actions 예시
name: Container Security
on:
  push:
    branches: [main]
  pull_request:

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

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

      - name: Trivy vulnerability scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          severity: CRITICAL,HIGH
          exit-code: 1
          format: sarif
          output: trivy-results.sarif

      - name: Upload scan results
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: trivy-results.sarif

      - name: Generate SBOM
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          format: spdx-json
          output: sbom.spdx.json

      - name: Upload SBOM
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.spdx.json

4. 런타임 하드닝

4.1 컨테이너 런타임 보안

Docker 하드닝

# /etc/docker/daemon.json
{
  "userns-remap": "default",
  "no-new-privileges": true,
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",
  "live-restore": true,
  "icc": false,
  "default-ulimits": {
    "nofile": { "Name": "nofile", "Hard": 65535, "Soft": 65535 }
  }
}

컨테이너 실행 시 보안 옵션

docker run -d \
  --name myapp \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=100m \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges:true \
  --security-opt seccomp=default.json \
  --pids-limit 100 \
  --memory 512m \
  --cpus 1.0 \
  --user 1000:1000 \
  myapp:latest

Kubernetes Pod Security

# Pod Security Standards - Restricted 프로파일
apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: app
      image: myapp:latest
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ['ALL']
      resources:
        limits:
          memory: 512Mi
          cpu: '1'
        requests:
          memory: 256Mi
          cpu: '0.5'
      volumeMounts:
        - name: tmp
          mountPath: /tmp
  volumes:
    - name: tmp
      emptyDir:
        sizeLimit: 100Mi

4.2 Kubernetes 클러스터 하드닝 체크리스트

영역항목명령/설정
인증익명 접근 차단--anonymous-auth=false
인가RBAC 활성화--authorization-mode=RBAC
APINodeRestriction 어드미션--enable-admission-plugins=NodeRestriction
Etcd암호화 설정--encryption-provider-config
네트워크NetworkPolicy 적용Calico/Cilium 네트워크 정책
PodPSA (Pod Security Admission)네임스페이스에 enforce: restricted
시크릿외부 시크릿 관리자Vault, AWS Secrets Manager
감사Audit 로깅--audit-log-path, --audit-policy-file
이미지서명 검증Cosign + Admission Webhook
런타임Seccomp/AppArmor 프로파일Pod securityContext

4.3 네트워크 정책 예시

# 기본 거부 정책 (deny-all)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

---
# 앱 특정 허용 정책
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-traffic
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: nginx-ingress
      ports:
        - port: 8080
          protocol: TCP
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - port: 5432
          protocol: TCP
    - to: # DNS 허용
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP

5. 취약점 대응 시나리오별 플레이북

시나리오 1: Critical CVE 긴급 패치

1. [즉시] CVE 공지 확인 → 영향받는 패키지·버전 식별
2. [1시간] 자산 인벤토리에서 영향 범위 파악
3. [2시간] 패치 가용 여부 확인
   ├── 패치 있음 → 스테이징 적용 → 테스트 → 프로덕션 롤링
   └── 패치 없음 → Workaround 적용 (WAF, 설정 변경, 서비스 격리)
4. [24시간] 프로덕션 패치 완료
5. [48시간] 스캔으로 조치 검증 → 보고서 작성

시나리오 2: 컨테이너 이미지 취약점

1. 이미지 스캔 결과 확인 (Trivy/Snyk)
2. 취약점 분류:
   ├── OS 패키지 → 베이스 이미지 업데이트
   ├── 언어 라이브러리 → dependency 업데이트
   └── 설정 문제 → Dockerfile 수정
3. 이미지 재빌드 + 스캔 재실행
4. 스테이징 배포 → E2E 테스트
5. 프로덕션 롤링 업데이트
6. 이전 이미지 태그 비활성화

시나리오 3: 런타임 침해 탐지 대응

1. [즉시] 알림 확인 (Falco, 감사 로그)
2. [즉시] 해당 Pod/컨테이너 격리 (NetworkPolicy deny-all)
3. [1시간] 포렌식 데이터 수집:
   - 컨테이너 프로세스 목록, 네트워크 연결
   - 감사 로그, 런타임 이벤트 로그
   - 이미지 레이어 분석
4. [4시간] 침입 경로 파악 → 취약점 조치
5. [24시간] 클린 이미지로 재배포
6. [1] 사후 분석 보고서 + 재발 방지 대책

6. 보안 스캔 자동화 아키텍처

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  코드 푸시   │ → │  CI 파이프라인 │ → │  이미지 빌드  │
└─────────────┘    └──────┬──────┘    └──────┬──────┘
                          │                   │
                   ┌──────▼──────┐    ┌──────▼──────┐
SAST 스캔   │    │  이미지 스캔  │
                     (Semgrep)  (Trivy)                   └──────┬──────┘    └──────┬──────┘
                          │                   │
                   ┌──────▼───────────────────▼──────┐
                   │         결과 집계 + 판정          │
                      (Critical → 빌드 실패)                      (High → 경고 + Jira 생성)                   └──────────────┬──────────────────┘
                   ┌──────────────▼──────────────────┐
                   │  레지스트리 푸시 (통과 시에만)                   └──────────────┬──────────────────┘
                   ┌──────────────▼──────────────────┐
                   │  정기 스캔 (주간, 레지스트리 전체)                   └─────────────────────────────────┘

7. 컴플라이언스 매핑

프레임워크관련 요구사항이 플레이북의 대응
CIS BenchmarkOS·Docker·K8s 벤치마크섹션 2, 4
NIST 800-53SI-2 (Flaw Remediation)섹션 2 (패치 관리)
PCI DSS 4.06.3 (취약점 식별·조치)전체 프로세스
SOC 2CC7.1 (모니터링), CC8.1 (변경관리)섹션 5, 6
ISMS-P2.10 (시스템·서비스 보안관리)전체

8. 도구 체인 추천

필수 도구

용도OSS상용
이미지 스캔Trivy, GrypeSnyk, Prisma Cloud
OS 컴플라이언스OpenSCAP, LynisQualys, Tenable
런타임 보안FalcoSysdig Secure
SBOM 생성Syft, TrivyAnchore
시크릿 스캔Gitleaks, TruffleHogGitGuardian
SASTSemgrep, CodeQLSonarQube, Checkmarx
이미지 서명Cosign (Sigstore)Docker Content Trust
정책 엔진OPA/Gatekeeper, KyvernoStyra DAS

최소 구성 추천

코드 단계: Gitleaks (시크릿) + Semgrep (SAST)
빌드 단계: Trivy (이미지 스캔) + Syft (SBOM)
배포 단계: Cosign (이미지 서명) + Kyverno (정책)
런타임:   Falco (이상 탐지) + 감사 로그

9. 보고서 템플릿

취약점 조치 보고서

# 보안 취약점 조치 보고서

## 개요

- 보고일: YYYY-MM-DD
- 작성자: 보안 운영팀
- 대상 기간: YYYY-MM-DD ~ YYYY-MM-DD

## 요약

| 심각도   | 발견 | 조치 완료 | 예외 처리 | 미조치 |
| -------- | ---- | --------- | --------- | ------ |
| Critical | 3    | 3         | 0         | 0      |
| High     | 12   | 10        | 2         | 0      |
| Medium   | 45   | 30        | 5         | 10     |

## Critical 취약점 상세

### CVE-YYYY-XXXXX

- 영향: [설명]
- 영향 자산: [서버/이미지 목록]
- 조치: [패치 버전/설정 변경]
- 조치일: YYYY-MM-DD
- 검증: [스캔 결과 첨부]

## 예외 처리 목록

| CVE            | 사유                  | 보완 조치   | 만료일     |
| -------------- | --------------------- | ----------- | ---------- |
| CVE-YYYY-XXXXX | 해당 코드 경로 미사용 | WAF 룰 추가 | YYYY-MM-DD |

## 개선 권고사항

1. [구체적 개선 사항]
2. [구체적 개선 사항]

10. 실전 점검 체크리스트

OS 보안

  • 불필요한 서비스 비활성화 (systemctl list-unit-files --state=enabled)
  • SSH 키 기반 인증만 허용, root 로그인 차단
  • 자동 보안 업데이트 설정
  • 방화벽 기본 거부(deny) 정책
  • 감사 로그(auditd) 활성화
  • AIDE/OSSEC 파일 무결성 모니터링

컨테이너 보안

  • 최소 베이스 이미지 사용 (distroless/chainguard)
  • 멀티스테이지 빌드로 빌드 도구 제거
  • non-root 사용자로 실행
  • 읽기 전용 파일시스템
  • 리소스 제한 (CPU, 메모리, PID)
  • 이미지 서명 + 검증

Kubernetes 보안

  • PSA(Pod Security Admission) restricted 모드
  • NetworkPolicy 기본 deny-all
  • RBAC 최소 권한 원칙
  • Etcd 암호화
  • 감사 로깅 활성화
  • 시크릿 외부 관리 (Vault 등)

마무리

보안은 한 번의 조치가 아니라 지속적인 프로세스다. 이 플레이북의 핵심을 세 줄로 요약한다.

  1. 자동화: 스캔·패치·검증을 CI/CD에 내장하여 수동 누락을 방지한다.
  2. 계층 방어: OS·이미지·런타임·네트워크를 모두 다루는 심층 방어 전략을 수립한다.
  3. 대응 체계: 발견→분류→조치→검증→보고의 사이클을 반복하며, SLA를 지킨다.

완벽한 보안은 불가능하지만, 체계적인 운영으로 리스크를 관리 가능한 수준으로 낮출 수 있다.