
iocflow: 프로덕션 AI SOC를 배포 가능한 오픈 소스(OSS) 라이브러리로 전환하기
요약
iocflow는 침해 지표(IOC)의 생명주기를 관리하기 위한 오픈 소스 Python 라이브러리입니다. LangGraph 기반의 멀티 에이전트 팀을 활용하여 추출, 보강, 탐색, 차단 과정을 오케스트레이션하며, 인간 참여형(human-in-the-loop) 구조를 통해 보안 조치의 안전성을 보장합니다.
핵심 포인트
- LangGraph 기반의 멀티 에이전트 오케스트레이션 제공
- 추출부터 차단까지 IOC 전체 생명주기 자동화
- 인간 참여형 게이트를 통한 안전한 보안 조치 승인
- 결정론적이고 테스트 가능한 6개의 독립적 레이어 구조
요약 (TL;DR) 🚀
저는 iocflow를 PyPI에 출시했습니다. 이는 침해 지표(Indicator-of-Compromise, IOC)의 전체 생명주기를 위한 오픈 소스 Python 라이브러리로, pip extras 뒤에 숨겨진 6개의 독립적으로 유용한 레이어로 구축되었습니다. 핵심은 "또 다른 IOC 파서"가 아닙니다. 바로 그 "구조"에 있습니다. 모든 레이어는 결정론적(deterministic)이고, 단순하며, 테스트 가능한 기본 단위(primitive)입니다. 그리고 최상위 레이어는 이러한 기본 단위들을 오케스트레이션하는 작은 **LangGraph 멀티 에이전트 팀(multi-agent team)**이며, AI와 파괴적인 동작 사이에는 **인간 참여형 게이트(human-in-the-loop gate)**가 배치되어 있습니다.
<table><tbody><tr><td width="33%"><h2>6개 레이어</h2>추출(extract) · 보강(enrich) · 주석(comment) · 탐색(hunt) · 차단(block) · 에이전트(agent) — 각 레이어는 고유한 pip extra임</td><td width="33%"><h2>1개 임포트</h2><code class="language-python">investigate(text)</code>가 멀티 에이전트 팀으로서 전체 체인을 실행함</td><td width="33%"><h2>0개의 독단적 차단</h2>LLM이 *제안*하고 · 인간이 *승인*하며 · 가드(guard)가 *거부*함</td></tr></tbody></table>이것은 제가 지난주에 작성했던 AI SOC인 SOC-in-a-Box의 오픈 소스(OSS) 형제입니다. SOC-in-a-Box가 실제 시스템을 대상으로 그 패턴을 입증했다면, iocflow는 누구나 pip-install 할 수 있도록 그 교훈을 패키징한 것입니다. 🧰

