InfraGuard

자동 차단 파이프라인

트리거를 충족한 공격 IP 를 수집기와 스케줄러가 자동으로 차단 목록에 올리고 ipset 으로 집행하는 백그라운드 방어 흐름입니다.

개요 — 무엇을 하나

자동 차단(Auto-block) 은 InfraGuard 가 공격 IP 를 사람 개입 없이 탐지 → 심사 → 차단하는 백그라운드 파이프라인입니다. 화면(페이지)이 아니라 수집기 · 스케줄러 · 커널 ipset 이 협력하는 흐름이며, 이 문서는 그 전체 구조를 설명합니다.

파이프라인은 두 개의 독립 차단 경로가 하나의 집행 지점(ipset)으로 수렴합니다.

경로 실행 주체 기록 대상 단위 원천
① 수집기 자동 차단 각 호스트의 Node 수집기(srct_tx·kts_tx·eve_ingest) blacklist_info /24 (C클래스) Suricata L7 · Kitsune ML
② 스케줄러 자동 차단 InfraGuard 앱(infr:auto-block → AutoBlockService) infr_block /32 (개별) 또는 /24 suricata_alert_ct · suricata_tls_ct
③ 집행(공통) infr_sync_black.mjs(호스트 cron) 커널 ipset blacklist hash:net ①+② 합집합

두 경로는 모두 차단하기 직전에 화이트리스트·무죄 추정 검증을 거칩니다. 확실한 침해만 남기고, 오탐 후보는 면책합니다. 경로 ②(스케줄러)는 기본값이 OFF 이며 관리자가 켜야 동작합니다.

언제 쓰나

화면 구성

자동 차단은 전용 화면이 없습니다. 제어·관측 지점은 다음과 같습니다.

사용법

1. 경로 ② 스케줄러 자동 차단 — 켜고 튜닝하기

기본값은 OFF 입니다(autoblock_on=0). 운영 정책 ▸ 보안 설정에서 마스터 스위치를 켠 뒤 정책을 조정합니다.

차단 방식(mode)

값 의미
global(기본) 한 서버에서 발견된 침해를 전 함대에 광역 적용(server_index=0).
per_server 공격당한 대상 서버에만 적용 — 공격 대상 dest_ip 를 infr_server_index 로 해석해 지정.

차단 범위(scope)

값 스케줄러 기록 설명
balanced(권장·기본) 개별 /32 IDS 프로그램은 /24, InfraGuard 는 개별 /32 — 이미 /24 로 덮인 IP 는 sync_black 이 중복 제외.
broad /24 C클래스 전체 차단 — 넓게 막지만 무고한 이웃 IP 포함 위험.
granular 개별 /32 IP 하나씩만 — 정밀하지만 로테이션 대응이 느림.

트리거(OR — 하나라도 충족하면 차단 후보)

① IDS 룰셋 심각도   suricata_alert_ct
   WHERE alert_ts >= NOW() - INTERVAL {window_min} MINUTE
     AND severity <= {sev_max}          -- crit=1, warn=2 (Suricata: 1=심각 … 3=낮음)
   GROUP BY src_ip HAVING COUNT(*) >= {sev_count}

② JA3 로테이션 봇넷  suricata_tls_ct
   WHERE event_ts >= NOW() - INTERVAL {ja3_window_min} MINUTE
     AND ja3_hash IS NOT NULL
   GROUP BY src_ip HAVING COUNT(DISTINCT ja3_hash) >= {ja3_rotation}

심각도 트리거의 시각 컬럼은 alert_ts 이고 JA3 트리거는 event_ts 입니다(테이블마다 다름). 임계 튜닝 시 각 창(window)이 어느 컬럼을 기준으로 하는지 유의하세요.

정책 기본값 (OpsPolicy::autoBlock())

설정 키 기본값 클램프 의미
autoblock_on 0 — 마스터 스위치(1=on). 기본 OFF
autoblock_mode global global|per_server 차단 방식
autoblock_scope balanced broad|granular|balanced 차단 범위
autoblock_interval_min 10 ≥1 스케줄러 실효 순회 주기(분)
autoblock_window_min 10 ≥1 심각도 평가 창(분)
autoblock_sev_on 1 — 트리거① 활성
autoblock_severity crit crit|warn crit=sev≤1, warn=sev≤2
autoblock_sev_count 1 ≥1 창 내 최소 경보 건수
autoblock_ja3_on 1 — 트리거② 활성
autoblock_ja3_rotation 50 ≥2 distinct JA3 지문 ≥ N
autoblock_ja3_window_min 360 ≥1 JA3 평가 창(분·6시간)
autoblock_innocent_on 1 — 무죄 추정 veto 활성
autoblock_innocent_days 30 ≥1 무죄 자동 화이트리스트 기간(일)
autoblock_reputation_cap 25 0–200 스윕당 평판 조회 상한

