🤖 AI 생성 콘텐츠 이 글은 AI(Claude)의 도움을 받아 작성되었습니다. 오류가 있을 수 있으니 중요한 내용은 참고 자료의 원문으로 확인해 주세요.
보안 스캐너가 CVE 목록을 쏟아내면 어디서부터 손대야 할지 막막해진다. 중요한 것은 모든 취약점을 즉시 고치는 것이 아니라, 리스크를 기준으로 우선순위를 매기고, 체계적으로 조치하고, 재발을 방지하는 프로세스 를 갖추는 것이다.
이 글에서는 OS 패키지 취약점 패치, 컨테이너 이미지 보안, 런타임 하드닝 을 아우르는 실전 플레이북을 제시한다.
┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 발견 │ → │ 분류 │ → │ 조치 │ → │ 검증 │ → │ 보고 │
│ Scanning │ │ Triage │ │ Remediate│ │ Verify │ │ Report │
└─────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
심각도 CVSS 점수 조치 기한 예시 Critical 9.0~10.0 24~72시간 RCE, 인증 우회 High 7.0~8.9 7일 권한 상승, 정보 유출 Medium 4.0~6.9 30일 XSS, 서비스 거부 Low 0.1~3.9 90일 또는 다음 정기 패치 정보 노출 (제한적)
핵심 원칙 : CVSS 점수만으로 판단하지 말고, Exploit 가능 여부(EPSS) , 인터넷 노출 여부 , 영향받는 자산의 중요도 를 종합 판단한다.
dnf updateinfo list security
dnf updateinfo list --sec-severity= Critical
dnf updateinfo info RHSA-2026:1234
apt update
apt list --upgradable 2 > /dev/null | grep -i security
trivy rootfs --severity CRITICAL,HIGH /
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
#!/usr/bin/env bash
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 " ; }
log "=== 패치 시작 ==="
log "스냅샷 생성: $SNAPSHOT_NAME "
rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' | sort > /tmp/packages-before.txt
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
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 "
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 "=== 패치 완료 ==="
dnf install -y dnf-automatic
systemctl enable --now dnf-automatic-install.timer
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
도구 라이선스 특징 CI/CD 통합 Trivy OSS (Apache-2.0) OS + 언어 패키지 + IaC + Secret GitHub Actions, GitLab CI Grype OSS (Apache-2.0) SBOM 기반 스캔 CLI 중심 Snyk Container 상용 (무료 플랜) 개발자 친화적 Fix PR 대부분 CI Prisma Cloud 상용 전체 CNAPP 엔터프라이즈 CI AWS ECR Scanning AWS 포함 Inspector 기반 AWS 네이티브
trivy image --severity CRITICAL,HIGH myapp:latest
trivy image --format spdx-json -o sbom.json myapp:latest
trivy image --exit-code 1 --severity CRITICAL myapp:latest
FROM cgr.dev/chainguard/python:latest-dev AS builder
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 .
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" ]
이미지 유형 크기 취약점 수 패키지 매니저 쉘 추천 용도 ubuntu:24.04~78MB 중간 apt bash 범용 alpine:3.20~7MB 낮음 apk ash 경량 서비스 distroless~20MB 매우 낮음 없음 없음 프로덕션 런타임 chainguard~15MB 거의 0 apk (dev만) 없음(dev만) 보안 중시 ubi-micro~35MB 낮음 없음 없음 RHEL 호환 필요 scratch0MB 0 없음 없음 Go 바이너리
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
{
"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
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
영역 항목 명령/설정 인증 익명 접근 차단 --anonymous-auth=false인가 RBAC 활성화 --authorization-mode=RBACAPI NodeRestriction 어드미션 --enable-admission-plugins=NodeRestrictionEtcd 암호화 설정 --encryption-provider-config네트워크 NetworkPolicy 적용 Calico/Cilium 네트워크 정책 Pod PSA (Pod Security Admission) 네임스페이스에 enforce: restricted 시크릿 외부 시크릿 관리자 Vault, AWS Secrets Manager 감사 Audit 로깅 --audit-log-path, --audit-policy-file이미지 서명 검증 Cosign + Admission Webhook 런타임 Seccomp/AppArmor 프로파일 Pod securityContext
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 :
- namespaceSelector : { }
podSelector :
matchLabels :
k8s-app : kube- dns
ports :
- port : 53
protocol : UDP
- port : 53
protocol : TCP
1. [ 즉시] CVE 공지 확인 → 영향받는 패키지·버전 식별
2. [ 1 시간] 자산 인벤토리에서 영향 범위 파악
3. [ 2 시간] 패치 가용 여부 확인
├── 패치 있음 → 스테이징 적용 → 테스트 → 프로덕션 롤링
└── 패치 없음 → Workaround 적용 ( WAF 룰, 설정 변경, 서비스 격리)
4. [ 24 시간] 프로덕션 패치 완료
5. [ 48 시간] 스캔으로 조치 검증 → 보고서 작성
1. 이미지 스캔 결과 확인 ( Trivy / Snyk )
2. 취약점 분류:
├── OS 패키지 → 베이스 이미지 업데이트
├── 언어 라이브러리 → dependency 업데이트
└── 설정 문제 → Dockerfile 수정
3. 이미지 재빌드 + 스캔 재실행
4. 스테이징 배포 → E2E 테스트
5. 프로덕션 롤링 업데이트
6. 이전 이미지 태그 비활성화
1. [ 즉시] 알림 확인 ( Falco , 감사 로그)
2. [ 즉시] 해당 Pod / 컨테이너 격리 ( NetworkPolicy deny- all)
3. [ 1 시간] 포렌식 데이터 수집:
- 컨테이너 프로세스 목록, 네트워크 연결
- 감사 로그, 런타임 이벤트 로그
- 이미지 레이어 분석
4. [ 4 시간] 침입 경로 파악 → 취약점 조치
5. [ 24 시간] 클린 이미지로 재배포
6. [ 1 주] 사후 분석 보고서 + 재발 방지 대책
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 코드 푸시 │ → │ CI 파이프라인 │ → │ 이미지 빌드 │
└─────────────┘ └──────┬──────┘ └──────┬──────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ SAST 스캔 │ │ 이미지 스캔 │
│ ( Semgrep ) │ │ ( Trivy ) │
└──────┬──────┘ └──────┬──────┘
│ │
┌──────▼───────────────────▼──────┐
│ 결과 집계 + 판정 │
│ ( Critical → 빌드 실패) │
│ ( High → 경고 + Jira 생성) │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ 레지스트리 푸시 ( 통과 시에만) │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ 정기 스캔 ( 주간, 레지스트리 전체) │
└─────────────────────────────────┘
프레임워크 관련 요구사항 이 플레이북의 대응 CIS Benchmark OS·Docker·K8s 벤치마크 섹션 2, 4 NIST 800-53 SI-2 (Flaw Remediation) 섹션 2 (패치 관리) PCI DSS 4.0 6.3 (취약점 식별·조치) 전체 프로세스 SOC 2 CC7.1 (모니터링), CC8.1 (변경관리) 섹션 5, 6 ISMS-P 2.10 (시스템·서비스 보안관리) 전체
용도 OSS 상용 이미지 스캔 Trivy, Grype Snyk, Prisma Cloud OS 컴플라이언스 OpenSCAP, Lynis Qualys, Tenable 런타임 보안 Falco Sysdig Secure SBOM 생성 Syft, Trivy Anchore 시크릿 스캔 Gitleaks, TruffleHog GitGuardian SAST Semgrep, CodeQL SonarQube, Checkmarx 이미지 서명 Cosign (Sigstore) Docker Content Trust 정책 엔진 OPA/Gatekeeper, Kyverno Styra DAS
코드 단계: Gitleaks ( 시크릿) + Semgrep ( SAST )
빌드 단계: Trivy ( 이미지 스캔) + Syft ( SBOM )
배포 단계: Cosign ( 이미지 서명) + Kyverno ( 정책)
런타임 : Falco ( 이상 탐지) + 감사 로그
# 보안 취약점 조치 보고서
## 개요
- 보고일: 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-YYYY-XXXXX | 해당 코드 경로 미사용 | WAF 룰 추가 | YYYY-MM-DD |
## 개선 권고사항
1. [구체적 개선 사항]
2. [구체적 개선 사항]
보안은 한 번의 조치가 아니라 지속적인 프로세스 다. 이 플레이북의 핵심을 세 줄로 요약한다.
자동화 : 스캔·패치·검증을 CI/CD에 내장하여 수동 누락을 방지한다.
계층 방어 : OS·이미지·런타임·네트워크를 모두 다루는 심층 방어 전략을 수립한다.
대응 체계 : 발견→분류→조치→검증→보고의 사이클을 반복하며, SLA를 지킨다.
완벽한 보안은 불가능하지만, 체계적인 운영으로 리스크를 관리 가능한 수준으로 낮출 수 있다 .