Passa al contenuto principale
Versione: 1.0.0

bitmapper

bitmapper risponde alla domanda "con cosa sta davvero parlando questa macchina, e quale processo lo sta facendo?"

bitcollector legge lo stato dell'host. bitscanner guarda verso l'esterno, verso il segmento. bitmapper osserva il traffico stesso — pacchetti dal vivo sulle interfacce dell'host, raggruppati in flussi e attribuiti al processo che possiede il socket.

Leggi questo prima di pianificare qualcosa attorno a bitmapper

bitmapper consegna record di flusso, non traffico. Da 0.1.0-ga il motore di cattura è collegato all'exporter, e le connessioni tracciate raggiungono la piattaforma come record di flusso: estremi, porte, protocollo, contatori di byte e pacchetti, stato della connessione e — quando può essere determinato, cosa oggi rara — il processo attribuito. Nessun byte di payload, nessuna cattura di pacchetti, e non la riga di comando del processo, che porta abitualmente credenziali come argomenti. Se ti aspettavi che arrivasse un PCAP da qualche parte, non è ciò che questo agente invia.

L'attribuzione dei processi funziona a malapena nel binario pubblicato, e la correzione non è ancora rilasciata. In 0.1.0-ga l'attribuzione è tentata una volta, alla creazione della connessione, contro un'istantanea della tabella dei socket che non può contenere il socket di una nuova connessione uscente, e non esiste alcuna inferenza dai socket in ascolto — quindi i flussi arrivano spesso senza alcun processo (misurato sull'host di sviluppo: 0 attribuiti su 12), e destination_process non viene mai valorizzato. Il sostituto — completamento per flusso, su entrambi gli estremi, e un campo attribution che distingue una corrispondenza diretta di socket (socket) da un'inferenza (listening_socket) — non è rilasciato. Quell'inferenza è vincolata: viene tratta solo per un indirizzo di questo host, solo quando è dimostrato che esattamente un processo detiene il socket in ascolto, e solo per TCP, quindi un flusso UDP non la porta mai. Leggi attribuzione dei processi prima di programmare qualsiasi cosa su di essa.

È un binario, su una sola piattaforma. 0.1.0-ga è pubblicato solo per linux/amd64. Non esiste alcun download per macOS, Windows o arm64, e le ragioni sono esposte più sotto anziché lasciate come un vuoto in una pagina di download.

Non cattura tutti i protocolli in modo predefinito. capture.protocols vale ["tcp", "udp"] che il tuo file di configurazione la nomini o no, quindi un'installazione di serie non vede ICMP, ICMPv6, SCTP, GRE, ESP o AH — il kernel li scarta e nulla nell'output lo dice. Allargarlo è una riga.

Parti di questo agente non sono ancora costruite. Non c'è aggregazione della topologia dei servizi, né metriche Prometheus, né rilevamento delle policy di rete; diverse chiavi di configurazione vengono interpretate e non fanno nulla, e lo stesso vale per tutte le opzioni della riga di comando tranne --config. Cosa non è collegato è l'elenco, e vale la pena leggerlo prima di pianificare qualcosa attorno a una chiave.

Release0.1.0-ga, commit 64ebc64, compilato con go1.25.12
PiattaformeSolo linux/amd64 — macOS, Windows e arm64 non sono pubblicati, ed ecco perché
Supply chainBuild riproducibile, SBOM CycloneDX, firma cosign, attestazione dell'SBOM, SHA256SUMS firmato — la stessa chiave di famiglia degli altri bits
StatoCattura, attribuisce e traccia le connessioni; esporta record di flusso verso la piattaforma
Superficie di rete in ingressoNessun servizio in ascolto. I server HTTP/WebSocket e gRPC esistono nell'albero dei sorgenti ma non vengono mai costruiti, ed entrambi sono distribuiti disattivati
Traffico in uscitaSolo https — registrazione, heartbeat e record di flusso verso l'endpoint che configuri
PrivilegiElevati: root, oppure CAP_NET_RAW + CAP_NET_ADMIN
Requisito di buildCGO e libpcap, collegata staticamente nel binario pubblicato

Dove si colloca tra i bits​

I quattro agenti rispondono a quattro domande diverse, e il confine è ciò che ciascuno di essi guarda:

