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 PodService를 구성하였습니다.
  • 스프린트 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 indexindex.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에 재시도 로직 추가
  • sleepfor 루프로 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에 퍼블리싱하면서,
오픈소스 프로젝트를 운영하는 과정을 경험했습니다.

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


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