Skip to content

Commit ccd7b24

Browse files
authored
Merge pull request #920 from Mathide-Team/feat/902-api-live-captures
feat(#902): API de capture en direct sur interfaces, un filtre BPF par point
2 parents 0f6d717 + c24eecf commit ccd7b24

8 files changed

Lines changed: 879 additions & 8 deletions

File tree

‎docs/api-rest.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ La spécification OpenAPI est disponible sur :
8181
| `POST` | `/tools/export` | Sous-ensemble filtré (`file`, `bpf`, `time_start`, `time_end`, `endpoints`, `format`) : `--export-pcap` (issue #886) |
8282
| `POST` | `/tools/adjust-time` | Recalage (`file`, puis `time_offset`, `normalize` ou `align_to`, `format`) : `--adjust-time-output` (issue #887) |
8383
| `POST` | `/tools/replay` | Rejeu (`file`, `interface`, `speed`, `loop`) : `--replay`. ÉMET du trafic réel ; refusé (403) sauf si `interface` figure dans `NETCROSS_REPLAY_INTERFACES` (liste séparée par des virgules, vide par défaut) ; 503 si tcpreplay absent (issue #871) |
84+
| `POST` | `/live-captures` | Capture en direct (JSON `points` : `name`, `interface`, `bpf` ; `duration` ; `points_order` ; `options` de `/captures/multi`) puis analyse ; 202. 403 si `NETCROSS_API_LIVE` n'est pas à 1, interface hors de `NETCROSS_API_LIVE_INTERFACES` ou `NETCROSS_API_TOKEN` absent ; 409 si une capture est déjà en cours (issue #902) |
85+
| `GET` | `/live-captures/{id}` | État (`capturing`, `analysing`, `completed`, `failed`), `bytes_captured` par point, `elapsed_seconds`, `packet_count`, `analysis_id` |
86+
| `POST` | `/live-captures/{id}/stop` | Arrêt anticipé ; l'analyse porte sur ce qui a été capturé |
8487
| `GET` | `/analyses/{id}/annotations` | Annotations par point (`point`, `tag`) : même format que le sidecar `<capture>.annotations.json` de la GUI (issue #910) |
8588
| `POST` | `/analyses/{id}/annotations` | Ajoute une annotation (JSON `point`, `frame_number`, `tag`, `comment`, `color`) ; même trame et étiquette = mise à jour ; 404 point inconnu, 422 saisie invalide |
8689
| `DELETE` | `/analyses/{id}/annotations?point=&frame_number=&tag=` | Supprime une annotation ; 404 si absente |

‎docs/class-diagram.md‎

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
> Il remplace l'ancienne section 3 de `docs/features-backlog.md`, tenue à la main, qui avait dérivé
1212
> (voir `docs/sessions/session-36.md`, issue #140).
1313
14-
198 modules · 285 classes · 690 fonctions publiques de module.
14+
199 modules · 288 classes · 694 fonctions publiques de module.
1515

1616
Conventions : `+` public, `-` privé (préfixe `_`) ; `int?` = `int | None` ; `list~str~` = `list[str]` ;
1717
`<<module>>` regroupe les fonctions publiques d'un module ; `A --> B : champ` = `A` a un champ annoté
@@ -42,8 +42,8 @@ flowchart TD
4242
netcross_gtk4 -->|"6 imports"| pcap_parser
4343
netcross_api -->|"19 imports"| netcross_report
4444
netcross_api -->|"5 imports"| netcross_ai
45-
netcross_api -->|"48 imports"| netcross_core
46-
netcross_api -->|"3 imports"| pcap_parser
45+
netcross_api -->|"49 imports"| netcross_core
46+
netcross_api -->|"4 imports"| pcap_parser
4747
netcross_report -->|"40 imports"| netcross_core
4848
netcross_ai -->|"10 imports"| netcross_core
4949
netcross_core -->|"19 imports"| pcap_parser
@@ -3893,6 +3893,7 @@ classDiagram
38933893
| `netcross_api.batches` | lots de captures de l'API (issue #872). |
38943894
| `netcross_api.bpf_routes` | bibliothèque de filtres BPF enregistrés (issue #906), équivalent API de ``--bpf-library NOM`` et des boutons « Filtres… » / « Enregistrer » de la GUI. |
38953895
| `netcross_api.history_routes` | historique SQLite des runs (issue #874). |
3896+
| `netcross_api.live_routes` | capture en direct sur des interfaces du serveur, puis analyse (issue #902), équivalent API de ``--live`` et de la case « Capture en direct » de la GUI. |
38963897
| `netcross_api.lua_doc_routes` | documentation hors ligne de l'API Lua Wireshark (issue #873), équivalent de ``netcross-lua-doc --json``. |
38973898
| `netcross_api.models` | modèles Pydantic pour les requêtes/réponses API (issue #209). |
38983899
| `netcross_api.store` | analyses de l'API : statut, document JSON, persistance. |
@@ -4043,6 +4044,34 @@ classDiagram
40434044
+register(app, verify) None
40444045
}
40454046
4047+
%% ===== netcross_api.live_routes =====
4048+
class LivePoint {
4049+
<<BaseModel>>
4050+
+str name
4051+
+str interface
4052+
+str? bpf
4053+
}
4054+
class LiveCaptureRequest {
4055+
<<BaseModel>>
4056+
+list~LivePoint~ points
4057+
+float duration
4058+
+list~str~? points_order
4059+
+dict~str, Any~ options
4060+
}
4061+
class _LiveCaptures {
4062+
+start(req) dict~str, Any~
4063+
+get(capture_id) dict~str, Any~
4064+
+stop(capture_id) dict~str, Any~
4065+
+public(entry) dict~str, Any~
4066+
}
4067+
class mod_netcross_api_live_routes["netcross_api.live_routes"] {
4068+
<<module>>
4069+
+live_enabled(environ) bool
4070+
+allowed_interfaces(environ) frozenset~str~
4071+
+max_duration(environ) float
4072+
+register(app, verify, deps) _LiveCaptures
4073+
}
4074+
40464075
%% ===== netcross_api.lua_doc_routes =====
40474076
class mod_netcross_api_lua_doc_routes["netcross_api.lua_doc_routes"] {
40484077
<<module>>
@@ -4152,6 +4181,7 @@ classDiagram
41524181
}
41534182
41544183
%% ===== relations =====
4184+
LiveCaptureRequest --> LivePoint : points
41554185
netcross_api_models_SecurityReport --> SecurityFinding : findings
41564186
MultiAnalysisSummary --> SegmentLoss : segments
41574187
```

‎docs/guide/api.md‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,37 @@ encore disponibles par l'API. Utilisez pour cela la ligne de commande
8989
(voir [Comparer deux situations](comparer.md)). Le tableau complet des
9090
écarts entre surfaces est dans [Parité GUI / CLI / API](../parite-surfaces.md).
9191

92+
## Capturer en direct sur le serveur
93+
94+
L'API peut capturer elle-même sur les interfaces du serveur, un filtre BPF
95+
par point, comme `netcross --live`. C'est désactivé par défaut :
96+
l'administrateur l'active et choisit les interfaces autorisées.
97+
98+
```bash
99+
export NETCROSS_API_TOKEN=un-secret # obligatoire pour la capture
100+
export NETCROSS_API_LIVE=1
101+
export NETCROSS_API_LIVE_INTERFACES=eth0,eth1
102+
export NETCROSS_API_LIVE_MAX_DURATION=300 # secondes, 300 par défaut
103+
```
104+
105+
Le compte du service doit avoir le droit de capturer (par exemple
106+
`sudo setcap cap_net_raw,cap_net_admin=eip /usr/bin/dumpcap`, ou le groupe
107+
`wireshark`).
108+
109+
```bash
110+
curl -X POST -H "X-API-Key: $NETCROSS_API_TOKEN" -H "Content-Type: application/json" \
111+
-d '{"points": [{"name": "LAN", "interface": "eth0", "bpf": "tcp port 443"},
112+
{"name": "WAN", "interface": "eth1"}],
113+
"duration": 60, "options": {"nat_tolerant": true}}' \
114+
http://localhost:8000/live-captures
115+
curl -H "X-API-Key: $NETCROSS_API_TOKEN" http://localhost:8000/live-captures/$CAPTURE_ID
116+
curl -X POST -H "X-API-Key: $NETCROSS_API_TOKEN" http://localhost:8000/live-captures/$CAPTURE_ID/stop
117+
```
118+
119+
L'état passe de `capturing` à `analysing` puis `completed` ; il donne alors
120+
`analysis_id`, et le résultat se lit sous `/analyses/{analysis_id}` comme
121+
pour des fichiers envoyés. Une seule capture à la fois par serveur.
122+
92123
## Annoter une analyse
93124

94125
```bash

‎docs/openapi.yaml‎

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,6 +2131,120 @@ paths:
21312131
application/json:
21322132
schema:
21332133
$ref: '#/components/schemas/HTTPValidationError'
2134+
/live-captures:
2135+
post:
2136+
tags:
2137+
- live
2138+
summary: Start Live Capture
2139+
description: Démarre une capture en direct puis son analyse (équiv. ``--live``
2140+
+ ``--live-duration``).
2141+
operationId: start_live_capture_live_captures_post
2142+
requestBody:
2143+
content:
2144+
application/json:
2145+
schema:
2146+
$ref: '#/components/schemas/LiveCaptureRequest'
2147+
required: true
2148+
responses:
2149+
'202':
2150+
description: Successful Response
2151+
content:
2152+
application/json:
2153+
schema: {}
2154+
'403':
2155+
description: Capture en direct désactivée (NETCROSS_API_LIVE), interface
2156+
hors de NETCROSS_API_LIVE_INTERFACES, ou NETCROSS_API_TOKEN non défini
2157+
'404':
2158+
description: Capture inconnue
2159+
'409':
2160+
description: Une capture est déjà en cours
2161+
'422':
2162+
description: Validation Error
2163+
content:
2164+
application/json:
2165+
schema:
2166+
$ref: '#/components/schemas/HTTPValidationError'
2167+
security:
2168+
- APIKeyHeader: []
2169+
/live-captures/{capture_id}:
2170+
get:
2171+
tags:
2172+
- live
2173+
summary: Get Live Capture
2174+
description: 'État : capturing, analysing, completed ou failed ; octets par
2175+
point ; analysis_id.'
2176+
operationId: get_live_capture_live_captures__capture_id__get
2177+
security:
2178+
- APIKeyHeader: []
2179+
parameters:
2180+
- name: capture_id
2181+
in: path
2182+
required: true
2183+
schema:
2184+
type: string
2185+
title: Capture Id
2186+
responses:
2187+
'200':
2188+
description: Successful Response
2189+
content:
2190+
application/json:
2191+
schema:
2192+
type: object
2193+
additionalProperties: true
2194+
title: Response Get Live Capture Live Captures Capture Id Get
2195+
'403':
2196+
description: Capture en direct désactivée (NETCROSS_API_LIVE), interface
2197+
hors de NETCROSS_API_LIVE_INTERFACES, ou NETCROSS_API_TOKEN non défini
2198+
'404':
2199+
description: Capture inconnue
2200+
'409':
2201+
description: Une capture est déjà en cours
2202+
'422':
2203+
description: Validation Error
2204+
content:
2205+
application/json:
2206+
schema:
2207+
$ref: '#/components/schemas/HTTPValidationError'
2208+
/live-captures/{capture_id}/stop:
2209+
post:
2210+
tags:
2211+
- live
2212+
summary: Stop Live Capture
2213+
description: Arrête la capture avant la fin de ``duration`` ; l'analyse porte
2214+
sur ce qui a été capturé.
2215+
operationId: stop_live_capture_live_captures__capture_id__stop_post
2216+
security:
2217+
- APIKeyHeader: []
2218+
parameters:
2219+
- name: capture_id
2220+
in: path
2221+
required: true
2222+
schema:
2223+
type: string
2224+
title: Capture Id
2225+
responses:
2226+
'200':
2227+
description: Successful Response
2228+
content:
2229+
application/json:
2230+
schema:
2231+
type: object
2232+
additionalProperties: true
2233+
title: Response Stop Live Capture Live Captures Capture Id Stop
2234+
Post
2235+
'403':
2236+
description: Capture en direct désactivée (NETCROSS_API_LIVE), interface
2237+
hors de NETCROSS_API_LIVE_INTERFACES, ou NETCROSS_API_TOKEN non défini
2238+
'404':
2239+
description: Capture inconnue
2240+
'409':
2241+
description: Une capture est déjà en cours
2242+
'422':
2243+
description: Validation Error
2244+
content:
2245+
application/json:
2246+
schema:
2247+
$ref: '#/components/schemas/HTTPValidationError'
21342248
/analyses/{analysis_id}/annotations:
21352249
get:
21362250
tags:
@@ -3408,6 +3522,61 @@ components:
34083522
type: object
34093523
title: HealthResponse
34103524
description: Réponse du health check.
3525+
LiveCaptureRequest:
3526+
properties:
3527+
points:
3528+
items:
3529+
$ref: '#/components/schemas/LivePoint'
3530+
type: array
3531+
title: Points
3532+
description: Points à capturer simultanément (au moins 1)
3533+
duration:
3534+
type: number
3535+
exclusiveMinimum: 0.0
3536+
title: Duration
3537+
description: Durée de capture en secondes (bornée par NETCROSS_API_LIVE_MAX_DURATION)
3538+
points_order:
3539+
anyOf:
3540+
- items:
3541+
type: string
3542+
type: array
3543+
- type: 'null'
3544+
title: Points Order
3545+
description: Ordre amont -> aval (sinon déduit du trafic)
3546+
options:
3547+
additionalProperties: true
3548+
type: object
3549+
title: Options
3550+
description: 'Options d''analyse de POST /captures/multi (mêmes noms : tls,
3551+
nat_tolerant, max_packets...)'
3552+
type: object
3553+
required:
3554+
- points
3555+
- duration
3556+
title: LiveCaptureRequest
3557+
description: Corps de ``POST /live-captures``.
3558+
LivePoint:
3559+
properties:
3560+
name:
3561+
type: string
3562+
title: Name
3563+
description: Nom du point (devient le point de capture de l'analyse)
3564+
interface:
3565+
type: string
3566+
title: Interface
3567+
description: Interface du serveur (autorisée par NETCROSS_API_LIVE_INTERFACES)
3568+
bpf:
3569+
anyOf:
3570+
- type: string
3571+
- type: 'null'
3572+
title: Bpf
3573+
description: Filtre BPF de capture de ce point (ex. 'tcp port 443')
3574+
type: object
3575+
required:
3576+
- name
3577+
- interface
3578+
title: LivePoint
3579+
description: 'Un point de capture : nom, interface du serveur, filtre BPF propre.'
34113580
MultiAnalysisSummary:
34123581
properties:
34133582
analysis_id:

‎docs/parite-surfaces.md‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ ici fait donc échouer la CI.
1616
|---|---|---|
1717
| CLI | `cross_capture_analyzer_cli.py` (110 options), `cross_capture_diff_cli.py` (27), `cross_capture_batch_cli.py` (11), `cross_history_cli.py`, `netcross_lua_doc_cli.py`, `netcross_ai_models_cli.py` | Surface de référence : tout y est |
1818
| GUI | `netcross_gtk4` : 3 pages, 43 cases à cocher, 31 réglages numériques | Analyse interactive et exploration visuelle |
19-
| API | 47 routes FastAPI | Analyse avec sécurité ; options NAT, TLS, QUIC, anonymisation ; exports texte, PDF, CSV (issue #670) ; options avancées (#672) ; comparaison baseline/courant (#669) ; recherche forensic, extraction, comparaison de postes, NetFlow (#675) |
19+
| API | 50 routes FastAPI | Analyse avec sécurité ; options NAT, TLS, QUIC, anonymisation ; exports texte, PDF, CSV (issue #670) ; options avancées (#672) ; comparaison baseline/courant (#669) ; recherche forensic, extraction, comparaison de postes, NetFlow (#675) |
2020

2121

2222
Légende : **oui** = disponible ; **non** = absent ; **auto** = toujours
@@ -118,9 +118,9 @@ est conservée telle quelle pour les clients existants.
118118

119119
| Fonction | GUI | CLI | API |
120120
|---|---|---|---|
121-
| Capture sur interfaces, un filtre BPF par point | oui (case « Capture en direct ») | `--live` | non |
122-
| Durée maximale | oui | `--live-duration` | non |
123-
| Arrêt manuel puis analyse | oui (« Arreter et analyser ») | Ctrl+C | non |
121+
| Capture sur interfaces, un filtre BPF par point | oui (case « Capture en direct ») | `--live` | `POST /live-captures` : `points` (`name`, `interface`, `bpf`), `points_order`, `options` (mêmes options d'analyse que `POST /captures/multi`) ; état par `GET /live-captures/{capture_id}`, puis résultat sous `/analyses/{analysis_id}`. Désactivé par défaut (`NETCROSS_API_LIVE=1`), interfaces de `NETCROSS_API_LIVE_INTERFACES` seulement, `NETCROSS_API_TOKEN` exigé (issue #902) |
122+
| Durée maximale | oui | `--live-duration` | `duration`, bornée par `NETCROSS_API_LIVE_MAX_DURATION` (300 s par défaut ; issue #902) |
123+
| Arrêt manuel puis analyse | oui (« Arreter et analyser ») | Ctrl+C | `POST /live-captures/{capture_id}/stop` (issue #902) |
124124
| Rotation de capture (ring buffer) | oui (« Rotation de capture », nombre de fichiers, durée par fichier) : même enregistreur que la CLI, répertoires et fichiers conservés indiqués dans le journal | `--ring-buffer N:SECONDES` : enregistre la capture brute de chaque point en pcapng tournants (ring buffer natif de tshark), incompatible avec `pipe://` (issue #676) | non |
125125
| Bibliothèque de filtres BPF enregistrés | oui (enregistrer, choisir) | `--bpf-library NOM` (lit `~/.netcross/bpf_filters.json`, issue #676) | `GET /bpf-filters`, `PUT /bpf-filters/{nom}`, `DELETE /bpf-filters/{nom}` (même fichier, `$NETCROSS_API_BPF_FILTERS` côté serveur, issue #906) ; champ `bpf_library` par point avec #902 |
126126
| Rapport HTML rafraîchi en continu | oui (« Rapport HTML en continu », répertoire, intervalle, « Servir la page » et port ; issue #676) | `--live-report`, `--live-report-interval`, `--live-report-serve` | non |
@@ -301,6 +301,9 @@ l'option CLI de même nom) : `nat_tolerant`, `nat_window_ms`, `tls`,
301301
| `GET /lua-doc/search` | Recherche dans la documentation Lua, `q`, `limit`, `full` (issue #873) |
302302
| `GET /comparisons/{comparison_id}/redact-map` | Table d'anonymisation commune baseline/courant (issue #876) |
303303
| `POST /tools/replay` | Rejeu sur une interface autorisée (issue #871) |
304+
| `POST /live-captures` | Capture en direct sur des interfaces autorisées puis analyse (issue #902) |
305+
| `GET /live-captures/{capture_id}` | État d'une capture en direct : octets par point, durée, `analysis_id` (issue #902) |
306+
| `POST /live-captures/{capture_id}/stop` | Arrêt anticipé d'une capture en direct (issue #902) |
304307
| `GET /analyses/{analysis_id}/annotations` | Annotations de l'analyse par point, filtres `point`, `tag` (issue #910) |
305308
| `POST /analyses/{analysis_id}/annotations` | Ajoute ou met à jour une annotation (issue #910) |
306309
| `DELETE /analyses/{analysis_id}/annotations` | Supprime une annotation, `point`, `frame_number`, `tag` (issue #910) |
@@ -335,7 +338,7 @@ Classés par impact pour un utilisateur :
335338
batch) relève de l'automatisation et reste en CLI **par choix**.
336339
Révisé le 11/10/2026 (#330) : plus aucune exemption, toute fonction
337340
doit exister en CLI, GTK4 et API. Restent : l'API de la capture en
338-
direct (#902 à #908) et la CLI et l'API des vues propres à la GUI
341+
direct (#903 à #908 ; #902 livrée) et la CLI et l'API des vues propres à la GUI
339342
(#909 cartographie, #911 dashboard, #912
340343
exploration statistique).
341344

‎src/netcross_api/app.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,38 @@ async def get_batch_report(batch_id: str, name: str, _auth: None = Depends(_veri
28292829

28302830
_lua_doc_routes.register(app, _verify_api_key)
28312831

2832+
# Issue #902 : capture en direct sur des interfaces du serveur, puis analyse
2833+
import inspect as _inspect # noqa: E402
2834+
2835+
from netcross_api import live_routes as _live_routes # noqa: E402
2836+
2837+
_LIVE_OPTION_DEFAULTS = {"nat_tolerant": False, "nat_window_ms": 200.0, "tls": False, "quic": False, "redact": False}
2838+
2839+
2840+
def _live_make_options(**kwargs: Any) -> ApiAnalysisOptions:
2841+
"""Options d'analyse d'une capture en direct : celles de POST /captures/multi."""
2842+
return _options(**{**_LIVE_OPTION_DEFAULTS, **kwargs})
2843+
2844+
2845+
live_captures = _live_routes.register(
2846+
app,
2847+
_verify_api_key,
2848+
{
2849+
"token": lambda: _API_TOKEN,
2850+
"max_files": lambda: _MAX_FILES,
2851+
"parse_points_order": lambda order, labels: _parse_points_order(order, labels),
2852+
"option_names": lambda: frozenset(_inspect.signature(_options).parameters) - _live_routes._EXCLUDED_OPTIONS,
2853+
"make_options": _live_make_options,
2854+
"create_pending": lambda metadata, options: store.create_pending(
2855+
{**metadata, "options": _options_metadata(options)}
2856+
),
2857+
"run_job": lambda analysis_id, captures, order, multi, options: _run_job(
2858+
analysis_id, captures, order, multi, options
2859+
),
2860+
"get_analysis": lambda analysis_id: store.get(analysis_id),
2861+
},
2862+
)
2863+
28322864
# Issue #910 : annotations d'une analyse (rangées dans ses métadonnées, par point)
28332865
from netcross_api import annotations_routes as _annotations_routes # noqa: E402
28342866

0 commit comments

Comments
 (0)