Zum Hauptinhalt springen
Version: 1.0.0

bitmapper

bitmapper beantwortet die Frage „womit spricht diese Maschine eigentlich, und welcher Prozess tut es?“

bitcollector liest den Zustand des Hosts. bitscanner blickt nach außen auf das Segment. bitmapper beobachtet den Verkehr selbst — Live-Pakete auf den eigenen Schnittstellen des Hosts, zu Flows zusammengefasst und dem Prozess zugeordnet, dem der Socket gehört.

Lesen Sie dies, bevor Sie mit bitmapper planen

bitmapper liefert Flow-Datensätze, keinen Verkehr. Seit 0.1.0-ga ist die Capture-Engine mit dem Exporter verbunden, und verfolgte Verbindungen erreichen die Plattform als Flow-Datensätze: Endpunkte, Ports, Protokoll, Byte- und Paketzähler, Verbindungszustand und — sofern er überhaupt bestimmt werden kann, was heute selten gelingt — der zugeordnete Prozess. Keine Nutzdaten-Bytes, keine Paketmitschnitte und nicht die Kommandozeile des Prozesses, die routinemäßig Zugangsdaten als Argumente trägt. Falls Sie erwartet haben, dass irgendwo ein PCAP ankommt: Das sendet dieser Agent nicht.

Die Prozesszuordnung funktioniert im veröffentlichten Binary kaum, und die Korrektur ist noch nicht veröffentlicht. In 0.1.0-ga wird die Zuordnung einmal versucht, beim Anlegen der Verbindung, gegen eine Momentaufnahme der Socket-Tabelle, die den Socket einer neuen ausgehenden Verbindung nicht enthalten kann, und es gibt überhaupt keine Ableitung aus lauschenden Sockets — Flows kommen daher oft ganz ohne Prozess an (auf dem Entwicklungshost gemessen: 0 von 12 zugeordnet), und destination_process wird nie gefüllt. Der Ersatz — Nachtragen pro Flow, an beiden Enden, und ein Feld attribution, das einen direkten Socket-Treffer (socket) von einer Ableitung (listening_socket) unterscheidet — ist unveröffentlicht. Diese Ableitung ist abgesichert: Sie wird nur für eine Adresse dieses Hosts gezogen, nur wenn bewiesen ist, dass genau ein Prozess den lauschenden Socket hält, und nur für TCP — ein UDP-Flow trägt sie also nie. Lesen Sie Prozesszuordnung, bevor Sie irgendetwas darauf aufbauen.

Es ist ein Binary, auf einer Plattform. 0.1.0-ga wird ausschließlich für linux/amd64 veröffentlicht. Es gibt keinen Download für macOS, Windows oder arm64, und die Gründe stehen weiter unten, statt als Lücke auf einer Download-Seite zu bleiben.

Er schneidet nicht standardmäßig jedes Protokoll mit. capture.protocols steht auf ["tcp", "udp"], ob Ihre Konfigurationsdatei den Schlüssel erwähnt oder nicht. Eine Standardinstallation sieht daher kein ICMP, ICMPv6, SCTP, GRE, ESP oder AH — der Kernel verwirft sie, und nichts in der Ausgabe sagt das. Das zu erweitern ist eine Zeile.

Teile dieses Agenten sind weiterhin nicht gebaut. Es gibt keine Aggregation der Service-Topologie, keine Prometheus-Metriken und keine Erkennung von Netzwerk-Policies; mehrere Konfigurationsschlüssel werden geparst und tun nichts, und dasselbe gilt für sämtliche Kommandozeilen-Optionen außer --config. Was nicht verdrahtet ist ist die Liste, und sie lohnt sich zu lesen, bevor Sie um einen Schlüssel herum planen.

