106 lines
4.6 KiB
Markdown
106 lines
4.6 KiB
Markdown
# Network MCP
|
||
|
||
A "source of truth" for network devices and ports, backed by Elasticsearch, OPNsense, and Nmap.
|
||
|
||
## Architecture
|
||
|
||
- **Elasticsearch**: Stores current state (`network-hosts`) and historical events (`network-events-*`).
|
||
- **OPNsense Collector**: Fetches DHCP/ARP/DNS data to discover hosts.
|
||
- **Nmap Collector**: Scans discovered hosts for open ports and OS info.
|
||
|
||
## Setup
|
||
|
||
1. **Environment Config**
|
||
Copy `.env.example` to `.env` and fill in your details:
|
||
```bash
|
||
cp .env.example .env
|
||
# Edit .env
|
||
```
|
||
|
||
2. **Bootstrap Elastic**
|
||
Run the bootstrap script (requires `requests` installed locally, or you can run it inside a container):
|
||
```bash
|
||
python3 scripts/bootstrap_indices.py
|
||
```
|
||
*Note: Ensure you have connectivity to your Elasticsearch instance.*
|
||
|
||
3. **Start Services**
|
||
```bash
|
||
docker-compose up -d --build
|
||
```
|
||
This brings up the collectors and the lightweight frontend (reachable on port `5001`).
|
||
|
||
## Configuration
|
||
|
||
- **Static Metadata**: Edit `static/host_metadata.json` to add manual notes, roles, or tags to hosts (keyed by `mac:xx:xx...`).
|
||
- **Intervals**: Adjust polling intervals in `.env`.
|
||
- **VLAN Discovery (default on)**: Discovery sweeps (`nmap -sn`) run periodically across the OPNsense interfaces listed in `NMAP_DISCOVERY_VLANS`. Adjust the list (or set the flag to `false`) if you only want targeted subnets.
|
||
- **Quick vs Full Port Scans**: Each collector loop runs a fast, common-port sweep (`NMAP_QUICK_EXTRA_ARGS`, `NMAP_QUICK_BATCH_SIZE`) while a deeper service scan (`NMAP_PORT_RANGE`, `NMAP_BATCH_SIZE`) is triggered once per `NMAP_FULL_INTERVAL_SECONDS` (default daily). Tune these env vars to balance coverage vs. runtime.
|
||
- **Inventory Overlay**: Entries in `./inventory_targets.yml` are mounted into the OPNsense collector and merged by IP—offline/static hosts from that file (names, notes, expected ports) now appear in `network-hosts` with `source: inventory`.
|
||
|
||
## Data Model
|
||
|
||
- **`network-hosts`**: Current state of every known host.
|
||
- **`network-events-YYYY.MM.DD`**: Immutable log of scans and discovery events.
|
||
|
||
## Usage
|
||
|
||
Query `network-hosts` for the latest view of your network:
|
||
```json
|
||
GET network-hosts/_search
|
||
{
|
||
"query": {
|
||
"match_all": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Quick Frontend
|
||
|
||
A minimal Flask frontend is bundled in docker-compose (service `frontend`) and is exposed on port `5001` so it can be reached from other machines:
|
||
|
||
```bash
|
||
docker-compose up -d frontend
|
||
```
|
||
|
||
Then visit `http://<host-ip>:5001/` to see the merged view (inventory entries are marked with `source: inventory`). If you prefer to run it without Docker for debugging, follow the steps below:
|
||
|
||
```bash
|
||
cd network-mcp
|
||
python3 -m venv .venv && source .venv/bin/activate
|
||
pip install -r frontend/requirements.txt
|
||
python frontend/app.py
|
||
```
|
||
|
||
### MCP / API Endpoints
|
||
|
||
The frontend doubles as a Model Context Protocol server. It exposes the manifest at `/.well-known/mcp.json` (or `/api/mcp`) and supports the standard JSON-RPC handshake (`initialize`, `tools/list`, `tools/call`) on the same URL. Agents can either use the RPC tools below or hit the underlying REST endpoints directly.
|
||
|
||
- MCP Resources are also available (`resources/list`, `resources/read`, `resources/templates/list`) for clients that prefer resource-style access to snapshots and queries.
|
||
|
||
- `GET /api/hosts` – merged host list (supports `limit`, `source`, and repeated `q` params to fuzzy search names, hostnames, IPs, or MACs in a single call).
|
||
- `GET /api/hosts/<host_id>` – single host document with optional `include_events=true`.
|
||
- `GET /api/events` – recent scan/discovery events (`limit`, `host_id`, `type`, `since` filters).
|
||
- `GET /api/hosts/<host_id>/events` – scoped events for a host.
|
||
- `GET /api/map` – high-level “network map” grouping hosts by detected /24 (IPv4) or /64 (IPv6).
|
||
|
||
RPC tool names (mirrored in the manifest) are:
|
||
|
||
- `list_hosts` – accepts `{limit, source, terms}` and returns the merged host list.
|
||
- `network_map` – optional `{limit}` for building /24-/64 summaries.
|
||
- `get_host` – requires `{host_id}` plus optional `include_events`, `events_limit`.
|
||
- `list_events` – `{limit, host_id, type, since}`.
|
||
- `host_events` – requires `{host_id}` plus optional `limit`, `type`, `since`.
|
||
|
||
Resource URI examples:
|
||
|
||
- `network://hosts?q=seele&limit=50`
|
||
- `network://host/mac:dc:a6:32:67:55:dc?include_events=true&events_limit=50`
|
||
- `network://events?type=discovery&limit=100`
|
||
|
||
All RPC and REST calls share the Elasticsearch credentials from `.env`, so an agent only needs HTTP access to port `5001` to query hosts, notes, and event timelines. Registering the MCP with Codex looks like:
|
||
|
||
```bash
|
||
codex mcp install network-mcp http://<host>:5001/.well-known/mcp.json
|
||
```
|