Skip to main content

오픈 소스 워게임 시뮬레이션 환경 c2z 회고

· 11 min read
te
Sofrware Engineer

title

KT Cloud TECHUP 사이버 보안 과정의 첫 팀 프로젝트로 CVE 기반 취약점 재현 환경을 위한 오픈 소스 시뮬레이션 환경을 개발했습니다.

프로젝트 개요​

결과 링크:

팀 구성:

  • 5인으로 이루어진 팀이며, 제가 리드 역할을 맡았습니다.
  • 1명의 컴퓨터 관련 전공자와 저를 포함한 4명의 비전공자로 구성됐습니다.
    • 팀원 다수가 레드팀/모의해킹 분야를 희망하였습니다.
  • 1달 동안의 프로젝트 기간을 2개의 스프린트로 나누어 수행하였습니다.
  • 각 스프린트 동안 서브 팀을 구성하여, 팀원들이 다양한 역할을 맡아 기여할 수 있도록 조직했습니다.
  • 프로젝트에서 각 팀원들의 작업이 서로 협업할 수 있도록 리드했습니다.
    • 각자 맡은 역할이 서로 선행되어야 진행될 수 있도록 태스크를 분배하여, 팀원 간 서로 맡은 작업을 이해할 수 있도록 하였습니다.
    • 각자 맡은 작업을 완료한 뒤, 보고서 및 문서화를 강조하여 서로 어떤 작업을 수행했는지 이해할 수 있도록 하였습니다.
    • 매일, 매주, 매 스프린트마다 회고를 진행하여 팀원 각자 어떤 노력을 했고, 기술적 성장을 이루었는지 공유했습니다.

프로젝트 기간:

  • 총 5주
  • Sprint 1 : 2025/12/08 ~ 2025/12/19
  • Sprint 2 : 2025/12/22 ~ 2026/01/07

역할

각 팀원의 관심사와 목표에 따라 스프린트 별로 내부 팀을 나누었습니다.

  • Sprint 1: 재현환경 개발팀 / 공격 설계팀
  • Sprint 2: 블루팀 / 레드팀

저는 각각의 스프린트에서 재현환경 개발팀-블루팀에 속하였으나,
팀 리드로서 팀 서포트 및 협업을 수행했습니다.


c2z 오픈소스 CVE 재현 환경 시뮬레이터​

소개​

c2z-pj

c2z(Container to Zone)는 Kubernetes 네이티브 환경에서 동작하는 자동화된 CVE 재현 환경 시뮬레이터입니다.

  • Helm Chart를 통해 배포되며, GitHub Pages를 통해 퍼블릭 Helm Repository로 제공됩니다.
  • 보안 연구자와 학습자가 실전적인 취약점 분석 환경을 빠르게 구축할 수 있습니다.
  • k3d(macOS) 및 k3s(Linux)를 지원하여 로컬 환경에서 손쉽게 실행 가능합니다.
  • 컨테이너 기반으로 가상머신 대비 현저히 낮은 리소스 점유율을 자랑합니다.
  • CVE별 Docker 이미지를 GHCR(GitHub Container Registry)에 배포하여 재현 환경을 표준화했습니다.
  • Cloudflare Tunnel을 활용한 외부 접근 자동화 스크립트를 제공합니다.

기여​

각 스프린트 별로는 다음과 같은 작업을 수행했습니다:

  • 스프린트 1 :
    • 재현환경 개발팀에 속하여 공격 설계팀이 조사 내용을 바탕으로 재현 환경에 필요한 Docker 이미지와 Kubernetes 매니페스트를 개발하였습니다.
    • 쿠버네티스 클러스터 구축을 위한 Helm 차트를 개발했습니다. /simulation과 /monitoring Pod 및 Service를 구성하였습니다.
  • 스프린트 2 :
    • 블루팀에 속하여 재현 환경이 정상적으로 실행되지 않을 때 디버깅하였고, 포트포워딩 및 구축 환경에 필요한 스크립트를 개발하였습니다.
    • 레드팀이 침투에 성공한 환경을 버전 패치하고, 추가 재현 환경을 위한 Pod를 개발했습니다.

