MQTT IoT Broker Guard (mqtt)
The MQTT Broker Guard plugin inspects Message Queuing Telemetry Transport protocols (MQTT 3.1.1 and MQTT 5.0). It inspects initial client CONNECT packets, validates Client Identifiers against prefix rules and maximum length constraints, blocks scanner botnets (e.g., Shodan and automated IoT probes), and shields brokers from connection storms.
Capabilities & Threat Defense
| Threat / Attack Vector | Defense Mechanism | Action Taken |
|---|---|---|
| IoT Scanner & Botnet Probing | Filters Client Identifier against blocked_client_id_prefixes | Connection terminated immediately with CONNACK refusal |
| Malformed Client ID Exploits | Enforces max_client_id_len length caps; sends MQTT CONNACK 0x02 (Identifier Rejected) before closing connection (since v3.4.0) | Client receives spec-compliant rejection; compliant clients will not retry indefinitely |
| Broker Connection Exhaustion | Enforces connection velocity limits and burst caps | Connection throttled at the proxy layer |
Installation
Install the plugin via CLI or declaratively:
# Install using short nametcp-warden plugins install mqtt# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/mqttConfiguration Reference
Add an MQTT guard service to tcp-warden.yaml:
services: mqtt_broker_guard: listen: ":1883" upstream: "127.0.0.1:18830" protocol: "mqtt" rate_limit: connections_per_minute: 60 burst: 15 plugin_config: blocked_client_id_prefixes: - "bot-" - "scanner-" - "shodan" - "censys" max_client_id_len: 64Configuration Options
| Field | Type | Default | Description |
|---|---|---|---|
listen | string | ":1883" | Local proxy listen address and port. |
upstream | string | "127.0.0.1:18830" | Target MQTT broker (Mosquitto, EMQX, HiveMQ) address. |
protocol | string | "mqtt" | Must be set to "mqtt". |
plugin_config.blocked_client_id_prefixes | []string | [...] | List of client ID prefixes blocked on connection attempt. |
plugin_config.max_client_id_len | int | 64 | Maximum allowable length for the MQTT Client Identifier. When exceeded, a well-formed MQTT CONNACK response with return code 0x02 (Identifier Rejected) is sent before the connection is closed, per MQTT 3.1.1 specification (since v3.4.0). |
Network & Deployment Architecture
Because MQTT operates on an unprivileged port (1883), packet redirection rules (nftables/iptables) are not required. Deploy RouteWarden using either direct reverse proxying or container network isolation:
Option A: Direct Reverse Proxy (Bare-Metal / VM)
Bind RouteWarden to :1883, with Mosquitto listening on loopback on an alternate port or private subnet IP:
# tcp-warden.yaml — RouteWarden binds to standard MQTT port 1883services: mqtt_broker_guard: listen: ":1883" upstream: "127.0.0.1:18830" # Or private backend "10.0.0.40:1883" protocol: "mqtt"Option B: Docker Compose Bridge Network
In containerized stacks, publish port 1883 on RouteWarden while keeping Mosquitto private to the internal Docker network:
services: routewarden: image: ghcr.io/routewarden/tcp-warden:latest ports: - "1883:1883" - "9091:9091" volumes: - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro depends_on: - mosquitto mosquitto: image: eclipse-mosquitto:2 # Do NOT publish host ports; keep internal to bridge networkTesting & Verification
Verify legitimate connections and test blocked scanner client prefixes:
# Connect legitimate MQTT client through RouteWardenmosquitto_sub -h 127.0.0.1 -p 1883 -i "device-001" -t "sensors/#" -v