· Tomasz Siroń · Bezpieczeństwo

MQTT hardening: QoS, bezpieczeństwo, federacja tożsamości i architektura multi-tenant

Spis treści

  1. QoS 1, QoS 2, persistent sessions i mechanizm LWT
  2. Autoryzacja ACL i zasada najmniejszych uprawnień
  3. Federacja tożsamości: integracja MQTT z Microsoft Entra ID
  4. MQTT over WebSocket: architektura front-endowa i eliminacja restrykcji firewalli
  5. Architektura tematów: multi-tenancy i izolacja danych
  6. FAQ

MQTT hardening: QoS, bezpieczeństwo, federacja tożsamości i architektura multi-tenant

Artykuł jest skierowany do architektów systemów, dyrektorów IT i inżynierów DevOps, którzy wdrażają lub utrzymują brokerów MQTT w środowiskach produkcyjnych. Pomijamy tu podstawy protokołu i decyzje biznesowe dotyczące wyboru platformy.

Jeśli szukasz analizy kosztowej TCO i decyzji architektonicznej na poziomie zarządczym, przeczytaj pierwszy artykuł z tej serii:

MQTT w produkcji: TCO, architektura i decyzja o HA przy skalowaniu IoT

Poniżej skupiamy się na warstwach decydujących o niezawodności i bezpieczeństwie systemu MQTT w warunkach produkcyjnych: jakości dostarczania wiadomości, autoryzacji opartej na ACL, federacji tożsamości z Microsoft Entra ID, tunelowaniu przez WebSocket i izolacji danych w architekturze wielo-tenant.


QoS 1, QoS 2, persistent sessions i mechanizm LWT

MQTT definiuje trzy poziomy gwarancji dostarczenia wiadomości. Wybór między nimi to decyzja techniczna z bezpośrednim wpływem na zachowanie systemu przy zawodnych łączach, a także na zużycie zasobów po stronie brokera.

QoS 0: at most once

Wiadomość jest wysyłana raz, bez potwierdzenia. Jeśli broker lub sieć nie odpowiadają w momencie wysyłki, wiadomość przepada. Odpowiedni dla danych, których utrata jest akceptowalna: np. odczyty temperatury wysyłane co sekundę, gdzie starszy odczyt i tak straci aktualność przed następną transmisją.

QoS 1: at least once

Broker potwierdza odebranie wiadomości pakietem PUBACK. Jeśli potwierdzenie nie dotrze do wydawcy w czasie keepAlive, wiadomość jest wysyłana ponownie z flagą DUP. Konsekwencja: ta sama wiadomość może dotrzeć do subskrybenta więcej niż raz. Subskrybent musi być idempotentny lub filtrować duplikaty po messageId.

QoS 1 jest właściwym wyborem dla większości scenariuszy IIoT: dane procesowe, alarmy, zdalne sterowanie pompami i zaworami. Koszt retransmisji jest niski, a gwarancja dostarczenia jest realna bez nadmiernego narzutu na brokera.

QoS 2: exactly once

Protokół używa czterostronnego uzgodnienia: PUBLISH, PUBREC, PUBREL, PUBCOMP. Wiadomość dotrze dokładnie raz, bez duplikatów. Jest to wariant najbardziej wymagający zasobowo: cztery pakiety zamiast dwóch, wyższe opóźnienie, większy narzut pamięciowy po stronie brokera dla każdej sesji w locie.

QoS 2 jest uzasadniony przy zdalnym sterowaniu urządzeniami, gdy wykonanie komendy dwa razy ma niepożądany skutek fizyczny — np. dwukrotne otwarcie zaworu ciśnieniowego, zdalne uruchomienie procedury awaryjnej lub przesłanie zlecenia do systemu MES, które nie powinno zostać zduplikowane.

Persistent sessions

Gdy klient MQTT łączy się z flagą cleanSession=false, broker przechowuje jego stan przez czas określony w konfiguracji: listę subskrypcji i kolejkę wiadomości QoS 1/2, które nadeszły podczas przerwy połączenia. Po powrocie klienta broker doręcza zakolejkowane wiadomości w kolejności ich nadejścia.

W środowiskach z niestabilnymi łączami — sieci komórkowe LTE, WiFi przemysłowy ze zmiennym zasięgiem — persistent sessions eliminują utratę danych bez konieczności buforowania po stronie urządzenia. Wymagają jednak monitorowania rozmiaru kolejki: nieaktywny klient z rosnącą kolejką zużywa pamięć RAM brokera proporcjonalnie do czasu nieobecności i częstotliwości wiadomości.

