Come bitmapper cattura
Questa pagina descrive la pipeline reale del comando capture: cosa apre, cosa filtra, cosa
ricorda e quanto ti costa ciascuna manopola.
Tutto ciò che segue avviene sull'host. I pacchetti stessi non lo lasciano mai — ciò che lo lascia è un record di flusso che riassume ogni connessione tracciata. Vedi cosa lascia l'host.
interfaces → BPF filter (kernel) → worker pool → connection tracker → flow record → platform
│ │
│ └→ un ultimo
│ tentativo di attribuzione
├→ attribuzione dei processi, ritentata sui
│ pacchetti successivi con backoff
└→ statistics (structured JSON log)
Scegliere le interfacce
bitmapper interfaces elenca le interfacce su cui questo host può catturare. Poi puoi
indicarle per nome, oppure non indicarne nessuna e lasciare che sia l'agente a scegliere:
capture:
interfaces: ["eth0", "eth1"] # empty = every interface that is up and not loopback
Con capture.interfaces vuoto, l'agente cattura su ogni interfaccia attiva e non di
loopback. Su un host con diverse NIC, un bridge e le interfacce virtuali dei container, di
solito è più di quanto intendessi — lo stesso pacchetto può essere visto più di una volta
mentre attraversa un bridge. Indica per nome le interfacce che ti interessano davvero.
capture.exclude_interfaces rimuove dei nomi dall'elenco con cui ti ritrovi, qualunque esso
sia:
capture:
interfaces: [] # auto-detect: every interface up and non-loopback
exclude_interfaces: ["docker0", "veth0"]
Si applica sia a un elenco esplicito in capture.interfaces sia all'insieme rilevato
automaticamente, e la corrispondenza non distingue tra maiuscole e minuscole e ignora gli spazi
circostanti. Questo lo rende lo strumento giusto per il caso più comune: lasciare che sia
l'agente a trovare le interfacce, per poi sottrarre i bridge e le interfacce virtuali che non
vuoi contare due volte.
Filtraggio: quattro chiavi, un solo filtro del kernel
Quattro chiavi decidono quali pacchetti bitmapper arriva a vedere, e tutte e quattro vengono compilate in un'unica espressione BPF consegnata a libpcap prima che la cattura si avvii. Tutto ciò che escludono viene scartato dal kernel — non viene filtrato via dall'output dopo, non viene copiato nell'agente affatto.
| Chiave | Predefinito | Cosa contribuisce |
|---|---|---|
capture.filter | "" — nessuno | Un'espressione BPF grezza, usata così com'è, fra parentesi |
capture.protocols | ["tcp", "udp"] | Una lista di ammessi: (tcp or udp). Accetta solo tcp, udp, icmp, all |
capture.ports | [] — nessuna | Una lista di ammessi: (port 80 or port 443) |
capture.exclude_ports | [] — nessuna | Una lista di esclusione: not (port 22) |
Le clausole sono unite con and, in quest'ordine. La configurazione distribuita con bitmapper
(configs/bitmapper.yaml) imposta protocols: [tcp, udp] ed exclude_ports: [22], che si
compila in:
(tcp or udp) and not (port 22)
Impostale tutte e quattro e ottieni una sola espressione — per esempio
filter: "host 10.0.0.1", protocols: [tcp], ports: [443], exclude_ports: [22] si compila
in (host 10.0.0.1) and (tcp) and (port 443) and not (port 22).
L'agente registra a log l'espressione compilata all'avvio ogni volta che differisce da ciò
che hai scritto in capture.filter: puoi quindi rileggere esattamente ciò che è stato consegnato
al kernel invece di dedurlo. Una porta fuori dall'intervallo 1–65535 in una delle due liste viene
rifiutata all'avvio, invece di fallire più tardi dentro libpcap.
Una versione precedente di questa pagina riportava un avviso intitolato «Tre chiavi di filtro che
non vengono lette da nessuno», che affermava che capture.protocols, capture.ports e
capture.exclude_ports venissero convalidate e poi ignorate. Era falso. Tutte e tre vengono
compilate nel filtro BPF descritto sopra e applicate dal kernel.
L'errore andava nella direzione che ti costa, e capture.exclude_ports è il motivo per rileggere
la tua configurazione adesso. La configurazione distribuita contiene exclude_ports: [22] — «non
catturare SSH» — e quell'esclusione è reale: i pacchetti SSH vengono scartati prima di
raggiungere l'agente. Se hai letto il vecchio avviso e hai cancellato la chiave ritenendola
peso morto, hai disattivato un controllo di riservatezza che funzionava e hai iniziato a
raccogliere traffico che avevi deliberatamente escluso. Nulla te ne avrebbe avvisato, perché dal
punto di vista dell'agente avevi semplicemente chiesto più traffico.
Anche il controesempio del vecchio avviso era esattamente rovesciato. capture.ports: [443] non
cattura tutto. Con i protocolli predefiniti si compila in (tcp or udp) and (port 443) e cattura
solo la porta 443:
# This captures TCP/UDP port 443 and nothing else — the ports key is applied, in the kernel
capture:
ports: [443]
Il valore predefinito è tcp + udp, quindi ICMP non viene catturato
capture.protocols vale in modo predefinito ["tcp", "udp"] anche se il tuo file di
configurazione non la nomina mai. Un host con una configurazione di serie non vede quindi mai
ICMP, ICMPv6, SCTP, GRE, ESP o AH: il kernel li scarta, l'agente non li riceve mai e nessun
contatore registra ciò che è stato filtrato via. Nell'output un'assenza e un silenzio sono
indistinguibili.
È un vero punto cieco se usi bitmapper per vedere ping sweep, traffico incapsulato in tunnel o IPsec. Per eliminarlo, chiedi che non ci sia alcun vincolo di protocollo:
capture:
protocols: ["all"]
Due dettagli facili da sbagliare:
protocols: ["icmp"]copre entrambe le famiglie — si compila in(icmp or icmp6), così chiedere ICMP non ti dà silenziosamente solo IPv4.- Non esiste alcun valore per SCTP, GRE, ESP o AH.
protocolsaccetta solotcp,udp,icmpeall; qualunque altra cosa viene rifiutata all'avvio coninvalid protocol: … (valid: tcp, udp, icmp, all). Se te ne serve uno in particolare, impostaprotocols: ["all"]ed esprimi il protocollo incapture.filter.
capture.exclude_ports da sola non crea questo punto cieco. Viene resa come
not (port 22), e un test di porta BPF è vero anche per i pacchetti che non hanno porte: quindi
escludere SSH non scarta ICMP per inerzia. Solo la lista di ammessi dei protocolli lo fa.
Preferisci il filtro più stretto, qualunque chiave lo esprima
Vale la pena imparare BPF in ogni caso. tcp and not port 22, port 443 or port 8443,
not net 10.0.0.0/8 — ciascuna di esse viene applicata prima che il pacchetto ti costi qualcosa,
e capture.filter resta l'unica chiave capace di esprimere vincoli di host, di rete o di
direzione. Le tre chiavi-lista sono la scorciatoia leggibile per i casi di protocollo e porta;
finiscono nello stesso posto.
Ciò che qui è davvero inerte sono le opzioni di logging
--log-level e --log-format non fanno nullaQuesta pagina additava un tempo le chiavi di filtro come ciò che viene analizzato senza avere effetto. Le opzioni che si comportano davvero così sono quelle di logging sulla riga di comando.
--log-level e --log-format sono dichiarate sul comando radice e compaiono in --help con i
valori predefiniti info e json, ma nessun codice legge né l'una né l'altra. Il logger è un
logger zap di produzione fisso, costruito all'avvio — JSON, livello info — e nessuna delle due
opzioni può cambiarlo. Passare --log-level debug non ti dà alcun dettaglio in più; passare
--log-format console ti dà comunque JSON.
Le altre opzioni di capture si comportano allo stesso modo. --interfaces, --filter,
--snaplen, --promiscuous, --buffer-size, --workers, --queue-size e --stats-interval
vengono legate in Viper sotto il loro nome di opzione mentre la configurazione è letta da chiavi
annidate (capture.filter, capture.snaplen, performance.workers, …), quindi vengono
analizzate e ignorate. --enable-grpc ed --enable-http sono peggio che ignorate: valgono
true in modo predefinito in --help e descrivono server che questo agente
non costruisce mai — non c'è alcun listener gRPC o HTTP da
abilitare.
--config è l'unica opzione che cambia ciò che l'agente fa. Metti tutto il resto nel file
YAML.
Leggere i pacchetti
| Chiave | Predefinito | Cosa fa |
|---|---|---|
capture.snaplen | 65535 | Byte catturati per pacchetto (convalidato 0–65535) |
capture.promiscuous | true | Modalità promiscua |
capture.buffer_size | 104857600 | Buffer di cattura del kernel, in byte |
capture.timeout | 100ms | Timeout di lettura dei pacchetti |
Snaplen è la leva che più vale la pena abbassare. bitmapper costruisce una mappa delle connessioni — chi parla con chi, su cosa, quanto — e questa vive interamente nelle intestazioni dei pacchetti. Catturare l'intero payload da 65535 byte copia nella memoria dell'agente dati applicativi che qui nessuno legge. Uno snaplen di poche centinaia di byte conserva tutte le intestazioni di cui il tracker ha bisogno e impedisce del tutto la copia del payload, il che è al tempo stesso più economico e un'esposizione molto minore se l'host viene compromesso.
La modalità promiscua fa sì che la NIC accetti frame non indirizzati a essa. Su una rete commutata questo produce per lo più broadcast e multicast; su una porta mirror/SPAN è ciò che rende la cattura utile. Richiede inoltre privilegi elevati e vale la pena disattivarla quando vuoi soltanto le conversazioni dell'host stesso.
Attribuzione dei processi
capture:
enable_process_mapping: true
process_mapping_interval: 5s
Tutto ciò che segue — l'attribuzione per flusso, il completamento successivo, l'inferenza dai
socket in ascolto e il campo attribution — non è rilasciato. Non è in 0.1.0-ga (commit
64ebc64), che resta l'unico binario pubblicato. Esegui bitmapper version e confronta prima di
programmare qualsiasi cosa su questa pagina.
Cosa fa il binario che puoi scaricare oggi. Letto dal codice al commit 64ebc64:
- L'attribuzione viene tentata una sola volta, alla creazione della connessione, da
un'istantanea della tabella dei socket che può essere vecchia fino a
process_mapping_interval. Non esiste alcun tentativo successivo. - Non esiste alcuna inferenza dai socket in ascolto. Il codice che la esegue non esiste a
quel commit.
0.1.0-ganon può mai emetterelistening_socket, né mai nominare un processo che non abbia trovato sul socket proprio del flusso. - La risposta è sempre registrata sull'estremo sorgente.
destination_processnon viene assegnato da nessuna parte in quella versione, quindi non può mai comparire in un record esportato. - L'oggetto processo esportato porta
pid,name,executableeuser— e nessun campoattribution.
Il socket di una connessione uscente non può trovarsi in un'istantanea presa prima che quella
connessione esistesse: i flussi di 0.1.0-ga spesso non portano quindi alcun processo. Misurato
sull'host di sviluppo: 0 flussi attribuiti su 12. Aspettatelo da questa versione, anziché
leggerlo come un errore di configurazione.
Cosa fa la prossima versione. Il tentativo viene ripetuto sui pacchetti successivi del flusso
e un'ultima volta sul percorso di export; entrambi gli estremi vengono attribuiti; e ogni oggetto
processo porta un campo attribution che dice se l'identità viene dal socket proprio del flusso
o da un socket in ascolto. Quell'inferenza è vincolata a tre condizioni: l'estremo deve essere un
indirizzo di questo host, dev'essere dimostrato che esattamente un processo detiene il socket
in ascolto, e il flusso dev'essere TCP.
È questo che distingue una cattura da un dump di pacchetti: una connessione tracciata viene risolta nel PID, nome del processo, percorso dell'eseguibile e utente che possiede il socket, leggendo le tabelle dei socket dell'host.
L'attribuzione avviene per flusso, non per pacchetto, ed è registrata sull'estremo del
flusso che possiede il socket — quindi un record di flusso può portare source_process,
destination_process, o nessuno dei due. Su un host che è uno dei due estremi della
conversazione, l'altro estremo è un peer remoto il cui processo non si trova su questa macchina e
non ci sarà mai; ci si aspetta che sia valorizzato un solo estremo.
Viene completata in un secondo momento, non risolta una volta sola
Il primo tentativo di attribuzione di un flusso avviene alla creazione della connessione, e fallisce molto spesso: il socket di una connessione uscente non può comparire in un'istantanea della tabella dei socket presa prima che la connessione esistesse. Per questo il tentativo viene ripetuto:
- Sui pacchetti successivi del flusso, con un backoff che parte da 100 ms e raddoppia fino a un tetto di 30 s. Un flusso che davvero non ha alcun socket locale — traffico che l'host si limita a inoltrare — si assesta così su un paio di ricerche al minuto anziché una per pacchetto.
- Sul percorso di export, prima che un record per quel flusso venga costruito. Per un flusso già terminato è la sua unica possibilità rimasta: un ciclo richiesta/risposta locale può durare ben meno di un millisecondo, quindi il suo socket è sparito microsecondi dopo il suo unico tentativo sul percorso dei pacchetti.
process_mapping_interval non è la manopola per i flussi non attribuitiQuesta pagina descriveva in precedenza un compromesso in cui un intervallo breve attribuisce
più spesso le connessioni di breve durata e uno lungo le perde. Non è più così che funziona
l'attribuzione, e ora contraddice la configurazione che l'agente distribuisce —
configs/bitmapper.yaml afferma il contrario in un commento su quella stessa chiave.
process_mapping_interval fissa l'aggiornamento pianificato della tabella dei socket. Non è
più l'unica cosa da cui dipende l'attribuzione: una ricerca che manca l'istantanea innesca una
rilettura su richiesta delle tabelle dei socket del kernel per quel singolo flusso, e il
tentativo viene ripetuto come descritto sopra. Abbassare l'intervallo non è il rimedio per i
flussi che arrivano senza attribuzione, e alzarlo non ti costa più i flussi brevi che ti costava
un tempo.
Le connessioni che non possono essere attribuite vengono comunque tracciate ed esportate; semplicemente non portano con sé alcuna identità di processo. Questa è una risposta corretta, non una lacuna — vedi sotto perché è preferibile all'alternativa.
Come è stata fatta l'attribuzione: socket vs listening_socket
Ogni oggetto processo in un record di flusso esportato porta un campo attribution che indica
come quell'identità è stata determinata. È un segnale di affidabilità, e i due valori non
sono intercambiabili:
"source_process": {
"pid": 41207,
"name": "curl",
"executable": "/usr/bin/curl",
"user": "deploy",
"attribution": "socket"
}
| Valore | Come è stato determinato | Quanto fidarsene |
|---|---|---|
socket | Ha corrisposto al socket proprio di questo flusso nelle tabelle dei socket dell'host, sulla quadrupla completa | Un fatto. Quel processo deteneva quel socket. |
listening_socket | Nessun socket è stato trovato per questo flusso. L'identità è stata presa dal processo in ascolto su quell'indirizzo e porta — e solo quando quell'indirizzo è su questo host, è dimostrato che esattamente un processo detiene il socket, e il flusso è TCP | Un'inferenza, etichettata come tale. È vincolata, ma resta un'affermazione più debole di socket: vedi la regola del proprietario unico e solo TCP. |
Il valore più debole esiste perché l'alternativa è non avere nulla: un flusso che vive qualche
centinaio di microsecondi è chiuso molto prima che una lettura di /proc possa vederne il
socket, e il socket in ascolto è l'unica cosa che gli sopravvive. Vale la pena riportarlo — ma
non deve essere letto come la stessa affermazione di socket.
Un consumatore che tratta i due allo stesso modo sta traendo una conclusione che l'agente non ha tratto. Se costruisci allarmi, mappe delle dipendenze o policy sui record di flusso, diramati su questo campo.
Una versione precedente di questa pagina portava un blocco «danger» intitolato
«listening_socket è un'inferenza, e oggi è applicata troppo largamente». Faceva due
affermazioni precise — che l'inferenza «non viene verificata contro l'host locale» e che «non
identifica quale processo in ascolto» — e ti diceva di scartare del tutto listening_socket per
qualsiasi estremo che non fosse un indirizzo dell'host che sta riportando.
Entrambe le affermazioni sono false per ogni versione di bitmapper che tu possa procurarti. Descrivevano uno stato interno corretto prima che venisse pubblicato qualsiasi cosa:
- La
0.1.0-gapubblicata non ha nulla da applicare troppo largamente. Non contiene alcuna inferenza dai socket in ascolto e non può emetterelistening_socket. - La prossima versione verifica entrambe le cose prima di inferire. L'estremo dev'essere un indirizzo di questo host — è ciò che impedisce di accreditare un flusso uscente verso un peer remoto a un processo locale in ascolto sull'indirizzo jolly — e dev'essere dimostrato che esattamente un processo detiene quel socket in ascolto, che è ciò che impedisce di nominare uno dei fratelli di un server che pre-forka. Misurato dopo la correzione, sullo stesso host e sullo stesso traffico che aveva prodotto le invenzioni: nessuna attribuzione inventata, e ogni attribuzione che l'agente ha effettivamente prodotto era corretta.
Il consiglio che seguiva da quelle affermazioni era un ragionamento valido su codice che nessun
cliente possiede. listening_socket resta il più debole dei due valori e vale ancora la pena
diramarsi su di esso — è un'inferenza, ed è etichettata come tale — ma è un'inferenza
vincolata, non una senza controlli, e «scartalo per un estremo non locale» descrive ormai una
regola che l'agente applica da sé, prima che il record venga costruito.
L'inferenza è solo TCP, quindi un flusso UDP non la porta mai
listening_socket può comparire soltanto su un flusso TCP. UDP non ha stato LISTEN: un
socket UDP associato a una porta è indistinguibile da quello di un server, quindi non c'è nulla
da cui inferire. L'inferenza non restituisce nulla per qualsiasi protocollo diverso da TCP,
prima ancora di esaminare qualunque altra cosa.
Non è un limite in attesa di essere rimosso: è ciò che impedisce una risposta sbagliata ben precisa. Un flusso UDP può benissimo arrivare su una porta su cui un server TCP è in ascolto; 53 e 853 sono il caso ordinario. Senza il controllo di protocollo, il processo TCP in ascolto di un server DNS verrebbe nominato come proprietario di traffico UDP estraneo sulla stessa porta.
Un flusso UDP il cui socket proprio non è stato trovato viene quindi esportato senza processo, e
per UDP è l'esito ordinario, non uno raro: una singola query DNS si esaurisce in ben meno di un
millisecondo, il suo socket è sparito prima che una lettura di /proc possa raggiungerlo, e non
c'è alcuna risposta più debole su cui ripiegare. Leggi un campo processo vuoto su un flusso UDP
come l'unica risposta onesta disponibile per quel protocollo, non come un difetto.
L'attribuzione diretta non è toccata dal controllo di protocollo. I socket UDP vengono letti
nelle stesse tabelle dei socket, quindi un flusso UDP il cui socket proprio è lì con una
quadrupla corrispondente viene attribuito esattamente come uno TCP, con "attribution": "socket".
Il punto è che un socket UDP su cui non è mai stata fatta una connect() non ha alcun indirizzo
remoto da dichiarare, quindi non c'è nessuna quadrupla da far combaciare — ed è l'altro motivo
per cui l'attribuzione è più rada in UDP che in TCP.
Un'inferenza richiede un socket con un solo proprietario
bitmapper prende un'identità da un socket in ascolto solo quando può dimostrare che esattamente un processo lo detiene. Dove non riesce a dimostrarlo, il flusso viene esportato senza alcun processo anziché con uno dei candidati.
Un socket in ascolto con più proprietari è il caso ordinario, non un caso esotico. Misurato
sull'host di sviluppo: un socket nginx in ascolto su 0.0.0.0:443 — un unico inode di socket
— era detenuto da 17 processi, che è l'aspetto di qualunque server che pre-forka i propri
processi worker. Un socket, diciassette nomi che potrebbero finire nel record. I flussi in
entrata verso un socket del genere restano deliberatamente senza attribuzione. Nominarne uno dei
diciassette metterebbe una supposizione nello stesso campo, e nella stessa forma, di una misura.
Solo il refresh periodico completo di /proc può produrre quella prova. Percorre ogni
processo, quindi può stabilire che un inode di socket compare nei descrittori di un processo e in
quelli di nessun altro. La scansione su richiesta, meno costosa — quella che una ricerca a vuoto
innesca per un singolo flusso — si ferma al primo processo che detiene l'inode che sta cercando:
può dimostrare che un processo detiene un socket, mai che sia l'unico.
Da un processo che si è appena messo in ascolto non si inferisce quindi finché il refresh completo successivo non lo ha coperto. Per un breve periodo dopo l'avvio di un servizio, il traffico effimero diretto ad esso può comparire senza alcuna attribuzione di processo. Due proprietà lo delimitano, ed entrambe contano quando devi decidere se hai davanti un difetto:
- Costa attribuzione, mai correttezza. In quella finestra non viene prodotto nessun processo sbagliato; il campo resta vuoto, non fuorviante.
- Un socket in ascolto sopravvive ampiamente all'intervallo di refresh, quindi questo è un effetto della finestra di avvio e non un regime permanente. Una volta che un refresh completo ha coperto un processo in ascolto, resta coperto per tutto il tempo in cui ascolta.
Quanto dura la finestra dipende da process_mapping_interval e dall'host, quindi non c'è una
cifra unica da citare. Il ciclo di refresh è limitato nel duty cycle — percorrere /proc non
deve diventare esso stesso il carico di una macchina occupata — perciò un host con una tabella
dei processi grande impiega più tempo a completare un refresh, e lì la finestra è
proporzionalmente più lunga.
Niente di tutto ciò impedisce a un flusso di essere catturato, tracciato, contato o esportato. Se
hai davanti record con source_process e destination_process vuoti, leggi le statistiche
periodiche che l'agente registra prima di concludere che l'agente è rotto: contatori di pacchetti
e connessioni che continuano ad avanzare ti dicono che la cattura funziona, e che quello che vedi
è un'attribuzione trattenuta.
Un'attribuzione sbagliata è peggio di nessuna attribuzione: un flusso non attribuito è visibilmente incompleto, mentre un flusso che nomina il processo sbagliato è un'affermazione falsa e sicura di sé che nessun consumatore può rilevare. Per questo un flusso non attribuibile resta senza attribuzione anziché essere indovinato, e per questo l'inferenza è etichettata anziché mescolata in silenzio ai fatti.
La pipeline di worker
| Chiave | Predefinito | Vincolo |
|---|---|---|
performance.workers | 4 | ≥ 1 |
performance.queue_size | 100000 | ≥ 100 |
I pacchetti vengono passati a un pool di worker attraverso una coda limitata. Quando la coda è piena, il pacchetto viene scartato e il contatore degli scarti viene incrementato — l'agente non blocca il percorso di cattura per stare al passo, perché bloccarlo lì farebbe invece traboccare il buffer del kernel stesso.
Il contatore degli scarti è il numero da tenere d'occhio. Compare nelle statistiche periodiche,
e un conteggio degli scarti che cresce con continuità significa che l'host vede più traffico di
quanto questa configurazione riesca a elaborare. In ordine di efficacia, i rimedi sono:
restringere il filtro del kernel — indifferentemente capture.filter,
capture.protocols, capture.ports o capture.exclude_ports, dato che tutte e quattro
finiscono nella stessa espressione —, poi abbassare capture.snaplen, poi aumentare
performance.workers.
Tracciamento delle connessioni
| Chiave | Predefinito | Vincolo |
|---|---|---|
performance.connection_table_size | 1000000 | ≥ 1000 — massimo di connessioni tracciate prima dello sfratto |
performance.connection_timeout | 5m | ≥ 1s — tempo di inattività prima che una connessione venga raccolta |
performance.gc_interval | 1m | ≥ 1s — con quale frequenza viene eseguita la raccolta |
Il tracker unisce entrambe le direzioni di una conversazione in una sola connessione, esegue
una macchina a stati TCP (SYN_SENT → ESTABLISHED → … → CLOSED) e conta i pacchetti e
i byte di ciascuna connessione.
Due limiti la mantengono finita, e sono meccanismi diversi:
connection_table_sizeè un tetto rigido. Oltre quel valore, le voci vengono sfrattate — un host sotto un'inondazione di connessioni perde le voci vecchie anziché crescere finché il processo non viene ucciso.connection_timeout+gc_intervalraccolgono le connessioni che sono semplicemente diventate inattive, che la tabella sia vicina al suo tetto oppure no.
Un host che apre molte connessioni in uscita di breve durata — un web crawler molto attivo, un runner di CI — farà girare questa tabella a ritmo serrato. È il comportamento voluto: una tabella limitata che dimentica è meglio di una illimitata che fa terminare il processo.
Statistiche
performance:
stats_interval: 10s # 0 disables the reporter entirely
I contatori di pacchetti, byte, scarti e connessioni vengono scritti come JSON strutturato attraverso il logger dell'agente con questo intervallo. Sono la prova locale che la cattura in sé sta funzionando, indipendentemente dal fatto che l'export stia raggiungendo o meno la piattaforma:
bitmapper capture --config /etc/bitmapper/config.yaml
L'output è JSON a livello info e non c'è alcuna opzione che lo cambi — vedi
le opzioni di logging. Passalo in jq se lo vuoi leggibile.
Osserva il contatore degli scarti su più intervalli. Zero scarti e un conteggio delle connessioni in crescita significano che la pipeline sta stando al passo. Se il contatore dei pacchetti resta a zero su un host che sai essere attivo, controlla il filtro compilato che l'agente ha registrato a log all'avvio prima di sospettare del percorso di cattura: il valore predefinito dei protocolli esclude più di quanto la maggior parte delle persone si aspetti.
Cosa lascia l'host, e cosa no
Il traffico in sé resta qui. Ciò che parte è un record di flusso — il riassunto di una connessione, non il suo contenuto. Per essere precisi:
| Dato | Dove finisce |
|---|---|
Intestazioni dei pacchetti e payload fino a snaplen | Solo la memoria dell'agente, per la durata dell'elaborazione del pacchetto |
| I byte di payload dei pacchetti | Da nessuna parte. Mai esportati, mai scritti su disco |
| I record di connessione, con l'attribuzione dei processi | La tabella delle connessioni in memoria — e, come record di flusso, verso la piattaforma tramite https |
Come è stata fatta ogni attribuzione (socket / listening_socket) | Viaggia nel record di flusso accanto al processo dalla prossima versione — vedi affidabilità dell'attribuzione |
| La riga di comando del processo | Da nessuna parte. Esclusa deliberatamente: le righe di comando portano abitualmente credenziali come argomenti |
| I contatori di pacchetti/byte/scarti/connessioni | L'output di log dell'agente stesso |
| La registrazione e l'heartbeat dell'agente | Il control plane, tramite https |
Un record di flusso porta gli estremi, le porte, il protocollo, lo stato della connessione, i
contatori di byte e pacchetti e — per l'estremo del flusso che possiede il socket — il PID del
processo attribuito, il suo nome, il percorso del suo eseguibile, il suo utente e — dalla
prossima versione — il marcatore attribution che indica come quell'identità è stata
determinata. Tutto qui. Se devi tenere una sottorete fuori da tutto questo,
output.filter.exclude_cidrs scarta il flusso in ingresso — prima che un record venga costruito
— e vale su entrambi gli estremi. Vedi la panoramica.
Non esiste alcuna implementazione di storage.* né alcun export su file, quindi nulla viene reso
persistente dall'agente. Quando il processo si arresta, la tabella delle connessioni se ne va
con lui.
Passi successivi
- Panoramica di bitmapper — release, piattaforme, privilegi e l'elenco completo delle chiavi che vengono convalidate ma non fanno nulla.
- bitscanner — la scoperta rivolta verso l'esterno.
- Cos'è bits? — come si incastrano gli agenti.
Questa pagina ti è stata utile?