WebサーバーにAIアクティブファイアウォールを実装した話 #
Fail2Banの固定閾値では検知できない未知のBotパターンやスパムリクエストを、ローカルLLMが15分ごとに分析して自動ブロックする仕組みを実装した。既存のnginx + Fail2Banの上にAI分析層を追加し、防御を3層構造にした。
Fail2Banは優秀だが「未知」に弱い #
WebサーバーのBot対策として、nginxのUA map + レートリミット + Fail2Banの組み合わせは定番です。403を10回/分出したIPを1時間Banする。シンプルで確実。
しかしこの方式には構造的な弱点があります。
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が先にBanしたIPはBASTIONが重複チェックして二重ブロックを防止。BASTIONの強みは「未知のパターンを検知できる」点です。
監視項目 #
BASTIONのWebサーバー監視テンプレートは、以下の6カテゴリを15分ごとに分析します。
| カテゴリ | データ源 | 検知内容 |
|---|---|---|
| HTTPステータス分布 | アクセスログ | 403/429/5xx集中IP。正常時との偏差 |
| Bot UA検知 | アクセスログ | 未知のBot UA × 高頻度リクエスト |
| スパムリクエスト | アクセスログ | 言語切替連打等の特定パラメータ連打 |
| マルウェア検知 | ClamAVログ | アップロードファイルのウイルス検出 |
| SSH認証失敗 | sshdログ | Failed password / Invalid user |
| アプリケーションエラー | PHP-FPMログ | FATAL/ERRORレベル |
ブロックの仕組み #
なぜWAN境界のFWではなくWebサーバー側でブロックするのか #
BASTIONのリアクティブ防御は、通常はWAN境界のファイアウォールAPI(OPNsense等)で攻撃元IPをブロックします(前回の記事参照)。しかし、WebサーバーがDNATで公開されている構成では、WAN境界FWのブロックリストではWebサーバー宛のトラフィックを捕捉できない場合があります。
そこで、Webサーバー側のfirewalld + nginx deny.confに直接ブロックルールを入れる独立した防御経路を用意しました。
BASTION (分析サーバー) → SSH接続(専用ユーザー・公開鍵認証・最小権限sudo) → firewall-cmd でIPをdrop(L3/L4レベル) → nginx deny.conf にIP追記 + reload(L7レベル)
SSH接続のセキュリティ #
分析サーバーからWebサーバーへの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分以内に検知してBanする。BASTIONは「403には至らないが明らかに不自然なパターン」を15分ごとのLLM分析で検知する。担当領域が異なるので、両方動かすことで死角が減ります。
実際の動作 #
分析レポート #
15分ごとの定期分析で、Webサーバーのセクションが自動的に出力されます。
Webサーバー詳細分析
リクエスト: 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件
自動ブロック実行 #
🛡️ Webサーバーブロック実行
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も有効化されます。環境の準備状況に合わせて段階的に強化できる設計です。
まとめ #
WebサーバーにBASTIONのAI分析層を追加し、nginx(第1層)→ Fail2Ban(第2層)→ BASTION(第3層)の3層防御を構築しました。固定閾値では捉えられない未知のBotパターンやスパムリクエストを、ローカルLLMが15分ごとに自動検知してfirewalldに即座にブロックを入れます。
SSH接続は専用アカウント・最小権限sudo・公開鍵認証。nginx deny.confはbest-effort設計で、環境の準備状況に応じて段階的に有効化可能。Fail2Banとは共存し、それぞれが異なる領域をカバーします。
BASTIONは閉域環境でAIセキュリティ監視を実現するサービスです。