Volver
30 de jun de 2025 · 14 min de lectura

Mosquitto + TLS sobre AWS EC2

Guía paso a paso para desplegar un broker MQTT seguro con cifrado TLS sobre una instancia EC2 de AWS, con Docker: instancia, certificados, ejecución y verificación.

MQTTTLSAWSDockerIoT
facumruiz/mosquitto-tls

Cómo desplegar un broker MQTT (Mosquitto) con TLS habilitado sobre una instancia EC2 de AWS, usando Docker y un repositorio con la configuración y los certificados. Pensada para montar rápido un broker seguro para IoT o mensajería entre dispositivos.

El proceso se divide en dos partes: (1) creación y configuración de una instancia EC2 en AWS, y (2) instalación de Docker, clonado del repositorio y ejecución del broker con TLS. Se suman dos secciones: cómo verificar la conexión TLS y solución de problemas comunes.

Prerrequisitos

Cuenta de AWS con permisos para crear instancias EC2 · conocimientos básicos de línea de comandos y SSH · Git y un cliente SSH en tu máquina local · acceso al repo de configuración (facumruiz/mosquitto-tls).

Costos: una instancia t2.micro o t3.micro entra en la capa gratuita de AWS el primer año (750 hs/mes). Fuera de ese período genera costos — detené o terminá la instancia cuando no la uses.

Parte 1 · Instancia EC2 en AWS

Paso 1 — Iniciar sesión y abrir EC2

Ingresá a la consola de AWS y accedé al servicio EC2 desde el buscador superior o el panel de servicios.

Panel de EC2 en la consola de AWS

Paso 2 — Crear una nueva instancia

Desde el Dashboard de EC2, entrá a «Instancias» → «Lanzar instancia». Poné un nombre descriptivo (por ej. mosquitto-tls), elegí una AMI basada en Linux (Ubuntu o Debian) y dejá el tipo t2.micro / t3.micro para mantenerte en la capa gratuita.

Selección de AMI y tipo de instancia

Paso 3 — Red y seguridad

En Configuraciones de red, el asistente ofrece checkboxes para SSH (22), HTTP y HTTPS (443, opcional). El puerto 8883 (MQTT sobre TLS) no aparece como checkbox: agregalo manualmente como regla personalizada — Tipo: TCP personalizado · Puerto: 8883 · Origen: 0.0.0.0/0 (mejor, tu rango de IPs conocidas).

En producción, evitá dejar el puerto 22 (SSH) abierto a 0.0.0.0/0; restringilo a tu IP pública.
Par de claves y configuración de red/seguridad al lanzar la instancia

Paso 4 — Par de claves (.pem)

En la sección de par de claves, elegí «Crear un nuevo par de claves», tipo RSA y formato .pem (OpenSSH/Linux/macOS) o .ppk (PuTTY en Windows). Guardá el .pem en un lugar seguro: hace falta para el SSH y no se puede volver a descargar.

Sección de par de clavesModal de creación de par de claves

Paso 5 — IP pública de la instancia

La necesitás para el SSH y para el certificado TLS (Parte 2, Paso 3). En EC2 → Instancias, abrí tu instancia y copiá el campo «IPv4 pública» en la pestaña Detalles.

AWS asigna una IP pública dinámica: si parás y reiniciás la instancia, la IP puede cambiar (habría que regenerar el certificado). Para fijarla, asociá una Elastic IP desde EC2 → Direcciones IP elásticas.

Paso 6 — Conectarse por SSH

chmod 400 tu_clave.pem
ssh -i tu_clave.pem <usuario>@<ip-publica-instancia>

Reemplazá <usuario> según la AMI: ubuntu (Ubuntu), admin (Debian) o ec2-user (Amazon Linux), y <ip-publica-instancia> por la IP del Paso 5.

Parte 2 · Docker, Git y el broker

Paso 1 — Instalar Docker y Git

# Actualizar el sistema
sudo apt update

# Instalar Docker y sus herramientas
sudo apt install docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin -y

# Instalar git
sudo apt install git -y

Paso 2 — Clonar el repositorio

git clone https://github.com/facumruiz/mosquitto-tls.git
cd mosquitto-tls
git checkout version/aws