Last Will and Testament (LWT)

LWT to mechanizm, w którym klient deklaruje przy połączeniu, co broker ma opublikować w przypadku nagłego zerwania połączenia (tzn. bez przesłania pakietu DISCONNECT). Konfiguruje się go przez pola will_topic, will_payload, will_qos i will_retain w pakiecie CONNECT.

Typowe zastosowanie w IIoT:

# Klient łączy się i deklaruje LWT:
topic:   fabryka1/linia3/robot-spawalniczy-01/status
payload: {"status": "offline", "timestamp": "2026-05-27T08:14:00Z"}
retain:  true
qos:     1

Gdy robot straci łączność bez czystego rozłączenia, broker automatycznie publikuje status offline na wskazanym temacie. System SCADA lub dashboard natychmiast otrzymuje informację o utracie urządzenia — bez potrzeby odpytywania (polling) ani timeout'u na poziomie aplikacji.

LWT w połączeniu z retained messages tworzy mechanizm wykrywania stanu urządzenia dostępny dla każdego nowego subskrybenta: ostatni znany stan jest zawsze dostępny, a przerwa w połączeniu jest wykrywana w czasie zbliżonym do wartości parametru keep-alive.


Autoryzacja ACL i zasada najmniejszych uprawnień

Uwierzytelnianie potwierdza tożsamość klienta. Autoryzacja określa, co ten klient może robić po nawiązaniu połączenia. W produkcyjnym brokerze MQTT każdy klient powinien mieć zdefiniowane reguły dostępu (Access Control List, ACL) na poziomie tematów i dozwolonych operacji.

Zasada najmniejszych uprawnień w praktyce

Każde urządzenie i każda aplikacja kliencka otrzymuje uprawnienia tylko do tematów niezbędnych do jej funkcji. Żadnych uprawnień na wyrost, żadnych wildcardów # dla urządzeń polowych.

Przykładowy podział ról:

  • Czujnik temperatury może publikować wyłącznie na fabryka1/linia3/sensor-temp-01/telemetry. Nie może subskrybować tematów poleceń innych urządzeń ani odczytywać danych z innych linii.
  • Aplikacja dashboardowa subskrybuje dane z całej linii 3, ale nie ma uprawnień do publikowania na żadnych tematach sterujących.
  • System sterowania może publikować polecenia na tematy przypisanych mu urządzeń, ale nie ma dostępu do konfiguracji innych sekcji ani danych z innych fabryk.

Konfiguracja ACL w EMQX

W EMQX reguły ACL definiuje się per klient, per nazwę użytkownika lub per rolę. Poniższy przykład reguły dla jednego czujnika:

# Czujnik sensor-temp-01:
allow publish  fabryka1/linia3/sensor-temp-01/telemetry
allow subscribe fabryka1/linia3/sensor-temp-01/commands
deny all

Reguła deny all na końcu jest kluczowa. Bez niej brak dopasowania do żadnej reguły może oznaczać domyślne zezwolenie — zależnie od konfiguracji brokera. Explicit deny eliminuje tę niejednoznaczność.

Monitoring naruszeń ACL

Próby dostępu do tematów poza zdefiniowanymi uprawnieniami powinny być logowane i monitorowane centralnie. W środowisku produkcyjnym podejrzana aktywność — urządzenie IoT próbujące odczytać tematy konfiguracyjne, niespodziewana próba publikacji na temacie sterującym — może świadczyć o przejęciu klienta lub błędzie konfiguracji. Integracja brokera z systemem SIEM, np. z Wazuh, daje centralny widok na anomalie z całej infrastruktury.


Federacja tożsamości: integracja MQTT z Microsoft Entra ID

W organizacjach klasy enterprise zarządzanie kontami użytkowników centralnie to standard, nie opcja. Tworzenie lokalnych kont w brokerze MQTT dla każdego pracownika i każdej aplikacji jest nieefektywne i podatne na błędy. Federacja z Microsoft Entra ID (dawniej Azure Active Directory) pozwala wykorzystać istniejącą infrastrukturę tożsamości bez duplikowania zarządzania kontami.

Jak działa integracja przez OAuth2 i JWT

