· Tomasz Siroń · Bezpieczeństwo
MQTT hardening: QoS, bezpieczeństwo, federacja tożsamości i architektura multi-tenant
Spis treści
- QoS 1, QoS 2, persistent sessions i mechanizm LWT
- Autoryzacja ACL i zasada najmniejszych uprawnień
- Federacja tożsamości: integracja MQTT z Microsoft Entra ID
- MQTT over WebSocket: architektura front-endowa i eliminacja restrykcji firewalli
- Architektura tematów: multi-tenancy i izolacja danych
- 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:
- Aplikacja kliencka (np. webowy panel operatora) loguje użytkownika przez Microsoft Entra ID zgodnie ze standardem OpenID Connect.
- Entra ID zwraca token JWT zawierający claims z rolami użytkownika, np.
IoT_Fabryka1_Czytelnicy. - Aplikacja przekazuje token do brokera MQTT jako hasło przy nawiązywaniu połączenia.
- Broker weryfikuje podpis tokenu przy użyciu klucza publicznego pobranego z endpointu JWKS Entra ID.
- Broker odczytuje role z claims JWT i mapuje je na reguły ACL: użytkownik z rolą
IoT_Fabryka1_Czytelnicydostaje uprawnienia subskrypcji do tematówfabryka1/#.
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