La rama version/aws trae la config lista para EC2: docker-compose.yml (define y expone el broker en 8883), Dockerfile (imagen sobre eclipse-mosquitto:2.0), config/mosquitto.conf (listener TLS 8883), generate-certs.sh (genera la CA y el certificado) y la carpeta certs/ (la crea el script).

Paso 3 — Generar los certificados TLS

Editá el script antes de correrlo: generate-certs.sh trae hardcodeada una IP de ejemplo (3.132.251.70) como CN y SAN. Si no la cambiás por la IP pública real de tu instancia (o tu dominio), los clientes rechazan la conexión por «hostname mismatch».
nano generate-certs.sh

# Reemplazar 3.132.251.70 por tu IP pública en:
#   -subj ".../CN=3.132.251.70"       (CSR del servidor)
#   subjectAltName = IP:3.132.251.70    (extensión SAN)
chmod +x generate-certs.sh
./generate-certs.sh

El script crea certs/ con la autoridad certificadora (ca.key, ca.crt) y el certificado de servidor (server.key, server.crt) firmado con el CN/SAN configurado.

Paso 4 — Levantar el contenedor

sudo docker compose up -d

La primera vez construye la imagen desde el Dockerfile (copiando config y certs) y levanta el contenedor mosquitto_tls en segundo plano.

Paso 5 — Verificar que corre

sudo docker ps

CONTAINER ID   IMAGE           PORTS                    NAMES
abc12345       mosquitto_tls   0.0.0.0:8883->8883/tcp   mosquitto_tls

Paso 6 — Verificar los logs

docker compose logs mosquitto

mosquitto-1  | mosquitto version 2.0.21 starting
mosquitto-1  | Config loaded from /mosquitto/config/mosquitto.conf.
mosquitto-1  | Opening ipv4 listen socket on port 8883.
mosquitto-1  | mosquitto version 2.0.21 running

Parte 3 · Cómo funciona el repositorio

Opcional: no hace falta para levantar el broker, pero ayuda a entender cada archivo y modificarlo con confianza.

docker-compose.yml

version: '3.7'

services:
  mosquitto:
    build: .
    container_name: mosquitto_tls
    ports:
      - "8883:8883"
    volumes:
      - ./config/mosquitto.conf:/mosquitto/config/mosquitto.conf
      - ./certs:/mosquitto/certs
    restart: unless-stopped

build: . construye la imagen desde el Dockerfile local. container_name le da un nombre fijo (mosquitto_tls). ports mapea 8883 del host al 8883 del contenedor. volumes monta la config y los certificados adentro, así editar cualquiera solo requiere reiniciar el contenedor. restart: unless-stopped lo relevanta solo si se cae o si reiniciás la EC2.

Dockerfile

FROM eclipse-mosquitto:2.0

COPY config/mosquitto.conf /mosquitto/config/mosquitto.conf
COPY certs /mosquitto/certs

Parte de la imagen oficial que ya trae el broker y copia la config y los certificados a las rutas donde Mosquitto los espera. Como el compose también los monta como volúmenes, en ejecución mandan los archivos de la instancia; el COPY deja la imagen funcional aunque un día la corras sin volúmenes.

generate-certs.sh

Crea una CA propia y un certificado de servidor firmado por ella. Analogía: la CA emite un «documento de identidad» (ca.crt) en el que los clientes confían; server.crt es el «DNI» del broker, firmado por esa CA. Lo vemos por bloques:

1 · Define los nombres de archivo dentro de certs/ y crea la carpeta.

CERT_DIR="./certs"
CA_KEY="$CERT_DIR/ca.key"
CA_CRT="$CERT_DIR/ca.crt"
SERVER_KEY="$CERT_DIR/server.key"
SERVER_CSR="$CERT_DIR/server.csr"
SERVER_CRT="$CERT_DIR/server.crt"
EXTFILE="$CERT_DIR/server_ext.cnf"

mkdir -p "$CERT_DIR"

2 · Crea la CA: una clave RSA 2048 (la «llave maestra») y con ella un certificado autofirmado válido 365 días. ca.crt es el archivo que se copia a cada cliente.

openssl genrsa -out "$CA_KEY" 2048

