AI 偵測到攻擊→透過 Slack 核准→建置了自動封鎖至防火牆的機制

AI 偵測到攻擊→透過 Slack 核准→建置了自動封鎖至防火牆的機制

3 min read

2026.04 / Tech Blog / BASTION

AI detects attack → Approve on Slack → Auto-block added to firewall #

We added “automatic response” to BASTION’s “detect → notify” loop. When AI detects a port scan, it posts a block proposal to Slack, and when approved by a human, the block rule is immediately added to the OPNsense firewall. Auto-removal after 24 hours.

Detection and notification alone are not enough #

BASTION previously worked by analyzing logs to detect anomalies and notifying via Slack (Part 1). However, the action that follows notification—blocking the attack source IP in the firewall—required a human to manually open the OPNsense admin console and operate it.

A notification arrives at 3 AM, and you log in the next morning to apply the block. In those 6 hours, the attacker can scan freely.

We implemented reactive defense in BASTION. When AI detects an attack, it automatically proposes adding a firewall block, and approving it on Slack immediately applies the rule to OPNsense.

However, we do not make it fully automatic immediately. Giving AI the authority to modify firewall rules carries the risk of blocking normal traffic due to misclassification. We start with a Slack approval-gate approach (“AI proposal → human approval → execution”) and will gradually introduce autonomy after accumulating results.

Overall flow #

Step Executed by Action
1. Attack detection analyze.sh (every 15 min) Log analysis detects port scans and brute-force attempts. Severity marked as HIGH
2. Block proposal propose-block.sh Whitelist check → Deduplication → Post proposal to Slack
3. Human approval Operator Slack command: @OpenClaw-Monitor approve block-XXXXX
4. Block execution fw-action.sh OPNsense API adds IP to Alias (BASTION_AutoBlock)
5. Auto-removal auto-expire.sh (hourly cron) Remove IP from Alias after 24 hours

Operation in practice #

1. Block proposal arrives on Slack #

When the regular 15-minute analysis detects severity: HIGH, a block proposal for the attack source IP is automatically posted to Slack.

incoming-webhook 13:31

🛡️ Block proposal [block-20260423-003]

Attack source IP: xxx.xxx.119.51
Detection reason: port_scan — 3,003 blocks/failures
Severity: HIGH
Auto-removal: 24 hours later

Approve: @OpenClaw-Monitor approve block-20260423-003
Reject: @OpenClaw-Monitor reject block-20260423-003

※ If no response within 30 minutes, will be automatically rejected.

incoming-webhook 13:31

🛡️ Block proposal [block-20260423-004]

Attack source IP: xxx.xxx.47.173
Detection reason: port_scan — 977 blocks/failures
Severity: HIGH
Auto-removal: 24 hours later

incoming-webhook 13:31

🛡️ Block proposal [block-20260423-005]

Attack source IP: xxx.xxx.102.23
Detection reason: port_scan — 965 blocks/failures
Severity: HIGH
Auto-removal: 24 hours later

Up to 3 proposals per analysis. Top IPs are selected by number of blocks, in descending order.

2. Approval immediately reflects in firewall #

When you send the approval command on Slack, fw-action.sh calls the OPNsense API to add the IP to the block list.

Operator 13:37
@OpenClaw-Monitor approve block-20260423-003
incoming-webhook 13:37

✅ Block execution complete [block-20260423-003]
IP: xxx.xxx.119.51
Auto-removal: 2026-04-24T13:37:14+09:00

On the OPNsense side, the IP is added to an Alias called BASTION_AutoBlock. A WAN block rule referencing this Alias is configured in advance, so adding an IP to the Alias via API immediately activates the block.

3. Auto-removal after 24 hours #

auto-expire.sh runs on an hourly cron and automatically removes expired blocks.

incoming-webhook 00:00

⏰ Auto-removal: xxx.xxx.119.51 (24 hours elapsed)

If the same IP scans again after removal, it will be proposed again in the next analysis.

Safety mechanisms #

Since we are automating firewall operations with AI, we need safety mechanisms to prevent misclassification from blocking legitimate traffic. We have implemented 5 layers of defense.

