---
title: 웹 서버에 AI 활성 방화벽을 구현한 이야기
description: "2026.04 / Tech Blog / BASTION # 웹 서버에 AI 액티브 방화벽을 구현한 이야기 Fail2Ban의 고정 임계값으로는 탐지할 수 없는 미지의 Bot 패턴이나 스팸 요청을, 로컬 LLM이 15분마다 분석하여 자동으로 차단하는 구조를 구현했다. 기존 nginx + Fail2Ban 위에 AI 분석…"
url: "https://bestnetllc.co.jp/ko/docs/%ec%9b%b9-%ec%84%9c%eb%b2%84%ec%97%90-ai-%ed%99%9c%ec%84%b1-%eb%b0%a9%ed%99%94%eb%b2%bd%ec%9d%84-%ea%b5%ac%ed%98%84%ed%95%9c-%ec%9d%b4%ec%95%bc%ea%b8%b0/"
updated: "2026-10-19"
category: Tech Blog / AI 보안 운영
---

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

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를 차단합니다([이전 글](https://bestnetllc.co.jp/ko/docs/ai%ea%b0%80-%ea%b3%b5%ea%b2%a9%ec%9d%84-%ea%b0%90%ec%a7%80%e2%86%92slack%ec%97%90%ec%84%9c-%ec%8a%b9%ec%9d%b8%e2%86%92%eb%b0%a9%ed%99%94%eb%b2%bd%ec%97%90-%ec%9e%90%eb%8f%99-%ec%b0%a8%eb%8b%a8/) 참조). 그러나 웹 서버가 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과의 차이

|  | Fail2Ban | BASTION |
| --- | --- | --- |
| 판단 방식 | 고정 임계값(“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 서비스 페이지](https://bestnetllc.co.jp/ko/bastion-5/)
[문의하기](https://bestnetllc.co.jp/ko/contact-5/)
