Saltar al contenido principal
Version: 1.0.0

bitmapper

bitmapper responde a «¿con qué está hablando realmente esta máquina, y qué proceso lo hace?»

bitcollector lee el estado del host. bitscanner mira hacia fuera, al segmento. bitmapper observa el tráfico en sí — paquetes en vivo en las propias interfaces del host, agrupados en flujos y atribuidos al proceso propietario del socket.

Lea esto antes de planificar nada en torno a bitmapper

bitmapper entrega registros de flujo, no tráfico. Desde 0.1.0-ga el motor de captura está conectado al exportador, y las conexiones seguidas llegan a la plataforma como registros de flujo: extremos, puertos, protocolo, contadores de bytes y paquetes, estado de la conexión y — cuando puede determinarse, cosa que hoy es infrecuente — el proceso atribuido. Ningún byte de carga útil, ninguna captura de paquetes, y no la línea de comandos del proceso, que habitualmente lleva credenciales como argumentos. Si esperaba que llegara un PCAP a alguna parte, no es lo que este agente envía.

La atribución de procesos apenas funciona en el binario publicado, y la corrección aún no está publicada. En 0.1.0-ga la atribución se intenta una vez, al crear la conexión, contra una instantánea de la tabla de sockets que no puede contener el socket de una conexión saliente nueva, y no hay inferencia alguna a partir de los sockets a la escucha — así que los flujos llegan a menudo sin proceso alguno (medido en el host de desarrollo: 0 atribuidos de 12), y destination_process nunca se rellena. El reemplazo — relleno posterior por flujo, en ambos extremos, y un campo attribution que distingue una coincidencia directa de socket (socket) de una inferencia (listening_socket) — está sin publicar. Esa inferencia está acotada: solo se hace para una dirección de este host, solo cuando se prueba que exactamente un proceso tiene el socket a la escucha, y solo para TCP, así que un flujo UDP nunca la lleva. Lea atribución de procesos antes de planificar nada sobre ello.

Es un binario, en una sola plataforma. 0.1.0-ga se publica solo para linux/amd64. No hay descarga para macOS, Windows ni arm64, y las razones están expuestas más abajo en lugar de quedar como un hueco en una página de descargas.

No captura todos los protocolos por defecto. capture.protocols vale por defecto ["tcp", "udp"] mencione o no su archivo de configuración esa clave, así que un despliegue de fábrica no ve ICMP, ICMPv6, SCTP, GRE, ESP ni AH — el kernel los descarta y nada en la salida lo indica. Ampliarlo es una línea.

Partes de este agente siguen sin construirse. No hay agregación de topología de servicios, ni métricas de Prometheus, ni detección de políticas de red; varias claves de configuración se analizan y no hacen nada, y lo mismo ocurre con todas las opciones de la línea de comandos salvo --config. Qué no está conectado es la lista, y merece leerse antes de planificar en torno a una clave.

Versión0.1.0-ga, commit 64ebc64, compilada con go1.25.12
PlataformasSolo linux/amd64 — macOS, Windows y arm64 no se publican, y por qué
Cadena de suministroCompilación reproducible, SBOM CycloneDX, firma cosign, atestación del SBOM, SHA256SUMS firmado — la misma clave de familia que el resto de los bits
EstadoCaptura, atribuye y hace seguimiento de conexiones; exporta registros de flujo a la plataforma
Superficie de red entranteNingún servicio a la escucha. Los servidores HTTP/WebSocket y gRPC existen en el árbol de código pero nunca se construyen, y ambos se distribuyen desactivados
SalienteSolo https — registro, latido y registros de flujo hacia el endpoint que usted configure
PrivilegiosElevados: root, o CAP_NET_RAW + CAP_NET_ADMIN
Requisito de compilaciónCGO y libpcap, enlazada estáticamente en el binario publicado

Dónde encaja entre los bits​

Los cuatro agentes responden a cuatro preguntas distintas, y la frontera es aquello que mira cada uno:

