MQTT-Broker

Wir benötigen einen MQTT-Broker welchen wir per API User, Queues und ACLs füttern können. In diesem Issue geht es um die Auswahl möglicher Broker und kurze Abwägung der Vor-/Nachteile sowie Schätzung wie kompliziert der API-Zugriff ist.

Der Broker muss cluster-fähig sein: eine Instanz lokal auf der Veranstaltung, eine remote im Internet (DoS-Potenzial bzw. unnötigen Traffic über die Internetanbindung der Veranstaltung reduzieren).

Anforderungen:

  • Authentifizierung auf Topic-Ebene (ACL)
    • Custom User per user/PW oder Cert
      • Anlegen/Löschen/sperren von Usern per API/Datei - @runtime
    • Readonly auf Topics
    • WriteOnly (publish)?
      • Für so etwas wie Saal-Pong sicher nicht unwichtig
  • Skalierbar (mind. zwei bridged Cluster (Instanzen) um im Konferenznetz und im Internet anbieten zu können)
  • Übertragung von Text und Binärdaten
  • Rate Limiting (Per user/Topic)
  • Topic-Basiertes Replay von Messages wäre schön

mqtt-structure

Mögliche Kandidaten

Emitter

Einfacher (skaliertbarer) MQTT-Server für Broadcast Messages.

  • Pro
    • MQTT (more or less)
    • Unterstützt Transportverschlüsselung
    • Gut skalierbar
    • Kümmert sich im Zweifel selbst um Zertifikate (Let's Encrypt)
    • Einfaches Erstellen von Channels mit dedizierten Rechten (s.u.)
  • Contra
    • Wird seit drei Jahren nicht mehr aktiv weiter entwickelt
    • Unterstützt nicht das komplette MQTT Protokoll

RabbitMQ mit MQTT Plugin

RabbitMQ ist unbestritten ein sehr erprobtes Tool.
MQTT wird jedoch nur recht halbherzig per Plugin angebunden und die interne Verarbeitung läuft via AMQP. Bedeuted im Zweifel doppelten Konfigurationsaufwand.

auch wird MQTT nicht vollständig implementiert.

Mosquito

Quasi die Referenziplementierung für MQTT und wahrscheonlich auch der am weitesten verbreitete Broker.

Vorteile:

  • Weite Verbreitung
  • Sehr performant
  • Skalierbar (allerdings mit schmerzen)
  • Mehrere Authentifizierungsmethoden
    • Auch dynamische Nutzerverwaltung möglich
  • Dynamische Authentifizierung via DB

Nachteile:

  • Etwas Altbacken
  • Skalierung ist nur kommerziell verfügbar

HiveMQ

Ist ein teilw. kommerzielles Produkt, welches als Community Edition unter Apache 2 Lizenz verfügbar ist.
Leider sind Features, wie Clustering den Bezahl-Versionen (contact sales) vorbehalten.

ejabberd

Bietet seit 2019 auch MQTT-Support.

Vorteile:

  • Ist hoch skalierbar
  • Kann Postgres für die Benutzerverwaltung nutzen

Nachteile:

  • primärer Einsatzzweck ist eigentlich xmpp

Offene Punkte:

  • Kann der Hub die User direkt in der DB anlegen? See SCRAM
    • Ist SCRAM mit SHA512 evt. ein wenig overkill
  • MQTT-Skalierung per mod_mqtt_bridge? See mod_mqtt_bridge
  • Cluster Test. See Clustering Should work for MQTT regarding to this bug

ejabberd Config

DB schema: https://github.com/processone/ejabberd/blob/master/sql/pg.new.sql

Working example

###
###              ejabberd configuration file
###
### The parameters used in this configuration file are explained at
###
###       https://docs.ejabberd.im/admin/configuration
###
###

hosts:
  - localhost

loglevel: debug

## If you already have certificates, list them here
# certfiles:
#  - /etc/letsencrypt/live/domain.tld/fullchain.pem
#  - /etc/letsencrypt/live/domain.tld/privkey.pem

listen:
  -
    port: 5443
    ip: "::"
    module: ejabberd_http
    tls: true
    request_handlers:
      /admin: ejabberd_web_admin
      /api: mod_http_api
      /mqtt: mod_mqtt

  -
    port: 5280
    ip: "::"
    module: ejabberd_http
    request_handlers:
      /admin: ejabberd_web_admin
      /.well-known/acme-challenge: ejabberd_acme
      /mqtt: mod_mqtt
  -
    port: 1883
    ip: "::"
    module: mod_mqtt
    backlog: 1000
  -
    port: 8883
    module: mod_mqtt
    backlog: 1000
    tls: true

s2s_use_starttls: optional

acl:
  local:
    user_regexp: ""
  loopback:
    ip:
      - 127.0.0.0/8
      - ::1/128

access_rules:
  local:
    allow: local
  c2s:
    deny: all
    allow: admin
  announce:
    allow: admin
  configure:
    allow: admin
  muc_create:
    allow: local
  pubsub_createnode:
    allow: local
  trusted_network:
    allow: loopback

api_permissions:
  "console commands":
    from:
      - ejabberd_ctl
    who: all
    what: "*"
  "admin access":
    who:
      access:
        allow:
          - acl: loopback
          - acl: admin
      oauth:
        scope: "ejabberd:admin"
        access:
          allow:
            - acl: loopback
            - acl: admin
    what:
      - "*"
      - "!stop"
      - "!start"
  "public commands":
    who:
      ip: 127.0.0.1/8
    what:
      - status
      - connected_users_number

shaper:
  normal:
    rate: 3000
    burst_size: 20000
  fast: 100000

shaper_rules:
  max_user_sessions: 10
  max_user_offline_messages:
    5000: admin
    100: all
  c2s_shaper:
    none: admin
    normal: all
  s2s_shaper: fast

modules:
  #mod_adhoc: {}
  mod_admin_extra: {}
  mod_fail2ban: {}
  mod_http_api: {}
  # mod_mqtt: {}
  # mod_mqtt_bridge: {} #TODO

auth_password_format: scram
# TODO discuss if sha512 is a bit to much?
auth_scram_hash: sha512
auth_use_cache: true

default_ram_db: mnesia
default_db: sql
sql_type: pgsql
sql_server: localhost
sql_database: ejabberd
sql_username: ejabberd
sql_password: A+27Twp6DTloMzulSw+O2/TYtyS+KJoyS9t8/BlDkho
new_sql_schema: true
auth_method: [sql]
sql_pool_size: 5


include_config_file:
  /opt/homebrew/etc/ejabberd/ejabberd.d/acl.yml:
    disallow: [listen, modules]

### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8

Included ACL File

# ejabberd acl file
append_host_config:
  localhost:
    acl:
      admin:
        user:
          - rehlein@localhost
          - admin@localhost
      # ACL_DEF
      publish_test1:
        user:
          - test1@localhost           
      # /ACL_DEF

    modules:
      mod_mqtt:
        max_topic_depth: 8 # Default is 8
        session_expiry: 1 # Default is 5 minutes
        use_cache: true


        access_publish:
          # ACCESS_RULES
          "test1/#": # resricted to one user
            - allow: publish_test1
            - deny
          # /ACCESS_RULES
          "#":
            - allow: admin
            - deny
        access_subscribe:
          # PUBLISH_RULES
          "test1/#": # resricted to one user
            - allow: publish_test1
            - deny
          "test1/public":
            - allow: all
          # /PUBLISH_RULES
          "#":
            - allow: admin
            - deny

This config is proofed on a apply silicon mac using a native installed ejabberd. Next step is to use a docker image instead.

Edited by Rehlein