프로젝트
오픈소스Nginx + ModSecurity 위에 얹은 이중 트랙 로그 파이프라인

WAF 플랫폼

차단된 요청의 실시간 관측과 사후 분석을 분리한 WAF 스터디 프로젝트입니다. 현업 보안 개발자의 코칭을 받으며 로그 파이프라인·대시보드·규칙 관리와 Kubernetes 배포를 설계하고 구현했습니다.

어드민에서 만든 커스텀 룰이 무중단 reload로 Nginx에 반영

기간
2025.08 — 2025.12
팀
외부 개발자 스터디
역할
설계 · 구현 담당 · 현업 보안 개발자 코칭

왜 만들었나

외부 개발자 스터디에서 현업 보안 개발자의 코칭을 받으며 설계와 구현을 맡았습니다. 애플리케이션 앞단에서 요청을 차단하는 것뿐 아니라, 차단 이후 운영자가 어떤 신호를 확인해야 하는지 살펴보고 싶었습니다.

스캐너 탐지 로그와 SQL 인젝션 같은 공격 이벤트를 같은 방식으로 처리하면 중요한 신호를 찾기 어렵습니다. 이벤트를 분류해 실시간 관측과 사후 분석으로 나누는 파이프라인을 2025년 8월부터 12월까지 구현했습니다.

지금 볼 것과 나중에 볼 것

ModSecurity는 감사 로그를 JSON으로 남깁니다(SecAuditLogFormat JSON, RelevantOnly). Fluent Bit이 이 파일을 tail 하면서 Lua 스크립트로 이벤트를 분류합니다. SQL 인젝션(942xxx) · XSS(941xxx) · RCE(932xxx) 룰이 걸렸거나 CRS 이상 점수가 20 이상이면 실시간 트랙, 스캐너 탐지(913xxx)는 노이즈로 보고 분석 트랙으로 보냅니다.

실시간 트랙은 Go로 쓴 처리기가 Kafka에서 읽어 심각도를 다시 계산하고(이상 점수 + 공격 유형 가중치 + 위험 IP), GeoIP를 붙여 InfluxDB에 씁니다. Grafana와 대시보드가 이 데이터를 봅니다. 분석 트랙은 ksqlDB가 1분 · 5분 텀블링 윈도우로 "한 IP에서 분당 N건 이상", "차단율 30% 초과", "가장 많이 공격받은 URI" 같은 집계 스트림을 만들고, Logstash가 GeoIP를 붙여 Elasticsearch에 일 단위 인덱스로 색인합니다. Kibana에서 사후 조사를 합니다.

룰을 화면에서 고치면 Nginx에 반영되기까지

대시보드 API(Spring Boot)에서 커스텀 룰을 만들면 메모리에 두었다가 주기적으로 ModSecurity 룰 파일로 써 내려가고, 리로드 신호 파일을 남깁니다. Nginx 컨테이너의 시작 스크립트가 이 신호를 감시하다가 nginx -t로 설정을 검증한 뒤 graceful reload를 겁니다. 잘못된 룰이 들어와도 검증에서 걸려 기존 설정이 유지됩니다.

프론트는 Next.js 15 + React 19로 대시보드, 실시간 로그(SSE), 알림, 룰 · 화이트리스트 관리 화면을 두었고, 로그인은 Google OAuth2와 JWT로 붙였습니다.

검증한 것

Kubernetes에 올린 상태에서 SQL 인젝션, XSS, 경로 조작, 명령어 주입 요청이 403 또는 연결 차단으로 막히고 정상 요청은 통과하는 것을 curl로 확인해 결과를 문서로 남겼습니다. 국가별 IP를 흉내 낸 공격 시뮬레이션 스크립트로 파이프라인 끝까지 이벤트가 흐르는 것도 확인했습니다. 배포는 네임스페이스 4개, Deployment 13개, StatefulSet 3개, NetworkPolicy와 PodMonitor를 포함한 매니페스트로 dev · staging · prod 환경을 나눴습니다.

검증 범위와 후속 과제

실제 서비스에 적용한 WAF가 아닌 스터디 결과물입니다. 검증한 범위는 테스트 요청 차단과 이벤트 파이프라인 동작이며, 운영 부하에서의 처리량이나 가용성은 측정하지 않았습니다.

운영 적용 전에는 자동화 테스트와 CI, 역할별 권한 관리, 외부 알림 전송을 보완해야 합니다. ClickHouse 분석 경로는 미연결 상태이며, 배포는 Helm이 아닌 Kubernetes 매니페스트와 스크립트를 사용합니다.

구조와 구현

클라이언트 요청 Nginx + ModSecurity OWASP CRS · 이상 점수 모드 정적 자원 화이트리스트 · 403 차단 백엔드 앱 통과 감사 로그 JSON · RelevantOnly Fluent Bit Lua 분류기 룰 ID · 이상 점수 Kafka · waf-realtime-events / waf-logs / waf-alerts 실시간 트랙 — 지금 봐야 할 것 Go 실시간 처리기 심각도 = 이상 점수 + 공격 유형 가중치 + 위험 IP GeoIP 조회 MaxMind GeoLite2 InfluxDB 시계열 · 7일 보관 SQLi · XSS · RCE · 이상 점수 ≥ 20 분석 트랙 — 나중에 볼 것 ksqlDB 1분 · 5분 윈도우 집계 Logstash GeoIP · 필드 정규화 Elasticsearch 일 단위 인덱스 ClickHouse 스키마만 · 미연결 빈도 · 차단율 · 상위 URI 알림 스캐너 노이즈는 여기로 집계 알림 대시보드 API (Spring Boot) + Next.js InfluxDB · Elasticsearch를 읽어 SSE로 실시간 로그 · 메트릭 · 알림 스트리밍 · Google OAuth2 로그인 커스텀 룰 CRUD 화이트리스트 룰 파일 생성 + reload 신호 nginx -t 검증 후 무중단 reload 룰 반영 Grafana 실시간 메트릭 Kibana 사후 조사
실제 저장소의 설정과 코드를 읽고 그린 구조도입니다. 점선은 코드에 자리만 있고 아직 연결되지 않은 경로입니다.
구현 범위 · 사용 기술
  • Nginx
  • ModSecurity · OWASP CRS
  • Fluent Bit · Lua
  • Kafka
  • ksqlDB
  • Go
  • InfluxDB
  • Logstash
  • Elasticsearch
  • Spring Boot 3.2
  • Next.js 15
  • Kubernetes
  • 차단 엔진Nginx + ModSecurity + OWASP CRS, 정적 자원 화이트리스트
  • 분류기Fluent Bit Lua — 룰 ID · 이상 점수로 트랙 결정
  • 실시간 트랙Go 처리기 → 심각도 산정 · GeoIP → InfluxDB → Grafana
  • 분석 트랙ksqlDB 윈도우 집계 · Logstash → Elasticsearch → Kibana
  • 대시보드 · 룰 관리Spring Boot API(SSE) + Next.js, 커스텀 룰 CRUD · 화이트리스트
  • 배포Docker Compose와 Kubernetes 매니페스트 (dev · staging · prod)

돌아보면

즉시 확인할 이벤트와 사후 분석할 로그를 나누면서 처리 지연과 조회 목적이 서로 다른 저장소·파이프라인을 구성했습니다.