MiraResponde
bitcollectorEl estado del propio host«¿Qué es esta máquina y en qué estado está?»
bitscannerEl segmento que rodea al host«¿Qué más hay en este cable que yo no conozca?»
bitmapperLos paquetes que cruzan las interfaces del host — TCP y UDP por defecto, más si lo pide«¿Con qué está hablando esta máquina, y qué proceso lo hace?»
bitenforcerLa configuración del host«¿Coincide esta máquina con la política que definí?»

Una tabla de vecinos le dice que un dispositivo existe. Una captura le dice que se habla con él, qué lo hace y con qué frecuencia — que es la diferencia entre un inventario y un mapa de dependencias.

Qué funciona hoy​

Presente y conectado en la ruta de código del comando capture:

  • Captura de paquetes en vivo — libpcap a través de gopacket, enlazada estáticamente en el binario Linux publicado, con filtro BPF, snaplen, modo promiscuo y tamaño del búfer de captura.
  • Filtrado de captura del lado del kernel — capture.filter, capture.protocols, capture.ports y capture.exclude_ports se compilan en una sola expresión BPF que aplica el kernel, de modo que el tráfico excluido nunca se copia al agente. La expresión compilada se registra al arrancar. Atención al valor por defecto: capture.protocols es ["tcp", "udp"] aunque su archivo lo omita, lo que significa que ICMP, SCTP, GRE y ESP no se capturan salvo que lo pida. Vea el filtrado.
  • Captura multiinterfaz — una lista de interfaces por nombre, o todas las interfaces que estén activas y no sean loopback cuando no nombra ninguna.
  • Atribución de procesos — presente, pero apenas funcional en el binario publicado. PID, nombre del proceso, ruta del ejecutable y usuario, resueltos a partir de las tablas de sockets del host. En 0.1.0-ga la búsqueda se intenta una sola vez, al crear la conexión, contra una instantánea que no puede contener el socket de una conexión saliente nueva, así que los flujos a menudo no llevan proceso alguno y destination_process nunca se rellena. El relleno posterior por flujo, la atribución de ambos extremos y el campo de confianza attribution (socket vs listening_socket) no están en el binario publicado, que no tiene inferencia alguna a partir de los sockets a la escucha — vea atribución de procesos para lo que se entrega hoy y lo que lo reemplaza. Ese reemplazo además declina en lugar de adivinar: no toma ninguna identidad de un socket a la escucha si la dirección no está en este host o no se ha probado que un único proceso lo tiene — un socket de nginx en el host de desarrollo lo tienen 17 procesos — y nunca infiere para UDP, que no tiene estado LISTEN. Un campo de proceso vacío es un rechazo, no un fallo.
  • Seguimiento de conexiones con estado — emparejamiento bidireccional de flujos, una máquina de estados TCP (SYN_SENT → ESTABLISHED → … → CLOSED), contadores de paquetes y bytes por conexión, una tabla con tope de tamaño y desalojo, y recolección periódica de las conexiones inactivas.
  • Flujo de eventos dentro del proceso — eventos de paquete, de conexión nueva y de estadísticas repartidos entre los suscriptores dentro del proceso.
  • Estadísticas periódicas — contadores de paquetes, bytes, descartes y conexiones escritos como JSON estructurado según performance.stats_interval.
  • Integración con el plano de control — registro y latido del agente cuando control_plane.* está configurado.
  • Exportación de flujos — cada conexión seguida pasa de la ruta de captura al exportador y se envía por POST a la plataforma como un registro de flujo. Cada flujo produce al menos un registro, y el último se marca como final con contadores completos; una conexión de larga duración se refresca una vez por batch_export.batch_interval si sus contadores se han movido, de modo que la ve antes de que termine.
  • Un filtrado de exportación que filtra de verdad — output.filter (exclude_cidrs, exclude_loopback, protocols, ports) se aplica en la entrada, antes de construir un registro, así que un flujo excluido nunca se guarda en memoria y nunca puede enviarse. Las exclusiones casan con cualquiera de los dos extremos: excluir 10.0.0.0/8 significa «no le cuenten a Cert-IX nada de esa red», e informar de que 10.1.2.3 abrió una conexión hacia fuera se lo cuenta igual de completamente. Una entrada que no puede compilarse rechaza la ejecución en lugar de degradarse a «exportarlo todo».
  • Validación de configuración con fallo inmediato — el agente se niega a arrancar con una configuración inválida o insegura por omisión, en lugar de arrancar en un estado debilitado.