Broker MQTT (np. EMQX w wersji Enterprise) obsługuje uwierzytelnianie przez OAuth2 i JWT jako wbudowaną metodę. Przepływ danych wygląda następująco:

  1. Aplikacja kliencka (np. webowy panel operatora) loguje użytkownika przez Microsoft Entra ID zgodnie ze standardem OpenID Connect.
  2. Entra ID zwraca token JWT zawierający claims z rolami użytkownika, np. IoT_Fabryka1_Czytelnicy.
  3. Aplikacja przekazuje token do brokera MQTT jako hasło przy nawiązywaniu połączenia.
  4. Broker weryfikuje podpis tokenu przy użyciu klucza publicznego pobranego z endpointu JWKS Entra ID.
  5. Broker odczytuje role z claims JWT i mapuje je na reguły ACL: użytkownik z rolą IoT_Fabryka1_Czytelnicy dostaje uprawnienia subskrypcji do tematów fabryka1/#.

Korzyści operacyjne

Integracja z Entra ID eliminuje lokalną bazę haseł w brokerze MQTT. Onboarding nowego pracownika polega wyłącznie na przypisaniu go do odpowiedniej grupy w Entra ID przez dział IT lub HR. Offboarding jest natychmiastowy: dezaktywacja konta w Entra ID uniemożliwia wystawienie nowego tokenu JWT, co blokuje dostęp do brokera bez żadnej dodatkowej interwencji po stronie konfiguracji MQTT.

Dla systemów przechodzących audyty (ISO 27001, NIS2) kluczowe jest, że pełna ścieżka dostępu jest zarejestrowana w Entra ID. Kto, kiedy, z jaką rolą uzyskał dostęp do danych IoT — bez prowadzenia oddzielnych logów po stronie brokera.

Co z urządzeniami IoT?

Urządzenia polowe — czujniki, sterowniki PLC, bramki protokołowe — nie uwierzytelniają się przez OAuth2. Dla nich właściwe są certyfikaty X.509 wystawione przez wewnętrzne CA lub przez Azure IoT Hub Certificate Authority, z ACL opartym na Common Name certyfikatu. Federacja z Entra ID dotyczy warstwy aplikacji i użytkowników, nie urządzeń polowych.


MQTT over WebSocket: architektura front-endowa i eliminacja restrykcji firewalli

Natywny MQTT działa na porcie 1883 (bez szyfrowania) lub 8883 (z TLS). Wiele środowisk korporacyjnych blokuje niestandardowe porty na poziomie firewalla lub proxy HTTP. MQTT over WebSocket rozwiązuje ten problem, tunelując komunikację MQTT przez port 80 (HTTP) lub 443 (HTTPS).

Dlaczego to ważne dla architektury front-endowej

Przeglądarki internetowe nie mają natywnego API pozwalającego otwierać gniazda TCP na dowolny port. WebSocket jest wyjątkiem: to protokół dostępny z poziomu JavaScript, działający przez standardowe porty HTTP. Dzięki temu aplikacje webowe mogą łączyć się bezpośrednio z brokerem MQTT bez dodatkowej warstwy pośredniczącej po stronie backendu.

Porównanie modeli komunikacji dla aplikacji front-endowych:

Model Mechanizm Opóźnienie Złożoność
HTTP polling Zapytanie co N sekund N sekund Niska
REST API z SSE Push jednokierunkowy Niskie Średnia
MQTT over WebSocket Push dwukierunkowy Bardzo niskie Niska

Zastąpienie pollingu modelem push redukuje zbędny ruch sieciowy i obciążenie backendu. Aplikacja mobilna operatora farmy solarnej dostaje aktualizacje stanu w momencie, gdy dane się zmieniają, a nie co 10 sekund. Serwer backendowy nie obsługuje setek zbędnych zapytań na minutę.

Dodatkowa korzyść: ruch WebSocket/MQTT przez port 443 (WSS) wygląda dla firewalla i korporacyjnego proxy jak standardowy ruch HTTPS. Wiele organizacji nie zgodzi się na otwarcie portu 8883 do Internetu, ale ruch WSS przez 443 zaakceptuje bez zastrzeżeń.

Konfiguracja listenera WebSocket w EMQX

Obsługa WebSocket to konfiguracja odpowiedniego listenera w pliku konfiguracyjnym brokera:

# EMQX: listener WebSocket (bez TLS, dla ruchu wewnętrznego)
listeners.ws.default {
  bind = "0.0.0.0:8083"
}

# EMQX: listener WebSocket z TLS (dla ruchu zewnętrznego)
listeners.wss.default {
  bind = "0.0.0.0:8084"
  ssl_options.certfile = "/etc/emqx/certs/server.crt"
  ssl_options.keyfile  = "/etc/emqx/certs/server.key"
}

