|
| 1 | +# T-Pot Attack Map - AI Coding Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +Real-time cyber attack visualization tool for the [T-Pot honeypot system](https://github.com/telekom-security/tpotce). Python backend processes honeypot events from Elasticsearch and streams to a Leaflet/D3.js frontend via WebSocket. |
| 5 | + |
| 6 | +## Architecture & Data Flow |
| 7 | +**Producer-Consumer model decoupled by Redis Pub/Sub:** |
| 8 | + |
| 9 | +1. **Data Source**: Elasticsearch with `logstash-*` indices (T-Pot honeypot events) |
| 10 | +2. **Producer** ([DataServer.py](../DataServer.py)): |
| 11 | + - Polls Elasticsearch every 0.5s for last 100 events |
| 12 | + - Queries stats every 10s (1m/1h/24h aggregations) |
| 13 | + - Maps ports → protocols via `port_to_type()` function |
| 14 | + - Publishes JSON to Redis channel `attack-map-production` |
| 15 | + - **Synchronous** (`redis.StrictRedis`, blocking operations) |
| 16 | +3. **Consumer & Web Server** ([AttackMapServer.py](../AttackMapServer.py)): |
| 17 | + - aiohttp server on port 1234 (configurable via `web_port`) |
| 18 | + - Subscribes to Redis channel, forwards to WebSocket clients |
| 19 | + - Serves static files from `static/` directory |
| 20 | + - **Fully async** (`redis.asyncio`, aiohttp, asyncio) |
| 21 | +4. **Frontend** ([static/map.js](../static/map.js), [dashboard.js](../static/dashboard.js)): |
| 22 | + - WebSocket client connects to `/websocket` |
| 23 | + - Leaflet.js map (CartoDB basemaps, dark/light themes) |
| 24 | + - D3.js v7 for animated attack lines and circles |
| 25 | + - IndexedDB cache with LocalStorage fallback (24h retention) |
| 26 | + |
| 27 | +## Critical Developer Patterns |
| 28 | + |
| 29 | +### Dual Configuration Toggle |
| 30 | +Connection strings are **hardcoded** with production/local variants: |
| 31 | +```python |
| 32 | +# Production (inside T-Pot Docker): |
| 33 | +# redis_url = 'redis://map_redis:6379' |
| 34 | +# Local development: |
| 35 | +redis_url = 'redis://127.0.0.1:6379' |
| 36 | +``` |
| 37 | +**Rule**: Always preserve both configurations. Active config is uncommented, production config is commented out. |
| 38 | + |
| 39 | +### Data: service_rgb Dictionary |
| 40 | +**Note**: Protocol-to-color mapping exists only in **DataServer.py**: |
| 41 | +- [DataServer.py](../DataServer.py#L30-L85) lines 30-85 |
| 42 | + |
| 43 | +**Adding a protocol**: |
| 44 | +1. Update `port_to_type()` in DataServer.py |
| 45 | +2. Add color to `service_rgb` in DataServer.py |
| 46 | + |
| 47 | +### Async/Sync Boundary |
| 48 | +- **AttackMapServer.py**: 100% async (use `await`, `asyncio.create_task`) |
| 49 | +- **DataServer.py**: 100% sync (no async/await, uses `time.sleep()`) |
| 50 | +- Redis clients are different: `redis.asyncio` vs `redis.StrictRedis` |
| 51 | + |
| 52 | +### Frontend Animation Management |
| 53 | +[map.js](../static/map.js) handles D3 animations with visibility checks: |
| 54 | +- Global `isPageVisible` flag prevents animation backlog when tab hidden |
| 55 | +- `isWakingUp` grace period (1s) suppresses burst on tab resume |
| 56 | +- D3 elements cleared on zoom to prevent coordinate desync |
| 57 | +- Use `d3.easeCircleIn` for consistent easing |
| 58 | + |
| 59 | +## Data Schema |
| 60 | + |
| 61 | +**WebSocket message types**: |
| 62 | +```javascript |
| 63 | +{type: "Traffic", ...} // Individual attack event |
| 64 | +{type: "Stats", ...} // Aggregate statistics (1m/1h/24h) |
| 65 | +``` |
| 66 | + |
| 67 | +**Attack event fields** (DataServer.py, lines 200-233): |
| 68 | +- `src_ip`, `src_lat`, `src_long`, `src_port`, `iso_code` |
| 69 | +- `dst_ip`, `dst_lat`, `dst_long`, `dst_port`, `dst_iso_code` |
| 70 | +- `protocol`, `color`, `honeypot`, `event_time`, `ip_rep` |
| 71 | +- `country`, `continent_code`, `tpot_hostname` |
| 72 | + |
| 73 | +## Environment & Dependencies |
| 74 | + |
| 75 | +**Python setup** (use virtual environment): |
| 76 | +```bash |
| 77 | +source bin/activate # Virtual env already exists |
| 78 | +pip install -r requirements.txt |
| 79 | +``` |
| 80 | + |
| 81 | +**Dependencies** ([requirements.txt](../requirements.txt)): |
| 82 | +- `aiohttp` (async web server) |
| 83 | +- `elasticsearch==8.18.1` (specific version!) |
| 84 | +- `redis` (includes asyncio support) |
| 85 | +- `pytz`, `tzlocal` (timezone handling) |
| 86 | + |
| 87 | +**Frontend assets** (all local in `static/`): |
| 88 | +- Leaflet.js, D3.js v7, Bootstrap 5 |
| 89 | +- Font Awesome, custom fonts (Inter, JetBrains Mono) |
| 90 | +- Flagpack icons in `static/flags/` |
| 91 | + |
| 92 | +## Common Workflows |
| 93 | + |
| 94 | +### Local Development Setup |
| 95 | +1. **Start Redis**: `redis-server` (port 6379) |
| 96 | +2. **Elasticsearch**: SSH tunnel or local instance on port 64298 |
| 97 | + ```bash |
| 98 | + ssh -L 64298:localhost:9200 tpot-server |
| 99 | + ``` |
| 100 | +3. **Data Server**: `python3 DataServer.py` (terminal 1) |
| 101 | +4. **Web Server**: `python3 AttackMapServer.py` (terminal 2) |
| 102 | +5. **Access**: http://localhost:1234 |
| 103 | + |
| 104 | +### Debugging Data Flow |
| 105 | +- **No attacks showing**: Check DataServer.py console for Elasticsearch errors |
| 106 | +- **WebSocket disconnects**: Check Redis connection in AttackMapServer.py |
| 107 | +- **Protocol showing as OTHER**: Add port mapping in `port_to_type()` |
| 108 | + |
| 109 | +### Theme Development |
| 110 | +- HTML `data-theme` attribute toggles dark/light |
| 111 | +- Map tiles auto-switch via `mapLayers` object ([map.js](../static/map.js#L37-L51)) |
| 112 | +- CSS custom properties defined in [index.css](../static/index.css) |
| 113 | + |
| 114 | +## Performance Gotchas |
| 115 | +- **Elasticsearch query size**: Limited to 100 events per poll (DataServer.py line 189) |
| 116 | +- **Cache limits**: Max 10,000 events in IndexedDB ([dashboard.js](../static/dashboard.js#L14)) |
| 117 | +- **Animation throttling**: Skip D3 animations when tab hidden (prevents memory bloat) |
| 118 | +- **Redis pubsub**: Single channel `attack-map-production`, all clients receive all events |
0 commit comments