웹 서버에 AI 활성 방화벽을 구현한 이야기

웹 서버에 AI 활성 방화벽을 구현한 이야기

3 min read

2026.04 / Tech Blog / BASTION

웹 서버에 AI 액티브 방화벽을 구현한 이야기 #

Fail2Ban의 고정 임계값으로는 탐지할 수 없는 미지의 Bot 패턴이나 스팸 요청을, 로컬 LLM이 15분마다 분석하여 자동으로 차단하는 구조를 구현했다. 기존 nginx + Fail2Ban 위에 AI 분석 계층을 추가하여 방어를 3단 구조로 만들었다.

Fail2Ban은 우수하지만 “미지”에 취약하다 #

웹 서버의 Bot 대책으로서, nginx의 UA map + 속도 제한 + Fail2Ban 조합은 정석입니다. 403을 10회/분 출력한 IP를 1시간 차단한다. 간단하고 확실합니다.

하지만 이 방식에는 구조적인 약점이 있습니다.

Fail2Ban의 임계값은 “고정”. “403이 10회/분”과 같은 정적 조건으로만 트리거된다. UA를 바꾸면서 천천히 크롤링하는 Bot, 403이 아닌 200을 반환하는 경로를 연타하는 스팸, 정규 브라우저 UA를 위조한 고빈도 액세스는 Fail2Ban의 임계값을 빠져나간다. 사람이 액세스 로그를 육안으로 보면 “명백히 이상하다”고 알 수 있는 패턴이라도, 고정 규칙으로는 포착할 수 없다.

BASTION에서는 이 “사람이라면 알아차릴 수 있지만 고정 규칙으로는 포착할 수 없는 패턴”을 로컬 LLM에게 판단시킵니다.

방어의 3단 구조 #

기존 nginx + Fail2Ban 위에 BASTION의 AI 분석 계층을 추가하여 3단 구조로 만들었습니다.

계층담당판단 방식대응 속도
제1계층nginx(UA map / 속도 제한 / deny.conf)정적 규칙즉시(요청 단위)
제2계층Fail2Ban(firewalld 연동)고정 임계값(403이 10회/분)1분 이내
제3계층BASTION(LLM 분석 → SSH 경유 firewalld)LLM이 로그의 문맥을 판단15분 이내

각 계층은 독립적으로 동작합니다. Fail2Ban이 먼저 차단한 IP는 BASTION이 중복 체크하여 이중 차단을 방지. BASTION의 강점은 “미지의 패턴을 탐지할 수 있다”는 점입니다.

모니터링 항목 #

BASTION의 웹 서버 모니터링 템플릿은 다음 6개 카테고리를 15분마다 분석합니다.

카테고리데이터 소스탐지 내용
HTTP 상태 분포액세스 로그403/429/5xx 집중 IP. 정상 시와의 편차
Bot UA 탐지액세스 로그미지의 Bot UA × 고빈도 요청
스팸 요청액세스 로그언어 전환 연타 등 특정 파라미터 연타
악성코드 탐지ClamAV 로그업로드 파일의 바이러스 검출
SSH 인증 실패sshd 로그Failed password / Invalid user
애플리케이션 오류PHP-FPM 로그FATAL/ERROR 레벨

차단 구조 #

왜 WAN 경계의 FW가 아닌 웹 서버 측에서 차단하는가 #

BASTION의 반응형 방어는 통상 WAN 경계의 방화벽 API(OPNsense 등)에서 공격원 IP를 차단합니다(이전 글 참조). 그러나 웹 서버가 DNAT로 공개된 구성에서는 WAN 경계 FW의 차단 리스트로 웹 서버 앞 트래픽을 포착할 수 없는 경우가 있습니다.

그래서 웹 서버 측의 firewalld + nginx deny.conf에 직접 차단 규칙을 넣는 독립적인 방어 경로를 마련했습니다.

BASTION (분석 서버)
  → SSH 연결(전용 사용자・공개키 인증・최소 권한 sudo)
  → firewall-cmd로 IP를 drop(L3/L4 레벨)
  → nginx deny.conf에 IP 추가 + reload(L7 레벨)

SSH 연결의 보안 #

분석 서버에서 웹 서버로의 SSH 연결은 전용 서비스 계정을 사용하며, sudo 권한은 필요 최소한의 명령만 허용하고 있습니다.