Ruch przez port 8083 lub 8084 można dalej obsłużyć przez reverse proxy Nginx lub HAProxy. Proxy terminuje TLS i kieruje ruch na brokera po wewnętrznej sieci bez TLS, co upraszcza zarządzanie certyfikatami i pozwala na routing po ścieżce URL.

Bezpieczeństwo przy WebSocket

Otwarcie brokera na ruch z przeglądarek wymaga przemyślanej autoryzacji na poziomie sesji. Standardowy przepływ: backend wystawia krótkotrwały token JWT po zalogowaniu użytkownika, aplikacja webowa przekazuje go jako hasło MQTT przy nawiązywaniu połączenia WebSocket. Token ma czas życia np. jednej godziny i musi być odnawiany przez aplikację. Broker weryfikuje podpis tokenu bez zapisywania haseł lokalnie.


Architektura tematów: multi-tenancy i izolacja danych

Struktura tematów MQTT (topic hierarchy) to pierwsza linia separacji danych w architekturze wielo-tenant. Dobrze zaprojektowana hierarchia pozwala obsłużyć wiele niezależnych jednostek organizacyjnych lub klientów na jednym brokerze, zachowując pełną izolację logiczną egzekwowaną przez ACL brokera.

Zasada projektowania hierarchii

Temat w MQTT to ciąg segmentów rozdzielonych ukośnikiem. Każdy segment może reprezentować jednostkę organizacyjną, lokalizację, urządzenie lub typ danych. Dobrze sprawdzający się wzorzec:

{tenant}/{lokalizacja}/{linia}/{urządzenie}/{typ-danych}

Przykład dla firmy produkcyjnej z zakładami w dwóch krajach:

fabryka-polska/krakow/linia3/robot-spawalniczy-01/metrics
fabryka-polska/krakow/linia3/robot-spawalniczy-01/status
fabryka-polska/krakow/linia3/robot-spawalniczy-01/commands
fabryka-niemcy/berlin/hala-b/prasa-hydrauliczna-04/metrics

Reguła ACL dla operatora zakładu polskiego:

allow subscribe fabryka-polska/#
deny all

Operator widzi wyłącznie swoje dane. Próba subskrypcji fabryka-niemcy/# jest odrzucana przez brokera na poziomie protokołu, zanim zapytanie dotrze do jakiejkolwiek warstwy aplikacyjnej.

Izolacja tematów poleceń sterujących

Tematy poleceń (commands) powinny być oddzielone od telemetrii i objęte ściśle ograniczonymi uprawnieniami publikowania. Tylko autoryzowany system sterowania może publikować na {urządzenie}/commands. Samo urządzenie subskrybuje wyłącznie swój temat poleceń:

# Reguły ACL dla robot-spawalniczy-01:
allow subscribe fabryka-polska/krakow/linia3/robot-spawalniczy-01/commands
allow publish   fabryka-polska/krakow/linia3/robot-spawalniczy-01/metrics
allow publish   fabryka-polska/krakow/linia3/robot-spawalniczy-01/status
deny all

Taka konfiguracja wyklucza scenariusz, w którym jedno urządzenie polowe mogłoby wstrzyknąć polecenie do innego urządzenia, np. w wyniku przejęcia klienta lub błędu aplikacyjnego.

Skalowanie zarządzania ACL przez Infrastructure as Code

Przy setkach urządzeń i dziesiątkach lokalizacji ręczne zarządzanie regułami ACL staje się nieefektywne i podatne na błędy. Automatyzacja przez Infrastructure as Code pozwala generować konfiguracje ACL z szablonów: nowa lokalizacja to nowy plik zmiennych, reguły ACL generują się automatycznie przez Ansible lub Terraform z dedykowanym providerem EMQX. Środowisko testowe i produkcyjne mają tożsamą strukturę konfiguracji, co eliminuje rozbieżności między etapami.

MQTT w produkcji: TCO, architektura i decyzja o HA przy skalowaniu IoT


FAQ — najczęściej zadawane pytania o MQTT, QoS i bezpieczeństwo

Czym różni się QoS 0, QoS 1 i QoS 2 w MQTT?

