Para asegurar una Raspberry Pi IoT tienes que cerrar las puertas fáciles que buscan los atacantes: inicios de sesión con contraseña, puertos abiertos, paquetes desactualizados, servicios que se ejecutan como root y tráfico sin cifrar. Esta checklist recorre cada capa con los comandos exactos para Raspberry Pi OS Bookworm y termina con una conexión MQTT cifrada con TLS que no necesita ningún puerto entrante. Complétala una vez y después incorpora el resultado a tu imagen para que cada nuevo dispositivo arranque ya bastionado.
Modelo de amenazas para una Pi en campo
Un gateway Pi es un pequeño servidor Linux situado en un lugar que no controlas por completo: la red de un cliente, una planta de producción, un armario junto a la carretera. Las amenazas realistas no son exóticas. Los escáneres automatizados prueban contraseñas por defecto y débiles por SSH. Los servicios sin parchear se explotan mediante vulnerabilidades conocidas. Una aplicación comprometida que se ejecuta como root toma el control de todo el dispositivo. Las credenciales olvidadas en un script acaban en un repositorio Git o en un ticket de soporte. El tráfico MQTT sin cifrar se lee o se modifica en una red compartida. Alguien con acceso físico extrae la tarjeta SD y la lee.
Cada punto de la checklist siguiente apunta a una de esas amenazas. Ninguno requiere hardware especial.
La checklist de un vistazo
| # | Área | Acción | Protege contra |
|---|---|---|---|
| 1 | Cuentas | Usuario único, contraseña robusta, sin imágenes compartidas con credenciales conocidas | Ataques con credenciales por defecto |
| 2 | SSH | Solo claves, sin login de root, restricción opcional de puerto | Fuerza bruta de contraseñas |
| 3 | Firewall | Denegar entrantes por defecto, permitir SSH solo desde redes de gestión | Servicios expuestos |
| 4 | Actualizaciones | Actualizaciones de seguridad desatendidas, actualizaciones completas periódicas | Vulnerabilidades conocidas |
| 5 | Servicios | Desactivar lo que no uses | Superficie de ataque innecesaria |
| 6 | Mínimo privilegio | Usuario de servicio dedicado, sandboxing con systemd | Escalada de privilegios |
| 7 | Secretos | Credenciales en un archivo de entorno propiedad de root, no en el código | Fugas de credenciales |
| 8 | MQTT | TLS en el puerto 8883, credenciales únicas por dispositivo | Escuchas, suplantación |
| 9 | Monitorización | Inicios de sesión, autenticaciones fallidas, estado por MQTT | Compromisos no detectados |
| 10 | Física | Carcasa, interfaces no usadas desactivadas, consideraciones de almacenamiento | Manipulación |
1. Cuentas
Las imágenes actuales de Raspberry Pi OS ya no incluyen el antiguo usuario pi con su contraseña por defecto; el usuario se crea en Raspberry Pi Imager o en el primer arranque. Elige un nombre de usuario poco obvio y una contraseña larga, y nunca distribuyas una imagen maestra en la que todos los dispositivos compartan las mismas credenciales. Si creas imágenes para una flota, genera credenciales por dispositivo en el momento del aprovisionamiento.
# comprobar qué cuentas pueden iniciar sesión
grep -vE "nologin|false" /etc/passwd
# bloquear la contraseña de cualquier cuenta que solo deba usar claves
sudo passwd -l someuser
2. SSH solo con claves
Copia tu clave pública a la Pi, confirma que puedes iniciar sesión con ella y después desactiva la autenticación por contraseña con un archivo drop-in, para que las actualizaciones de paquetes no sobrescriban tu cambio.
# en tu estación de trabajo
ssh-keygen -t ed25519 -C "admin@workstation"
ssh-copy-id admin@gateway-01.local
# en la Pi
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf > /dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
MaxAuthTries 3
EOF
sudo sshd -t && sudo systemctl reload ssh
Mantén abierta tu sesión actual y prueba un nuevo inicio de sesión en una segunda terminal antes de cerrarla. Si no necesitas SSH en absoluto en los dispositivos desplegados, desactívalo con sudo systemctl disable --now ssh y usa un procedimiento de mantenimiento que lo habilite temporalmente.
3. Firewall
Un gateway MQTT solo establece conexiones salientes, así que apenas necesita reglas de entrada. ufw mantiene la configuración legible:
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 192.168.10.0/24 to any port 22 proto tcp # solo la subred de gestión
sudo ufw limit 22/tcp
sudo ufw enable
sudo ufw status verbose
Sustituye la subred por tu red de gestión. En entornos más estrictos, deniega también el tráfico saliente y permite solo DNS, NTP, los repositorios de paquetes y tu broker MQTT en el puerto 8883. Añade fail2ban si SSH debe ser accesible desde redes más amplias.
4. Actualizaciones
La mayoría de los compromisos reales explotan vulnerabilidades que ya tienen corrección. Activa las actualizaciones de seguridad automáticas y programa actualizaciones completas periódicas:
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
sudo unattended-upgrade --dry-run --debug | tail -n 20
Incluye tus dependencias de Python en la rutina. Fija las versiones en un requirements.txt, prueba las actualizaciones primero en un dispositivo y después despliégalas en la flota.
5. Desactivar los servicios que no se usan
Cada servicio en ejecución es algo que parchear y algo que atacar. Lista lo que está habilitado y apaga lo que tu gateway no necesita:
systemctl list-unit-files --state=enabled --type=service
sudo systemctl disable --now avahi-daemon cups 2>/dev/null
sudo systemctl disable --now bluetooth # solo si no usas BLE
En dispositivos sin pantalla también puedes desactivar el Wi-Fi o el Bluetooth a nivel de hardware en config.txt con dtoverlay=disable-wifi o dtoverlay=disable-bt cuando el dispositivo solo usa Ethernet.
6. Mínimo privilegio para tu aplicación
Ejecuta el cliente MQTT como un usuario de sistema dedicado, sin shell de inicio de sesión y solo con los grupos que necesite (por ejemplo gpio o dialout). Después deja que systemd lo aísle en un sandbox:
sudo useradd --system --home /opt/gateway --shell /usr/sbin/nologin gateway
sudo usermod -aG gpio gateway
[Service]
User=gateway
Group=gateway
EnvironmentFile=/etc/gateway/mqtt.env
ExecStart=/opt/gateway/.venv/bin/python -u /opt/gateway/gateway.py
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/gateway
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
ProtectSystem=strict deja todo el sistema de archivos en solo lectura para el servicio, salvo las rutas que indiques, de modo que un script comprometido no puede modificar archivos del sistema ni instalar persistencia. Ejecuta systemd-analyze security gateway.service para ver qué otras opciones son aplicables. La unidad completa, incluidas las políticas de reinicio, está en ejecutar un script Python MQTT al arrancar.
7. Mantener los secretos fuera del código
Guarda las credenciales del broker en un archivo de entorno legible solo por root (systemd lo lee antes de reducir privilegios):
sudo mkdir -p /etc/gateway
sudo tee /etc/gateway/mqtt.env > /dev/null <<'EOF'
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
EOF
sudo chmod 600 /etc/gateway/mqtt.env
Mantén el archivo fuera del control de versiones y de cualquier imagen que compartas. Si un dispositivo se pierde o se retira, revoca sus credenciales en el lado del broker.
8. Asegurar la conexión MQTT
MQTT sin cifrar en el puerto 1883 envía las credenciales y los datos en texto claro. Usa TLS en el puerto 8883. Los brokers de ConnectMQ presentan un certificado de Let's Encrypt, en el que la Pi ya confía a través de su paquete de CA del sistema, así que no hace falta ningún archivo de certificado:
import os
import paho.mqtt.client as mqtt
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=os.environ["MQTT_CLIENT_ID"])
client.username_pw_set(os.environ["MQTT_USERNAME"], os.environ["MQTT_PASSWORD"])
client.tls_set() # verifica el certificado del servidor y el nombre de host
client.connect(os.environ["MQTT_HOST"], int(os.environ["MQTT_PORT"]), keepalive=60)
client.loop_forever()
Nunca llames a tls_insecure_set(True) en producción; desactiva la comprobación del nombre de host y anula el propósito. Asegúrate de que el reloj es correcto, porque la validación del certificado falla si la Pi arranca con una fecha errónea y NTP aún no ha sincronizado. Usa credenciales únicas para cada dispositivo: en ConnectMQ, los client IDs están vinculados a las credenciales emitidas, y los inicios de sesión fallidos repetidos desde una dirección provocan el bloqueo de la IP. La guía de MQTT con TLS en Python trata las opciones de certificados con más profundidad, y las buenas prácticas de seguridad MQTT abordan el lado del broker. Como el dispositivo solo se conecta hacia fuera, el firewall del paso 3 permanece cerrado.
Recuerda que el servidor MQTT público gratuito es legible por cualquiera. Es solo para pruebas; los dispositivos de producción deben estar en un broker privado.
9. Monitorización
Un dispositivo bastionado sigue necesitando vigilancia. Revisa journalctl -u ssh en busca de inicios de sesión inesperados, publica eventos de estado y seguridad (intentos de SSH fallidos, reinicios de servicio, reinicios inesperados) en un topic MQTT y genera alertas ante anomalías. Un topic de estado con Last Will retained te avisa de inmediato cuando un dispositivo se desconecta, lo que puede ser la primera señal de manipulación.
10. Seguridad física
Si alguien puede extraer el almacenamiento, puede leer todo lo que contiene, incluidas las credenciales. Usa una carcasa con cerradura, considera arrancar desde el almacenamiento interno en dispositivos basados en Compute Module, desactiva USB y HDMI no utilizados cuando tu hardware lo permita y trata cualquier dispositivo que haya estado fuera de tu control como no fiable hasta volver a grabar su imagen. La documentación de Raspberry Pi explica las opciones de configuración relevantes.
Estos pasos combinan bien con los consejos de fiabilidad para Raspberry Pi y, juntos, forman la base de cualquier gateway construido a partir del tutorial de Raspberry Pi con MQTT.
Conéctate de forma segura con ConnectMQ
Un dispositivo bastionado merece un broker pensado para producción. ConnectMQ es un broker MQTT gestionado con TLS en el 8883, WebSocket seguro en el 8084, credenciales por dispositivo y protección contra inundaciones. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y conecta tu primera Pi bastionada por TLS en minutos.
Preguntas frecuentes
¿Necesito abrir algún puerto para MQTT en la Raspberry Pi?
No. Un cliente MQTT se conecta hacia fuera al broker, así que la Pi no necesita puertos entrantes para MQTT. Permite SSH entrante solo desde redes de gestión de confianza, si es que lo permites.
¿Es aceptable alguna vez el puerto 1883?
Solo para pruebas locales con datos no sensibles. Todo lo que atraviese una red no fiable debería usar TLS en el puerto 8883 para que las credenciales y los payloads viajen cifrados.
¿Debe tener cada dispositivo sus propias credenciales MQTT?
Sí. Las credenciales únicas te permiten revocar un solo dispositivo perdido o comprometido sin tocar el resto de la flota, y hacen que los logs tengan sentido.
¿Ayuda el cifrado de disco en una Raspberry Pi?
Protege los datos si roban el almacenamiento, pero entonces el dispositivo necesita una forma de desbloquearse al arrancar sin una persona presente, algo difícil de hacer de forma segura en una Pi. Para la mayoría de los gateways resulta más práctico minimizar los secretos almacenados y usar credenciales revocables.