openssl req -x509 -new -nodes -key "$CA_KEY" -sha256 -days 365 \
  -subj "/C=AR/ST=BuenosAires/L=CABA/O=FacundoRP/CN=MQTT_CA" \
  -out "$CA_CRT"

3 · Genera la clave privada del servidor (nunca sale de la instancia) y un CSR: una solicitud de certificado con el CN = la IP que los clientes van a validar. Ese CN es el dato a reemplazar por tu IP (Parte 2, Paso 3).

openssl genrsa -out "$SERVER_KEY" 2048

openssl req -new -key "$SERVER_KEY" \
  -subj "/C=AR/ST=BuenosAires/L=CABA/O=FacundoRP/CN=3.132.251.70" \
  -out "$SERVER_CSR"

4 · Agrega el SAN (subjectAltName). Los clientes modernos —incluido mosquitto_sub— validan el SAN además del CN; sin él, el certificado sería rechazado.

cat > "$EXTFILE" <<EOF
subjectAltName = IP:3.132.251.70
EOF

5 · La CA firma el CSR del servidor incluyendo el SAN. El resultado, server.crt, es el certificado final que Mosquitto presenta a cada cliente.

openssl x509 -req -in "$SERVER_CSR" -CA "$CA_CRT" -CAkey "$CA_KEY" \
  -CAcreateserial -out "$SERVER_CRT" -days 365 -sha256 \
  -extfile "$EXTFILE"

6 · Borra el CSR y el archivo de extensión temporales: ya quedaron «incorporados» dentro de server.crt.

rm -f "$SERVER_CSR" "$EXTFILE"
Al final quedan cuatro archivos en certs/: ca.key y ca.crt (tu CA) y server.key y server.crt (el certificado del broker). El compose monta la carpeta y mosquitto.conf indica cuáles usar para el listener TLS del 8883.

Parte 4 · Verificar la conexión TLS

Opción A — desde la misma instancia

# Instalar el cliente
sudo apt install mosquitto-clients -y

# Suscribirse a un tópico
mosquitto_sub -h localhost -p 8883 \
  --cafile ./certs/ca.crt -t "test/topic" -d

# Publicar (en otra terminal)
mosquitto_pub -h localhost -p 8883 \
  --cafile ./certs/ca.crt -t "test/topic" -m "¡Hola TLS!"

Corré ambos parado en la carpeta del repo (donde está certs/). Si el mensaje aparece en la terminal del mosquitto_sub, el broker funciona con TLS.

Opción B — desde tu computadora

scp -i tu_clave.pem admin@<ip-publica-instancia>:~/mosquitto-tls/certs/ca.crt .

mosquitto_sub -h <ip-publica-instancia> -p 8883 \
  --cafile ca.crt -t "test/topic" -d

Solo funciona si el certificado se generó con el CN/SAN igual a esa IP pública (Parte 2, Paso 3) y si el 8883 está abierto en el grupo de seguridad (Parte 1, Paso 3).

Buenas prácticas de seguridad

Restringí SSH (22) y, si podés, 8883 a IPs conocidas · mantené el SO y Docker actualizados (sudo apt update && sudo apt upgrade) · sumá autenticación por usuario/contraseña o certificados de cliente además de TLS · rotá los certificados antes de que expiren · no guardes el .pem en repos ni carpetas compartidas.

Solución de problemas

No puedo conectarme por SSH

El .pem debe tener permisos 400 (chmod 400 tu_clave.pem) · el grupo de seguridad debe permitir el 22 desde tu IP · usá el usuario correcto según la AMI (ubuntu, admin, ec2-user).

No puedo conectarme al broker (8883)

Confirmá la regla de firewall TCP 8883 (Parte 1, Paso 3) · revisá docker compose logs mosquitto por errores de arranque · verificá que el contenedor corre con docker ps.

Error de certificado (hostname mismatch)

Es el error más común: el CN/SAN no coincide con la IP a la que te conectás porque no se editó generate-certs.sh (Parte 2, Paso 3). Regenerá los certificados con la IP correcta y reiniciá: docker compose up -d --build. Como último recurso solo para pruebas, existe --insecure en mosquitto_sub/pub — nunca en producción.

Volver