# sudoers의 허용 명령(이것 외에는 실행할 수 없음)
svc-monitor ALL=(root) NOPASSWD: /usr/bin/firewall-cmd
svc-monitor ALL=(root) NOPASSWD: /usr/sbin/nginx
svc-monitor ALL=(root) NOPASSWD: /usr/bin/tee -a /etc/nginx/deny.d/*
svc-monitor ALL=(root) NOPASSWD: /usr/bin/sed -i * /etc/nginx/deny.d/*

root 로그인이 아닌, 전용 서비스 계정을 생성하여 ISMS 심사에도 견딜 수 있는 최소 권한 설계입니다.

Fail2Ban과의 차이 #

Fail2BanBASTION
판단 방식고정 임계값(”403이 N회/분”)LLM이 로그의 문맥을 읽고 판단
미지 패턴탐지 불가(규칙 추가 필요)로그의 이상 패턴을 자동 탐지
UA 위조403이 나오지 않으면 탐지 불가요청 빈도×경로×시간대로 판단
ClamAV 연계없음FOUND 탐지→업로드원 IP 추적→차단
알림메일(MTA 의존)Slack(즉시 알림, 대화 조작 가능)
해제bantime으로 고정24시간 자동 해제 + Slack에서 즉시 해제 가능
감사fail2ban.log구조화 감사 로그([HB_BLOCK]/[HB_UNBLOCK] 태그)

Fail2Ban을 대체하는 것이 아니라 공존시킵니다. Fail2Ban은 “403 연발”을 1분 이내에 탐지하여 차단한다. BASTION은 “403에는 이르지 않지만 명백히 부자연스러운 패턴”을 15분마다의 LLM 분석으로 탐지한다. 담당 영역이 다르므로 둘 다 가동하면 사각지대가 줄어듭니다.

실제 동작 #

분석 리포트 #

15분마다 정기 분석에서 웹 서버 섹션이 자동으로 출력됩니다.

incoming-webhook 07:15

웹 서버 상세 분석

요청: 1,847건
상태: 200=1,650 / 403=112 / 429=45 / 5xx=0
403 집중 IP: xxx.xxx.156.12 (40건) — UA: GPTBot/1.0
Bot 의심 UA: “SomeNewBot/2.0” (180건/15분) — 미등록 UA
ClamAV: 탐지 없음
SSH 실패: 0건 / PHP-FPM ERROR: 0건

자동 차단 실행 #

incoming-webhook 00:53

🛡️ 웹 서버 차단 실행

IP: xx.xxx.156.12
이유: bot_spam
firewalld: drop 추가 완료
nginx deny: 적용 완료

수동 해제: @OpenClaw-Monitor ブロック解除 xx.xxx.156.12

ClamAV FOUND 탐지 시 자동 대응 #

ClamAV가 업로드 파일에서 악성코드를 검출한 경우, BASTION은 즉시 CRITICAL 판정을 내립니다. ClamAV 자체는 파일을 격리하는 것에 그치지만, BASTION은 추가로 액세스 로그에서 업로드원 IP를 특정하여 firewalld로 자동 차단합니다. 파일 격리(ClamAV) + 네트워크 차단(BASTION)의 이중 방어입니다.

nginx deny.conf는 “best-effort” 설계 #

firewalld 차단과 nginx deny.conf의 이중 방어를 설계했지만, nginx deny.conf 디렉터리 설정이 없는 환경에서도 firewalld 단독으로 방어가 작동하도록 설계했습니다. deny.conf를 사용할 수 없는 경우 “skipped”라고 로그에 기록하고 firewalld만으로 계속 진행합니다. 나중에 nginx의 include 설정을 추가하면 코드 변경 없이 자동으로 nginx deny도 활성화됩니다. 환경의 준비 상황에 맞춰 단계적으로 강화할 수 있는 설계입니다.

정리 #

웹 서버에 BASTION의 AI 분석 계층을 추가하여 nginx(제1계층) → Fail2Ban(제2계층) → BASTION(제3계층)의 3단 방어를 구축했습니다. 고정 임계값으로는 포착할 수 없는 미지의 Bot 패턴이나 스팸 요청을 로컬 LLM이 15분마다 자동 탐지하여 firewalld에 즉시 차단을 추가합니다.

SSH 연결은 전용 계정・최소 권한 sudo・공개키 인증. nginx deny.conf는 best-effort 설계로 환경의 준비 상황에 따라 단계적으로 활성화 가능합니다. Fail2Ban과는 공존하며 각자 다른 영역을 커버합니다.

BASTION은 폐쇄 환경에서 AI 보안 모니터링을 실현하는 서비스입니다.

BASTION 서비스 페이지
문의하기

Updated on 2026年6月9日

What are your feelings

  • Happy
  • Normal
  • Sad