Release0.1.0-ga, Commit 64ebc64, gebaut mit go1.25.12
PlattformenAusschließlich linux/amd64 — macOS, Windows und arm64 werden nicht veröffentlicht, und warum
LieferketteReproduzierbarer Build, CycloneDX-SBOM, cosign-Signatur, SBOM-Attestierung, signierte SHA256SUMS — derselbe Familienschlüssel wie bei den übrigen bits
StatusSchneidet mit, ordnet zu und verfolgt Verbindungen; exportiert Flow-Datensätze an die Plattform
Eingehende NetzwerkflächeNichts lauscht. Die HTTP/WebSocket- und gRPC-Server existieren im Quellbaum, werden aber nie konstruiert, und beide sind ab Werk deaktiviert
AusgehendAusschließlich https — Registrierung, Heartbeat und Flow-Datensätze an den Endpunkt, den Sie konfigurieren
BerechtigungenErhöht: root oder CAP_NET_RAW + CAP_NET_ADMIN
Build-VoraussetzungCGO und libpcap, statisch in das veröffentlichte Binary gelinkt

Wo er unter den bits steht​

Die vier Agenten beantworten vier verschiedene Fragen, und die Grenze ist das, worauf jeder blickt:

Blickt aufBeantwortet
bitcollectorDen eigenen Zustand des Hosts„Was ist diese Maschine, und in welchem Zustand ist sie?“
bitscannerDas Segment um den Host herum„Was ist sonst noch an dieser Leitung, das ich nicht kenne?“
bitmapperDie Pakete, die die Schnittstellen des Hosts passieren — standardmäßig TCP und UDP, mehr auf Wunsch„Womit spricht diese Maschine, und welcher Prozess tut es?“
bitenforcerDie Konfiguration des Hosts„Entspricht diese Maschine der Policy, die ich gesetzt habe?“

Eine Nachbartabelle sagt Ihnen, dass ein Gerät existiert. Ein Mitschnitt sagt Ihnen, dass mit ihm gesprochen wird, von was und wie oft — und das ist der Unterschied zwischen einem Inventar und einer Abhängigkeitskarte.

Was heute funktioniert​

