Controllare l'ESP8266 senza port forwarding: un percorso MQTT in uscita
Un broker raggiungibile da internet consente di controllare un dispositivo domestico tramite una connessione che ha aperto lui stesso. Cosa cambia per la memoria TLS, i topic e la ripetizione.
Il dispositivo sta dietro un router domestico senza port forwarding e senza indirizzo pubblico, e deve comunque ricevere la configurazione in pochi secondi. Il trucco non è un buco nel firewall. È una connessione che il dispositivo apre verso l'esterno verso un broker che riesce a raggiungere, e che poi restituisce i messaggi lungo lo stesso percorso già stabilito. Ogni proprietà che piace di questo progetto, e ogni trappola che contiene, deriva da quell'inversione.
Cosa passa davvero attraverso il NAT
Quando il dispositivo apre una connessione TCP verso il broker, il router di casa crea una mappatura e la mantiene viva per tutta la durata del flusso. Le risposte su quel flusso tornano al dispositivo perché la mappatura esiste già. Il broker non avvia mai nulla verso la casa, quindi non c'è alcun pacchetto in ingresso non richiesto che il router debba rifiutare. È tutto l'argomento di rete, ed è per questo che l'approccio funziona su reti che non controlli.
Il dispositivo deve mantenere viva la connessione o ristabilirla, il che diventa la questione di affidabilità una volta fissata l'architettura.
Il broker instrada i topic, non fa da proxy HTTP
MQTT definisce scambi di pubblicazione e sottoscrizione: il dispositivo si sottoscrive a un filtro di topic che gli è consentito leggere, e un controller autorizzato pubblica su un topic corrispondente. Il broker abbina le pubblicazioni alle sottoscrizioni e consegna copie. Non sa cos'è una richiesta del browser e non inoltrerà un HTTP GET al server web del dispositivo. Se il firmware esistente parla solo HTTP, il progetto con broker richiede una modifica dell'applicazione o un componente di traduzione deliberato.
Anche i nomi dei topic non sono autorizzazione. La maggior parte dei broker supporta liste di controllo degli accessi per client, e i dispositivi dovrebbero essere limitati ai propri alberi di pubblicazione e sottoscrizione invece di condividere un unico spazio dei nomi globale.
Le risorse del dispositivo decidono la forma di TLS
L'ESP8266 ha RAM limitata, e la stretta di mano TLS è la cosa più costosa che fa. Il core Arduino fornisce BearSSL proprio per questo vincolo, e la scelta tra verificare una CA e appuntare un'impronta digitale cambia sia l'impronta di memoria sia ciò che un attaccante con un certificato valido per un altro nome può fare. Verificare la CA del broker è l'opzione più solida quando lo heap lo consente. Anche la dimensione del payload conta: il client MQTT mantiene un buffer, e i messaggi più grandi vengono troncati in silenzio a meno che il buffer non venga aumentato deliberatamente.
Il keepalive e i timeout del socket fanno parte dello stesso budget, perché un client che non nota mai una connessione morta smette di ricevere comandi senza segnalare un errore.
La semantica dei messaggi che morde
Un messaggio conservato viene memorizzato dal broker e consegnato ai sottoscrittori corrispondenti successivi, il che è eccellente per lo stato attuale e pericoloso per i comandi una tantum: un dispositivo che si riconnette può ricevere il comando di ieri come se fosse nuovo. QoS 1 garantisce la consegna almeno una volta, quindi i duplicati sono normali e l'applicazione deve essere sicura rispetto alle ripetizioni, idealmente con un identificatore o un timestamp che possa rifiutare. L'ultimo testamento ti dà un segnale di stato quando il dispositivo cade, ma solo se la connessione fallisce davvero invece di restare appesa.
Il comportamento della sessione pulita decide se il broker conserva le tue sottoscrizioni mentre sei via, e sbagliarlo è il motivo per cui alcuni dispositivi perdono il primo messaggio dopo ogni riconnessione.
TLS protegge il tratto fino al broker. Non è cifratura del payload end-to-end nei confronti dell'operatore del broker, che può leggere tutto ciò che pubblichi attraverso di esso.
Limita il ciclo di vita e provalo con dati innocui
Pianifica la riconnessione con un arretramento invece di un ciclo di tentativi serrato, risottoscrivi esplicitamente se la sessione non è persistente, e registra l'esito della connessione così che un dispositivo silenzioso offline lasci prove. Prova il percorso di riconnessione con payload di esempio che non fanno nulla, perché il primo test di produzione di un canale di comandi non dovrebbe essere un comando reale. Poi conferma cosa fa un messaggio conservato obsoleto alla riconnessione, e decidi se quel comportamento è accettabile per ogni topic.
Fatto così, il progetto elimina un requisito di rete in ingresso e lascia il software del dispositivo, la politica del broker e la semantica dei messaggi come lavoro visibile invece di assunzioni nascoste.