M-Bus Gateway
← Tilbage til blog
· 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.

Se MQTT QoS guide eller IoT netværkssikkerhed guide.