feat(api, web): add Linux nft destination port hits for blocked IPs
Track tcp/udp dports via deny_port_hits, expose aggregate and per-IP ports in UI; install-link re-run refreshes nft rules. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
+16
-2
@@ -20,7 +20,7 @@ curl -fsSL https://<cp>/agent-install/<id> | bash
|
||||
3. После enroll статус станет **Pending** — одобрите агента (Approve).
|
||||
4. **Approved** — агент синхронизирует политику.
|
||||
|
||||
**Повторный запуск той же install-ссылки** на хосте, где агент уже стоит: обновляет sync-скрипт / timer (или MikroTik scheduler), **без** повторного enroll — `CLIENT_ID`/`token` сохраняются. Полный переустановки с новым токеном: `EVOFW_INSTALL_FORCE=1` (Linux).
|
||||
**Повторный запуск той же install-ссылки** на хосте, где агент уже стоит: обновляет sync-скрипт / timer (или MikroTik scheduler) **и пересоздаёт nft rules** (в т.ч. `deny_port_hits`), **без** повторного enroll — `CLIENT_ID`/`token` сохраняются. Сбрасывается `last_hash`, затем сразу force sync. Полный переустановки с новым токеном: `EVOFW_INSTALL_FORCE=1` (Linux).
|
||||
|
||||
API (auth): `POST /api/v1/install-links` `{ "name": "web-01", "platform": "linux" | "mikrotik" }`.
|
||||
|
||||
@@ -58,7 +58,7 @@ Whitelist: nft chain policy drop + allow set. Blacklist: policy accept + deny se
|
||||
Linux agent reports optional `ip_hits` in `POST /v1/agent/apply-report`:
|
||||
|
||||
- **nft:** tries set `deny_v4` with `flags interval; counter;`. If the kernel rejects counters on interval sets, falls back to plain interval (aggregate Traffic ↓ still works; per-IP empty).
|
||||
- Upgrade path: on install-link re-run, `last_hash` is cleared once so sets can be recreated (chain deleted before set replace).
|
||||
- Upgrade path: on install-link re-run, `last_hash` is cleared once so sets/chain can be recreated (chain deleted before set replace) — **обновляет и port-hit правила**.
|
||||
- **ipset:** prefers `hash:net … counters` on create; existing sets without counters are left as-is.
|
||||
- Payload: only entries with `packets > 0`, **top 200** by packets.
|
||||
- Control plane: `agent_ip_block_stats`, accumulates **deltas** of absolute kernel counters (как Traffic ↓). После flush set/chain (policy apply) CP сбрасывает per-IP baseline (`last_reported`), иначе вторая эпоха счётчиков теряется (Traffic растёт, Blocked IPs — нет).
|
||||
@@ -67,6 +67,20 @@ Linux agent reports optional `ip_hits` in `POST /v1/agent/apply-report`:
|
||||
|
||||
IPv6 skipped.
|
||||
|
||||
## Destination ports (Linux nft)
|
||||
|
||||
На **nft** агент ведёт dynamic set `deny_port_hits` (`ipv4_addr . inet_proto . inet_service`, timeout 1h, counter):
|
||||
|
||||
- Deny rule для tcp/udp: `update @deny_port_hits { ip saddr . meta l4proto . th dport }` + drop; прочие протоколы — plain drop.
|
||||
- В `apply-report`: optional `port_hits` top **500** `{ ip, port, protocol, packets }`.
|
||||
- CP: `agent_port_block_stats` (absolute deltas). Set **не** flush’ится на каждый policy apply (элементы живут по timeout) — baseline не сбрасывается при Traffic flush.
|
||||
- `GET /api/v1/agents/:id/blocked-ports` — aggregate top 50 портов; в `blocked-ips` у каждого IP — `ports` top 5.
|
||||
- UI: **Top ports** + колонка Ports в Blocked IPs (только `platform=linux`).
|
||||
- **ipset/iptables:** `port_hits: []`. MikroTik — без port hits.
|
||||
- Чтобы подтянуть правила на уже установленном агенте: **re-run install one-liner** (см. выше).
|
||||
|
||||
IPv6 skipped.
|
||||
|
||||
## MikroTik (RouterOS 7.21+)
|
||||
|
||||
В UI `/agents` → **Добавить агента** → platform **MikroTik**. Скопируйте one-liner:
|
||||
|
||||
+52
-3
@@ -397,14 +397,14 @@ paths:
|
||||
|
||||
/api/v1/agents/{id}/blocked-ips:
|
||||
get:
|
||||
summary: Per-IP/CIDR drop counters (Linux nft/ipset)
|
||||
summary: Per-IP/CIDR drop counters (Linux nft/ipset; MikroTik HITS)
|
||||
tags: [ops]
|
||||
security: [{ bearerAuth: [] }]
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Id'
|
||||
responses:
|
||||
'200':
|
||||
description: Top blocked IPs by accumulated packets
|
||||
description: Top blocked IPs by accumulated packets (+ optional top ports per IP on Linux)
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -420,6 +420,43 @@ paths:
|
||||
packets: { type: integer }
|
||||
first_seen_at: { type: string, format: date-time }
|
||||
last_seen_at: { type: string, format: date-time }
|
||||
ports:
|
||||
type: array
|
||||
description: Top destination ports for this IP (Linux nft)
|
||||
items:
|
||||
type: object
|
||||
required: [port, protocol, packets]
|
||||
properties:
|
||||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||||
protocol: { type: string, enum: [tcp, udp] }
|
||||
packets: { type: integer }
|
||||
|
||||
/api/v1/agents/{id}/blocked-ports:
|
||||
get:
|
||||
summary: Aggregate destination ports hit by denied sources (Linux nft)
|
||||
tags: [ops]
|
||||
security: [{ bearerAuth: [] }]
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/Id'
|
||||
responses:
|
||||
'200':
|
||||
description: Top ports by accumulated packets across all blocked IPs
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [port, protocol, packets, last_seen_at]
|
||||
properties:
|
||||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||||
protocol: { type: string, enum: [tcp, udp] }
|
||||
packets: { type: integer }
|
||||
last_seen_at: { type: string, format: date-time }
|
||||
|
||||
/api/v1/integrations/evobgp/communities:
|
||||
get:
|
||||
summary: Proxy EvoBGP communities
|
||||
@@ -520,7 +557,7 @@ paths:
|
||||
|
||||
/v1/agent/apply-report:
|
||||
post:
|
||||
summary: Apply report + packet stats (+ optional ip_hits)
|
||||
summary: Apply report + packet stats (+ optional ip_hits / port_hits)
|
||||
tags: [agent]
|
||||
security: [{ agentToken: [] }]
|
||||
requestBody:
|
||||
@@ -548,6 +585,18 @@ paths:
|
||||
properties:
|
||||
ip: { type: string, maxLength: 64 }
|
||||
packets: { type: integer, minimum: 0 }
|
||||
port_hits:
|
||||
type: array
|
||||
maxItems: 500
|
||||
description: Linux nft deny_port_hits (ip × proto × dport, packets > 0)
|
||||
items:
|
||||
type: object
|
||||
required: [ip, port, protocol, packets]
|
||||
properties:
|
||||
ip: { type: string, maxLength: 64 }
|
||||
port: { type: integer, minimum: 1, maximum: 65535 }
|
||||
protocol: { type: string, enum: [tcp, udp] }
|
||||
packets: { type: integer, minimum: 0 }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
|
||||
Reference in New Issue
Block a user