문제
로그 수집은 Graylog, 자동대응은 Shuffle SOAR처럼 외부 도구에 기능이 흩어져 있었다. 도구 사이의 콜백이 유실되거나 실패가 조용히 묻히는 장애가 반복됐고, 고객사가 늘수록 운영 부담이 커졌다.
해결
기존 FastAPI + Vue 시스템을 Next.js 기반으로 다시 만들고, 외부 도구가 맡던 수집·귀속 파이프라인과 자동대응 엔진을 플랫폼 안에 직접 구현했다. 약 1년째 개발하면서 실제 고객사 관제에 운영 중이다.
데이터 흐름
Wazuh manager ─ fluent-bit ─┬─ archive 수집 워커 ─ OpenSearch (고객사별 색인, 30일)
└─ alert 수집 워커 ─ OpenSearch + PostgreSQL (365일)
│
▼
자동대응 큐 → 위협 인텔 병렬 조회 → 판정 → 방화벽 차단 → 인시던트 생성 → 알림 → 통계
주요 기능
- 대시보드와 관제실 벽 화면용 상황판
- 알림: 실시간 목록, 원본 로그, GeoIP, IOC
- 인시던트: 생성과 추적, LLM 분석
- LLM 위협 탐지: 저레벨 다발 알림과 신규 로그 템플릿 탐지, 3회 다수결 에이전트 조사
- 위협 인텔리전스: VirusTotal과 사내 위협정보 플랫폼 연동
- 멀티테넌시: 고객사별 대응 정책
- 보고서: 일일·월간 보고서, 헤드리스 Chromium으로 PDF 생성
- 설정: 커넥터, 자동대응, Suricata, 공격유형 매핑, 수집 규칙, 사용자, 감사로그
설계 포인트
- 자동대응 엔진 직접 구현: 조회(enrichment), 판정(decision), 차단(action)을 레지스트리와 벤더 어댑터 구조로 분리했다. 인텔 소스를 추가해도 DB 마이그레이션이 필요 없다. 실행을 동기 방식으로 바꿔 “콜백 미도달” 장애가 원천적으로 생기지 않는다. → 자동대응(SOAR) 설계
- 실패를 성공으로 기록하지 않는다: “조회 실패는 깨끗함이 아니다”, “차단하지 않았으면 차단했다고 기록하지 않는다.” 조회 실패는 1·2·4분 간격으로 재조회하고, 차단 실패는 별도 상태로 남긴다. 방화벽 차단은 멱등이며, 그룹 한도를 관리하고, 쓴 뒤 다시 읽어 검증한다.
- 로그 템플릿 정규화: 한 고객사 기준 로그 템플릿을 15,102종에서 289종으로 줄였다. LLM 병행 탐지 결과 476,156건을 전수 대조해 회귀가 0건임을 확인했다.
- 수집·귀속 파이프라인: 로그를 어느 고객사에 붙일지 정하는 규칙을 출발지, 에이전트, 매니저 순으로 적용한다. 규칙은 DB와 설정 화면에서 관리한다.
- 시간 처리 규약: DB는 naive UTC, API는 UTC(
Z)로 통일하고 표시용 변환은 프론트에서만 한다. 전수 점검으로 KST가 두 번 더해지던 버그를 제거했다. - 보고서 정합성: 월간 보고서는 일일 통계로 다시 조립하고, 보존기간 경계에서는 스냅샷으로 고정한다. 원본 색인이 없으면 통계 재생성을 거부한다.
규모
코드 약 11만 줄(Python/TypeScript), API 라우터 30여 개, systemd 서비스 4개, OpenSearch 3노드
스택
Next.js 15 · React 19 · TypeScript · shadcn/ui · Tailwind CSS 4 · TanStack Query · FastAPI · SQLModel · Alembic · APScheduler · PostgreSQL 16 · OpenSearch (Wazuh indexer) · Wazuh · Suricata · fluent-bit · FortiGate REST · 로컬 LLM (gpt-oss-20b) · nginx · systemd