- workspace: capmetro-monitor, github-notifications, model-selector - workspace-security: vt-monitor, monitor-unauthorized - workspace-home: cron-manager, monitor-unauthorized - extensions: vt-sentinel (VT-Sentinel plugin) Includes sync.sh for pull/push, README, AGENTS.md, .gitignore.
183 lines
7.0 KiB
Markdown
183 lines
7.0 KiB
Markdown
---
|
|
name: monitor-unauthorized
|
|
description: >-
|
|
Monitor and report unauthorized WebSocket gateway connections. Parses logs
|
|
every 30 minutes, detects new and returning IPs, and manages per-IP Discord
|
|
threads for tracking unauthorized access attempts.
|
|
metadata:
|
|
openclaw:
|
|
emoji: "🚨"
|
|
---
|
|
|
|
# Monitor Unauthorized Gateway Connections
|
|
|
|
Detects and tracks unauthorized WebSocket connection attempts to the OpenClaw gateway. Maintains per-IP Discord threads for ongoing tracking.
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
# Cron tool (every 30 minutes)
|
|
bash skills/monitor-unauthorized/scripts/cron-wrapper.sh
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Scripts (data layer — no Discord/OpenClaw calls)
|
|
|
|
| Script | Purpose |
|
|
|--------|---------|
|
|
| `scripts/log-splitter.sh` | Extracts unauthorized connection entries from the gateway log into `/tmp/openclaw/unauthorized-connections.log` (incremental, byte-offset tracked) |
|
|
| `scripts/check.sh` | Reads new entries from the unauthorized log, categorizes IPs as `new_ips` or `returning_ips`, updates `seen-connections.json` |
|
|
| `scripts/index-threads.sh` | Manages the thread index — lookup, record, and staleness checks. The index maps IPs to their Discord thread session keys |
|
|
| `scripts/cron-wrapper.sh` | Orchestrates the above scripts and outputs structured ACTION blocks for the agent |
|
|
|
|
### Agent (action layer — thread management via OpenClaw tools)
|
|
|
|
The agent parses the cron-wrapper output and handles all Discord thread operations using OpenClaw's built-in session and thread tools.
|
|
|
|
## Cron Behavior
|
|
|
|
1. `log-splitter.sh` extracts new unauthorized connection log lines (incremental)
|
|
2. `check.sh` processes new entries, outputs JSON with `new_ips[]` and `returning_ips[]`
|
|
3. `cron-wrapper.sh` formats ACTION blocks the agent must parse and act on
|
|
4. **If no new activity**: script produces no output → agent replies `NO_REPLY`
|
|
|
|
## Parsing Cron Output
|
|
|
|
The cron-wrapper outputs ACTION blocks. Parse them as follows:
|
|
|
|
### `ACTION:INDEX_THREADS`
|
|
|
|
The thread index is missing or stale. You must refresh it before processing connections.
|
|
|
|
**Steps:**
|
|
1. List all threads in the security Discord channel (`1471181304782389381`)
|
|
2. For each thread whose name starts with `🚨` and contains `unauthorized gateway access`:
|
|
- Extract the IP address from the thread name (format: `🚨 <ip> — unauthorized gateway access`)
|
|
- Record it: `bash scripts/index-threads.sh record "<ip>" "<session_key>" "<thread_name>"`
|
|
3. The session key format is: `agent:security:discord:channel:1471181304782389381:thread:<thread_name>`
|
|
|
|
### `ACTION:NEW_THREAD`
|
|
|
|
A new unauthorized IP was detected. Create a thread and report.
|
|
|
|
**Format:**
|
|
```
|
|
ACTION:NEW_THREAD
|
|
IP:<ip_address>
|
|
---
|
|
<report content>
|
|
---END_ACTION---
|
|
```
|
|
|
|
**Steps:**
|
|
1. Construct the thread name: `🚨 <ip> — unauthorized gateway access`
|
|
2. Construct the session key: `agent:security:discord:channel:1471181304782389381:thread:<thread_name>`
|
|
3. Use `sessions_send` to send the report content (between `---` and `---END_ACTION---`) to that session key. This creates the thread if it doesn't exist.
|
|
4. Record the thread in the index: `bash scripts/index-threads.sh record "<ip>" "<session_key>"`
|
|
|
|
### `ACTION:UPDATE_THREAD`
|
|
|
|
A previously-seen IP has new connection attempts. Update the existing thread.
|
|
|
|
**Format:**
|
|
```
|
|
ACTION:UPDATE_THREAD
|
|
IP:<ip_address>
|
|
SESSION_KEY:<session_key or empty>
|
|
---
|
|
<update content>
|
|
---END_ACTION---
|
|
```
|
|
|
|
**Steps:**
|
|
1. If `SESSION_KEY` is provided and non-empty, use it directly
|
|
2. If `SESSION_KEY` is empty, construct it: `agent:security:discord:channel:1471181304782389381:thread:🚨 <ip> — unauthorized gateway access`
|
|
3. Use `sessions_send` to post the update content to that session key
|
|
4. Update the index: `bash scripts/index-threads.sh record "<ip>" "<session_key>"`
|
|
|
|
## Thread Index Management
|
|
|
|
The thread index (`memory/thread-index.json`) maps IPs to their Discord thread session keys. This avoids redundant thread creation and enables reliable updates.
|
|
|
|
### One-Time Bootstrap
|
|
|
|
On first run (or when the index is missing), the agent must:
|
|
1. List all existing threads in channel `1471181304782389381`
|
|
2. Identify threads matching the `🚨 <ip> — unauthorized gateway access` pattern
|
|
3. Record each one via `scripts/index-threads.sh record`
|
|
|
|
### Ongoing Maintenance
|
|
|
|
- After creating a new thread (`ACTION:NEW_THREAD`), always `record` it in the index
|
|
- After sending an update (`ACTION:UPDATE_THREAD`), always `record` it to refresh timestamps
|
|
- The index auto-expires after 24 hours; the cron-wrapper will emit `ACTION:INDEX_THREADS` when a refresh is needed
|
|
|
|
### Index Script Commands
|
|
|
|
```bash
|
|
# Check if index needs refresh
|
|
bash scripts/index-threads.sh needs-refresh
|
|
# Returns: "fresh" (exit 1), "stale" (exit 0), or "missing" (exit 0)
|
|
|
|
# Look up a thread by IP
|
|
bash scripts/index-threads.sh lookup "1.2.3.4"
|
|
# Returns: JSON with session_key, thread_name, etc. (exit 0 = found, exit 1 = not found)
|
|
|
|
# Record/update a thread entry
|
|
bash scripts/index-threads.sh record "1.2.3.4" "agent:security:discord:channel:...:thread:..." "🚨 1.2.3.4 — unauthorized gateway access"
|
|
|
|
# Check index status
|
|
bash scripts/index-threads.sh status
|
|
# Returns: JSON with entry count, age, staleness
|
|
```
|
|
|
|
## Storage Files
|
|
|
|
| File | Location | Purpose |
|
|
|------|----------|---------|
|
|
| `seen-connections.json` | Skill directory | All IPs ever seen — first_seen, last_seen, total_attempts, metadata |
|
|
| `authorized-ips.json` | Skill directory | Whitelist — these IPs are silently skipped |
|
|
| `memory/thread-index.json` | State directory | Maps IPs to Discord thread session keys |
|
|
| `memory/unauth-splitter-offset` | State directory | Byte offset for log-splitter (gateway log) |
|
|
| `memory/unauth-check-offset` | State directory | Byte offset for check (unauthorized log) |
|
|
|
|
## Authorized IPs (Whitelist)
|
|
|
|
Edit `authorized-ips.json` to suppress reporting for known IPs:
|
|
|
|
```json
|
|
{
|
|
"whitelist": ["127.0.0.1", "::1", "localhost", "192.168.1.100"]
|
|
}
|
|
```
|
|
|
|
## Log Files
|
|
|
|
| Log | Path | Contents |
|
|
|-----|------|----------|
|
|
| Gateway log | `/tmp/openclaw/openclaw.log` | Full OpenClaw gateway log (source) |
|
|
| Unauthorized log | `/tmp/openclaw/unauthorized-connections.log` | Extracted unauthorized connection entries only |
|
|
|
|
## What This Monitors
|
|
|
|
Gateway WebSocket authorization failures containing `forwardedFor` IP addresses. Specifically:
|
|
- Entries with `"forwardedFor"` in the JSON log
|
|
- Entries with cause `"unauthorized"` or `"pairing-required"`
|
|
|
|
## Example Agent Flow
|
|
|
|
```
|
|
1. Cron fires → bash scripts/cron-wrapper.sh
|
|
2. Output contains ACTION:INDEX_THREADS → agent lists threads, records them
|
|
3. Output contains ACTION:NEW_THREAD for IP 203.0.113.42 →
|
|
a. agent constructs session key
|
|
b. agent calls sessions_send with report content
|
|
c. agent runs: bash scripts/index-threads.sh record "203.0.113.42" "<key>"
|
|
4. Output contains ACTION:UPDATE_THREAD for IP 198.51.100.7 →
|
|
a. agent uses SESSION_KEY from output (or constructs it)
|
|
b. agent calls sessions_send with update content
|
|
c. agent runs: bash scripts/index-threads.sh record "198.51.100.7" "<key>"
|
|
5. No output → agent replies NO_REPLY
|
|
```
|