REBELION_EN_LA_GRANJA/02_device_lab/ansible/README.md
s1to 74bfbe9437 Rebelión en la granja — investigación y laboratorio de dispositivos
Investigación sobre granjas de dispositivos y automatización completa para
levantar un laboratorio Android sobre un chasis de blades Debian.

Conclusión que ordena el repositorio: el cuello de botella nunca fue el
cómputo sino la identidad. Desde que la atestación de dispositivo pasó de
estadística a criptográfica, un servidor no puede hacerse pasar por un
teléfono — le falta una clave grabada en fábrica que ningún software produce.

Contenido:

- 01_investigacion/ — capa de identidad (verificación telefónica, eSIM,
  reputación de IP, atestación por hardware, correlación entre cuentas),
  mercado y players, stack técnico, marco legal en la UE
- 02_device_lab/ansible/ — Ansible para 16 blades: preflight de solo lectura
  que identifica el hardware por DMI, despliegue de redroid en Docker y
  control de flota por adb. Solo ansible.builtin, sin colecciones externas
- docs/ — requisitos, costes y repaso de seguridad con 11 hallazgos
- 03_charla/ — guion para HackMadrid / Hackmeeting València

Estado: la automatización está escrita pero SIN ejecutar contra hardware.
YAML validado, plantillas renderizadas en ambos modos de red y fleet.sh
comprobado con bash -n. El modelo de blade sigue sin confirmar.
2026-08-08 19:42:14 +02:00

11 KiB

Ansible — provisionar la granja Android sobre los blades Debian

Automatiza el montaje completo: de blades Debian pelados a una flota de Android virtuales controlables por adb desde un único sitio.

No hay móviles físicos en esta granja. Cada "dispositivo" es un contenedor redroid — Android real corriendo sobre el kernel del blade. Es la única forma sensata de hacer esto con el hardware que hay.

La arquitectura

Dell PowerEdge M1000e — 16 blades
│
├── blade-01 ................ COORDINADOR
│    └── adb + fleet.sh          único punto de control de toda la flota
│
└── blade-02 .. blade-16 .... NODOS DE CARGA (15)
     └── Debian 12 pelado
          └── Docker
               ├── redroid-blade-NN-01   Android 13   ~2 GB
               ├── redroid-blade-NN-02   Android 13   ~2 GB
               └── redroid-blade-NN-03   Android 13   ~2 GB

     Con 8 GB por blade → 3 instancias → ~45 Android en total

Debian va directo sobre el blade. Sin Proxmox, sin VMs, sin LXC anidado: con 8 GB por blade, cada capa de hipervisor se come una instancia Android entera.


Requisitos

Nodo de control (tu sobremesa):

sudo apt install ansible

Blades:

  • Debian 12 bookworm instalado
  • Acceso SSH desde el nodo de control, con sudo o como root
  • Salida a internet, o un mirror local de download.docker.com y deb.debian.org
  • Kernel con binder_linux.ko (el estándar de Debian lo trae)

Ejecuta siempre desde este directorio (ansible/) — ansible.cfg y las rutas de roles son relativas a él.


Puesta en marcha

1. Inventario

cp inventory/hosts.yml.example inventory/hosts.yml
$EDITOR inventory/hosts.yml       # pon las IPs reales de los blades

2. Preflight — esto va primero, siempre

ansible-playbook playbooks/preflight.yml

Es de solo lectura, no toca nada. Contesta lo que ahora mismo no sabemos:

  • Qué blade hay dentro del chasis (lo lee de DMI — no hace falta ir a mirar el frontal)
  • Si esas CPUs tienen las instrucciones que Android x86_64 necesita
  • Si el kernel trae binder
  • Cuántas instancias caben en cada blade según su RAM real

Deja un informe en reports/preflight-<fecha>.md con la tabla de toda la flota y el total de capacidad. Si algún blade sale NO APTO, lo dice ahí y no cuando estés a medio despliegue.

3. Montar la granja

ansible-playbook site.yml

Idempotente: relanzable sin romper nada.

4. Comprobar