Qué no está conectado​

Dicho explícitamente, para que nadie tenga que descubrirlo leyendo el código fuente:

  • Los servidores HTTP/WebSocket y gRPC nunca se construyen. Como nada los crea, el middleware de seguridad que hay detrás — autenticación, lista de IP permitidas, limitación de tasa, registro de auditoría — no se ejecuta. No trate hoy la configuración security.* como una protección. La contrapartida es la honesta: sin ningún servidor construido, el agente no expone ningún puerto entrante.
  • Sin agregación de topología de servicios. Los tipos ServiceTopology y ServiceEdge existen pero nunca se construyen, así que todavía no hay grafo de topología.
  • Sin métricas de Prometheus y sin endpoint /metrics.
  • Sin detección de políticas de red.

Claves de configuración que se validan pero no hacen nada​

Estas se analizan, y el agente las aceptará, pero ningún código las consume:

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.

Las opciones de la línea de comandos entran en la misma categoría, y hay más de ellas que de claves. --log-level y --log-format están declaradas, documentadas en --help, y no las lee nada: el registrador es un registrador zap de producción fijo (JSON, nivel info). Las opciones de capture (--interfaces, --filter, --snaplen, --promiscuous, --buffer-size, --workers, --queue-size, --stats-interval) se enlazan bajo su nombre de opción mientras que la configuración se lee de claves anidadas, así que se analizan y se ignoran; --enable-grpc y --enable-http valen true por defecto en --help y se refieren a servidores que nunca se construyen. --config es la única opción que cambia el comportamiento. No tome bitmapper --help como una declaración de lo que hace esta versión.

Tres cosas no están en esa lista, y las distinciones importan:

  • output.filter se lee y se aplica en la ruta de exportación desde 0.1.0-ga. Antes estaba en esta lista, y mientras el agente no exportaba nada en absoluto eso era solo peso muerto. En cuanto los registros de flujo empezaron a salir del host se habría convertido en una falsa declaración de privacidad: un operador que excluyera una subred sensible no obtendría protección, ni error, ni forma de saberlo. Ahora se aplica en la entrada. Cuatro claves que nunca podían actuar sobre nada (export_packets, export_stats, min_packet_size, require_payload) se eliminaron del esquema en lugar de dejarlas analizándose, y el agente las nombra al arrancar si su fichero todavía fija alguna.
  • capture.protocols, capture.ports y capture.exclude_ports se compilan en el filtro BPF del kernel y se aplican ahí. Una versión anterior de esta página enumeraba las tres como validadas-pero-inertes, y eso era falso — vea la corrección más abajo.
  • capture.exclude_interfaces se lee y se aplica en la ruta de captura en vivo. Vea elegir las interfaces.
Corrección: capture.exclude_ports es un control de privacidad que funciona

Hasta 0.1.0-ga esta página contaba capture.protocols, capture.ports y capture.exclude_ports entre las claves que «se analizan y no hacen nada», y la página de captura lo repetía. Era lo contrario de la verdad: las tres se compilan en el filtro BPF y las aplica el kernel antes de que un paquete llegue al agente.

capture.exclude_ports es sobre la que hay que actuar. La configuración que bitmapper distribuye fija exclude_ports: [22] — «no capturar SSH» — y funciona. Si leyó el texto antiguo y borró la clave por considerarla lastre, quitó un control que le estaba protegiendo y empezó a recoger tráfico que había excluido deliberadamente, sin nada que indicara el cambio. Compruebe qué dice su configuración desplegada antes de dar por hecho que no le afectó.