JA3 로테이션 임계 튜닝(실측): 실환경의 JA3 로테이션 오펜더는 대개 /32 세션당 서로 다른 JA3 약 32개 수준(데이터센터·클라우드 봇, GREASE 없이 안정적)입니다. 따라서 기본 ja3_rotation=50 은 아무것도 잡지 못합니다. 실제로 발화시키려면 15–20 정도로 낮추세요. --dry 로 후보 수를 먼저 확인하는 것을 권장합니다.

2. 후보 심사 순서 (AutoBlockService::run)

수집된 후보 IP 마다 아래 게이트를 순서대로 통과해야 실제 차단됩니다.

  1. 자기망/내부 제외 — SecurityScope::isInternal($ip) 이면 skip(skipped_internal). 등록 인스턴스 IP(/24 접두 + 정확 IP)와 운영 정책 internal_networks 화이트리스트를 통합한 판정입니다. 쿼리 단계에서도 excludePrefixes() 를 NOT LIKE 로 선제 배제합니다.
  2. 화이트리스트 면책 — infr_whitelist 에 /32 또는 /24 로 활성·미만료 등록이 있으면 skip(skipped_wl).
  3. 무죄 추정 veto — 하드 신호(심각도 트리거)가 없는 JA3 단독 후보만 대상. reputation_cap 한도 내에서 AbuseIPDB 평판을 조회해 완전 청정(악용점수 0 AND 신고 0 AND Tor 아님)이면 차단 대신 infr_whitelist(source=auto) 로 innocent_days 일 면책(innocent). 심각도 후보는 이 관문을 건너뛰고 즉시 차단합니다.
  4. server_index 해석 — global→[0], per_server→대상 dest_ip 를 ServerIndexResolver::byIp 로 해석(없으면 [0]).
  5. 기록 — InfrBlock::updateOrCreate([server_index, cidr], …) 로 infr_block 에 upsert. source='schedule', is_active=true, reason='[InfraGuard Auto] …'(근거 note).
후보 수집(OR) → 내부 제외 → 화이트리스트 → (JA3단독) 무죄 veto → server_index → infr_block 기록

isSpotless() 는 평판 조회가 실패·미설정·미해석이면 false(=신중하게 차단)로 판정합니다. 무죄 면책은 "확실히 결백할 때만" 적용되는 보수적 관문입니다.

3. 스케줄러 & 커맨드

infr:auto-block 은 데몬이 아니라 1회 평가 후 종료하는 단발 커맨드입니다. 스케줄러가 매분 tick 하되 실효 주기는 autoblock_interval_min 입니다.

# 게이트 확인(운영정책 OFF 면 즉시 no-op) 후 1회 평가·기록
php artisan infr:auto-block

# 기록 없이 후보만 집계(튜닝·검증용)
php artisan infr:auto-block --dry

# 운영 정책 게이트(on·주기)를 우회해 강제 1회 실행
php artisan infr:auto-block --force

# 차단이 없으면 출력 억제(스케줄용)
php artisan infr:auto-block --quiet-if-ok

스케줄 등록(routes/console.php):

Schedule::command('infr:auto-block --quiet-if-ok')
    ->everyMinute()
    ->withoutOverlapping(2)
    ->onOneServer()
    ->when($is_leader);   // 리더 게이팅 — 다중 서버에서 1곳만

게이트 로직:

출력 예:

infr:auto-block — 후보 12(sev 3·ja3 9) · 차단 7 · 화이트skip 1 · 내부skip 2 · 무죄면책 2

4. 경로 ① 수집기 자동 차단 (Node)

각 호스트의 Node 수집기가 Suricata 이벤트를 소비해 직접 blacklist_info 에 /24 로 차단합니다. 스케줄러(경로 ②)와 독립이며 항상 동작합니다.

수집기 계층 탐지
infr_eve_ingest.cjs 인제스트 Suricata eve.json → suricata_http_ct·suricata_tls_ct·suricata_flow_ct·suricata_alert_ct 적재
infr_srct_tx.cjs 시그니처/휴리스틱 Suricata L7 종합 → 5카테고리 스캐너/봇 탐지 → blacklist_info /24
infr_kts_tx.cjs Kitsune ML conntrack + 오토인코더 이상탐지(anomaly_score) → blacklist_info /24

srct_tx 의 5가지 위협 카테고리:

(A) [HTTP] URL 스캐너      .env / .git / wp-login 등 스캐너 지문 URL
(B) [HTTP] 봇 User-Agent   curl / python-requests / sqlmap 등 툴 지문
(C) [HTTP] 무차별 탐색      짧은 시간에 4xx 다수(경로 브루트포스)
(D) [TLS]  JA3 지문 이상   다중 JA3 · SNI 부재 등 봇넷 지문
(E) [FLOW] TCP 플래그 이상 짧은 스캔 세션 다수(포트 스캔)

