Hace unas semanas comentando con colegas sobre mi artículo del censo de nodos que había publicado, llegamos a la conclusión que está contado como una aburrida bitácora y se echa de menos la historia tras el trabajo, y tenían razón. Voy a intenterar contar la historia que hay detrás con todo lo que me ha ido pasando. Los números, siguen estando en la bitácora y no los voy a repetir mucho aquí.

¿Por qué resucitar bitnodes.io?

Llevo tiempo interesado en el OSINT aplicado a la infraestructura de Bitcoin: no solo en la cadena, que ya la mira todo el mundo, también en las máquinas que la sostienen, dónde están, quién las aloja y de qué dependen. De ese interés salió en junio el análisis de la centralización de Nostr detrás de Cloudflare y, desde principios de este, un proyecto que llamo Pesquisa Audit, una especie de Shodan del ecosistema Bitcoin: escanear, catalogar y auditar nodos, incluidos los que viven en Tor o i2p. Un escáner así tiene un problema previo que no es nada glamuroso: necesita una lista fiable y fresca de nodos alcanzables sobre la que trabajar. Y la fuente de esa lista, para todo el mundo y durante mucho tiempo, había sido bitnodes.io.

Bitnodes.io fue el trabajo de Addy Yeow entre 2013 y 2024: el payload de snapshots, la distribución geográfica, las banderas de los seeders DNS. Media comunidad tiraba de su API sin pensarlo. En algún momento dejó de mantenerse y los endpoints empezaron a fallar. Lo que quedaba eran mapas bonitos, como los de bitref o newhedge, que al final se alimentan de datos de otros, y soluciones improvisadas: en julio llegué a escanear con Shodan doce mil IPs exportadas de mi instancia de peer-observer para rellenar el catálogo de Pesquisa Audit, con un 7% de acierto.

El código de bitnodes.io, en cambio, estaba publicado: el repositorio ayeowch/bitnodes, abandonado pero completo. El crawler, la parte que descubre nodos y hace el handshake, seguía siendo la pieza correcta. Así que hice un fork, y lo primero que hice en él fue arreglar un bug que excluía todas las direcciones cuando la lista de ASNs a incluir estaba vacía, que es como viene por defecto. Ese mismo día monté encima un dashboard muy sencillo y una API compatible con la v1 de bitnodes.io, para que cualquier herramienta que hablara con el original pudiera seguir hablando con esto sin cambiar una línea.

Con el tiempo el proyecto ha ido cogiendo tres sentidos más: es algo que puedo enseñar, es una herramienta para mi trabajo de seguridad, y tiene una API que pueden consultar agentes. Lo que nunca ha pretendido es competir con bitnod.es ni con otras alternativas actuales. Lo que yo quería era tener el aparato de medida en mis manos y saber qué está midiendo exactamente, que es algo que con el tracker de otro no se sabe nunca del todo. Lo que no sabía en ese momento es cuánto me iba a costar entender mi propio aparato.

Dos meses creyéndome un número

Lo levanté sobre una máquina de 2 vCPU. El demonio de Tor se puso al 100% de CPU el primer día y de ahí no bajó, así que fui recortando lo que pude: menos workers, muestreo de direcciones onion al 25%. El contador se estabilizó en unos 1.400 nodos y, sinceramente, me pareció realmente poco.

Tras pasar a una máquina de 8 vCPU, el contador se triplicó hasta los 4.170 nodos y ahí se quedó clavado durante dos meses. Lo miraba casi a diario. Un número que no se mueve en dos meses no parece un fallo, parece una propiedad de la red, y nada en los datos indicaba lo contrario. Tampoco es que yo lo buscara con mucho empeño, todo hay que decirlo.

Lo que acabó rompiendo la ilusión fue mirar la composición del snapshot, que es precisamente lo primero que tendría que haber mirado. De aquellos 4.170 nodos, solamente doce eran onion. El otro tracker publicaba unos 20.000 y la diferencia era casi toda .onion. El muestreo al 25% y el único Tor eran el parche que había puesto en mayo para la máquina pequeña, y había cambiado de máquina sin acordarme de que ese parche existía. Es decir, el techo me lo había puesto yo mismo en mayo y en julio ya no sabía que estaba ahí.

