Privacy Tools Guide

Secure MQTT Broker Setup for IoT

MQTT is the messaging backbone of most smart home and IoT systems. Left unsecured, a public-facing MQTT broker leaks device state, sensor readings, and control commands to anyone who can connect. This guide sets up a hardened Mosquitto broker with TLS encryption, mutual certificate authentication, and topic-level access controls.

Why Default MQTT Setups Are Dangerous

The default Mosquitto configuration listens on port 1883 with no authentication and no encryption. Anyone on the network — or the internet if port-forwarded — can:

This is not hypothetical. Shodan regularly indexes thousands of open MQTT brokers. Many broadcast door lock states, motion sensor triggers, and energy usage in real time.

Prerequisites

Step 1 — Install Mosquitto

sudo apt update && sudo apt install -y mosquitto mosquitto-clients

# Stop the default instance — we'll configure before starting
sudo systemctl stop mosquitto

Step 2 — Generate a Certificate Authority

You’ll create your own CA to sign broker and client certificates. This enables mutual TLS — both sides prove their identity.

# Create directory structure
sudo mkdir -p /etc/mosquitto/certs
cd /etc/mosquitto/certs

# Generate CA key and certificate
sudo openssl genrsa -out ca.key 4096
sudo openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
  -subj "/CN=IoT-CA/O=HomeNetwork/C=US"

Step 3 — Generate the Broker Certificate

# Broker private key
sudo openssl genrsa -out broker.key 4096

# Certificate signing request
sudo openssl req -new -key broker.key -out broker.csr \
  -subj "/CN=mqtt.local/O=HomeNetwork/C=US"

# Sign the broker cert with your CA
sudo openssl x509 -req -days 825 -in broker.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out broker.crt

# Set strict permissions
sudo chmod 600 /etc/mosquitto/certs/*.key
sudo chown mosquitto:mosquitto /etc/mosquitto/certs/*

Step 4 — Generate Client Certificates

Each IoT device gets its own certificate. This example creates one for a temperature sensor:

# Create a cert for each device
DEVICE="temp-sensor-01"

sudo openssl genrsa -out ${DEVICE}.key 4096
sudo openssl req -new -key ${DEVICE}.key -out ${DEVICE}.csr \
  -subj "/CN=${DEVICE}/O=HomeNetwork/C=US"
sudo openssl x509 -req -days 825 -in ${DEVICE}.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out ${DEVICE}.crt

# Copy key and cert to the device (via SCP or similar)

Repeat for each device: thermostat-01, door-sensor-01, hub-01, etc.

Step 5 — Configure Mosquitto

Replace /etc/mosquitto/mosquitto.conf with a hardened configuration:

# /etc/mosquitto/mosquitto.conf

# Disable unencrypted listener
# listener 1883   <-- this line should NOT exist

# TLS listener only
listener 8883
protocol mqtt

# TLS certificates
cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/broker.crt
keyfile /etc/mosquitto/certs/broker.key

# Require client certificates — no anonymous connections
require_certificate true
use_identity_as_username true

# ACL file for topic permissions
acl_file /etc/mosquitto/acl.conf

# Password file (used alongside cert auth for belt-and-suspenders)
password_file /etc/mosquitto/passwd

# Disable anonymous access
allow_anonymous false

# Persistence
persistence true
persistence_location /var/lib/mosquitto/

# Logging
log_dest file /var/log/mosquitto/mosquitto.log
log_type all

# Connection limits
max_connections 100
max_inflight_messages 20

Step 6 — Configure Topic ACLs

The ACL file controls which clients can publish or subscribe to which topics:

# /etc/mosquitto/acl.conf

# temp-sensor-01 can only publish to its own topic
user temp-sensor-01
topic write home/sensors/temperature/01

# thermostat-01 can read temperature and write its own state
user thermostat-01
topic read home/sensors/temperature/#
topic write home/hvac/thermostat/01

# door-sensor-01 can only publish to door events
user door-sensor-01
topic write home/security/doors/01

# hub-01 has read access to everything
user hub-01
topic read #

# No user should write to system topics
# (implicit — no rules grant write to $SYS/#)

Topic ACLs follow the principle of least privilege: each device only touches what it needs.

Step 7 — Create Password File (Belt-and-Suspenders)

Even with certificate auth, add a password as a second factor:

# Create password file with first user
sudo mosquitto_passwd -c /etc/mosquitto/passwd temp-sensor-01

# Add more users
sudo mosquitto_passwd /etc/mosquitto/passwd thermostat-01
sudo mosquitto_passwd /etc/mosquitto/passwd door-sensor-01
sudo mosquitto_passwd /etc/mosquitto/passwd hub-01

# Permissions
sudo chmod 640 /etc/mosquitto/passwd
sudo chown mosquitto:mosquitto /etc/mosquitto/passwd

Step 8 — Start and Test

sudo systemctl enable --now mosquitto
sudo systemctl status mosquitto

# Test with mosquitto_sub using client cert
mosquitto_sub \
  --cafile /etc/mosquitto/certs/ca.crt \
  --cert /etc/mosquitto/certs/hub-01.crt \
  --key /etc/mosquitto/certs/hub-01.key \
  -h mqtt.local -p 8883 \
  -u hub-01 -P yourpassword \
  -t "home/#" -v

# Publish a test message from another terminal
mosquitto_pub \
  --cafile /etc/mosquitto/certs/ca.crt \
  --cert /etc/mosquitto/certs/temp-sensor-01.crt \
  --key /etc/mosquitto/certs/temp-sensor-01.key \
  -h mqtt.local -p 8883 \
  -u temp-sensor-01 -P yourpassword \
  -t "home/sensors/temperature/01" \
  -m '{"temp": 21.5, "unit": "C"}'

Step 9 — Firewall Rules

Block all MQTT traffic except from trusted devices:

# Allow port 8883 only from LAN
sudo ufw allow from 192.168.1.0/24 to any port 8883
sudo ufw deny 8883

# Block legacy unencrypted port entirely
sudo ufw deny 1883

Certificate Rotation

Client certificates expire. Automate rotation with a script:

#!/bin/bash
# Renew a device certificate
DEVICE=$1
DAYS=825
CA_DIR=/etc/mosquitto/certs

openssl genrsa -out ${CA_DIR}/${DEVICE}.key 4096
openssl req -new -key ${CA_DIR}/${DEVICE}.key \
  -out ${CA_DIR}/${DEVICE}.csr \
  -subj "/CN=${DEVICE}/O=HomeNetwork/C=US"
openssl x509 -req -days ${DAYS} \
  -in ${CA_DIR}/${DEVICE}.csr \
  -CA ${CA_DIR}/ca.crt \
  -CAkey ${CA_DIR}/ca.key \
  -CAcreateserial \
  -out ${CA_DIR}/${DEVICE}.crt

echo "Certificate renewed for ${DEVICE}. Deploy ${DEVICE}.key and ${DEVICE}.crt to device."

Built by theluckystrike — More at zovo.one