GuardaRisponde a
bitcollectorLo stato dell'host stesso"Cos'è questa macchina, e in che stato si trova?"
bitscannerIl segmento attorno all'host"Cos'altro c'è su questo cavo di cui non sono a conoscenza?"
bitmapperI pacchetti che attraversano le interfacce dell'host — TCP e UDP in modo predefinito, di più se lo chiedi"Con cosa sta parlando questa macchina, e quale processo lo sta facendo?"
bitenforcerLa configurazione dell'host"Questa macchina corrisponde alla policy che ho impostato?"

Una tabella dei vicini ti dice che un dispositivo esiste. Una cattura ti dice che ci si sta parlando, da parte di cosa e con quale frequenza — che è la differenza tra un inventario e una mappa delle dipendenze.

Cosa funziona oggi​

Presente e collegato al percorso di codice del comando capture:

  • Cattura di pacchetti dal vivo — libpcap tramite gopacket, collegata staticamente nel binario Linux pubblicato, con filtro BPF, snaplen, modalità promiscua e dimensione del buffer di cattura.
  • Filtraggio della cattura lato kernel — capture.filter, capture.protocols, capture.ports e capture.exclude_ports vengono compilate in un'unica espressione BPF applicata dal kernel, così il traffico escluso non viene mai copiato nell'agente. L'espressione compilata viene registrata a log all'avvio. Attenzione al predefinito: capture.protocols vale ["tcp", "udp"] anche se il tuo file la omette, il che significa che ICMP, SCTP, GRE ed ESP non vengono catturati se non lo chiedi. Vedi il filtraggio.
  • Cattura su più interfacce — un elenco di interfacce indicate per nome, oppure ogni interfaccia attiva e non di loopback quando non ne indichi nessuna.
  • Attribuzione dei processi — presente, ma a malapena funzionante nel binario pubblicato. PID, nome del processo, percorso dell'eseguibile e utente, risolti dalle tabelle dei socket dell'host. In 0.1.0-ga la ricerca è tentata una sola volta, alla creazione della connessione, contro un'istantanea che non può contenere il socket di una nuova connessione uscente, quindi i flussi spesso non portano alcun processo e destination_process non viene mai valorizzato. Il completamento per flusso, l'attribuzione di entrambi gli estremi e il campo di affidabilità attribution (socket vs listening_socket) non sono nel binario pubblicato, che non ha alcuna inferenza dai socket in ascolto — vedi attribuzione dei processi per ciò che viene consegnato oggi e ciò che lo sostituisce. Quel sostituto inoltre rifiuta anziché indovinare: nessuna identità viene presa da un socket in ascolto se l'indirizzo non è su questo host o se non è dimostrato che un solo processo lo detiene — un socket nginx sull'host di sviluppo è detenuto da 17 processi — e non inferisce mai per UDP, che non ha stato LISTEN. Un campo processo vuoto è un rifiuto, non un guasto.
  • Tracciamento con stato delle connessioni — corrispondenza bidirezionale dei flussi, una macchina a stati TCP (SYN_SENT → ESTABLISHED → … → CLOSED), contatori di pacchetti e byte per connessione, una tabella con dimensione limitata e sfratto delle voci, e garbage collection periodica delle connessioni inattive.
  • Flusso di eventi interno al processo — eventi di pacchetto, di nuova connessione e di statistica distribuiti ai sottoscrittori all'interno del processo.
  • Statistiche periodiche — contatori di pacchetti, byte, scarti e connessioni scritti come JSON strutturato secondo performance.stats_interval.
  • Integrazione con il control plane — registrazione e heartbeat dell'agente quando control_plane.* è configurato.
  • Export dei flussi — ogni connessione tracciata passa dal percorso di cattura all'exporter e viene inviata in POST alla piattaforma come record di flusso. Ogni flusso produce almeno un record, e l'ultimo è marcato come finale con contatori completi; una connessione di lunga durata viene aggiornata una volta per batch_export.batch_interval se i suoi contatori si sono mossi, così la vedi prima che termini.
  • Un filtraggio dell'export che filtra davvero — output.filter (exclude_cidrs, exclude_loopback, protocols, ports) è applicato in ingresso, prima che un record venga costruito, così un flusso escluso non viene mai tenuto in memoria e non può mai essere inviato. Le esclusioni valgono su entrambi gli estremi: escludere 10.0.0.0/8 significa «non parlare a Cert-IX di quella rete», e riportare che 10.1.2.3 ha aperto una connessione verso l'esterno gliene parla altrettanto compiutamente. Una voce che non può essere compilata rifiuta l'esecuzione anziché degradare a «esporta tutto».
  • Convalida fail-fast della configurazione — l'agente si rifiuta di avviarsi con una configurazione non valida o insicura per omissione, anziché avviarsi in uno stato indebolito.

Cosa non è collegato​

Dichiarato esplicitamente, così nessuno debba scoprirlo leggendo il codice sorgente:

  • I server HTTP/WebSocket e gRPC non vengono mai costruiti. Poiché nulla li costruisce, il middleware di sicurezza che sta dietro di essi — autenticazione, allowlist di IP, rate limiting, audit logging — non viene eseguito. Non considerare oggi la configurazione security.* come una protezione. Il lato positivo è quello onesto: senza alcun server costruito, l'agente non espone alcuna porta in ingresso.
  • Nessuna aggregazione della topologia dei servizi. I tipi ServiceTopology e ServiceEdge esistono ma non vengono mai costruiti, quindi non esiste ancora alcun grafo della topologia.
  • Nessuna metrica Prometheus e nessun endpoint /metrics.
  • Nessun rilevamento delle policy di rete.

Chiavi di configurazione convalidate che non fanno nulla​

Vengono interpretate, e l'agente le accetta, ma nessun codice le consuma:

output.backend, output.kafka, output.nats, output.redis, output.webhook, output.file, kubernetes.*, storage.*, logging.*, performance.batch_size, performance.connection_pooling, performance.memory_limit, performance.cpu_limit.

Le opzioni della riga di comando rientrano nella stessa categoria, e sono più numerose delle chiavi. --log-level e --log-format sono dichiarate, documentate in --help, e non le legge nulla: il logger è un logger zap di produzione fisso (JSON, livello info). Le opzioni di capture (--interfaces, --filter, --snaplen, --promiscuous, --buffer-size, --workers, --queue-size, --stats-interval) sono legate sotto il loro nome di opzione mentre la configurazione è letta da chiavi annidate, quindi vengono analizzate e ignorate; --enable-grpc ed --enable-http valgono true in modo predefinito in --help e si riferiscono a server che non vengono mai costruiti. --config è l'unica opzione che cambia il comportamento. Non prendere bitmapper --help come una dichiarazione di ciò che questa release fa.

Tre cose non sono in quell'elenco, e le distinzioni contano:

  • output.filter viene letta e applicata sul percorso di export da 0.1.0-ga. Prima era in questo elenco, e finché l'agente non esportava proprio nulla era solo peso morto. Nel momento in cui i record di flusso hanno cominciato a lasciare l'host sarebbe diventata una falsa dichiarazione di privacy: un operatore che escludeva una sottorete sensibile non avrebbe ottenuto protezione, né errore, né modo di accorgersene. Ora è applicata in ingresso. Quattro chiavi che non potevano agire su nulla (export_packets, export_stats, min_packet_size, require_payload) sono state rimosse dallo schema anziché lasciate a farsi interpretare, e l'agente le nomina all'avvio se il tuo file ne imposta ancora una.
  • capture.protocols, capture.ports e capture.exclude_ports vengono compilate nel filtro BPF del kernel e applicate lì. Una versione precedente di questa pagina elencava tutte e tre come convalidate-ma-inerti, ed era falso — vedi la correzione qui sotto.
  • capture.exclude_interfaces viene letta e applicata sul percorso di cattura dal vivo. Vedi scegliere le interfacce.
Correzione: capture.exclude_ports è un controllo di riservatezza che funziona

Fino a 0.1.0-ga questa pagina annoverava capture.protocols, capture.ports e capture.exclude_ports fra le chiavi che «vengono interpretate e non fanno nulla», e la pagina sulla cattura lo ripeteva. Era l'opposto della verità: tutte e tre vengono compilate nel filtro BPF e applicate dal kernel prima che un pacchetto raggiunga l'agente.

capture.exclude_ports è quella su cui agire. La configurazione distribuita con bitmapper imposta exclude_ports: [22] — «non catturare SSH» — e funziona. Se hai letto il vecchio testo e hai cancellato la chiave ritenendola peso morto, hai rimosso un controllo che ti stava proteggendo e hai iniziato a raccogliere traffico che avevi deliberatamente escluso, senza nulla che segnalasse il cambiamento. Controlla che cosa dice la tua configurazione in produzione prima di dare per scontato di non essere stato coinvolto.