구체적으로 c2z를 개발하면서 제가 기여한 부분은 다음과 같습니다:

  • 프로젝트 전체 아키텍처 설계 및 Kubernetes 리소스 구조 설계
  • 5개 CVE 재현 환경 개발 (Docker 이미지 빌드 및 K8s 매니페스트 작성)
    • CVE-2025-55182: Next.js 16 (React2Shell)
    • CVE-2025-66205: Frappe (Frappe/ Redis / MySQL)
    • CVE-2025-56749: Academy LMS (Apache/ PHP / MySQL)
    • CVE-2025-63531: Blood Bank (Apache/ PHP / MariaDB)
    • CVE-2025-63689: Money POS (Apache/ PHP / MySQL)
  • Helm Chart 개발 및 GitHub Pages 기반 Helm Repository 구축
  • 멀티 플랫폼(linux/amd64, linux/arm64) Docker 이미지 빌드 자동화
  • install.sh 자동 설치 스크립트 개발
  • Cloudflare Tunnel 기반 외부 노출 자동화 스크립트 (expose_simulation_tunnel.py)
  • CI/CD 배포 파이프라인 개발 및 유지보수

시스템 설계 및 동작 원리​

c2z는 Kubernetes를 기반으로 한 컨테이너 오케스트레이션 환경에서 동작합니다.

k8s

전체 시스템은 크게 세 가지 계층으로 구성됩니다:

1. 배포 계층 (Deployment Layer) 사용자는 install.sh 스크립트를 실행하여 환경을 자동으로 구축합니다. 스크립트는 다음을 수행합니다:

  • Python 가상환경 설정 및 의존성 설치
  • 시스템 요구사항 검증 (RAM, Docker 등)
  • Kubernetes 클러스터 설치 (macOS는 k3d, Linux는 k3s)
  • Helm 설치 및 c2z Chart 배포
  • GHCR 인증을 위한 Secret 생성 및 복제

2. 런타임 계층 (Runtime Layer) Kubernetes 클러스터 내에서 CVE 재현 환경이 Pod로 실행됩니다.

  • 각 CVE는 독립된 Pod와 Service로 격리되어 실행
  • simulation 네임스페이스에서 모든 취약점 환경 관리
  • GHCR에서 프라이빗 이미지를 Pull하여 컨테이너 실행
  • 멀티 컨테이너 Pod 지원 (예: 웹 서버 + 데이터베이스)

3. 외부 노출 계층 (Exposure Layer) 로컬 클러스터의 서비스를 외부에서 접근 가능하도록 만듭니다.

  • kubectl port-forward로 로컬 포트 바인딩
  • Cloudflare Tunnel(cloudflared)을 통해 공개 URL 생성
  • Python 스크립트로 전체 프로세스 자동화

이러한 구조를 통해 한 번의 설치로 여러 CVE 환경을 동시에 실행할 수 있으며,
각 환경은 독립적으로 관리되어 서로 영향을 주지 않도록 작업했습니다.


CVE 재현 작업​

프로젝트 기간 동안 총 5개의 CVE 재현 환경을 개발했습니다. 각 CVE마다 고유한 기술 스택과 환경 설정이 필요했기에, 다양한 학습과 트러블 슈팅을 경험할 수 있었습니다.

CVE-2025-55182: Next.js 16 취약점​

Next.js 16.0.6Next.js 16.0.10
c2z-nextjs-16.0.6c2z-nextjs-16.0.10
React Server Component + Next.js (No API route)Next.js 16.0.6 → 16.0.10 패치 + API route 엔드포인트 개발

Next.js 16 버전의 SSR 관련 취약점을 재현하는 환경입니다. 이 작업에서는 Node.js 기반 애플리케이션을 컨테이너화하는 과정을 학습했습니다.

작업 내용:

  • Next.js 16 애플리케이션 Docker 이미지 빌드
  • 멀티 스테이지 빌드를 통한 이미지 크기 최적화
  • Kubernetes Pod 및 Service 매니페스트 작성
  • GHCR 프라이빗 레지스트리 배포 자동화

CVE-2025-66205: Frappe Framework​

c2z-frappe

Frappe는 Python 기반 풀스택 프레임워크로, 상당히 복잡한 설정이 필요했습니다.
특히 초기화 컨테이너(Init Container) 패턴을 처음 적용해본 작업이었습니다.

작업 내용:

  • Frappe 취약 버전 기본 이미지(frappe/base:v15.85.1) 기반 커스텀 이미지 빌드
  • MariaDB 연동을 위한 멀티 컨테이너 Pod 구성
  • Init Container를 통한 데이터베이스 초기화 자동화
  • bench 명령어를 활용한 Frappe 애플리케이션 설정