단 한 번의 호출: 보고서에서 IOC 추출 → 보강 → 탐색 제안 → 차단 제안 → 인간의 대기 → 방화벽에서 차단. 무해한 8.8.8.8은 제안조차 되지 않습니다.
제가 이어온 교훈
SOC-in-a-Box는 메시지 버스(message bus)를 통해 하나의 로컬 LLM이 8개의 분석가 역할을 수행하며, 프로덕션 환경에 대해서는 읽기 전용(read-only)으로 작동하고, 인간이 모든 봉쇄 조치를 승인하는 방식이었습니다. 실제로 이를 신뢰할 수 있게 만든 것은 에이전트가 아니었습니다. 도구를 사용하는 LLM 루프(LLM-with-tools loop)는 새로운 것이 아닙니다. 그것은 두 가지 아키텍처적 약속 덕분이었습니다:
- 모델은 오케스트레이션(orchestrates)할 뿐, 직접 실행(do)하지 않습니다. SIEM 쿼리, 차단 목록(denylist) 작성, 호스트 격리와 같은 되돌릴 수 없는 작업은 모델이 단순히 _호출(calls)_하는 일반적이고 결정론적인(deterministic) 코드에 의해 수행됩니다. LLM은 무엇을 그리고 언제 할지를 선택하며, 도구는 매번 동일한 방식으로 어떻게 할지를 결정합니다.
- 파괴적인 작업에 대한 단일 권한을 부여하지 않습니다. AI는 하루 종일 격리 조치를 제안할 수 있습니다. 하지만 실제 버튼을 누르는 것은 사람이며, 그 아래에는 허용 목록(allowlist)에 있는 항목은 무엇도 건드리지 못하도록 차단하는 단순한 안전 점검(safety check) 장치가 자리 잡고 있습니다.
이 두 가지 아이디어는 SOC에만 국한된 것이 아닙니다. 이는 프로덕션(production) 환경에 영향을 미치는 모든 AI 시스템을 실제로 배포할 수 있을 만큼 안전하게 만드는 방법입니다. 그래서 저는 이 개념들을 SOC에서 분리하여 이를 중심으로 깔끔한 공개 라이브러리를 구축했습니다. 🧱
결정론적 프리미티브(Deterministic primitives) 우선, 에이전트(agents)는 마지막에
iocflow는 계층적으로 성장하며, 각 계층은 자체적인 추가 기능(extra) 뒤에 숨겨져 있습니다. 따라서 import iocflow는 단일 의존성 설치를 유지하며, 사용자가 요청하지 않은 기능은 가져오지 않습니다:
-
L1 — extract (
iocflow): 비정형 텍스트에서 IP, 도메인, URL, 해시, CVE, MITRE 기술 ID, 위협 행위자(threat actors), 악성코드 패밀리를 추출합니다. 이때 직접 작성해야 했을 오탐(false-positive) 방어 로직(Public Suffix List 검증, 양성 허용 목록(benign allowlists),evil-domain[.]ru와 같은 재-팽킹(re-fanging) 처리)이 포함됩니다. -
L2 — enrich (
iocflow[enrich]): 각 지표(indicator)를 VirusTotal / AbuseIPDB / abuse.ch와 대조하여 조회하고, 가장 최악의 결과(worst-wins)를 판결로 반환합니다. -
L3 — comment (
iocflow[ai]): LLM이 강화(enrichment) 보고서를 구조화된 평가로 변환합니다. 모델이 구성되지 않은 경우, 보고서에서 파생된 결정론적인 요약으로 대체(fallback)됩니다. 이 과정에서 절대로 예외(raise)를 발생시키지 않습니다. -
L4 — hunt (
iocflow[hunt]): 지표로부터 즉시 실행 가능한 헌팅(hunt) 쿼리인 CrowdStrike CQL, Cortex XQL, Sigma를 오프라인 및 표준 라이브러리(stdlib)만 사용하여 생성합니다. LLM이 행동 기반 헌팅(behavioral hunts)을 _추가_할 수는 있지만, 결정론적인 쿼리는 항상 준비되어 있습니다. -
L5 — block (
iocflow[block]): 운영 중인 제어 지점(control points) — Palo Alto (EDL 피드 + live User-ID API), Zscaler, CrowdStrike, Abnormal — 으로 악성 지표(malicious indicators)를 푸시합니다.dry_run=True가 모든 곳에서 기본값으로 설정되어 있으며, 권위 있는 허용 목록(allowlist) 가드가 적용됩니다. -
L6 — agent (
iocflow[agent]): 대미를 장식하는 단계입니다. 🤖
L1–L5 단계는 에이전트(agent)의 존재를 전혀 알지 못한다는 점에 주목하십시오. 이들은 안정적인 입출력 타입을 가진 함수일 뿐입니다: ExtractedEntities → enrich() → EnrichmentReport → comment() → Commentary → suggest() → HuntPlan → block() → BlockReport. 이 중 어떤 것이든 단독으로 사용할 수 있습니다. 이는 의도된 설계입니다 — 에이전트는 프리미티브(primitives)를 대체하는 것이 아니라, 프리미티브를 소비하는 주체입니다.
대미를 장식하는 단계: 소규모 멀티 에이전트 팀
Layer 6은 보고서를 관리자(supervisor)에게 전달하며, 관리자는 이를 추출기(extractor), 보강기(enricher), 헌터(hunter), 대응기(responder)와 같은 전문 에이전트에게 라우팅합니다. 각 에이전트는 L1–L5를 도구로 사용하며, 케이스가 완료될 때까지 루프를 반복합니다.
flowchart TB
START([report text]) --> SUP{supervisor<br/>routes next step}
SUP -->|extract| EX[extractor<br/>L1 entities]
...
from iocflow.agent import investigate
case = investigate(report_text) # 안전함: 기본적으로 아무것도 차단되지 않음
...
모델은 어떠한 LangChain 채팅 모델(chat model)이든 사용할 수 있습니다. 번들로 제공되는 default_agent_model()은 FailoverChatModel을 구축합니다 — 이는 기본 모델과 자동 보조 모델을 갖춘 형태로, 제가 이전에 SOC에서 추출하여 공개했던 것과 동일한 페일오버(failover) 모델입니다. iocflow가 자신의 제품을 직접 사용하는 '도그푸딩(dogfooding)'을 실천하고 있는 것입니다. 🐕 그리고 이 시스템을 견고하게 만드는 부분은 다음과 같습니다: 모델이 전혀 구성되지 않은 상태에서도, 그래프는 고정된 결정론적(deterministic) 순서로 레이어를 실행하며 여전히 완전한 Case를 생성합니다. LLM은 강화 요소일 뿐, 의존 대상이 아닙니다.
3계층 권한 (가장 중요한 부분) 🔒
차단(Blocking)은 사용자에게 해를 끼칠 수 있는 유일한 단계이므로, SOC-in-a-Box의 모든 보호 조치를 받게 됩니다:
- 에이전트가 제안합니다. 대응자(Responder)는 L5의 드라이 런 (dry run) — 전체 감사(audit)를 수행하되 변경 사항은 적용하지 않음 — 을 수행하고 이를 제안(proposal)으로 전환합니다.
- 사람이 승인합니다.
ApprovalGate가 제안을 검토하고 승인된 하위 집합을 반환합니다. 기본값은DenyAllGate이며, 무인 실행(unattended run) 시에는 아무것도 차단되지 않습니다. - 가드(Guard)가 거부권을 행사합니다. 이 두 단계의 하단에서, Layer 5 허용 목록(allowlist) 가드는 공용 리졸버(public resolvers), 사설 범위(private ranges), 그리고 잘 알려진 도메인(well-known domains)을 건드리는 것을 거부합니다 — 설령 보고서가 이를 악성으로 잘못 분류했을지라도 말입니다. 이 라이브러리를 통해
8.8.8.8을 차단할 수는 없습니다. LLM은 파괴적인 동작에 대한 유일한 권한을 결코 갖지 않습니다.
게이트(gate)를 위해, 저는 실제 Slack에 연결했습니다 — 인바운드 웹훅(inbound webhook) 서버 없이, 단순히 게시하고 폴링(post-and-poll)하는 방식입니다:
from iocflow.agent import investigate
from iocflow.agent.chat_gate import SlackApprovalGate
...
이 방식은 제안된 차단 목록을 채널에 게시하고, **허용 목록(allowlisted)**에 있는 승인자의 반응을 폴링합니다 — ✅는 계획을 승인하고, ❌ 또는 침묵은 거부를 의미하며, 타임아웃 시 기본값은 _거부(deny)_입니다. 전체 프로세스는 두 개의 메서드(post, reactions)를 가진 ChatTransport를 통한 ChatApprovalGate이므로, 두 개의 함수만 작성하면 동일한 흐름을 Webex, Teams 또는 웹 UI에 적용할 수 있습니다. 트랜스포트(transport)는 얇은 이음매(thin seam) 역할을 하므로, 게이트 로직을 단 한 번의 네트워크 호출 없이도 유닛 테스트(unit-tested)할 수 있습니다.
왜 이런 방식으로 구축했는가
- 📦 PyPI:
pip install iocflow - 🛠️ 소스 (Source): github.com/vinayvobbili/iocflow
- 🧠 이 라이브러리가 탄생한 SOC: SOC-in-a-Box
직접 사용해 보신다면, 어떤 제어 지점 (control points)을 연결하실지 꼭 듣고 싶습니다. 👋
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기