capture.protocols requiere la atención contraria: vale por defecto ["tcp", "udp"] la fije usted o no, así que un despliegue de fábrica no captura calladamente ni ICMP, ni ICMPv6, ni SCTP, ni GRE, ni ESP, ni AH. El valor por defecto de protocolos explica cómo ampliarlo.

Por qué se siguen aceptando

Forman parte del esquema de configuración y se validan en su forma, de modo que un fichero escrito con ellas se carga. Y por eso mismo se enumeran aquí: una clave que se analiza sin errores y no hace nada es justo el tipo de cosa que se lee como una funcionalidad que funciona. Dé por hecho que una clave no tiene ningún efecto salvo que aparezca en qué funciona hoy.

El paquete de enriquecimiento de Kubernetes se eliminó en lugar de dejarlo latente. No tenía importadores y construía su propio cliente HTTP, que seguía las redirecciones y podía filtrar el token bearer de una cuenta de servicio hacia un destino en texto plano cuando un kubeconfig apuntaba a un servidor que redirigía. El bloque kubernetes.* sigue en el esquema y ahora no lo consume nada.

Cómo ejecutarlo​

bitmapper es un único binario con tres comandos:

# 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 y --log-format son opciones persistentes del comando raíz, pero solo --config hace algo. No hay manera de subir el detalle del registro mientras pone el agente en marcha: el registrador está fijado a JSON, nivel info. Lo que el agente sí le dice al arrancar es el filtro de captura compilado y las interfaces que seleccionó, y en cada performance.stats_interval registra los contadores de paquetes, bytes, descartes y conexiones — esa es la evidencia que hay que leer al poner un host en servicio.

Necesita privilegios elevados​

La captura de paquetes es una operación privilegiada. En la plataforma publicada:

PlataformaQué necesita
LinuxCAP_NET_RAW y CAP_NET_ADMIN (o root)

Necesita CGO — y el binario publicado ya lo tiene​

La captura pasa por gopacket/pcap, que es un enlace a una biblioteca en C. Una compilación con CGO_ENABLED=0 sigue compilando, pero la captura y el listado de interfaces se niegan a funcionar en tiempo de ejecución:

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

Es deliberado — un binario que en silencio no capturara nada sería peor que uno que dice por qué no puede. Ese es además el único hecho que decide qué plataformas se publican, y por eso la cadena de publicación rechaza cualquier binario que no se haya compilado con CGO, que no esté enlazado estáticamente, o que no lleve libpcap dentro. Vea plataformas.

La descarga que recibe no necesita ninguna libpcap instalada en el host: libpcap va enlazada estáticamente en el binario publicado.

La credencial de enrolamiento​

Si configura control_plane.*, la credencial de enrolamiento se lee de control_plane.enrollment_token_file. El agente exige que sea un fichero regular, con modo 0600 o más restrictivo, propiedad de root o del propio usuario del agente, y dentro de un directorio en el que no puedan escribir ni el grupo ni el resto del mundo. Un directorio en el que cualquiera puede escribir permite que alguien sustituya el fichero, así que comprobar solo el modo del propio fichero sería comprobar lo que no toca.

La comprobación se basa en POSIX. Eso cubre todos los binarios publicados, ya que 0.1.0-ga solo se distribuye en Linux — pero conviene saber que es una propiedad de la plataforma y no del agente, por si algún día apareciera una compilación para 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"

Ambas URL deben ser https://, e ingest_url es obligatoria en cuanto el bloque del plano de control se usa siquiera. tenant_id se envía como la cabecera X-Tenant-ID — es un identificador, no una credencial, así que no es lo que autentica al agente.

La credencial también puede venir del entorno como BITMAPPER_CONTROL_PLANE_ENROLLMENT_TOKEN, o sustituirse por completo por un certificado de cliente TLS mutuo (control_plane.client_cert_file / client_key_file, con el fichero de la clave sometido a la misma comprobación de permisos). Fijar control_plane.enrollment_token en línea en el YAML funciona y está desaconsejado — un secreto en un fichero de configuración es un secreto en disco.

Qué cuesta en el host​

La captura de paquetes es lo más caro que hace cualquiera de los bits, y el coste escala con el tráfico, no con el tamaño del host. Dos controles son los que más importan:

  • Un filtro BPF es la reducción más barata posible — descarta los paquetes en el kernel antes de que lleguen siquiera a copiarse al agente.
  • La tabla de conexiones tiene un tope de tamaño, con desalojo y recolección periódica de las entradas inactivas, de modo que un host que ve muchas conexiones efímeras no hace crecer la tabla sin límite.

performance.memory_limit y performance.cpu_limit no están implementados — están en la lista de arriba. Acote el proceso con los controles de su sistema de arranque (MemoryMax=, CPUQuota= en una unidad de systemd) si necesita un techo real.

Los flujos que esperan exportación están acotados por separado por batch_export.max_buffered_connections, y no por performance.connection_table_size, cuyo valor por defecto pondría una segunda tabla de seis cifras en un host de captura. Cada descarte se cuenta y se registra; ninguno es silencioso.

Plataformas​

0.1.0-ga publica un binario firmado. Es la lista de plataformas más estrecha de la familia, y cada omisión de abajo es una decisión medida y no un descuido:

PlataformaPublicadaPor qué no
Linux amd64Sí
Linux arm64NoUn gcc amd64 no puede ensamblarla, y no hay máquina de compilación arm64
macOS amd64/arm64NoNecesita clang y el SDK de macOS
Windows amd64NoCompila, pero nunca se ha ejecutado en Windows — ver abajo

La causa única detrás de todo esto es que bitmapper es el único bit que no puede compilarse de forma cruzada. La captura es gopacket → libpcap → CGO, así que una compilación necesita una cadena de herramientas C real y una libpcap real para el destino. Todos los demás bits son Go puro, y basta con GOOS/GOARCH.

La trampa que esto crea es concreta y merece nombrarse. internal/capture lleva un stub //go:build !cgo que compila en todas las plataformas y no captura nada. Una compilación con CGO_ENABLED=0 por tanto tiene éxito: produce un binario que arranca, se registra, late, aparece sano, y nunca ve un paquete. Por eso la cadena de publicación controla el artefacto en lugar del comando de compilación: rechaza cualquier binario que no se haya compilado con CGO, que no esté enlazado estáticamente, o que no lleve libpcap dentro.

Windows es el caso interesante, y el honesto. Compila de verdad — el soporte de Windows de gopacket es Go puro y carga wpcap.dll en tiempo de ejecución — así que podríamos publicarlo hoy. Nunca se ha ejecutado en Windows. Eso lo convierte en una laguna de pruebas y no de cadena de herramientas, y publicarlo significaría distribuir un binario firmado cuya ruta de captura nadie ha visto funcionar nunca. Un binario firmado que no puede capturar es peor que no tener binario, porque la firma se lee como una afirmación sobre su aptitud. Se retiene hasta que alguien lo haya ejecutado.

Si hoy necesita estos datos desde un host macOS, Windows o arm64, bitmapper no es la respuesta para ese host. bitcollector se distribuye en las cinco plataformas e informa de los pares de red establecidos de ese host — una respuesta más gruesa que la atribución por flujo, pero real en la máquina que de verdad tiene.

Próximos pasos​

  • Cómo funciona la captura — interfaces, filtros BPF, atribución de procesos y la máquina de estados de conexión, y qué pone cada una de esas cosas en memoria.
  • bitscanner — el agente que encuentra los dispositivos que su inventario no contiene.
  • bitcollector — el inventario del host que mira hacia dentro.
  • Verificación de sus descargas — el ancla de confianza, y por qué importa más que la firma.
  • ¿Qué es bits? — cómo encajan los agentes entre sí.

¿Te resultó útil esta página?