Vorhanden und in den Codepfad des Befehls capture verdrahtet:

  • Live-Paketmitschnitt — libpcap über gopacket, statisch in das veröffentlichte Linux-Binary gelinkt, mit BPF-Filter, Snaplen, Promiscuous-Modus und Größe des Mitschnitt-Puffers.
  • Filterung im Kernel — capture.filter, capture.protocols, capture.ports und capture.exclude_ports werden zu einem einzigen BPF-Ausdruck kompiliert und vom Kernel durchgesetzt, sodass ausgeschlossener Verkehr nie in den Agenten kopiert wird. Der kompilierte Ausdruck wird beim Start protokolliert. Achten Sie auf die Voreinstellung: capture.protocols ist ["tcp", "udp"], auch wenn Ihre Datei den Schlüssel weglässt — das heißt, ICMP, SCTP, GRE und ESP werden nicht mitgeschnitten, sofern Sie es nicht verlangen. Siehe Filterung.
  • Mitschnitt über mehrere Schnittstellen — eine benannte Liste von Schnittstellen, oder jede Schnittstelle, die aktiv und kein Loopback ist, wenn Sie keine benennen.
  • Prozesszuordnung — vorhanden, aber im veröffentlichten Binary kaum funktionsfähig. PID, Prozessname, Pfad der ausführbaren Datei und Benutzer, aufgelöst aus den Socket-Tabellen des Hosts. In 0.1.0-ga wird die Abfrage genau einmal versucht, beim Anlegen der Verbindung, gegen eine Momentaufnahme, die den Socket einer neuen ausgehenden Verbindung nicht enthalten kann — Flows tragen daher oft gar keinen Prozess, und destination_process wird nie gefüllt. Das Nachtragen pro Flow, die Zuordnung beider Enden und das Vertrauensfeld attribution (socket vs. listening_socket) sind nicht im veröffentlichten Binary, das überhaupt keine Ableitung aus lauschenden Sockets kennt — siehe Prozesszuordnung für das, was heute ausgeliefert wird, und das, was es ersetzt. Dieser Ersatz verweigert außerdem, statt zu raten: Von einem lauschenden Socket wird keine Identität übernommen, wenn die Adresse nicht auf diesem Host liegt oder nicht bewiesen ist, dass genau ein Prozess ihn hält — ein nginx-Socket auf dem Entwicklungshost wird von 17 Prozessen gehalten —, und für UDP, das keinen LISTEN-Zustand hat, wird nie abgeleitet. Ein leeres Prozessfeld ist eine Verweigerung und kein Fehler.
  • Zustandsbehaftete Verbindungsverfolgung — bidirektionaler Flow-Abgleich, ein TCP-Zustandsautomat (SYN_SENT → ESTABLISHED → … → CLOSED), Paket- und Byte-Zähler je Verbindung, eine größenbegrenzte Tabelle mit Verdrängung und periodische Garbage Collection untätiger Verbindungen.
  • Prozessinterner Event-Stream — Paket-, Neue-Verbindung- und Statistik-Events, die innerhalb des Prozesses an Abonnenten verteilt werden.
  • Periodische Statistiken — Paket-, Byte-, Drop- und Verbindungszähler, geschrieben als strukturiertes JSON im Takt von performance.stats_interval.
  • Control-Plane-Anbindung — Registrierung und Heartbeat des Agenten, wenn control_plane.* konfiguriert ist.
  • Flow-Export — jede verfolgte Verbindung wird vom Mitschnittpfad an den Exporter übergeben und als Flow-Datensatz per POST an die Plattform gesendet. Jeder Flow erzeugt mindestens einen Datensatz, und sein letzter ist mit vollständigen Zählern als final markiert; eine langlebige Verbindung wird einmal je batch_export.batch_interval aufgefrischt, sofern sich ihre Zähler bewegt haben, sodass Sie sie sehen, bevor sie endet.
  • Eine Export-Filterung, die wirklich filtert — output.filter (exclude_cidrs, exclude_loopback, protocols, ports) wird beim Eingang angewendet, bevor ein Datensatz gebaut wird, sodass ein ausgeschlossener Flow nie im Speicher gehalten und nie gesendet werden kann. Ausschlüsse greifen an beiden Endpunkten: 10.0.0.0/8 auszuschließen heißt „erzählt Cert-IX nichts über dieses Netz“, und zu melden, dass 10.1.2.3 eine Verbindung nach außen geöffnet hat, erzählt ihnen genauso vollständig davon. Ein Eintrag, der sich nicht kompilieren lässt, verweigert den Lauf, statt zu „alles exportieren“ zu degradieren.
  • Fail-fast-Validierung der Konfiguration — der Agent verweigert den Start bei einer ungültigen oder durch Auslassung unsicheren Konfiguration, statt in geschwächtem Zustand zu starten.

Was nicht verdrahtet ist​

Ausdrücklich gesagt, damit niemand es durch Lesen des Quellcodes herausfinden muss:

  • Die HTTP/WebSocket- und gRPC-Server werden nie konstruiert. Da nichts sie baut, läuft die dahinterliegende Sicherheits-Middleware — Authentifizierung, IP-Allowlist, Rate Limiting, Audit-Logging — nicht. Behandeln Sie die Konfiguration security.* heute nicht als Schutz. Der Vorteil ist der ehrliche: Da kein Server konstruiert wird, öffnet der Agent keinen eingehenden Port.
  • Keine Aggregation der Service-Topologie. Die Typen ServiceTopology und ServiceEdge existieren, werden aber nie gebaut, also gibt es noch keinen Topologiegraphen.
  • Keine Prometheus-Metriken und kein /metrics-Endpunkt.
  • Keine Erkennung von Netzwerk-Policies.

Konfigurationsschlüssel, die validiert werden, aber nichts tun​

Diese werden geparst, und der Agent akzeptiert sie, aber kein Code konsumiert sie:

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.