Puse seis demonios de Tor y el muestreo al 100%. Las direcciones onion pasaron de 12 a 226 en dos horas, y el total siguió en 4.170. Por cada onion que entraba, salía un IPv4. Estuve un buen rato sin entenderlo y luego resultó evidente: un snapshot no puede contener más nodos que sockets abiertos a la vez, y los sockets abiertos son los procesos de ping multiplicados por los workers de cada uno. Siete por seiscientos, 4.200. Llevaba dos meses publicando el tamaño de mi pool de sockets bajo el título de “nodos alcanzables”. Tras subir a 24.000 slots, en cinco horas tenía 11.600 nodos y, por primera vez, todo el IPv4 que hay en la red.

I2P no estaba vacío, estaba mal leído

Añadí I2P porque casi nadie lo tenía en cuenta y porque un cliente SAM se escribe en una tarde. Dieciséis horas después de activarlo seguía con cero nodos, y lo primero que pensé fue que I2P era sencillamente pequeño. Después miré la cola de direcciones pendientes y no había ni una .b32.i2p: los peers de clearnet prácticamente no te pasan direcciones de I2P, de ahí que sembrara 512 destinos de las semillas de Bitcoin Core. La cola subió a 3.900 direcciones y los nodos alcanzables siguieron en cero.

El error era mío. El serializador de direcciones decidía que una dirección era IPv4 si contenía puntos, y una .b32.i2p contiene puntos. Cada handshake reventaba justo después de abrir el stream, y visto desde fuera eso es indistinguible de “no hay nodos I2P”. Una vez corregido, aparecieron 4.700.

El día de los 22.000, y el número equivocado

Aquel día empezó mal. Tras un reinicio, onion no subía: 17, 21, 0, con los demonios de Tor casi parados. En los logs aparecían 247.000 circuitos caducados frente a 212 completados. Un crawler concentra todas sus peticiones de circuito en uno o dos guards, y eso es justo el patrón que la defensa anti-DoS de Tor estrangula. Como yo no necesito anonimato, la solución era una línea de torrc, UseEntryGuards 0. Trece horas después tenía 10.800 conexiones onion abiertas simultáneamente y 22.100 nodos en total; en la ventana de ocho días, 27.000, por encima de bitnod.es. Me puse francamente muy contento, era el número que iba buscando hace tiempo.

No obstante, ese mismo día me di cuenta de que llevaba semanas detrás del número equivocado. Las conexiones Tor son transitorias, así que un snapshot instantáneo siempre va a infra-contar onion, y de hecho nadie publica onion instantáneo: lo que publica todo el mundo es una unión de n días en ventana.

Yo había estado optimizando una cifra que solo me importaba a mí (nodos alcanzables en un instante) y comparándola con otra distinta (unión de los nodos alcanzados en algún momento desde hace 8 días, por ejemplo).

¿Y quién paga todo esto?

El 1 de agosto me llegó la factura en forma de alarma. El proveedor de nube detectó un 2.307% más de transferencia de la esperada, unos 330 GB al día de forma constante, y lo interpretó como lo interpretaría cualquiera: una máquina comprometida haciendo de proxy. Me la pusieron en cuarentena. Dashboard caído y SSH bloqueado durante media hora, hasta que conseguí revertirlo. El malware era mi crawler.

Medí a dónde iba ese tráfico y más del 99% era mantenimiento de las redes de overlay: construcción de túneles I2P, lookups de leasesets y, en el lado de Tor, unas mil conexiones TLS nuevas por minuto, que es el precio directo de haber quitado los guards. El protocolo Bitcoin propiamente dicho era el 0,1%. A 0,09 dólares el gigabyte, salían unos 660 dólares al mes solamente de transferencia, por mirar dos redes de anonimato desde fuera. Decidí irme ese mismo día del proveedor y paré el crawler.

Lo que más me cuesta contar

Desde julio el dashboard mostraba una estimación de “nodos únicos” que ponderaba cada dirección por el número de redes que el nodo anunciaba, con la idea de no contar dos veces una misma máquina presente en IPv4 y en onion. En agosto la retiré, y el endpoint devuelve ahora un 410 con la explicación.

Estaba mal por dos sitios. La entrada no eran las redes del propio nodo, sino las de su libreta de direcciones, así que un nodo que solo estaba en clearnet pero conocía direcciones onion contaba como un tercio de máquina. Y aunque hubiera corregido eso, el problema de fondo no tiene solución: vincular la IPv4 de un nodo con su .onion es exactamente lo que Tor está diseñado para impedir. Por tanto, no existe un recuento deduplicado honesto, ni en mi dashboard ni en ningún otro. Lo publiqué durante semanas con aspecto de número normal. A día de hoy cada cifra del dashboard lleva al lado cómo se calcula, y la que no lo pueda llevar no se publica.

