ESP8266 besturen zonder port forwarding: een uitgaand MQTT-pad
Een broker die vanaf internet bereikbaar is, laat een apparaat thuis besturen via een verbinding die het zelf opende. Wat dat verandert voor TLS-geheugen, topics en herhaling.
Het apparaat staat achter een thuisrouter zonder port forwarding en zonder openbaar adres, en het moet toch binnen seconden zijn configuratie ontvangen. De truc is geen gat in de firewall. Het is een verbinding die het apparaat naar buiten opent naar een broker die het kan bereiken, en die de berichten vervolgens terugstuurt over hetzelfde opgebouwde pad. Elke eigenschap die mensen aan dit ontwerp waarderen, en elke valkuil erin, komt uit die omkering.
Wat er werkelijk door de NAT komt
Wanneer het apparaat een TCP-verbinding naar de broker opent, maakt de thuisrouter een mapping aan en houdt die in leven zolang de stroom duurt. Antwoorden op die stroom komen terug bij het apparaat omdat de mapping al bestaat. De broker initieert nooit iets richting het huis, dus er is geen ongevraagd inkomend pakket dat de router moet weigeren. Dat is het hele netwerkargument, en daarom werkt de aanpak op netwerken die je niet beheert.
Het apparaat moet de verbinding in leven houden of opnieuw opbouwen, wat de betrouwbaarheidsvraag wordt zodra de architectuur vaststaat.
De broker routeert topics, hij proxyt geen HTTP
MQTT definieert uitwisselingen van publiceren en abonneren: het apparaat abonneert zich op een topicfilter dat het mag lezen, en een geautoriseerde controller publiceert naar een overeenkomend topic. De broker koppelt publicaties aan abonnementen en levert kopieën. Hij weet niet wat een browserverzoek is, en hij stuurt geen HTTP GET door naar de webserver van het apparaat. Als de bestaande firmware alleen HTTP spreekt, heeft het brokerontwerp een applicatiewijziging of een bewuste vertaalcomponent nodig.
Topicnamen zijn ook geen autorisatie. De meeste brokers ondersteunen toegangscontrolelijsten per client, en apparaten moeten beperkt blijven tot hun eigen publiceer- en abonneerbomen in plaats van één globale naamruimte te delen.
De bronnen van het apparaat bepalen de TLS-vorm
Een ESP8266 heeft beperkt RAM, en een TLS-handshake is het duurste dat hij doet. De Arduino-kern levert BearSSL juist vanwege deze beperking, en de keuze tussen het verifiëren van een CA en het vastpinnen van een vingerafdruk verandert zowel de geheugenvoetafdruk als wat een aanvaller met een geldig certificaat voor een andere naam kan doen. De CA van de broker verifiëren is de sterkere optie wanneer de heap het toelaat. Ook de grootte van de payload maakt uit: de MQTT-client houdt een buffer vast, en grotere berichten worden stil afgekapt tenzij de buffer bewust wordt verhoogd.
Keepalive en socket-time-outs horen bij hetzelfde budget, want een client die een dode verbinding nooit opmerkt, stopt met het ontvangen van opdrachten zonder een fout te melden.
De berichtsemantiek die bijt
Een bewaard bericht wordt door de broker opgeslagen en aan latere overeenkomende abonnees geleverd, wat uitstekend is voor de huidige toestand en gevaarlijk voor eenmalige opdrachten: een apparaat dat opnieuw verbindt, kan de opdracht van gisteren ontvangen alsof die nieuw is. QoS 1 garandeert levering ten minste één keer, dus duplicaten zijn normaal en de applicatie moet herhaalveilig zijn, idealiter met een identificatie of tijdstempel die hij kan weigeren. Het laatste testament geeft je een statussignaal wanneer het apparaat wegvalt, maar alleen als de verbinding echt mislukt in plaats van blijft hangen.
Het gedrag van een schone sessie bepaalt of de broker je abonnementen bewaart terwijl je weg bent, en dat verkeerd doen is de reden dat sommige apparaten na elke herverbinding het eerste bericht missen.
TLS beschermt het stuk naar de broker. Het is geen end-to-end-versleuteling van de payload tegen de brokerbeheerder, die alles kan lezen wat je erdoor publiceert.
Begrens de levenscyclus en test hem met onschadelijke gegevens
Plan herverbinding met backoff in plaats van een strakke herhaal-lus, abonneer expliciet opnieuw als de sessie niet persistent is, en log het verbindingsresultaat zodat een stil offline apparaat bewijs achterlaat. Test het herverbindingspad met voorbeeldpayloads die niets doen, want de eerste productietest van een opdrachtkanaal zou geen echte opdracht moeten zijn. Bevestig daarna wat een verouderd bewaard bericht doet bij herverbinding, en beslis of dat gedrag voor elk topic acceptabel is.
Zo gedaan verwijdert het ontwerp één inkomende netwerkvereiste en laat het de apparaatsoftware, het brokerbeleid en de berichtsemantiek als zichtbaar werk achter in plaats van verborgen aannames.