ポート転送なしでESP8266を制御する:MQTTの外向き経路
インターネットから到達できるbrokerがあれば、家庭内の機器を自分が開いた接続越しに制御できる。TLSのメモリ、トピック、再送に何が変わるか。
機器はポート転送も公開アドレスもない家庭用ルーターの背後にあり、それでも数秒以内に設定を受け取らなければならない。仕掛けはファイアウォールの穴ではない。機器が到達できるbrokerへ外向きに接続を開き、brokerが同じ確立済みの経路でメッセージを返すのだ。この設計で人々が好む性質も、潜む罠も、すべてこの逆転から生まれる。
NATを実際に通過するもの
機器がbrokerへTCP接続を開くと、家庭のルーターは対応付けを作り、フローの存続中それを維持する。そのフロー上の応答は、対応付けがすでにあるため機器へ戻る。brokerは家側へ何も開始しないので、ルーターが拒否すべき一方的な受信パケットは存在しない。これがネットワーク上の論拠のすべてであり、管理していないネットワークでもこの手法が機能する理由である。
機器は接続を維持するか再確立する必要があり、アーキテクチャが固まればこれが信頼性の問いになる。
brokerはトピックを振り分けるのであって、HTTPを代理しない
MQTTはパブリッシュとサブスクライブの交換を定義する。機器は読み取りを許可されたトピックフィルターを購読し、認可されたコントローラーが一致するトピックへパブリッシュする。brokerはパブリッシュを購読と突き合わせ、コピーを配信する。ブラウザーの要求が何かは知らず、HTTP GETを機器のWebサーバーへ転送することもない。既存のファームウェアがHTTPしか話さないなら、brokerの設計にはアプリケーションの変更か、意図的な変換コンポーネントが必要だ。
トピック名も認可ではない。ほとんどのbrokerはクライアントごとのアクセス制御リストに対応しており、機器は単一のグローバル名前空間を共有するのではなく、自前のパブリッシュと購読のツリーに制限すべきだ。
機器の資源がTLSの形を決める
ESP8266のRAMは限られており、TLSハンドシェイクはそれが行う最も高価な処理だ。ArduinoコアがBearSSLを同梱しているのはまさにこの制約のためであり、CAを検証するか指紋を固定するかの選択は、メモリ使用量と、別名の有効な証明書を持つ攻撃者ができることの両方を変える。ヒープが許すならbrokerのCAを検証する方が強い選択だ。ペイロードの大きさも重要で、MQTTクライアントはバッファーを保持し、それより大きいメッセージはバッファーを意図的に増やさない限り黙って切り詰められる。
キープアライブとソケットのタイムアウトは同じ予算の一部だ。死んだ接続に気づかないクライアントは、エラーを報告しないままコマンドを受け取れなくなる。
牙をむくメッセージの意味論
保持メッセージはbrokerに保存され、後から一致する購読者へ配信される。現在の状態には最適だが、一度きりのコマンドには危険だ。再接続した機器は昨日のコマンドを新しいものとして受け取ることがある。QoS 1は少なくとも一回の配信を保証するので重複は当然であり、アプリケーションは再実行に耐える必要がある。拒否できる識別子やタイムスタンプを持つとよい。遺言は機器が落ちたときの状態シグナルを与えるが、それは接続がぶら下がらず実際に失敗した場合に限る。
クリーンセッションの挙動は、離れている間brokerが購読を保つかどうかを決める。ここを誤ると、再接続のたびに最初のメッセージを取りこぼす機器が生まれる。
TLSはbrokerまでの区間を守る。brokerの運営者に対するペイロードのエンドツーエンド暗号化ではない。運営者はあなたがそこを通して公開するすべてを読める。
ライフサイクルを区切り、無害なデータで試す
再接続は厳しい再試行ループではなくバックオフ付きで計画し、セッションが永続的でなければ明示的に再購読し、接続結果を記録して、黙ってオフラインになった機器が証拠を残すようにする。コマンド経路の最初の本番テストは本物のコマンドであるべきではないので、何もしないサンプルペイロードで再接続経路を試す。次に、古い保持メッセージが再接続時に何をするかを確認し、その挙動がトピックごとに許容できるか決める。
こうすれば、設計は受信側のネットワーク要件を一つ取り除き、機器のソフトウェア、brokerのポリシー、メッセージの意味論を隠れた前提ではなく見える作業として残す。