capture.protocols richiede l'attenzione opposta: vale ["tcp", "udp"] che tu la imposti o no, quindi un'installazione di serie non cattura silenziosamente né ICMP, né ICMPv6, né SCTP, né GRE, né ESP, né AH. Il valore predefinito dei protocolli spiega come allargarlo.

Perché vengono comunque accettate

Fanno parte dello schema di configurazione e vengono convalidate nella forma, così un file scritto usandole viene caricato. È esattamente per questo che sono elencate qui: una chiave che viene interpretata senza errori e non fa nulla è il genere di cosa che si legge come una funzionalità funzionante. Dai per scontato che una chiave non abbia alcun effetto a meno che non compaia in cosa funziona oggi.

Il pacchetto di arricchimento Kubernetes è stato rimosso anziché lasciato dormiente. Non aveva alcun importatore e costruiva un proprio client HTTP, che seguiva i redirect e poteva far trapelare un bearer token di service account verso una destinazione in chiaro quando un kubeconfig puntava a un server che effettuava redirect. Il blocco kubernetes.* resta nello schema e ora non viene consumato da nulla.

Come eseguirlo​

bitmapper è un singolo binario con tre comandi:

# What you have
bitmapper version
# Bitmapper 0.1.0-ga
# Git Commit: 64ebc64
# Build Date: 2026-08-10T05:56:16Z

# Which interfaces this host can capture on
bitmapper interfaces

# Capture, with the configuration you supply
bitmapper capture --config /etc/bitmapper/config.yaml

--config, --log-level e --log-format sono flag persistenti sul comando radice, ma solo --config fa qualcosa. Non c'è modo di alzare il dettaglio del logging mentre lo stai configurando: il logger è fisso su JSON, livello info. Ciò che l'agente ti dice davvero all'avvio è il filtro di cattura compilato e le interfacce che ha selezionato, e a ogni performance.stats_interval registra a log i contatori di pacchetti, byte, scarti e connessioni — sono queste le prove da leggere mentre metti in servizio un host.

Richiede privilegi elevati​

La cattura dei pacchetti è un'operazione privilegiata. Sulla piattaforma pubblicata:

PiattaformaCosa richiede
LinuxCAP_NET_RAW e CAP_NET_ADMIN (oppure root)

Richiede CGO — e il binario pubblicato ce l'ha già​

La cattura passa da gopacket/pcap, che è un binding a una libreria C. Una build con CGO_ENABLED=0 si compila comunque, ma la cattura e l'elenco delle interfacce si rifiutano di funzionare a runtime:

packet capture requires CGO (build with CGO_ENABLED=1 and libpcap installed)

È una scelta deliberata — un binario che in silenzio non catturasse nulla sarebbe peggio di uno che dice perché non può farlo. È anche l'unico fatto che decide quali piattaforme vengono pubblicate, ed è il motivo per cui la pipeline di rilascio rifiuta qualsiasi binario non compilato con CGO, non collegato staticamente, o privo di libpcap al suo interno. Vedi piattaforme.

Il download che ricevi non richiede alcuna libpcap installata sull'host: libpcap è collegata staticamente nel binario pubblicato.

La credenziale di enrolment​

Se configuri control_plane.*, la credenziale di enrolment viene letta da control_plane.enrollment_token_file. L'agente esige che sia un file regolare, con modo 0600 o più restrittivo, di proprietà di root o dell'utente dell'agente stesso, e dentro una directory non scrivibile dal gruppo né da tutti. Una directory in cui chiunque può scrivere permette a qualcuno di sostituire il file, quindi controllare soltanto il modo del file stesso significherebbe controllare la cosa sbagliata.

Il controllo si basa su POSIX. Questo copre ogni binario pubblicato, dato che 0.1.0-ga è distribuito solo su Linux — ma vale la pena sapere che è una proprietà della piattaforma e non dell'agente, se un giorno dovesse comparire una build per Windows.

control_plane:
gateway_url: "https://<your-cert-ix-agent-endpoint>"
ingest_url: "https://<your-cert-ix-ingest-endpoint>"
tenant_id: "<your-tenant-id>"
enrollment_token_file: "/etc/bitmapper/enrollment.token"

Entrambi gli URL devono essere https://, e ingest_url è obbligatorio non appena il blocco del control plane viene usato. tenant_id viene inviato come header X-Tenant-ID — è un identificatore, non una credenziale, quindi non è ciò che autentica l'agente.