Layer Content Purpose
Whitelist Internal IPs, DNS, company global IPs are never blocked Prevent blocking own traffic
Slack approval gate Human verification before execution (initial phase) Catch AI misclassifications
Rate limiting Maximum 10 blocks per hour Prevent abnormal mass blocking
Auto-removal Automatically remove block after 24 hours Auto-recovery even if misclassified
Emergency flush Instant removal of all blocks from Slack Emergency rollback if business impact occurs

Whitelist matching implementation #

Whitelist matching is implemented using Python’s ipaddress module. It supports CIDR notation (10.0.0.0/8 etc.) and automatically excludes private IPs, loopback, link-local, multicast, and reserved addresses via built-in checks.

# Whitelist matching flow
1. Built-in check: is_private / is_loopback / is_link_local / is_multicast / is_reserved
2. File matching: Check each line in block-whitelist.txt (single IP or CIDR)
3. Result: exit 0=block prohibited / exit 1=block allowed

OPNsense API integration #

The block mechanism itself is an OPNsense Alias (a group of IP addresses). We create an Alias called “BASTION_AutoBlock” in advance and configure a WAN block rule that uses this Alias as the source.

# Add IP (execute block)
curl -k -u "${API_KEY}:${API_SECRET}" \
  -X POST "${OPNSENSE}/api/firewall/alias_util/add/BASTION_AutoBlock" \
  -d '{"address":"attack_source_ip"}'

# Remove IP (remove block)
curl -k -u "${API_KEY}:${API_SECRET}" \
  -X POST "${OPNSENSE}/api/firewall/alias_util/delete/BASTION_AutoBlock" \
  -d '{"address":"attack_source_ip"}'

alias_util directly manipulates the runtime table, so a single API call immediately activates the block. Firewall rule reloading is not necessary.

Audit log #

All blocks, removals, approvals, rejections, and timeouts are recorded in the audit log.

2026-04-23 13:31:33 [PROPOSE] new block-20260423-003 ip=xxx.xxx.119.51 reason=port_scan count=3003
2026-04-23 13:37:14 [BLOCK]   approved block-20260423-003 ip=xxx.xxx.119.51 expires=2026-04-24T13:37:14
2026-04-24 00:00:03 [EXPIRE]  auto-expire block-20260423-003 ip=xxx.xxx.119.51 api=done

Grep by tag ([PROPOSE] / [BLOCK] / [EXPIRE] etc.) to extract specific operations only.

Gradual autonomy #

Currently we use a Slack approval-gate approach, but the design allows for gradual autonomy as we accumulate results.

Phase Model Migration condition
Phase 1 (current) Slack approval gate
Phase 2 Conditional autonomy High confidence + certain block count or more → auto-execute. Otherwise approval gate
Phase 3 Full autonomy Misclassification rate below 0.1% for 3 consecutive months

Why do this gradually? #

During BASTION development, we experienced LLM hallucinations where it “fabricated non-existent incidents.” One account reported “locked out 18 times,” but the actual event count in logs was zero. If reactive defense had been implemented earlier, we might have blocked IPs for phantom attacks. This experience is the basis for our design decision to “have humans verify before executing.”

Limitations of the Slack approval gate are already visible #

With Slack approval via OpenClaw, we have seen cases where the LLM fails to execute some commands during batch processing yet reports “completed.” When an agent-based LLM is in the middle, this kind of uncertainty is structurally unavoidable. With full automation (direct pipeline analyze.sh → propose-block.sh → fw-action.sh), since we bypass LLM response generation, this problem disappears.

Summary #

BASTION’s reactive defense fully automates the flow of log analysis → block proposal → Slack approval → OPNsense API block → 24-hour auto-removal. The 5 safety layers (whitelist, approval gate, rate limiting, auto-removal, emergency flush) minimize business impact from misclassifications.

We are currently operating with a Slack approval-gate approach, but we will accumulate result data and gradually introduce autonomy. The monitoring system that previously ended at “detect → notify” now closes the loop at “detect → judge → respond → auto-remove.”

BASTION is a service that realizes AI security monitoring in isolated environments.

BASTION Service Page
Contact

Updated on 2026年6月9日

What are your feelings

  • Happy
  • Normal
  • Sad