REBELION_EN_LA_GRANJA/docs/seguridad.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

218 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 20112016 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.