ansible-playbook playbooks/status.yml

5. Instalar un APK en toda la flota

ansible-playbook playbooks/apk.yml -e apk_local_path=~/COFRE/CODERS/OASIS_APK/oasis.apk

Control diario desde el coordinador

Entra por SSH al coordinador y usa fleet:

fleet connect               # conecta adb a toda la flota
fleet list                  # qué hay conectado
fleet install app.apk       # instala en todos
fleet shell 'getprop ro.build.version.release'
fleet ip                    # IP de cada Android — útil para el caso de dos APKs
fleet reboot

Los dos modos de red

Se controla con redroid_network_mode en group_vars/all.yml.

portmap (por defecto)

Cada Android detrás de NAT de Docker; adb en <ip_blade>:<puerto>, empezando en 5555. Funciona en cualquier sitio sin saber nada de la LAN. Es el modo con el que empezar.

macvlan — el que quieres para oasis_mobile

Cada Android coge una IP real de la LAN a través del switch FlexIO del chasis. Los dos APKs que se tienen que ver entre sí se ven como pares de red de verdad, no a través de traducciones. Es lo más cerca de "dos móviles en la misma wifi" sin tener dos móviles.

redroid_network_mode: macvlan
redroid_macvlan_parent: eno1
redroid_macvlan_subnet: 10.0.0.0/24
redroid_macvlan_gateway: 10.0.0.1
redroid_macvlan_ip_prefix: "10.0.0."

Y en el inventario, un redroid_macvlan_ip_offset por blade (el ejemplo ya trae 20, 30, 40…).

El gotcha de macvlan. Un anfitrión no puede hablar con sus propios contenedores macvlan. Es una limitación del driver, no un fallo de configuración. Por eso el coordinador es un blade distinto: desde fuera sí se llega. Si algún día pones adb en un nodo de carga y no ve sus propios Android, es esto.

Reserva también un rango de IPs para los Android en el DHCP de la LAN, o acabarás con colisiones.


El binder, que es donde esto se atasca

Debian 12 compila binder como módulo pero sin binderfs, así que redroid no puede crear sus devices solo. El rol binder lo resuelve: carga binder_linux con los tres nombres que Android espera (binder, hwbinder, vndbinder) y lo deja persistente para los siguientes arranques.

Los contenedores de un blade comparten ese binder del kernel anfitrión. Si no está, no arranca ni una instancia.

Si el playbook falla con "faltan devices binder": el módulo estaba ya cargado y en uso, así que no se pudo recargar con los parámetros nuevos. La configuración persistente ya quedó escrita — reinicia ese blade y relanza. Es un fallo de una sola vez.


Ajustes que vas a querer tocar

Todo en group_vars/all.yml.

Variable Por defecto Para qué
redroid_image redroid/redroid:13.0.0-latest Versión de Android. Hay 11, 12, 13 y 14
redroid_instances 0 (auto) Instancias por blade. 0 = calculado desde la RAM real
redroid_mem_mb 2048 RAM por Android
redroid_host_reserve_mb 1500 RAM que se le deja a Debian + Docker
redroid_gpu_mode guest Sin GPU en los blades → render por software
redroid_network_mode portmap portmap o macvlan

Cambiar la versión de Android en toda la flota, sin editar nada:

ansible-playbook playbooks/devices.yml -e redroid_image=redroid/redroid:14.0.0-latest

La palanca real: la RAM

El cuello de botella son los 8 GB por blade, no la CPU. Si los blades resultan ser M620 (DDR3 ECC), ampliar a 32 GB cuesta poco en segunda mano:

ansible-playbook playbooks/devices.yml -e redroid_instances=14

De ~45 Android a ~210, por menos de lo que cuesta un mes de luz del chasis.


Playbooks

Playbook Qué hace Toca algo
playbooks/preflight.yml Reconocimiento: modelo, CPU, RAM, binder, capacidad No
site.yml La granja entera de cero
playbooks/provision.yml Solo el hierro: base + binder + Docker
playbooks/devices.yml Solo los contenedores Android
playbooks/coordinator.yml Regenera el listado de flota y fleet
playbooks/apk.yml Instala un APK en toda la flota
playbooks/status.yml Estado real de la granja No
playbooks/destroy.yml Para y borra los contenedores Sí, destructivo