Die Kommandozeilen-Optionen gehören in dieselbe Kategorie, und es sind mehr davon als Schlüssel. --log-level und --log-format sind deklariert, in --help dokumentiert und werden von nichts gelesen: Der Logger ist ein fester zap-Produktionslogger (JSON, Level info). Die capture-Optionen (--interfaces, --filter, --snaplen, --promiscuous, --buffer-size, --workers, --queue-size, --stats-interval) werden unter ihrem Optionsnamen gebunden, während die Konfiguration aus verschachtelten Schlüsseln gelesen wird — sie werden also geparst und ignoriert; --enable-grpc und --enable-http stehen in --help auf true und beziehen sich auf Server, die nie konstruiert werden. --config ist die einzige Option, die das Verhalten ändert. Nehmen Sie bitmapper --help nicht als Aussage darüber, was dieses Release tut.

Drei Dinge stehen nicht in dieser Liste, und die Unterschiede sind wichtig:

  • output.filter wird seit 0.1.0-ga auf dem Exportpfad gelesen und durchgesetzt. Es stand früher in dieser Liste, und solange der Agent überhaupt nichts exportierte, war das bloß totes Gewicht. In dem Moment, in dem Flow-Datensätze den Host zu verlassen begannen, wäre es zu einer falschen Datenschutzaussage geworden — ein Betreiber, der ein sensibles Subnetz ausschließt, bekäme keinen Schutz, keinen Fehler und keine Möglichkeit, es zu merken. Es wird nun beim Eingang angewendet. Vier Schlüssel, die auf nichts wirken konnten (export_packets, export_stats, min_packet_size, require_payload), wurden aus dem Schema entfernt, statt sie weiter parsen zu lassen, und der Agent benennt sie beim Start, wenn Ihre Datei noch einen davon setzt.
  • capture.protocols, capture.ports und capture.exclude_ports werden in den BPF-Filter des Kernels kompiliert und dort durchgesetzt. Eine frühere Fassung dieser Seite führte alle drei als validiert-aber-wirkungslos auf, und das war falsch — siehe die Korrektur weiter unten.
  • capture.exclude_interfaces wird gelesen und auf dem Live-Mitschnittpfad angewendet. Siehe Schnittstellen auswählen.
Korrektur: capture.exclude_ports ist eine funktionierende Datenschutz-Maßnahme

Bis 0.1.0-ga zählte diese Seite capture.protocols, capture.ports und capture.exclude_ports zu den Schlüsseln, die „geparst werden und nichts tun“, und die Mitschnitt-Seite wiederholte das. Das war das Gegenteil der Wahrheit: Alle drei werden in den BPF-Filter kompiliert und vom Kernel durchgesetzt, bevor ein Paket den Agenten erreicht.

capture.exclude_ports ist der Schlüssel, bei dem Sie handeln sollten. Die mitgelieferte Konfiguration setzt exclude_ports: [22] — „SSH nicht mitschneiden“ — und das funktioniert. Wenn Sie den alten Text gelesen und den Schlüssel als totes Gewicht gelöscht haben, haben Sie eine Maßnahme entfernt, die Sie geschützt hat, und begonnen, Verkehr zu sammeln, den Sie bewusst ausgeschlossen hatten — ohne jeden Hinweis auf die Änderung. Prüfen Sie, was Ihre ausgerollte Konfiguration sagt, bevor Sie annehmen, dass Sie nicht betroffen waren.

capture.protocols braucht die umgekehrte Aufmerksamkeit: Der Schlüssel steht auf ["tcp", "udp"], ob Sie ihn setzen oder nicht, sodass eine Standardinstallation stillschweigend kein ICMP, ICMPv6, SCTP, GRE, ESP oder AH mitschneidet. Die Protokoll-Voreinstellung erklärt, wie Sie das erweitern.

Warum sie trotzdem akzeptiert werden

Sie sind Teil des Konfigurationsschemas und werden auf ihre Form hin validiert, sodass eine dagegen geschriebene Datei lädt. Genau deshalb stehen sie hier: Ein Schlüssel, der sauber parst und nichts tut, ist genau die Art von Sache, die sich wie eine funktionierende Funktion liest. Gehen Sie davon aus, dass ein Schlüssel keine Wirkung hat, sofern er nicht unter was heute funktioniert auftaucht.