이 과정에서 Init Container가 메인 컨테이너보다 먼저 실행되어
사전 작업(DB 마이그레이션, 설정 초기화 등)을 수행하는 패턴을 익혔습니다.

CVE-2025-63531: Blood Bank 시스템​

c2z-bb

PHP 기반 레거시 애플리케이션으로, 데이터베이스 연동이 핵심이었습니다.

작업 내용:

  • Apache + PHP 환경 Docker 이미지 구성
  • MariaDB 컨테이너와의 네트워크 연결 설정
  • 환경 변수를 통한 DB 크레덴셜 관리
  • Pod 내 볼륨 마운트를 통한 초기 SQL 스크립트 개발

레거시 PHP 애플리케이션을 컨테이너화하면서,
기존 코드 수정 없이 환경 변수만으로 설정을 주입하는 방법을 배웠습니다.

CVE-2025-63689: Money POS​

Java Spring Boot 기반의 복잡한 엔터프라이즈 애플리케이션이었습니다.
여러 의존성 서비스를 조율하는 작업이 가장 도전적이었습니다.

작업 내용:

  • MySQL, Redis, XXL-Job-Admin, Money-App-Biz 4개 컴포넌트 통합
  • 각 컴포넌트별 Docker 이미지 빌드
  • Kubernetes Deployment 및 Service 리소스 작성
  • 컴포넌트 간 네트워크 통신 검증

이 작업을 통해 마이크로서비스 아키텍처를 Kubernetes로 구현하는 실전 경험을 쌓을 수 있었습니다.

CVE-2025-56749: Academy LMS​

PHP 기반 학습 관리 시스템으로, 권한 상승 BAC 취약점을 재현하는 환경이었습니다.

작업 내용:

  • Apache + PHP + MySQL 스택 구성
  • 볼륨 마운트를 통한 영구 데이터 저장
  • 파일 업로드 디렉토리 권한 설정

Kubernetes 및 Helm 기술 스택​

Kubernetes 네이티브 환경 구축​

이전에는 가상머신 기반으로 보안 랩을 구성했었는데,
이번 프로젝트에서는 Kubernetes를 선택했습니다.

Kubernetes를 선택한 이유:

  • 컨테이너 기반으로 리소스 효율성이 높음
  • 선언적 설정(YAML)으로 환경 재현이 용이
  • Pod, Service, Namespace 등으로 명확한 격리 가능
  • Helm을 통한 패키징 및 배포 자동화
  • OpenStack 환경 설치 용이성을 고려

c2z 또한 오픈소스를 감안하여 작업하였기애, 누구나 git clone으로 쉽게 설치할 수 있도록 install.sh 스크립트를 작성했습니다.
macOS와 Linux 환경 모두 지원하기 위해:

  • macOS: k3d (Docker Desktop 위에서 실행되는 경량 Kubernetes)
  • Linux: k3s (네이티브 경량 Kubernetes)

를 각각 지원하도록 install.sh 스크립트를 작성했습니다.

특히 k3d 클러스터 생성 시 로드밸런서를 통한 포트 포워딩 설정이 중요했습니다:

k3d cluster create c2z \
--api-port 6443 \
--port "80:80@loadbalancer" \
--port "443:443@loadbalancer" \
--port "3000-3005:3000-3005@loadbalancer" \
--agents 1 \
--k3s-arg "--disable=traefik@server:*"

이 설정으로 호스트의 특정 포트를 클러스터 내부 서비스로 직접 연결할 수 있었습니다.

Helm Chart 개발​

Kubernetes 매니페스트를 직접 관리하는 것은 복잡하고 오류가 발생하기 쉽습니다.
이에 Helm을 도입하여 패키징, 버전 관리, 배포를 표준화했습니다.

Helm Chart 구조:

charts/c2z/
├── Chart.yaml # Chart 메타데이터
├── values.yaml # 기본 설정 값
└── templates/
├── namespaces.yaml
├── simulation/ # CVE 재현 환경 템플릿
│ ├── cve-2025-55182.yaml
│ ├── cve-2025-55182-service.yaml
│ ├── cve-2025-66205.yaml
│ └── ...
└── monitoring/ # 모니터링 스택 (구현 예정)