srct_tx 도 차단 직전 화이트리스트 필터 + 무죄 자동 면책을 수행합니다: E-only(짧은 스캔 단독) 후보만 AbuseIPDB 평판을 조회해, 신고 0·악용점수 0·Tor 아님 이면 30일 면책(infr_whitelist(source=auto))으로 등록하고 차단을 건너뜁니다. 하드 신호(A/A2/B/C/D)가 하나라도 있으면 즉시 차단합니다. 상수·임계는 규칙 임계 에서 관리합니다.

5. 경로 ③ 집행 — sync_black → ipset

infr_sync_black.mjs 가 각 호스트에서 주기 실행되어 두 목록을 커널 ipset blacklist 로 동기화합니다.

# 호스트별 server_index 로 실행(root 필요)
node infr_sync_black.mjs sv=4

# crontab (매 5분, flock 로 중복 방지)
*/5 * * * * /usr/bin/flock -n /var/run/sync_black.lock \
  /usr/bin/node /root/server_monitor/infr_sync_black.mjs sv=4 \
  >> /var/log/sync_black.log 2>&1

동작:

  1. blacklist_info(/24) + infr_block(/32) 에서 server_index IN (0, <sv>) AND is_active/is_use=1 로드.
  2. 균형 dedup — 부모 /24 가 이미 덮은 /32 는 건너뜀.
  3. 커널 ipset 을 hash:net 으로 강제(다른 타입이면 재생성), 임시 셋에 로드 후 atomic swap(무중단).
  4. iptables -I INPUT -m set --match-set blacklist src -j DROP 존재 보장(idempotent).
  5. 안전장치 — 사설망(RFC1918)·loopback·자기망 접두(HOME_NET_PREFIX)는 DB 에 실수로 들어와도 ipset 에 절대 로드하지 않음.

sync_black 은 root 권한과 커널 ipset/iptables 가 필요하며, 대상 호스트에 사전에 blacklist ipset 이 존재해야 합니다:

ipset create blacklist hash:net family inet hashsize 8192 maxelem 65536
iptables -I INPUT -m set --match-set blacklist src -j DROP

데이터 소스

모든 원천·기록 테이블은 legacy_glory 커넥션에 있습니다(수집기·집행 스크립트가 공유하는 DB).

테이블 커넥션 역할 단위
suricata_alert_ct legacy_glory 심각도 트리거 원천(컬럼 alert_ts·severity·src_ip·dest_ip·signature) —
suricata_tls_ct legacy_glory JA3 로테이션 트리거 원천(컬럼 event_ts·ja3_hash·src_ip) —
blacklist_info legacy_glory 수집기 자동 차단 목록(IDS 프로그램·srct/kts) /24
infr_block legacy_glory InfraGuard 스케줄러 소유 차단 목록(마이그레이션 000751) /32·/24
infr_whitelist legacy_glory 차단 면책(수동·무죄 자동·seed) /32·/24
infr_server_index 기본(MAIN) 희소·비순차 server_index ↔ alias/instance 룩업(000750) —

infr_block 스키마 (요약)

컬럼 타입 의미
server_index int 0=전역 · else=infr_server_index 로 해석된 서버
cidr varchar(43) 개별 IP(/32) 또는 정규화 /24 · UNIQUE(server_index,cidr)
ip varchar(45) 원 IP(표시)
reason varchar(500) 트리거 근거([InfraGuard Auto] sev1 x3 …)
source varchar(16) auto · manual · schedule
is_active bool sync_black 이 로드하는 활성 여부
expires_at datetime NULL=영구
last_seen_at datetime 마지막 재탐지(purge 판단)

권한

자동 차단에는 사용자 대면 실행 UI 가 없습니다. 관련 권한 게이트는 다음과 같습니다(레벨: demo=1 · staff=5 · admin=10).

대상 필요 권한
자동 차단 정책 설정(운영 정책 ▸ 보안 설정) 관리자(Administration, 레벨 5 이상 / admin)
차단 결과 조회(Blocklist) 보안 열람(staff, 레벨 2 이상)
수동 차단/해제·Whitelist 쓰기 staff(레벨 2 이상) + 감사 로그
스케줄러 실행(infr:auto-block) 시스템(로그인 사용자 아님) — 리더 게이팅

스케줄러가 쓰는 자격증명은 하드코딩하지 않고 infr_collector·.env 로 외부화됩니다. sync_black 의 DB 크리덴셜·자기망 접두도 env 또는 Inventory ▸ Tools 매시업 치환으로 주입합니다(하드코딩 금지).

문제해결 · FAQ

켰는데 아무것도 차단되지 않습니다.

차단이 실제 방화벽에 반영되지 않습니다.

무고한 IP 가 차단됐습니다.

per_server 인데 전역(server_index=0)으로 기록됩니다.

스케줄러가 여러 서버에서 중복 실행됩니다.

관련 항목

자동 차단 스위치가 있는 운영 정책 보안 탭
자동 차단 스위치가 있는 운영 정책 보안 탭
자동 차단 결과가 쌓이는 차단 목록
자동 차단 결과가 쌓이는 차단 목록