Lo que me enseñó b10c sin pretenderlo

En el mismo mensaje, b10c me sugería subir MaxCircuitDirtiness en Tor para que los circuitos sobrevivieran a la revisita y bajara el churn de handshakes. Hice una prueba A/B de 38 horas con medio pool tratado y medio de control, y el resultado no fue el que esperábamos ninguno de los dos. El mecanismo funciona, un 63% menos de conexiones nuevas por hora en el brazo tratado, pero ese brazo transportó un 58% menos de conexiones sostenidas y, por unidad de trabajo, salió al doble de caro. Retener circuitos durante una hora atasca el caudal. Queda por probar el hueco entre 35 y 40 minutos, justo por encima de mi ciclo de revisita. A modo de confesión, ha sido lo que más he disfrutado de todo el proyecto, seguramente porque la pregunta no era mía.

Y entonces me lo llevé al officelab

El 20 de septiembre lo moví todo a una máquina virtual en el Proxmox del officelab de HackNodes, que sale a internet por una línea de datos estándar de Digi de 1 Gbit, con un túnel de Cloudflare delante. La mudanza en sí fue bien. El primer ciclo del crawler allí, no tanto: 627 nodos IPv4, el 10% de lo que encontraba en la nube, con IPv6 igual que antes. El router de recorta la tasa de conexiones nuevas IPv4 de la máquina virtual y, además, le impone una especie de tope de sesiones, de forma que el ping solo consigue mantener unas 640 abiertas. Tras hacer varias pruebas, coonfirmado que el router decide que la máquina virtual se comporta de forma sospechosa.

De ahí que el número público de mi observatorio sea hoy 2.700. Llevo meses sosteniendo que el recuento de un tracker mide la capacidad del tracker y no a la red, y acabo de demostrármelo a mí mismo de la manera menos cómoda posible.

¿Y ahora qué? Dos caminos

Tengo dos opciones encima de la mesa.

La primera es quitar ese router de en medio. Esto tiene un coste pero creo que es asumible, un nuevo punto de acceso. Diría que es el camino que más ilusión me hace, porque la idea desde el principio era precisamente esa: la soberanía.

La segunda es llevarlo a un dedicado de Hetzner, o algo parecido, y olvidarme del “egress” y de mantener ciertas coas. El tráfico que en el hyperscaler costaba 660 dólares al mes, allí va incluido, no hay router que me recorte sesiones, y tendría la máquina a su aire. Ganaría tranquilidad y poder dedicar tiempo a otras cosas.

Creo que me inclino por una tercera posibilidad, que es un poco de las dos: dejar clearnet en el officelab, que es el 0,1% del tráfico y cabe de sobra, y encender Tor e I2P solo donde el “egress” no se cobra. En cualquiera de los tres casos lo que cambia es dónde está el techo, no que lo haya.

Para concluir

No me gustaría que quien lea esto se quede con la impresión de que esto ha sido una sucesión de errores sin más, aunque errores ha habido unos cuantos.

  1. No me creo ningún recuento de nodos que no venga con su metodología explicada, y el primero del que desconfío es el mío.
  2. Dudaba y, he confirmado que cuando has lanzado un crawler y encuentra más de 20000 nodos no son nodos vivos. Realmente lo que suelen hacer es unir el conjunto de nodos detectados en los últimos n días (bitnodes.io recuerdo que usaba los últimos 8 días).

Cuando dos trackers discrepan, la pregunta interesante no es quién tiene razón, sino qué cuello de botella no ha encontrado todavía cada uno de ellos. Las cifras de IPv4 se parecen entre trackers porque IPv4 es barato de enumerar; las de onion e I2P no se parecen porque dependen de la capacidad de circuitos, de los guards, de las semillas y, como he comprobado, de si uno puede pagar la factura de su proveedor.

Y, personalmente, el proyecto me ha dado lo que buscaba, que no era un número grande, sino saber qué hace el aparato cuando da un número. Los repositorios son alt-bitnodes y el fork del crawler, ambos bajo licencia MIT y con los snapshots en crudo publicados. Si alguien quiere discutir alguna cifra, ahí tiene con qué hacerlo, y lo agradeceré.


alt-bitnodes es un proyecto open source independiente mantenido por el autor. HackNodes Lab es su consultora de seguridad y con el apoyo de Libreria de Satoshi; el observatorio no es un producto comercial.