· Mosquitto· MQTT· IoT· TLS· ACL· broker· gateway· sikkerhed· Docker
Mosquitto MQTT broker — TLS, ACL og persistens til IoT gateway
Mosquitto MQTT broker konfiguration til IoT: TLS 1.3 med klientcertifikater, per-gateway ACL, persistent sessions, last will og bridge til cloud.
Af M-Bus Gateway
M-Bus Gateway platformen bruger Mosquitto som MQTT broker på Hetzner. Her er produktionskonfigurationen med TLS, per-gateway ACL og persistens.
Mosquitto i Docker Compose
# docker-compose.yml — Mosquitto service
services:
mosquitto:
image: eclipse-mosquitto:2.0
restart: always
ports:
- "8883:8883" # TLS (ekstern — gateways)
- "1883:1883" # Plain (intern Docker-netværk — server)
volumes:
- ./mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
- ./mosquitto/certs:/mosquitto/certs:ro
- ./mosquitto/acl:/mosquitto/acl:ro
- mosquitto-data:/mosquitto/data
- mosquitto-log:/mosquitto/log
networks:
- internal
volumes:
mosquitto-data:
mosquitto-log:
mosquitto.conf: Fuld produktionskonfiguration
# /mosquitto/config/mosquitto.conf
# ─── Plain listener (kun intern Docker-trafik) ───────────────────────
listener 1883 mosquitto
allow_anonymous true
# Kun tilgængeligt fra Docker-netværket — ikke eksponeret eksternt
# ─── TLS listener (gateway-klienter) ─────────────────────────────────
listener 8883
allow_anonymous false
# TLS 1.3 certifikater:
cafile /mosquitto/certs/ca.crt
certfile /mosquitto/certs/server.crt
keyfile /mosquitto/certs/server.key
tls_version tlsv1.3
require_certificate true # Klientcertifikat PÅKRÆVET
use_identity_as_username true # CN fra certifikat = username
# ─── Adgangskontrol ───────────────────────────────────────────────────
acl_file /mosquitto/acl/acl.conf
# ─── Persistens ──────────────────────────────────────────────────────
persistence true
persistence_location /mosquitto/data/
autosave_interval 60 # Gem til disk hvert 60 sek
# ─── Logging ─────────────────────────────────────────────────────────
log_dest file /mosquitto/log/mosquitto.log
log_type error
log_type warning
log_type notice
log_timestamp true
# ─── Performance ─────────────────────────────────────────────────────
max_inflight_messages 20
message_size_limit 65536 # 64KB max payload (gateways sender ~25KB)
max_keepalive 300 # 5 min keepalive maksimum
ACL: Per-gateway isolation
# /mosquitto/acl/acl.conf
# Certifikat CN = gateway ID (fx "GW-0001")
# %u = authenticated username (= CN fra certifikat)
# Server-konto — kan læse alle gateways:
user server-backend
topic readwrite meters/#
topic readwrite $SYS/#
# Per-gateway isolation:
# Hvert gateway kan KUN publish/subscribe på sit eget prefix
pattern readwrite meters/%u/#
# Eksempel:
# Gateway GW-0001 (CN=GW-0001) kan:
# publish: meters/GW-0001/data ✅
# subscribe: meters/GW-0001/cmd ✅
# publish: meters/GW-0002/data ✗ (afvises)
# subscribe: meters/# ✗ (afvises)
Klientcertifikater: Provision-script
#!/bin/bash
# scripts/provision-gateway.sh — Generér certifikat til nyt gateway
GATEWAY_ID=${1:?"Usage: $0 <GATEWAY_ID>"}
CERTS_DIR="mosquitto/certs/gateways"
CA_KEY="mosquitto/certs/ca.key"
CA_CRT="mosquitto/certs/ca.crt"
mkdir -p "$CERTS_DIR/$GATEWAY_ID"
# Generér gateway privat nøgle:
openssl genrsa -out "$CERTS_DIR/$GATEWAY_ID/client.key" 4096
# Certificate Signing Request (CN = GATEWAY_ID):
openssl req -new \
-key "$CERTS_DIR/$GATEWAY_ID/client.key" \
-out "$CERTS_DIR/$GATEWAY_ID/client.csr" \
-subj "/CN=$GATEWAY_ID/O=MBusGateway"
# Signer med CA (gyldig 10 år — IoT SIM-livstid):
openssl x509 -req \
-in "$CERTS_DIR/$GATEWAY_ID/client.csr" \
-CA "$CA_CRT" \
-CAkey "$CA_KEY" \
-CAcreateserial \
-out "$CERTS_DIR/$GATEWAY_ID/client.crt" \
-days 3650 \
-sha256
echo "Certifikat genereret: $CERTS_DIR/$GATEWAY_ID/"
echo "Installer på gateway: /etc/mbus-gateway/mqtt/"
paho-mqtt klient fra gateway (Python)
# gateway/src/mqtt/client.py
import paho.mqtt.client as mqtt
import ssl
def create_mqtt_client(config: dict) -> mqtt.Client:
"""
Opret MQTT-klient med TLS og klientcertifikat.
Last will registreres ved connect — sendes ved uventet disconnect.
"""
client = mqtt.Client(
client_id=config["GATEWAY_ID"],
protocol=mqtt.MQTTv5,
clean_session=False, # Persistent session — modtag QoS1 offline
)
# TLS med klientcertifikat:
client.tls_set(
ca_certs="/etc/mbus-gateway/mqtt/ca.crt",
certfile="/etc/mbus-gateway/mqtt/client.crt",
keyfile="/etc/mbus-gateway/mqtt/client.key",
tls_version=ssl.PROTOCOL_TLS_CLIENT,
)
# Last will: Besked sendes automatisk ved uventet disconnect:
gateway_id = config["GATEWAY_ID"]
client.will_set(
topic=f"meters/{gateway_id}/status",
payload='{"online": false, "reason": "unexpected_disconnect"}',
qos=1,
retain=True, # Retain = seneste offline-status gemmes i broker
)
return client
Retain + Last Will: Persistent status
Retain flag i MQTT:
→ Broker gemmer seneste besked på topic
→ Nye subscribers modtager øjeblikkeligt seneste status (ingen ventetid)
→ Bruges til gateway-status: "Er denne gateway online?"
Last Will:
→ Registreres ved connect (ikke publish)
→ Broker sender automatisk ved TCP-timeout / uventet disconnect
→ Kombineret med retain: Online-status altid tilgængelig
Flow:
1. Gateway connect → registrerer last will (online=false, retain)
2. Gateway publish → "online=true" (retain=true)
3. Broker gemmer "online=true" → nye subscribers ser dette straks
4. Gateway disconnects uventet → broker sender last will "online=false"
5. Server subscriber modtager alarm → trigger heartbeat-missing alert
Konklusion
Mosquitto med TLS 1.3 + klientcertifikater giver stærk mutual authentication — gateways kan ikke forfalske hinandens identitet. ACL med %u-pattern isolerer gateways fra hinanden. Persistent sessions + retain + last will sikrer at offline-gateways detekteres automatisk og at ny server-instance straks ser korrekt status.