Especificaciones tomadas del manual del propietario del gabinete PowerEdge M1000e (Dell, modelo BMX01, rev. A06, oct. 2019), que corrige el spec sheet comercial usado hasta ahora. Hardware: - Peso máximo 200,5 kg, no 179. Profundidad 75,5 cm - Seis fuentes y nueve ventiladores obligatorios, se usen 3 blades o 16. Toda bahía vacía necesita relleno - Conector CEI/IEC C20 a 16 A. La fuente de 3.000 W exige 200-240 V - Irrupción de 55 A por fuente durante 10 ms: un magnetotérmico de curva C salta, hace falta curva D - Temperatura de funcionamiento continuo 10-35 °C - ECM obligatorio con blades M630 de 120 W o más, o por encima de 30 °C Red: - Fabric A (ranuras A1/A2) es la Ethernet integrada de cada blade, así que no hacen falta tarjetas intermedias. Fabrics B y C sí las exigirían y no se usan - Pendiente saber si en A1/A2 hay un switch o un módulo de paso a través: cambia el montaje de red por completo Costes recalculados: el suelo fijo del chasis sube de 350 a ~500 W al contar los nueve ventiladores, los módulos de I/O y las pérdidas de las seis fuentes. 360 €/mes a 24/7, 59 €/mes por ventanas. Que las fuentes y los ventiladores sean obligatorios convierte en hecho documentado lo que antes era estimación: los pilotos parciales en el chasis son el peor caso posible. Documentación: - assets/ con cuatro diagramas SVG propios: arquitectura, chasis, stack de software y modos de red - docs/usabilidad.md: órdenes, flujos de trabajo, problemas conocidos y ergonomía del hardware - Diagramas ASCII del panel frontal, el posterior y la topología de fabrics en hardware_m1000e.md - Eliminada la parte de charla y divulgación, fuera del alcance del repo
216 lines
8.6 KiB
Markdown
216 lines
8.6 KiB
Markdown
# 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:<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.
|