destroy.yml exige -e confirmar=si, y conserva los /data salvo que añadas -e borrar_datos=si.


Probarlo antes de tocar el chasis

inventory/hosts.yml.example trae un bloque comentado de piloto de un nodo: el sobremesa hace de coordinador y de nodo de carga a la vez. Descoméntalo, comenta el bloque granja, y tienes el flujo entero contra una sola máquina. Con 62 GB de RAM salen unas 8 instancias (tope redroid_max_instances).

Es la forma sensata de empezar: valida todo el pipeline sin encender 2 kW.


Estructura

ansible/
├── ansible.cfg
├── site.yml                          playbook maestro
├── inventory/hosts.yml.example       16 blades + piloto de un nodo
├── group_vars/all.yml                los mandos del operador
├── playbooks/
│   ├── preflight.yml                 reconocimiento (solo lectura)
│   ├── provision.yml                 base + binder + docker
│   ├── devices.yml                   contenedores Android
│   ├── coordinator.yml               listado de flota + fleet
│   ├── apk.yml                       instalar APK en la flota
│   ├── status.yml                    estado (solo lectura)
│   ├── destroy.yml                   desmontar (destructivo)
│   └── templates/preflight-report.md.j2
├── roles/
│   ├── preflight/                    DMI, flags de CPU, binder, capacidad
│   ├── base/                         paquetes, hora
│   ├── binder/                       los tres devices, persistentes
│   ├── docker/                       Docker CE + compose v2
│   ├── redroid/                      genera el compose y levanta Android
│   └── coordinator/                  adb + devices.txt + fleet.sh
└── reports/                          informes de preflight (se genera solo)

Estado de verificación

Escrito el 2026-08-08. No se ha ejecutado contra hardware real — el chasis todavía no está accesible y el sobremesa no tiene Docker instalado.

Lo que sí está comprobado:

  • Los 18 ficheros YAML parsean correctamente
  • La plantilla de docker-compose renderiza YAML válido en los dos modos de red, con los puertos y las IPs bien asignados
  • fleet.sh pasa bash -n una vez renderizado, sin Jinja pendiente

Lo que no está comprobado y solo dirá la realidad:

  • Que las imágenes x86_64 de Android arranquen en la generación de CPU de estos blades (el riesgo real si resultan ser M610 — preflight lo detecta antes de gastar tiempo)
  • El comportamiento de macvlan sobre los switches FlexIO concretos del chasis
  • Que 3 instancias por blade sean cómodas con 8 GB en carga real, no solo en el papel

Siguiente capa: GADS

GADS da UI web y Appium integrado encima de esto. No está automatizado aquí a propósito: montar un despliegue de Node que no puedo probar sería añadir complejidad sin verificar. Merece la pena cuando la flota pase de una decena de dispositivos o cuando haga falta lanzar tests automatizados en vez de trastear con fleet shell.


Seguridad — lee esto antes de encender

El repaso completo está en ../../docs/seguridad.md (11 hallazgos). Lo que no se puede saltar:

adb no tiene autenticación, las imágenes de redroid permiten adb root, y los contenedores son privileged porque redroid lo exige. Encadenado: quien alcance el puerto 5555 de un blade acaba siendo root en ese blade, sin exploit ninguno. Con 45 dispositivos son 45 puertas.

Mitigación parcial ya en el código — ata adb solo a la interfaz interna del chasis:

blade-02: { ansible_host: 10.0.0.12, redroid_adb_bind_ip: 10.0.0.12 }

Lo que lo cierra de verdad es una VLAN aislada para la granja. En modo macvlan, redroid_adb_bind_ip no aplica (adb escucha en la IP de LAN del propio Android) y la VLAN es la única defensa.

Y antes de nada: cambiar las credenciales de fábrica del CMC y de los 16 iDRAC (root/calvin).