Implementing an AI Active Firewall on Web Servers #
We implemented a system where a local LLM analyzes unknown Bot patterns and spam requests that Fail2Ban’s fixed thresholds cannot detect, automatically blocking them every 15 minutes. We added an AI analysis layer on top of the existing nginx + Fail2Ban setup, creating a 3-layer defense structure.
Fail2Ban is Excellent but Weak Against “Unknown” Threats #
The combination of nginx UA mapping + rate limiting + Fail2Ban is the standard for Bot protection on web servers. Ban IPs that generate 403 errors 10 times/minute for one hour. Simple and reliable.
However, this approach has a structural weakness.
BASTION has a local LLM make judgments on these “patterns that humans would notice but fixed rules cannot catch.”
3-Layer Defense Structure #
We added BASTION’s AI analysis layer on top of the existing nginx + Fail2Ban setup, creating a 3-layer structure.
| Layer | Responsibility | Decision Method | Response Speed |
|---|---|---|---|
| Layer 1 | nginx (UA mapping / rate limiting / deny.conf) | Static rules | Immediate (per request) |
| Layer 2 | Fail2Ban (firewalld integration) | Fixed thresholds (403 errors 10 times/minute) | Within 1 minute |
| Layer 3 | BASTION (LLM analysis → firewalld via SSH) | LLM judges log context | Within 15 minutes |
Each layer operates independently. If Fail2Ban bans an IP first, BASTION performs duplicate checking to prevent double blocking. BASTION’s strength is its “ability to detect unknown patterns.”
Monitoring Items #
BASTION’s web server monitoring template analyzes the following 6 categories every 15 minutes.
| Category | Data Source | Detection Content |
|---|---|---|
| HTTP Status Distribution | Access logs | 403/429/5xx concentrated IPs. Deviation from normal state |
| Bot User-Agent Detection | Access logs | Unknown Bot User-Agent × High-frequency requests |
| Spam Requests | Access logs | Specific parameter spam like language switching floods |
| Malware Detection | ClamAV logs | Virus detection in uploaded files |
| SSH Authentication Failures | sshd logs | Failed password / Invalid user |
| Application Errors | PHP-FPM logs | FATAL/ERROR level |
Blocking Mechanism #
Why Block on the Web Server Rather Than the WAN Boundary Firewall #
BASTION’s reactive defense normally blocks attack source IPs at the WAN boundary firewall API (such as OPNsense) (see previous article). However, in configurations where the web server is exposed via DNAT, the WAN boundary firewall’s blocklist may not be able to capture traffic destined for the web server.
Therefore, we prepared an independent defense path that places blocking rules directly in the web server’s firewalld + nginx deny.conf.
BASTION (Analysis Server) → SSH connection (dedicated service account, public key authentication, minimum privilege sudo) → firewall-cmd drops IP (L3/L4 level) → nginx deny.conf appends IP + reload (L7 level)
SSH Connection Security #
SSH connections from the analysis server to the web server use a dedicated service account, with sudo privileges limited to only the necessary commands.
# sudoers authorized commands (nothing else can execute) 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/*
Rather than root login, we created a dedicated service account with minimum privilege design that can withstand ISMS audits.
Difference from Fail2Ban #
| Fail2Ban | BASTION | |
|---|---|---|
| Decision Method | Fixed thresholds (“403 N times/minute”) | LLM reads log context and judges |
| Unknown Patterns | Cannot detect (requires rule updates) | Automatically detects log anomaly patterns |
| User-Agent Spoofing | Cannot detect without 403 responses | Judges by request frequency × path × time of day |
| ClamAV Integration | None | FOUND detection → trace upload source IP → block |
| Notification | Email (MTA dependent) | Slack (instant notification, interactive operations) |
| Unblock | Fixed by bantime | Auto-unblock after 24 hours + instant unblock from Slack |
| Audit | fail2ban.log | Structured audit logs ([HB_BLOCK]/[HB_UNBLOCK] tags) |
BASTION coexists rather than replacing Fail2Ban. Fail2Ban detects and bans “403 floods” within 1 minute. BASTION detects “clearly unnatural patterns that don’t result in 403” through 15-minute periodic LLM analysis. Since they cover different areas, running both reduces blind spots.
Actual Operation #
Analysis Report #
With 15-minute periodic analysis, the web server section is automatically output.
Web Server Detailed Analysis
Requests: 1,847
Status: 200=1,650 / 403=112 / 429=45 / 5xx=0
403 Concentrated IP: xxx.xxx.156.12 (40 instances) — User-Agent: GPTBot/1.0
Bot Suspect User-Agent: “SomeNewBot/2.0” (180 instances/15 min) — Unregistered User-Agent
ClamAV: No detections
SSH Failures: 0 / PHP-FPM ERROR: 0
Automatic Block Execution #
🛡️ Web Server Block Execution
IP: xx.xxx.156.12
Reason: bot_spam
firewalld: drop added
nginx deny: applied
Manual unblock: @OpenClaw-Monitor unblock xx.xxx.156.12
Automatic Response to ClamAV FOUND Detection #
When ClamAV detects malware in an uploaded file, BASTION immediately outputs a CRITICAL determination. ClamAV itself only quarantines the file, but BASTION goes further by identifying the upload source IP from access logs and automatically blocking it with firewalld. A two-pronged approach: file quarantine (ClamAV) + network isolation (BASTION).
nginx deny.conf is “Best-Effort” Design #
We designed dual defense with firewalld blocking and nginx deny.conf, but we also designed it so that firewalld alone provides protection in environments without nginx deny.conf directory configuration. If deny.conf cannot be used, it is logged as “skipped” and firewalld continues alone. Once nginx include configuration is added later, nginx deny will automatically become active without code changes. This design allows gradual strengthening according to environment readiness.
Summary #
We added BASTION’s AI analysis layer to the web server, building a 3-layer defense of nginx (Layer 1) → Fail2Ban (Layer 2) → BASTION (Layer 3). Unknown Bot patterns and spam requests that fixed thresholds cannot catch are automatically detected every 15 minutes by local LLM and immediately blocked by firewalld.
SSH connections use dedicated accounts, minimum privilege sudo, and public key authentication. nginx deny.conf uses best-effort design and can be gradually enabled according to environment readiness. BASTION coexists with Fail2Ban, with each covering different areas.
BASTION is a service that enables AI security monitoring in closed environments.