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.
This commit is contained in:
commit
74bfbe9437
42 changed files with 2989 additions and 0 deletions
302
02_device_lab/ansible/README.md
Normal file
302
02_device_lab/ansible/README.md
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
# 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](https://github.com/remote-android/redroid-doc) — 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):
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml
|
||||
```
|
||||
|
||||
Idempotente: relanzable sin romper nada.
|
||||
|
||||
### 4. Comprobar
|
||||
|
||||
```bash
|
||||
ansible-playbook playbooks/status.yml
|
||||
```
|
||||
|
||||
### 5. Instalar un APK en toda la flota
|
||||
|
||||
```bash
|
||||
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`:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```yaml
|
||||
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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```bash
|
||||
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 | Sí |
|
||||
| `playbooks/provision.yml` | Solo el hierro: base + binder + Docker | Sí |
|
||||
| `playbooks/devices.yml` | Solo los contenedores Android | Sí |
|
||||
| `playbooks/coordinator.yml` | Regenera el listado de flota y `fleet` | Sí |
|
||||
| `playbooks/apk.yml` | Instala un APK en toda la flota | Sí |
|
||||
| `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](https://github.com/shamanec/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](../../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:
|
||||
|
||||
```yaml
|
||||
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`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue