# Repaso de seguridad Revisión del montaje completo — arquitectura, hardware y el código Ansible de [`02_device_lab/ansible/`](../02_device_lab/ansible/). Fecha: 2026-08-08. Incluye los fallos que introduce el propio código de este proyecto, no solo los del entorno. ## Resumen | | Hallazgo | Severidad | Estado | |---|---|---|---| | **S1** | adb sin autenticación + contenedor privilegiado → root del anfitrión | **Crítico** | Mitigado a medias | | **S2** | Credenciales por defecto del CMC / iDRAC (`root`/`calvin`) | **Alto** | Sin verificar | | **S3** | macvlan mete 45 Android en la LAN de producción | **Alto** | Por diseño, hay que aislar | | **S4** | `host_key_checking = False` en `ansible.cfg` | Medio | Aceptado, documentado | | **S5** | SSH como root en el inventario de ejemplo | Medio | Documentado | | **S6** | Imagen de Android por tag mutable | Medio | Abierto | | **S7** | Sin gestión de secretos | Medio | Abierto | | **S8** | Estado persistente en `/data` sin cifrar | Medio | Abierto | | **S9** | `fleet install` no verifica el APK | Bajo | Abierto | | **S10** | Microcódigo antiguo en CPUs de 2011-2016 | Bajo | Aceptado | | **S11** | Infraestructura de doble uso | Contextual | Ver [legal_ue.md](../01_investigacion/legal_ue.md) | --- ## S1 · adb sin autenticación sobre contenedor privilegiado — **crítico** Una cadena de tres eslabones que por separado parecen asumibles: 1. **adb no tiene autenticación** cuando escucha en TCP. Quien llega al puerto, entra. 2. **Las imágenes de redroid son `userdebug`**, así que `adb root` funciona: shell de root dentro del Android. 3. **El contenedor es `privileged: true`** — redroid lo exige para acceder a binder. Un privilegiado ve los devices del anfitrión y puede montar su sistema de ficheros. Encadenado: cualquiera que alcance el puerto 5555 de un blade acaba siendo root en ese blade, sin exploit, solo con `adb connect`. Con 45 dispositivos son 45 puertas. Corregido en parte: el compose ataba adb a `0.0.0.0`. Ahora hay `redroid_adb_bind_ip`, que permite atarlo 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 cierra el agujero:** - **Red de laboratorio aislada.** VLAN propia para la granja, sin ruta hacia la LAN de casa ni hacia internet salvo lo imprescindible. Es la medida que importa; el resto son parches. - **Cortafuegos en cada blade** (nftables) que solo acepte 5555+ desde la IP del coordinador. - **Nunca exponer estos puertos a internet.** Ni con reenvío de puertos, ni con VPN mal segmentada. En modo `macvlan` el problema es peor: adb queda escuchando en la IP de LAN del propio Android, donde `redroid_adb_bind_ip` no aplica. Ahí la única defensa es la VLAN. --- ## S2 · Credenciales por defecto del CMC y de los iDRAC — **alto** Los Dell PowerEdge salen de fábrica con **`root` / `calvin`** en iDRAC, y el CMC del M1000e con las mismas. Es la forma clásica en que se toma uno de estos chasis: no hay que romper nada, se entra. El M1000e es de 2016 o anterior, así que el firmware de gestión arrastra años de CVEs conocidas y TLS antiguo. Antes de enchufarlo a nada: - Cambiar las credenciales del CMC **y de cada uno de los 16 iDRAC**, no solo del CMC. - Actualizar firmware de CMC e iDRAC hasta donde Dell lo permita para esa generación. - **VLAN de gestión separada**, sin salida a internet. La gestión de un chasis nunca va en la misma red que el resto. - Desactivar IPMI sobre LAN si no se usa (`cipher 0` es un clásico de acceso sin autenticar). Esto es independiente de la granja: aplica al hierro desde el momento en que se encienda. --- ## S3 · macvlan expone 45 Android en la LAN — **alto** El modo `macvlan` es el que hace falta para el caso de los dos APKs de `oasis_mobile`, pero pone cada Android **con IP real en la red**, cada uno con S1 encima. - VLAN dedicada para la granja, con el coordinador como único puente. - Reservar el rango de IPs en el DHCP, o habrá colisiones con máquinas reales. - No usar la LAN doméstica. --- ## S4 · `host_key_checking = False` — medio Lo puse yo en `ansible.cfg` por comodidad de arranque. El coste: si alguien se hace pasar por un blade, Ansible se conecta igual y le entrega **comandos de root y, si se usa, la contraseña de sudo**. Es asumible en el primer aprovisionamiento de una red aislada. Después: ```bash ssh-keyscan -H 10.0.0.11 10.0.0.12 ... >> ~/.ssh/known_hosts ``` y poner `host_key_checking = True`. --- ## S5 · SSH como root — medio `inventory/hosts.yml.example` trae `ansible_user: root`. Funciona, pero: - Usuario dedicado con sudo en vez de root directo - **Solo clave, sin contraseña**: `PasswordAuthentication no` - `PermitRootLogin no` en los blades - Clave separada para la granja, no la personal --- ## S6 · Imagen de Android por tag mutable — medio `redroid/redroid:13.0.0-latest` es un tag que **se mueve**. Consecuencias reales: - Dos blades provisionados con una semana de diferencia acaban con imágenes distintas, y depurar eso es un infierno. - Cadena de suministro: si la cuenta del registro se compromete, el siguiente `docker compose pull` se trae otra cosa. Y entra en 45 contenedores privilegiados. Fijar por digest: ```yaml redroid_image: "redroid/redroid@sha256:" ``` Con `docker buildx imagetools inspect redroid/redroid:13.0.0-latest` se saca el digest actual. Se actualiza a mano cuando se quiera subir de versión, que es justo lo que se quiere. --- ## S7 · Sin gestión de secretos — medio El inventario de ejemplo menciona `ansible_become_password` sin montar `ansible-vault`. Si se pone una contraseña ahí en claro, queda en el disco y en cualquier copia del proyecto. ```bash ansible-vault create group_vars/granja/vault.yml ansible-playbook site.yml --ask-vault-pass ``` Mejor aún: sudo sin contraseña para el usuario de despliegue, restringido a los comandos que necesita, y ninguna contraseña en ningún fichero. --- ## S8 · Estado persistente sin cifrar — medio Cada Android guarda su `/data` en `{{ redroid_base_dir }}/data/device-NN` del blade, sin cifrar. Si en las pruebas se entra con cuentas reales — cuentas SSB, credenciales de Gitea, lo que sea — esas credenciales quedan en el disco del blade en claro. `destroy.yml` **conserva los datos por defecto**, a propósito, así que sobreviven a un redespliegue y a un cambio de manos del hardware. - Cuentas de prueba desechables, nunca las reales. - Cifrado de disco en los blades si van a manejar algo sensible. - Al dar de baja un blade: `destroy.yml -e confirmar=si -e borrar_datos=si` **y** borrado del disco. --- ## S9 · `fleet install` no verifica nada — bajo `fleet install app.apk` mete el APK en los 45 dispositivos sin comprobar firma ni hash. Como los APK son propios (`oasis_mobile`), el riesgo es bajo, pero el patrón es malo: un fichero equivocado se propaga a toda la flota de una vez. Comprobar el hash antes, y a ser posible la firma con `apksigner verify --print-certs`. --- ## S10 · Microcódigo antiguo — bajo CPUs de 2011–2016 con microcódigo sin actualizar: las mitigaciones de Spectre/Meltdown dependen de microcódigo + kernel. En un laboratorio aislado que solo corre código propio, el riesgo es bajo. Subiría si algún día se corre software de terceros no confiable en los Android. `apt install intel-microcode` en los blades, y aun así esa generación no recibe todo. --- ## S11 · Infraestructura de doble uso — contextual Técnicamente, esto es indistinguible de una granja de cuentas: 45 Android automatizados, con control central y despliegue masivo de APKs. Lo que separa un laboratorio de QA de una granja de fraude no es la infraestructura, son **tres cosas que no están aquí**: SIMs, proxies residenciales y cuentas de plataforma. Para que siga siendo lo que es: - No conectar la granja a proveedores de SIM ni de números por API. - No meter proxies residenciales o móviles. - Documentar el propósito con evidencia — este repositorio ya lo hace. El contexto legal está en [legal_ue.md](../01_investigacion/legal_ue.md). Y recuerda el hallazgo de la investigación: estos Android **son detectables como emulador**, así que para las plataformas sociales no servirían aunque se quisiera. --- ## Mínimos antes de encender Por orden: 1. Cambiar credenciales del **CMC y de los 16 iDRAC** (S2) 2. **VLAN aislada** para granja y gestión, separadas entre sí (S1, S2, S3) 3. `redroid_adb_bind_ip` a la IP interna de cada blade (S1) 4. Claves SSH, sin root, sin contraseñas (S5) 5. Poblar `known_hosts` y `host_key_checking = True` (S4) 6. Fijar la imagen por digest (S6) Los tres primeros no son opcionales. Sin ellos, la granja es 45 puertas abiertas a la red.