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
218
docs/seguridad.md
Normal file
218
docs/seguridad.md
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
# 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**
|
||||
|
||||
El fallo más serio, y es 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.**
|
||||
No hace falta ningún exploit, solo `adb connect`.
|
||||
|
||||
Y con 45 dispositivos son 45 puertas.
|
||||
|
||||
**Qué se ha corregido hoy.** El compose ataba adb a `0.0.0.0` — todas las interfaces. Ahora hay
|
||||
`redroid_adb_bind_ip`, así que se puede atar solo a la interfaz interna del chasis:
|
||||
|
||||
```yaml
|
||||
blade-02: { ansible_host: 10.0.0.12, redroid_adb_bind_ip: 10.0.0.12 }
|
||||
```
|
||||
|
||||
**Qué falta, y es lo que de verdad 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 de casa. Nunca.
|
||||
|
||||
---
|
||||
|
||||
## 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:<digest>"
|
||||
```
|
||||
|
||||
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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue