El escaneo y los dos interruptores que lo arman
El escaneo activo es la única parte de bitscanner que envía paquetes no solicitados a dispositivos que son de otra persona. Sondear una red que no está autorizado a sondear es una cuestión legal antes que técnica — y el segmento que hay delante del agente lo eligen las interfaces del host, no usted: una VPN, una red de contenedores en puente o una interfaz corporativa muy amplia pueden poner a su alcance espacio de direcciones que nadie pretendía.
Por eso el valor por defecto es la envolvente, no la capacidad. Todos los interruptores de más abajo fallan en cerrado, y los que importan solo puede ponerlos una persona editando un fichero.
Para saber qué reporta el agente sin nada de esto, consulte la visión general de bitscanner.
Los dos interruptores
scanning.enabledarma la envolvente: «este agente puede sondear, este es el alcance, y estoy autorizado para ello».scanning.probes.<name>elige qué sondas se ejecutan dentro de ella — es decir, cuánto ruido se hace.
Todas las sondas valen false por defecto, así que scanning.enabled: true por sí solo
sigue enviando exactamente cero paquetes.
Son dos decisiones porque los dos modos de fallo son distintos: un alcance equivocado sondea a las personas equivocadas, un conjunto de sondas equivocado sondea a las personas correctas con demasiada agresividad. Ampliar cualquiera de los dos es siempre una edición explícita.
scanning:
enabled: false # the envelope
i_accept_scanning_authorization: false # your assertion that you may probe these networks
allowed_cidrs: [] # the ONLY address space that may ever be probed
probes:
neighbor_discovery: false # …and every probe, individually
Una sonda armada mientras la envolvente está apagada es un fallo de arranque que nombra ambas claves, no un ajuste que se ignora en silencio. Ignorarlo en silencio dejaría a un operador leyendo su propio fichero creyendo que hay un escaneo en marcha — o concluyendo que el producto está roto cuando no aparecen resultados:
$ bitscanner config validate --config config.yaml
Error: configuration validation failed: failed to load config: config validation failed:
scanning.probes.neighbor_discovery, scanning.probes.port_scan are true but
scanning.enabled is false: the probe toggles choose WHICH probes run, scanning.enabled
arms the envelope that permits any of them at all, and with the envelope off every one of
those probes would be denied. Either set scanning.enabled: true (together with
scanning.i_accept_scanning_authorization and scanning.allowed_cidrs), or turn off
scanning.probes.neighbor_discovery, scanning.probes.port_scan
El mismo trato se aplica a una sonda cuyo requisito previo está apagado. Un escaneo de puertos se ejecuta sobre la lista de vecinos que produce el descubrimiento de vecinos, así que pedir uno sin el otro es una configuración que se ejecutaría sin producir nada — indistinguible, desde el lado del operador, de un agente roto:
scanning.probes.port_scan is true but scanning.probes.neighbor_discovery is false: the
port scan probes the neighbours that neighbour discovery finds; with neighbour discovery
off the target list is empty and nothing would be scanned. Set
scanning.probes.neighbor_discovery: true as well, or turn off scanning.probes.port_scan
Y habilitar la envolvente sin el reconocimiento de autorización:
scanning.enabled is true but scanning.i_accept_scanning_authorization is false: active
scanning sends unsolicited packets to other people's devices, and setting this key is you
asserting that you are authorised to probe every network listed in scanning.allowed_cidrs
(you own them, or you hold written permission from whoever does). Probing a segment you
are not authorised to probe — a shared/colocated network, or a partner network reachable
over a VPN — may be unlawful.
Tampoco hay un valor por defecto implícito de «escanearlo todo». allowed_cidrs debe estar no
vacío cuando la envolvente está activa, las entradas deben ser direcciones de red
canónicas, y un 0.0.0.0/0 se rechaza de plano:
scanning.allowed_cidrs entry "10.20.1.3/16" is not a network address: it authorises
10.20.0.0/16 (65536 addresses), which is almost certainly wider than intended. Write the
network explicitly, or use a /32 (or /128) for a single host
Ese rechazo existe porque todos los analizadores de CIDR del mundo amplían en silencio una dirección de host a la que se le pone una máscara de red. El operador escribió una dirección y autorizó 65 534.
scanning.* y security.* solo pueden venir del fichero
$ BITSCANNER_SCANNING_ENABLED=true bitscanner config validate --config config.yaml
Error: configuration validation failed: failed to load config: refusing to start:
BITSCANNER_SCANNING_ENABLED set in the environment, and the scanning envelope and the
security block are settable ONLY from the config file. Those keys — scanning.enabled,
scanning.i_accept_scanning_authorization, scanning.allowed_cidrs, every
scanning.probes.*, and security.i_accept_plaintext_egress — are acknowledgements that a
person is authorised to probe somebody else's network, or that a customer's network map
may cross the wire in cleartext. An acknowledgement has to be written by hand into a file
that can be reviewed and diffed, not inherited from a shell profile, a systemd drop-in, a
compose file or a parent process. This is NOT ignored and NOT applied: the agent stops.
Este es el sentido de los reconocimientos, y vale la pena ser franco sobre el porqué.
Todo el valor de i_accept_scanning_authorization está en que una persona lo escribió en un
fichero que puede revisarse, compararse y mantenerse en gestión de configuración. Una
variable exportada por un perfil de shell, un drop-in de systemd, un fichero compose o un
proceso padre comprometido no es nada de eso. Si el entorno pudiera ponerlo, el control sería
una formalidad.
Tres propiedades lo hacen estructural en lugar de una regla que alguien deba recordar:
- El entorno no es una fuente para esas claves. El enlace con el entorno es una lista de
permitidos explícita de claves operativas corrientes (
agent.id, intervalos, tamaños de lote). Nada de lo que contiene puede armar una sonda ni relajar la seguridad del transporte. - Los subárboles de seguridad se releen desde el fichero mediante un segundo cargador que no tiene ningún enlace con el entorno, y esos valores sobrescriben lo que produjera el cargador principal.
- Un intento es un rechazo, no un filtrado. Todo el espacio de nombres
BITSCANNER_SCANNING_*yBITSCANNER_SECURITY_*está reservado, y el error nombra la variable — porque a un operador que cree haber armado un escaneo no se le puede dejar sin respuesta.
El espacio de nombres reservado es deliberadamente más amplio que las claves de configuración:
una variable propia suya llamada BITSCANNER_SECURITY_TOKEN también se rechaza. Un rechazo que
nombra la variable se resuelve en segundos; una lista de patrones que tiene que adivinar qué
grafías importan, no.
La escalera de sondas
Las siete valen false por defecto. Se listan de la menos intrusiva a la más — y cada línea
dice qué sale realmente al cable, porque un operador no puede consentir a algo descrito
únicamente por su nombre.
| Sonda | Qué pone en el cable | Necesita |
|---|---|---|
neighbor_discovery | Nada. Lee la propia caché ARP/NDP del kernel y enriquece cada entrada con una tabla de fabricantes MAC/OUI incorporada y sin conexión | — |
gateway_discovery | Nada nuevo. Tabla de rutas más atribución de MAC desde la caché ARP y la tabla OUI sin conexión. ⚠️ Falso en 0.1.3-ga y versiones anteriores — también enviaba un eco ICMP a cada pasarela encontrada; véase la advertencia más abajo | — |
reverse_dns | Una consulta PTR por cada vecino sin nombre. Esto es un paquete | neighbor_discovery |
service_discovery | Una consulta mDNS a 224.0.0.251:5353 y un M-SEARCH SSDP a 239.255.255.250:1900. Un paquete cada uno — y todos los dispositivos del dominio de difusión los ven | neighbor_discovery |
gateway_probe | Conexiones TCP a los puertos de la propia pasarela (80, 443, 22, 23, 53; luego 8080/8443 para caracterizar servicios, con lectura de banners), más un eco ICMP | gateway_discovery |
active_arp_sweep | INTRUSIVA. Recorre el rango utilizable de cada subred conectada e intenta conexiones TCP a un puñado de puertos de cada dirección — de cientos a miles de intentos de conexión por ciclo | neighbor_discovery |
port_scan | LA MÁS INTRUSIVA. Unos 19 puertos TCP habituales en cada vecino descubierto, más lectura de banners en los que presenten uno (21/22/25/110/143) | neighbor_discovery |
Tres de ellas merecen más que una fila de tabla.
reverse_dns es un interruptor porque no lo era
El descubrimiento de vecinos se presenta como la primera ejecución segura: leer las tablas del
kernel, no enviar nada. Esa afirmación era falsa. Todos los vecinos sin nombre se resolvían de
forma inversa sin condiciones, y no había configuración que lo desactivara — una
observación de la envolvente de escaneo con solo neighbor_discovery armado registró dos
sondas concedidas por ciclo: eran estas resoluciones.
Una resolución inversa es un paquete, y revela más de lo que parece. Va al resolutor al que apunte este host — en un segmento corporativo, a menudo uno que opera y registra el propio equipo de seguridad del cliente — y la consulta revela con precisión cuáles de sus hosts ha enumerado este agente, un PTR cada vez. En un segmento donde el agente se está probando sin que lo sepa el equipo de redes, esa es la divulgación, no el escaneo de puertos.
Está desactivada por defecto para que el modo de cero paquetes sea alcanzable por configuración.
gateway_probe necesita un privilegio que puede no tener
La comprobación de alcanzabilidad abre un socket ICMP en bruto (ip4:icmp), lo que exige
root o CAP_NET_RAW. Sin él, la apertura falla y el agente recurre a un datagrama UDP — y
ese recurso alternativo pasa por el guardián por separado, porque un objetivo no autorizado
no debe volverse autorizado solo porque el primer método falló.
La pasarela es el router de alguien, y a menudo el único dispositivo cuyo fallo tumba todo el
segmento. Estar en su tabla de rutas no es autorización para sondearla, y por eso
gateway_discovery (pasiva, responde a «¿detrás de qué router estoy y cambió su MAC?») y
gateway_probe (activa) son claves distintas.
gateway_discovery también abría este socket en bruto, en 0.1.3-ga y versiones anterioresLa fila de arriba dice que gateway_discovery no pone nada nuevo en la red, y el párrafo de
arriba dice que el socket ICMP en bruto pertenece a gateway_probe. En todas las versiones
hasta 0.1.3-ga incluida, ninguna de las dos cosas es cierta.
La llamada de alcanzabilidad estaba en la rama que se toma cuando gateway_probe está
desactivada. Así que armar gateway_discovery y declinar deliberadamente gateway_probe —
que es exactamente como un operador dice «no abran un socket en bruto contra mi router y no me
obliguen a conceder CAP_NET_RAW» — abría uno igualmente, una vez por pasarela y por ciclo, con
un datagrama UDP al puerto 33434 como recurso alternativo cada vez que el socket en bruto era
rechazado.
Lo que nunca se vio afectado: esa sonda seguía autorizada por el guardián de escaneo, de modo que
solo podía alcanzar una dirección dentro de sus allowed_cidrs, y se rechazaba por completo con
scanning.enabled: false. El defecto es que un interruptor documentado como apagado actuaba
igualmente — no que algo escapara de la envolvente.
Si ejecuta 0.1.3-ga o una versión anterior con gateway_discovery armado, acepte que su
pasarela recibe un ping en cada ciclo o desactive gateway_discovery hasta que pueda
actualizar. En el código fuente, la vía pasiva ya no envía nada en absoluto: informa de
alcanzabilidad únicamente a partir de la entrada ARP resuelta por el propio núcleo, no informa de
ningún tiempo de ida y vuelta que no haya medido, y la compilación falla si alguna función
distinta de la vía de gateway_probe puede alcanzar ese socket — fijar el socket a una función
en lugar de a sus llamadores es lo que permitió que esto sobreviviera a dos revisiones.
active_arp_sweep está acotada por el fichero, no por el host
Las subredes vienen de las propias interfaces de este host, así que es una VPN o una
interfaz corporativa muy amplia — y no su configuración — la que decide qué hay delante del
barrido. allowed_cidrs y min_prefix_length son lo que lo contiene. min_prefix_length vale
22 por defecto (unos 1022 hosts) y se rechaza por debajo de /16 sin excepción: no por los
paquetes, que acota el presupuesto de hosts, sino porque el bucle del barrido en un /8
recorrería 16 millones de direcciones pidiendo permiso para cada una.
El resultado de cero paquetes, medido
La afirmación «el descubrimiento de vecinos por sí solo no envía nada» es de las que hay que
medir en lugar de asegurar. Ejecutado en un host Linux corriente con scanning.enabled: true,
neighbor_discovery armado y todas las demás sondas apagadas — con el rango permitido de abajo
sustituido por un rango de documentación y sin alterar nada más:
WARN scanguard active scanning is ENABLED: this agent will send unsolicited probes
{"allowed_cidrs": ["10.20.0.0/24"], "allow_public_targets": false,
"min_prefix_length": 22, "max_hosts_per_cycle": 1024, "max_concurrency": 16,
"per_probe_delay": "50ms", "max_cycle_duration": "5m0s"}
WARN agent ACTIVE SCANNING ARMED: this agent will send unsolicited probes
{"armed_probes": ["neighbor_discovery"], "confined_to_allowed_cidrs": ["10.20.0.0/24"], …}
INFO neighbor_scanner neighbor discovery complete
{"neighbors_found": 116, "subnets": 27, "duration": "71.116454ms"}
INFO network_collector scan envelope
{"probes_allowed_since_start": 0, "probes_denied_since_start": 0,
"denied_by_reason_since_start": {}, "hosts_probed_this_cycle": 0,
"host_budget_remaining_this_cycle": 1024}
INFO network_collector collection cycle complete
{"queued_for_delivery": ["network_state", "neighbor_table", "neighbor_discovery"]}
Cuatro ciclos consecutivos, de 116 a 119 vecinos reales en 27 subredes conectadas, y
probes_allowed_since_start se mantuvo en 0 en todos ellos. Al guardián de escaneo nunca
se le consultó, porque no había nada que preguntarle: los vecinos salieron de las tablas del
kernel y los nombres de fabricante de una tabla incorporada. Aun así se encolaron tres
registros de telemetría por ciclo.
Esa es la forma de un primer despliegue: encienda la envolvente, arme solo
neighbor_discovery y averigüe qué hay en el segmento antes de decidir si se justifica
algo más ruidoso.
probes_allowed_since_start y probes_denied_since_start son totales del tiempo de vida del
proceso; hosts_probed_this_cycle y host_budget_remaining_this_cycle se reinician en cada
ciclo. Antes se imprimían juntos bajo un encabezado que afirmaba que los cuatro eran por ciclo,
y en un agente en marcha el recuento de concedidas subió de 686 a 1142 entre ciclos mientras el
recuento de hosts se mantenía en 16 — lo que se lee como un escaneo que se dispara y no era
nada de eso. El guardián no lleva contadores de decisiones por ciclo, así que los totales se
etiquetan como totales en lugar de inventar una cifra por ciclo para la línea de registro.
scanguard: la decisión se toma donde sale el paquete
Cada sonda se autoriza inmediatamente antes de intentar la conexión, no cuando se construye la lista de objetivos.
Una lista de objetivos filtrada es una convención, y las convenciones son lo que las futuras rutas de código sortean sin hacer ruido. Alguien añade una ruta que construye su propia lista, o reutiliza una dirección de una respuesta, y el filtro que se aplicó tres funciones antes no se le aplica. Poner la comprobación donde sale el paquete significa que una ruta nueva o le pregunta al guardián o no compila contra la fontanería del recopilador.
El guardián falla en cerrado en todos los niveles: el guardián sin configurar lo deniega todo; un guardián nulo entregado a un recopilador se sustituye por un «denegar todo» en lugar de saltárselo; una dirección que no se analiza — o se analiza de forma ambigua — se deniega; el agotamiento del presupuesto, del ritmo o del plazo deniega en lugar de degradar.
Cada decisión, permitir o denegar, se registra con un motivo legible por máquina:
| Rechazado | Motivo registrado |
|---|---|
| El escaneo activo está desactivado | scanning_disabled |
| El objetivo no se analiza | unparseable_target |
| Bucle local, enlace local, multidifusión, dirección no especificada, dirección de red o de difusión del rango permitido | reserved_address |
Cualquier cosa fuera de allowed_cidrs | outside_allowed_cidrs |
Espacio de direcciones público sin allow_public_targets | public_target_not_allowed |
Se alcanzó max_hosts_per_cycle | host_budget_exhausted |
Se alcanzó max_cycle_duration | cycle_duration_exceeded |
Una subred más amplia que min_prefix_length, o fuera de alcance | subnet_wider_than_min_prefix, subnet_outside_allowed_cidrs |
| Un grupo de multidifusión que no es un grupo de descubrimiento conocido | not_a_discovery_multicast_group |
| El agente se está deteniendo, o el contexto del ciclo se canceló durante el sondeo | context_cancelled |
Las concesiones también llevan motivo — in_scope_private,
in_scope_public_explicitly_allowed, link_local_discovery_group,
subnet_within_sweep_limits — de modo que el rastro de auditoría diga por qué se permitió una
sonda, y no solo que se permitió.
Los rechazos se sostienen incluso cuando la lista de permitidos coincidiría. Eso lo ejercita
directamente la propia batería de pruebas del guardián, que pasa en el commit distribuido: el
bucle local (v4 y v6), el enlace local (v4 y v6), la multidifusión incluido el grupo SSDP, la
dirección no especificada, la dirección de difusión limitada y las direcciones de red y de
difusión del rango permitido se deniegan todas estando dentro de allowed_cidrs; un host
privado dentro del alcance se concede sin la activación explícita de objetivos públicos; y una dirección IPv6 mapeada
a IPv4 no puede esquivar la lista de permitidos.
0 probes sent, 4094 denied outside_allowed_cidrs no es un mal funcionamiento. Es la prueba
visible para el operador de que es la lista de permitidos la que decide.
Los techos forman parte de la envolvente
| Clave | Por defecto | Límites | Qué contiene |
|---|---|---|---|
min_prefix_length | 22 | 16–32 | La subred más amplia que puede recorrer un barrido |
max_hosts_per_cycle | 1024 | 1–65536 | Hosts distintos sondeados por ciclo, en todas las subredes |
max_concurrency | 16 | 1–256 | Sondas en vuelo a la vez, impuesto por el propio guardián |
per_probe_delay | 50ms | 0–10 s | Separación mínima entre paquetes de sonda — por sonda, así que un escaneo de 19 puertos a un host se ritma 19 veces |
max_cycle_duration | 5m | ≤ 1 h | Techo de tiempo real para el sondeo dentro de un ciclo |
allow_public_targets | false | — | Sondear fuera de RFC1918 / RFC6598 / RFC4193 |
No son sugerencias de ajuste. Un max_concurrency de 10 000 con retardo cero sobre un /22 es
una denegación de servicio contra el propio segmento del cliente, entregada por un binario que
instaló por recomendación nuestra — así que los valores se comprueban por rango en la carga y
se rechazan fuera de él.
Próximos pasos
- Qué sale del host — qué contienen realmente los registros descubiertos, cómo viajan y los límites con los que se distribuye esta versión.
- Visión general de bitscanner — qué produce un ciclo, cuánto cuesta y cómo verificar la descarga.
- Las protecciones de bitenforcer — el mismo instinto de diseño aplicado a otro radio de impacto: medirlo, nombrarlo y rechazar en lugar de adivinar.
- Gestión de activos — donde los dispositivos descubiertos se convierten en activos con propietario.
¿Te resultó útil esta página?