Das Paket zur Kubernetes-Anreicherung wurde entfernt, statt schlafend liegen gelassen zu werden. Es hatte keine Importeure und baute seinen eigenen HTTP-Client, der Redirects folgte und ein Service-Account-Bearer-Token an ein Klartext-Ziel leaken konnte, wenn eine Kubeconfig auf einen umleitenden Server zeigte. Der Block kubernetes.* bleibt im Schema und wird nun von nichts konsumiert.

Ausführen​

bitmapper ist ein einzelnes Binary mit drei Befehlen:

# 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 und --log-format sind persistente Flags am Root-Befehl, aber nur --config bewirkt etwas. Es gibt keine Möglichkeit, das Logging-Detail während der Inbetriebnahme hochzudrehen: Der Logger ist auf JSON, Level info, festgelegt. Was der Agent Ihnen beim Start sagt, ist der kompilierte Mitschnittfilter und die von ihm ausgewählten Schnittstellen; und in jedem performance.stats_interval protokolliert er Paket-, Byte-, Drop- und Verbindungszähler — das sind die Belege, die man beim Inbetriebnehmen eines Hosts liest.

Er braucht erhöhte Berechtigungen​

Paketmitschnitt ist privilegiert. Auf der veröffentlichten Plattform:

PlattformWas er braucht
LinuxCAP_NET_RAW und CAP_NET_ADMIN (oder root)

Er braucht CGO — und das veröffentlichte Binary hat es bereits​

Der Mitschnitt läuft über gopacket/pcap, eine Anbindung an eine C-Bibliothek. Ein Build mit CGO_ENABLED=0 kompiliert zwar noch, aber Mitschnitt und Auflisten der Schnittstellen verweigern zur Laufzeit:

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

Das ist Absicht — ein Binary, das stillschweigend nichts mitschneidet, wäre schlimmer als eines, das sagt, warum es nicht kann. Das ist zugleich die einzige Tatsache, die über die veröffentlichten Plattformen entscheidet, und deshalb verweigert die Release-Pipeline jedes Binary, das nicht mit CGO gebaut wurde, nicht statisch gelinkt ist oder kein libpcap enthält. Siehe Plattformen.

Der Download, den Sie erhalten, braucht kein auf dem Host installiertes libpcap: libpcap ist statisch in das veröffentlichte Binary gelinkt.

Die Enrolment-Zugangsdaten​

Wenn Sie control_plane.* konfigurieren, werden die Enrolment-Zugangsdaten aus control_plane.enrollment_token_file gelesen. Der Agent verlangt, dass es eine reguläre Datei ist, mit Modus 0600 oder strenger, im Besitz von root oder des eigenen Benutzers des Agenten, und in einem Verzeichnis, das weder gruppen- noch weltschreibbar ist. Ein Verzeichnis, in das jeder schreiben kann, erlaubt es jemandem, die Datei zu ersetzen; nur den Modus der Datei selbst zu prüfen hieße also, das Falsche zu prüfen.

Die Prüfung beruht auf POSIX. Das deckt jedes veröffentlichte Binary ab, da 0.1.0-ga nur unter Linux ausgeliefert wird — es ist aber gut zu wissen, dass es eine Eigenschaft der Plattform ist und nicht des Agenten, falls je ein Windows-Build erscheinen sollte.

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"

Beide URLs müssen https:// sein, und ingest_url ist verpflichtend, sobald der Control-Plane-Block überhaupt verwendet wird. tenant_id wird als Header X-Tenant-ID gesendet — es ist eine Kennung, keine Zugangsberechtigung, und damit nicht das, was den Agenten authentifiziert.

Die Zugangsdaten können auch aus der Umgebung kommen, als BITMAPPER_CONTROL_PLANE_ENROLLMENT_TOKEN, oder vollständig durch ein Mutual-TLS-Client-Zertifikat ersetzt werden (control_plane.client_cert_file / client_key_file, wobei die Schlüsseldatei derselben Berechtigungsprüfung unterliegt). control_plane.enrollment_token direkt im YAML zu setzen funktioniert und ist nicht empfohlen — ein Geheimnis in einer Konfigurationsdatei ist ein Geheimnis auf der Festplatte.

