Passa al contenuto principale
Versione: 1.0.0

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.

ChiavePredefinitoCosa contribuisce
capture.filter"" — nessunoUn'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[] — nessunaUna lista di ammessi: (port 80 or port 443)
capture.exclude_ports[] — nessunaUna 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.

Correzione: queste tre chiavi sono attive, e questa pagina affermava il contrario

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. protocols accetta solo tcp, udp, icmp e all; qualunque altra cosa viene rifiutata all'avvio con invalid protocol: … (valid: tcp, udp, icmp, all). Se te ne serve uno in particolare, imposta protocols: ["all"] ed esprimi il protocollo in capture.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 nulla

Questa 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​

ChiavePredefinitoCosa fa
capture.snaplen65535Byte catturati per pacchetto (convalidato 0–65535)
capture.promiscuoustrueModalità promiscua
capture.buffer_size104857600Buffer di cattura del kernel, in byte
capture.timeout100msTimeout 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
Questa sezione descrive la PROSSIMA versione, non il binario scaricabile

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-ga non può mai emettere listening_socket, né mai nominare un processo che non abbia trovato sul socket proprio del flusso.
  • La risposta è sempre registrata sull'estremo sorgente. destination_process non viene assegnato da nessuna parte in quella versione, quindi non può mai comparire in un record esportato.
  • L'oggetto processo esportato porta pid, name, executable e user — e nessun campo attribution.

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 attribuiti

Questa 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"
}
ValoreCome è stato determinatoQuanto fidarsene
socketHa corrisposto al socket proprio di questo flusso nelle tabelle dei socket dell'host, sulla quadrupla completaUn fatto. Quel processo deteneva quel socket.
listening_socketNessun 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 è TCPUn'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.

Correzione: questa pagina descriveva un'inferenza applicata troppo largamente che nessuna versione contiene

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-ga pubblicata non ha nulla da applicare troppo largamente. Non contiene alcuna inferenza dai socket in ascolto e non può emettere listening_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​

ChiavePredefinitoVincolo
performance.workers4≥ 1
performance.queue_size100000≥ 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​

ChiavePredefinitoVincolo
performance.connection_table_size1000000≥ 1000 — massimo di connessioni tracciate prima dello sfratto
performance.connection_timeout5m≥ 1s — tempo di inattività prima che una connessione venga raccolta
performance.gc_interval1m≥ 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_interval raccolgono 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:

DatoDove finisce
Intestazioni dei pacchetti e payload fino a snaplenSolo la memoria dell'agente, per la durata dell'elaborazione del pacchetto
I byte di payload dei pacchettiDa nessuna parte. Mai esportati, mai scritti su disco
I record di connessione, con l'attribuzione dei processiLa 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 processoDa nessuna parte. Esclusa deliberatamente: le righe di comando portano abitualmente credenziali come argomenti
I contatori di pacchetti/byte/scarti/connessioniL'output di log dell'agente stesso
La registrazione e l'heartbeat dell'agenteIl 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?