Adding connections api
This commit is contained in:
67
README.md
67
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:
|
||||
|
||||
69
api/connections.php
Normal file
69
api/connections.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/connections.php
|
||||
*
|
||||
* Returns connections from the last 10 minutes (or ?minutes=N) as JSON.
|
||||
*
|
||||
* Authentication: same TRIGGER_TOKEN as trigger.php
|
||||
* Authorization: Bearer <token>
|
||||
* or ?token=<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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<?php
|
||||
define('APP_VERSION', '2603.1');
|
||||
define('APP_VERSION', '2603.2');
|
||||
|
||||
Reference in New Issue
Block a user