Was er auf dem Host kostet​

Paketmitschnitt ist das Teuerste, was irgendeines der bits tut, und die Kosten skalieren mit dem Verkehr, nicht mit der Größe des Hosts. Zwei Stellschrauben zählen am meisten:

  • Ein BPF-Filter ist die billigstmögliche Reduktion — er verwirft Pakete im Kernel, bevor sie überhaupt zum Agenten kopiert werden.
  • Die Verbindungstabelle ist größenbegrenzt, mit Verdrängung und periodischer GC untätiger Einträge, sodass ein Host mit vielen kurzlebigen Verbindungen die Tabelle nicht unbegrenzt wachsen lässt.

performance.memory_limit und performance.cpu_limit sind nicht implementiert — sie stehen in der Liste weiter oben. Begrenzen Sie den Prozess mit den Mitteln Ihres Init-Systems (MemoryMax=, CPUQuota= in einer systemd-Unit), wenn Sie eine echte Obergrenze brauchen.

Flows, die auf den Export warten, sind separat durch batch_export.max_buffered_connections begrenzt und nicht durch performance.connection_table_size, dessen Standardwert eine zweite sechsstellige Tabelle auf einen Mitschnitt-Host legen würde. Jeder Verlust wird gezählt und protokolliert; keiner ist stumm.

Plattformen​

0.1.0-ga veröffentlicht ein signiertes Binary. Das ist die schmalste Plattformliste der Familie, und jede Auslassung unten ist eine gemessene Entscheidung, kein Versehen:

PlattformVeröffentlichtWarum nicht
Linux amd64Ja
Linux arm64NeinLässt sich von einem amd64-gcc nicht assemblieren, und es gibt keinen arm64-Builder
macOS amd64/arm64NeinBraucht clang und das macOS-SDK
Windows amd64NeinBaut, wurde aber nie unter Windows ausgeführt — siehe unten

Die einzige Ursache hinter alldem ist, dass bitmapper das einzige bit ist, das sich nicht cross-kompilieren lässt. Mitschnitt heißt gopacket → libpcap → CGO, ein Build braucht also eine echte C-Toolchain und ein echtes libpcap für das Ziel. Alle anderen bits sind reines Go, und GOOS/GOARCH genügt.

Die Falle, die daraus entsteht, ist konkret und gehört benannt. internal/capture trägt einen //go:build !cgo-Stub, der auf jeder Plattform kompiliert und nichts mitschneidet. Ein CGO_ENABLED=0-Build gelingt daher — er erzeugt ein Binary, das startet, sich registriert, Heartbeats sendet, als gesund gemeldet wird und nie ein Paket sieht. Die Release-Pipeline prüft deshalb das Artefakt statt des Build-Befehls: Sie verweigert jedes Binary, das nicht mit CGO gebaut wurde, nicht statisch gelinkt ist oder kein libpcap enthält.

Windows ist der interessante und der ehrliche Fall. Es baut tatsächlich — gopackets Windows-Unterstützung ist reines Go und lädt wpcap.dll zur Laufzeit — wir könnten es also heute veröffentlichen. Es wurde nie unter Windows ausgeführt. Das macht es zu einer Test-Lücke und nicht zu einer Toolchain-Lücke, und es zu veröffentlichen hieße, ein signiertes Binary auszuliefern, dessen Mitschnittpfad niemand je hat arbeiten sehen. Ein signiertes Binary, das nicht mitschneiden kann, ist schlimmer als gar keines, weil die Signatur als Aussage über die Tauglichkeit gelesen wird. Es wird zurückgehalten, bis jemand es ausgeführt hat.

Wenn Sie diese Daten heute von einem macOS-, Windows- oder arm64-Host brauchen, ist bitmapper für diesen Host nicht die Antwort. bitcollector wird auf allen fünf Plattformen ausgeliefert und meldet die aufgebauten Netzwerk-Peers dieses Hosts — eine gröbere Antwort als die Zuordnung je Flow, aber eine echte auf der Maschine, die Sie tatsächlich haben.

Nächste Schritte​

War diese Seite hilfreich?