Helm을 통해 얻은 이점:

  • 템플릿화: 공통 패턴을 재사용하여 코드 중복 제거
  • 값 주입: values.yaml로 환경별 설정 관리
  • 원클릭 배포: helm install 한 번으로 전체 환경 구축
  • 버전 관리: Chart 버전으로 릴리스 추적

Helm Repository (GitHub Pages)​

c2z-helm

Helm Chart를 누구나 사용할 수 있도록 퍼블릭 Repository로 배포했습니다.
GitHub Pages를 활용하여 Helm Repository 또한 연동하였고, CI/CD 파이프라인에 통합했습니다.

구현 내용:

  1. GitHub Actions 워크플로우 작성 (.github/workflows/deploy.yml)
  2. main 브랜치에 푸시 시 자동으로 Chart 패키징
  3. helm repo index로 index.yaml 생성 및 업데이트
  4. GitHub Pages에 배포

사용자는 다음 명령어로 c2z를 설치할 수 있습니다:

helm repo add c2z https://s2n0n.github.io/c2z/
helm repo update
helm install c2z c2z/c2z

이를 통해 오픈소스 프로젝트로서의 접근성을 크게 높일 수 있었습니다.

Cloudflare Tunnel을 활용한 외부 노출​

c2z-tunnel

로컬 Kubernetes 클러스터의 서비스를 외부에서 접근 가능하게 만들어야 했습니다.
ngrok도 고려했지만, 최종적으로 Cloudflare Tunnel을 선택했습니다.

Cloudflare Tunnel의 장점:

  • 무료로 무제한 터널 생성 가능
  • HTTPS 자동 적용
  • Docker 이미지로 제공되어 쉽게 실행 가능
  • 안정적인 연결 유지

자동화 스크립트 (expose_simulation_tunnel.py) 동작 원리:

  1. kubectl get svc -n simulation으로 서비스 목록 조회
  2. 각 서비스마다 사용 가능한 로컬 포트 찾기
  3. kubectl port-forward로 서비스를 로컬 포트에 바인딩
  4. cloudflared tunnel로 해당 로컬 포트를 공개 URL로 노출
  5. 생성된 URL 목록 출력

이 스크립트 하나로 모든 CVE 환경을 동시에 외부 접근 가능하게 만들 수 있었습니다.


인프라 자동화 및 배포 파이프라인​

GHCR Secret 관리 자동화​

프라이빗 이미지를 Kubernetes에서 Pull하려면 ImagePullSecret이 필요합니다.
처음에는 수동으로 Secret을 생성했는데, 여러 네임스페이스에 복제하는 과정이 번거로웠습니다.

install.sh 스크립트에 자동화 로직 추가:

  1. .env.local 파일에서 GitHub 인증 정보 로드
  2. c2z-system 네임스페이스에 ghcr-secret 생성
  3. simulation 네임스페이스로 Secret 복제
  4. 모든 Pod 템플릿에 imagePullSecrets 자동 적용

이 자동화를 통해 사용자는 한 번의 설정으로 모든 CVE 환경을 배포할 수 있게 되었습니다.

원클릭 설치 스크립트 (install.sh)​

초기에는 Kubernetes 설치, Helm 설치, Chart 배포를 모두 수동으로 진행했습니다.
하지만 팀원들과 사용자들이 쉽게 환경을 구축할 수 있도록 자동화 스크립트를 개발했습니다.

install.sh의 주요 기능:

  • Python 가상환경 자동 생성 및 의존성 설치
  • 시스템 요구사항 검증 (RAM, Docker 등)
  • OS 감지 및 적절한 Kubernetes 배포 (k3d/k3s)
  • Helm 자동 설치
  • GHCR Secret 대화형 생성
  • c2z Helm Chart 자동 배포
  • CLI 래퍼 스크립트 생성

사용자는 단 두 줄의 명령어로 전체 환경을 구축할 수 있습니다:

chmod +x install.sh
./install.sh

이 스크립트를 작성하면서 **사용자 경험(UX)**의 중요성을 다시 한번 느꼈습니다.


트러블 슈팅 및 기술적 도전​

c2z 프로젝트는 s2n보다 훨씬 다양한 기술 스택을 다루었기에,
더 많은 트러블 슈팅과 학습이 필요했습니다.

ImagePullBackOff 해결​

Kubernetes 초보자가 가장 흔히 마주치는 에러 중 하나입니다.
처음 CVE Pod를 배포했을 때, 모든 Pod가 ImagePullBackOff 상태였습니다.

