From 3634f502a1c052360769403052ac24fba949cac0 Mon Sep 17 00:00:00 2001 From: DAProgs Date: Wed, 11 Mar 2026 10:57:40 -0400 Subject: [PATCH] Adding connections api --- README.md | 67 +++++++++++++++++++++++++++++++++++++++- api/connections.php | 69 ++++++++++++++++++++++++++++++++++++++++++ includes/functions.php | 31 +++++++++++++++++++ version.php | 2 +- 4 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 api/connections.php diff --git a/README.md b/README.md index 9c4c89a..8aef4f1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ Each portspoof_py instance runs independently and exposes a JSON API. portspoof_ 7. [Adding nodes](#adding-nodes) 8. [Fetch cron](#fetch-cron) 9. [HTTP trigger endpoint](#http-trigger-endpoint) -10. [Dashboard](#dashboard) +10. [JSON API](#json-api) +11. [Dashboard](#dashboard) 11. [Upgrading](#upgrading) 12. [Troubleshooting](#troubleshooting) @@ -56,6 +57,8 @@ portspoof_concentrator/ │ ├── footer.php Shared footer with version number │ ├── functions.php Node CRUD, fetch helpers, run_fetch(), dashboard queries │ └── style.php Shared CSS (included inline by both pages) +├── api/ +│ └── connections.php JSON API — recent connections (token-protected) └── cron/ └── fetch.php CLI polling script — run via cron or manually (CLI only) ``` @@ -421,6 +424,68 @@ When the endpoint is disabled: `503 Service Unavailable`. --- +## JSON API + +### `GET /api/connections.php` + +Returns connections ingested within a configurable lookback window, newest first. + +Uses the same `TRIGGER_TOKEN` for authentication as `trigger.php` — set it in `config.php` before use. + +#### Parameters + +| Parameter | Default | Max | Description | +|---|---|---|---| +| `minutes` | `10` | `1440` | Lookback window in minutes | +| `node_id` | _(all)_ | — | Filter results to a specific node ID | + +#### Authentication + +```bash +# Authorization header (preferred) +curl -H "Authorization: Bearer your-token" https://yourserver/api/connections.php + +# Query string +curl "https://yourserver/api/connections.php?token=your-token" +``` + +#### Examples + +```bash +# Last 10 minutes from all nodes +curl -H "Authorization: Bearer your-token" https://yourserver/api/connections.php + +# Last 30 minutes from node 2 +curl -H "Authorization: Bearer your-token" "https://yourserver/api/connections.php?minutes=30&node_id=2" +``` + +#### Response + +```json +{ + "since": "2026-03-11T14:01:00Z", + "minutes": 10, + "count": 3, + "connections": [ + { + "id": 9821, + "occurred_at": "2026-03-11 14:10:42.831204", + "node_id": 1, + "node_name": "honeypot-eu-1", + "src_ip": "198.51.100.42", + "src_port": 54312, + "dst_port": 443, + "banner_hex": "485454502f312e31203230300d0a", + "banner_len": 14 + } + ] +} +``` + +`banner_hex` is null when portspoof_py sent no banner. `occurred_at` is in the database timezone (UTC recommended). + +--- + ## Dashboard `index.php` auto-refreshes every 30 seconds and shows: diff --git a/api/connections.php b/api/connections.php new file mode 100644 index 0000000..f421cac --- /dev/null +++ b/api/connections.php @@ -0,0 +1,69 @@ + + * or ?token= + * + * Optional query parameters: + * minutes int Lookback window in minutes (default 10, max 1440) + * node_id int Filter to a specific node + */ + +require_once __DIR__ . '/../includes/functions.php'; + +header('Content-Type: application/json'); + +// ── Auth ────────────────────────────────────────────────────────────────────── + +if (TRIGGER_TOKEN === '') { + http_response_code(503); + echo json_encode(['error' => 'API is disabled. Set TRIGGER_TOKEN in config.php.']); + exit; +} + +$provided = ''; +$auth_header = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; +if (str_starts_with($auth_header, 'Bearer ')) { + $provided = substr($auth_header, 7); +} +if ($provided === '' && isset($_REQUEST['token'])) { + $provided = $_REQUEST['token']; +} + +if (!hash_equals(TRIGGER_TOKEN, $provided)) { + http_response_code(401); + echo json_encode(['error' => 'Unauthorized']); + exit; +} + +// ── Parameters ──────────────────────────────────────────────────────────────── + +$minutes = min(1440, max(1, (int)($_GET['minutes'] ?? 10))); +$node_id = isset($_GET['node_id']) ? (int)$_GET['node_id'] : null; + +// ── Query ───────────────────────────────────────────────────────────────────── + +$rows = connections_since($minutes, $node_id); + +$since = date('Y-m-d\TH:i:s\Z', time() - $minutes * 60); + +echo json_encode([ + 'since' => $since, + 'minutes' => $minutes, + 'count' => count($rows), + 'connections' => array_map(fn($r) => [ + 'id' => (int)$r['id'], + 'occurred_at' => $r['occurred_at'], + 'node_id' => (int)$r['node_id'], + 'node_name' => $r['node_name'], + 'src_ip' => $r['src_ip'], + 'src_port' => (int)$r['src_port'], + 'dst_port' => (int)$r['dst_port'], + 'banner_hex' => $r['banner_hex'], + 'banner_len' => (int)$r['banner_len'], + ], $rows), +], JSON_PRETTY_PRINT); diff --git a/includes/functions.php b/includes/functions.php index 0ce2cca..586a785 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -180,6 +180,37 @@ function run_fetch(): array { return $results; } +// ── API queries ─────────────────────────────────────────────────────────────── + +/** + * Return connections from the last $minutes minutes, newest first. + * Optionally filtered to a single node. + */ +function connections_since(int $minutes = 10, ?int $node_id = null): array { + $since = date('Y-m-d H:i:s', time() - $minutes * 60); + + if ($node_id !== null) { + $s = db()->prepare( + 'SELECT c.id, c.occurred_at, c.src_ip, c.src_port, c.dst_port, + c.banner_hex, c.banner_len, n.name AS node_name, n.id AS node_id + FROM connections c JOIN nodes n ON n.id = c.node_id + WHERE c.node_id = ? AND c.occurred_at >= ? + ORDER BY c.occurred_at DESC' + ); + $s->execute([$node_id, $since]); + } else { + $s = db()->prepare( + 'SELECT c.id, c.occurred_at, c.src_ip, c.src_port, c.dst_port, + c.banner_hex, c.banner_len, n.name AS node_name, n.id AS node_id + FROM connections c JOIN nodes n ON n.id = c.node_id + WHERE c.occurred_at >= ? + ORDER BY c.occurred_at DESC' + ); + $s->execute([$since]); + } + return $s->fetchAll(); +} + // ── Dashboard stats ─────────────────────────────────────────────────────────── function global_stats(): array { diff --git a/version.php b/version.php index 038b7a7..9fab5af 100644 --- a/version.php +++ b/version.php @@ -1,2 +1,2 @@