La credenziale può anche arrivare dall'ambiente come BITMAPPER_CONTROL_PLANE_ENROLLMENT_TOKEN, oppure essere sostituita del tutto da un certificato client mutual-TLS (control_plane.client_cert_file / client_key_file, con il file della chiave sottoposto allo stesso controllo dei permessi). Impostare control_plane.enrollment_token direttamente nello YAML funziona ed è sconsigliato — un segreto in un file di configurazione è un segreto su disco.

Quanto costa sull'host​

La cattura dei pacchetti è la cosa più costosa che faccia uno qualsiasi dei bits, e il costo cresce con il traffico anziché con la dimensione dell'host. Due controlli contano più di tutti:

  • Un filtro BPF è la riduzione più economica possibile — scarta i pacchetti nel kernel prima ancora che vengano copiati verso l'agente.
  • La tabella delle connessioni ha una dimensione massima, con sfratto delle voci e GC periodica di quelle inattive, così un host che vede molte connessioni di breve durata non fa crescere la tabella senza limiti.

performance.memory_limit e performance.cpu_limit non sono implementati — sono nell'elenco qui sopra. Limita il processo con i controlli del tuo sistema di init (MemoryMax=, CPUQuota= in un'unità systemd) se ti serve un tetto reale.

I flussi in attesa di export sono limitati separatamente da batch_export.max_buffered_connections, e non da performance.connection_table_size, il cui valore predefinito metterebbe una seconda tabella a sei cifre su un host di cattura. Ogni scarto è contato e registrato; nessuno è silenzioso.

Piattaforme​

0.1.0-ga pubblica un binario firmato. È l'elenco di piattaforme più stretto della famiglia, e ogni omissione qui sotto è una decisione misurata e non una dimenticanza:

PiattaformaPubblicataPerché no
Linux amd64Sì
Linux arm64NoNon può essere assemblata da un gcc amd64, e non esiste una macchina di build arm64
macOS amd64/arm64NoRichiede clang e l'SDK di macOS
Windows amd64NoCompila, ma non è mai stata eseguita su Windows — vedi sotto

La causa unica dietro tutto questo è che bitmapper è l'unico bit che non può essere compilato in modo incrociato. La cattura è gopacket → libpcap → CGO, quindi una build richiede una vera toolchain C e una vera libpcap per il target. Tutti gli altri bits sono Go puro, e bastano GOOS/GOARCH.

La trappola che questo crea è precisa e merita di essere nominata. internal/capture porta uno stub //go:build !cgo che compila su ogni piattaforma e non cattura nulla. Una build con CGO_ENABLED=0 quindi riesce — produce un binario che parte, si registra, invia heartbeat, risulta sano, e non vede mai un pacchetto. Per questo la pipeline di rilascio controlla l'artefatto anziché il comando di build: rifiuta qualsiasi binario non compilato con CGO, non collegato staticamente, o privo di libpcap al suo interno.

Windows è il caso interessante, e quello onesto. Compila davvero — il supporto Windows di gopacket è Go puro e carica wpcap.dll a runtime — quindi potremmo pubblicarlo oggi. Non è mai stato eseguito su Windows. Questo lo rende una lacuna di test e non di toolchain, e pubblicarlo significherebbe distribuire un binario firmato il cui percorso di cattura nessuno ha mai visto funzionare. Un binario firmato che non può catturare è peggio di nessun binario, perché la firma viene letta come un'affermazione sull'idoneità. Resta trattenuto finché qualcuno non lo avrà eseguito.

Se oggi ti servono questi dati da un host macOS, Windows o arm64, bitmapper non è la risposta per quell'host. bitcollector è distribuito su tutte e cinque le piattaforme e riporta i peer di rete stabiliti di quell'host — una risposta più grossolana dell'attribuzione per flusso, ma reale sulla macchina che hai davvero.

Passi successivi​

  • Come funziona la cattura — le interfacce, i filtri BPF, l'attribuzione dei processi e la macchina a stati delle connessioni, e cosa ciascuno di essi mette in memoria.
  • bitscanner — l'agente che trova i dispositivi che il tuo inventario non contiene.
  • bitcollector — l'inventario dell'host che guarda verso l'interno.
  • Verificare i tuoi download — l'ancora di fiducia, e perché conta più della firma.
  • Cos'è bits? — come si incastrano gli agenti.

Questa pagina ti è stata utile?