원인 분석:

  • GHCR 프라이빗 레지스트리에서 이미지를 Pull하려면 인증 필요
  • imagePullSecrets가 Pod 스펙에 정의되지 않음
  • Secret이 잘못된 네임스페이스에 생성됨

해결 방법:

  1. kubectl create secret docker-registry로 Secret 생성
  2. Pod 템플릿에 imagePullSecrets 추가
  3. install.sh에서 Secret을 필요한 모든 네임스페이스에 복제하도록 자동화

이 경험을 통해 Kubernetes의 네임스페이스 격리와 Secret 관리의 중요성을 배웠습니다.

Init Container CrashLoopBackOff​

Frappe CVE 환경을 배포할 때, Pod는 생성되지만 Init:CrashLoopBackOff 상태였습니다.

원인 분석:

  • Init Container가 데이터베이스 초기화 수행
  • 하지만 MariaDB 컨테이너가 아직 준비되지 않아 연결 실패
  • Init Container가 재시작을 반복

해결 방법:

  • Init Container에 재시도 로직 추가
  • sleep과 for 루프로 DB가 준비될 때까지 대기
  • MariaDB의 readinessProbe 설정으로 준비 상태 명확히 정의

Kubernetes의 컨테이너 생명주기 관리에 대해 깊이 이해할 수 있었습니다.

Port Forwarding 충돌 해결​

여러 서비스를 동시에 port-forward하면 포트 충돌이 발생했습니다.
expose_simulation_tunnel.py 스크립트에 동적 포트 할당 로직을 구현했습니다.

구현 내용:

  • 8000번 포트부터 순차적으로 사용 가능한 포트 탐색
  • socket.connect_ex()로 포트 사용 여부 확인
  • 이미 사용한 포트를 set으로 추적하여 중복 방지

이를 통해 수십 개의 서비스도 자동으로 포트를 할당받아 동시에 실행할 수 있게 되었습니다.

Cloudflare Tunnel URL 파싱​

cloudflared는 터널 URL을 stdout 또는 stderr에 출력하는데,
출력 포맷이 일정하지 않아 파싱이 어려웠습니다.

해결 방법:

  • 정규표현식으로 *.trycloudflare.com 패턴 추출
  • 타임아웃(30초) 설정하여 무한 대기 방지
  • 백그라운드 스레드로 나머지 출력 소비하여 버퍼 블로킹 방지

정규표현식과 멀티스레딩을 활용한 파싱 로직 작성이 좋은 학습 경험이었습니다.


회고 및 배운 점​

Kubernetes 생태계에 대한 이해​

이번 프로젝트를 통해 Kubernetes의 핵심 개념을 실전에서 익혔습니다:

  • Pod, Service, Namespace, Secret 등 리소스 관리
  • Init Container, Sidecar Container 패턴
  • ImagePullSecrets, ConfigMap, Volume 활용
  • Readiness Probe, Liveness Probe 설정

단순히 튜토리얼을 따라하는 것과,
실제 문제를 해결하며 배우는 것은 확실히 다르다는 것을 느꼈습니다.

Infrastructure as Code의 가치​

모든 설정을 YAML 파일로 관리하면서,
재현 가능한 인프라의 중요성을 체감했습니다.

Git으로 버전 관리되는 YAML 파일 하나면,
언제든지 동일한 환경을 다시 만들 수 있습니다.
이는 협업과 문서화 측면에서 엄청난 이점이었습니다.

자동화의 힘​

install.sh, expose_simulation_tunnel.py 같은 스크립트를 작성하면서,
반복 작업을 자동화하는 것이 얼마나 중요한지 깨달았습니다.

처음에는 스크립트 작성이 시간이 걸리더라도,
장기적으로는 엄청난 시간 절약과 오류 감소 효과가 있었습니다.

오픈소스 프로젝트 운영 경험​

Helm Repository를 GitHub Pages로 배포하고,
Docker 이미지를 GHCR에 퍼블리싱하면서,
오픈소스 프로젝트를 운영하는 과정을 경험했습니다.

사용자 입장에서 얼마나 쉽게 설치하고 사용할 수 있는지가
프로젝트의 성공을 좌우한다는 것을 배웠습니다.


긴 글 읽어주셔서 감사합니다!
힘든 상황에서도 함께 힘써주신 팀원 여러분, 영운, 민욱, 민재, 서원님께도 감사드립니다!