QoS 0 to "wyślij i zapomnij": brak potwierdzenia, brak retransmisji. QoS 1 gwarantuje dostarczenie co najmniej raz przez mechanizm PUBACK, ale dopuszcza duplikaty. QoS 2 gwarantuje dostarczenie dokładnie raz przez czterostronny protokół uzgodnień, kosztem najwyższego narzutu komunikacyjnego i pamięciowego. W praktyce QoS 1 jest dobrym domyślnym wyborem dla telemetrii przemysłowej, a QoS 2 stosuje się wyłącznie tam, gdzie duplikat komendy ma realne konsekwencje fizyczne lub finansowe.

Jak skonfigurować ACL w brokerze MQTT?

ACL definiuje się jako zestaw reguł powiązanych z identyfikatorem klienta, nazwą użytkownika lub rolą. Reguła określa: temat (z obsługą wildcardów + dla jednego segmentu i # dla drzewa), operację (publish, subscribe lub obie) i decyzję (allow lub deny). Poprawna konfiguracja kończy się regułą deny all jako catch-all dla niepasujących żądań. Większość brokerów (EMQX, Mosquitto, HiveMQ) obsługuje ACL z pliku konfiguracyjnego, z bazy danych lub z zewnętrznego REST API.

Jak zintegrować broker MQTT z Microsoft Entra ID?

Integracja przebiega przez OAuth2 i JWT. Aplikacja kliencka uzyskuje token JWT z Entra ID, a broker weryfikuje jego podpis przy użyciu klucza publicznego pobranego z endpointu JWKS Entra ID. Broker EMQX Enterprise obsługuje JWT auth jako wbudowaną metodę uwierzytelniania. Mapowanie grup Entra ID na reguły ACL odbywa się przez claims JWT odczytywane przez broker przy nawiązywaniu sesji. Urządzenia IoT uwierzytelniają się oddzielnie, przez certyfikaty X.509, a nie przez OAuth2.

Kiedy używać MQTT over WebSocket zamiast natywnego MQTT?

WebSocket jest potrzebny, gdy klient łączy się z poziomu przeglądarki (JavaScript nie może otwierać gniazd TCP na dowolny port) lub gdy infrastruktura sieciowa blokuje porty 1883 i 8883. WebSocket przez port 443 (WSS) wygląda dla firewalla i proxy jak standardowy ruch HTTPS. W środowiskach, gdzie urządzenia IoT łączą się przez dedykowaną sieć M2M z otwartymi portami, natywny MQTT (TCP/TLS na 8883) jest efektywniejszy i ma niższy narzut połączenia.

Co to jest LWT i jak go skonfigurować?

LWT (Last Will and Testament) to wiadomość, którą broker wysyła automatycznie po wykryciu nieoczekiwanej utraty połączenia z klientem, tzn. gdy klient zniknie bez przesłania pakietu DISCONNECT. Konfiguruje się go przez pola will_topic, will_payload, will_qos i will_retain w pakiecie CONNECT wysyłanym przy nawiązywaniu sesji. Typowe zastosowanie: urządzenie IoT deklaruje LWT z payloadem {"status":"offline"} i flagą retain=true. Gdy zniknie bez czystego rozłączenia, broker opublikuje tę wiadomość, a każdy nowy subskrybent natychmiast otrzyma ostatni znany stan urządzenia.

Jak zbudować architekturę multi-tenant dla MQTT?

Hierarchia tematów powinna zaczynać się od identyfikatora tenanta lub lokalizacji, np. {fabryka}/{linia}/{urządzenie}/{typ}. ACL dla każdego tenanta ogranicza dostęp do jego przestrzeni nazw przez reguły oparte na wildcardzie # po prefixie tenanta. Izolacja jest egzekwowana przez broker na poziomie protokołu, niezależnie od warstwy aplikacyjnej. Przy dużej liczbie tenantów i urządzeń automatyzacja konfiguracji ACL przez Infrastructure as Code jest koniecznością, a nie opcją.


Skonsultuj architekturę przed wdrożeniem produkcyjnym

Hardening brokera MQTT jest znacznie łatwiejszy do wykonania raz porządnie niż do poprawiania w działającym środowisku produkcyjnym. Chętnie przejrzymy istniejącą konfigurację lub zaplanujemy architekturę od podstaw.

Kontakt: [email protected] | sparksome.pl/#kontakt

Potrzebujesz konsultacji technicznej?

Masz pytania dotyczące wpisu lub swojej infrastruktury? Napisz do nas. Pomożemy Ci to spokojnie przeanalizować i znaleźć rozwiązanie.

logo SparkSome

NIP: 6793289948

REGON: 527616291

KRS: 0001085500

Kapitał zakładowy: 50 000 zł

© Copyright
SparkSome Venture sp. z o.o.