Mosquittoを家庭ラボの常用MQTT基盤にする前に、ACL拒否とretain残留を作って観測する

MQTT brokerの接続中心を描いたアイキャッチ画像

家庭ラボでセンサー値や状態通知をMQTTでまとめたいとき、Mosquittoは軽くて扱いやすいブローカーです。ただ、便利なメッセージ中継として常用環境に入れる前に、確認しておくと安心な挙動がいくつかあります。古いretain値が現在値として扱われること、ACLの設定ミスで必要なtopicだけ届かないこと、広すぎる権限で状態を書き換えられること、再起動後に残るはずの状態が消えること。これらは設定を曖昧にしたまま実機やHome Assistantへつなぐと、後から原因の切り分けが難しくなります。

この記事は導入手順ではなく、隔離した使い捨て環境で「届かない」「残り続ける」「許可しすぎる」状態を意図的に作って観測した検証ログです。知りたいのは、Mosquittoを常用MQTTブローカーとして入れる前に、どの設定と失敗ログを最低限確認すべきか、という点だと考えています。

目次

検証環境

検証は、Dockerを備えた予約済みの使い捨てVM上で実施しました。通信はすべてコンテナ内のループバック(127.0.0.1)に限定し、外部ネットワークへは公開していません。資格情報はすべて検証用のダミー値で、実運用の認証情報ではありません。各検証はコンテナの再作成と破棄で前の状態に戻しています。

  • 実行基盤: Docker入りの隔離VM(検証後に破棄)
  • イメージ: eclipse-mosquitto:2.0.18
  • Docker server: 29.6.0
  • 通信範囲: コンテナ内ループバック(127.0.0.1)のみ
  • 資格情報: 検証用ダミー(labuser / dummy-pass-123)

設定ファイルとデータは隔離ディレクトリにまとめ、最後に丸ごと削除しています。

実行コマンドと結果

1. 匿名接続ありの最小構成

まず最小構成の設定ファイルを用意します。

listener 1883 127.0.0.1
allow_anonymous true

この設定で起動します。

# docker run -d --rm --name mqtt-lab \
    -v /tmp/issue172-lab/config:/mosquitto/config \
    eclipse-mosquitto:2.0.18

1メッセージ受信で終了する subscriber を起動し、別セッションから publish します。

# mosquitto_sub -h 127.0.0.1 -t lab/test -C 1 -W 5
# mosquitto_pub -h 127.0.0.1 -t lab/test -m hello-anon

subscriber の出力は次のとおりで、publish の終了コードは0でした。ここは正常系の基準点です。

hello-anon

2. 認証必須へ切替と認証失敗の見え方

ダミーユーザーを作成し、匿名接続を無効化して password_file を指定します。

# mosquitto_passwd -b -c /mosquitto/config/passwd labuser dummy-pass-123
listener 1883 127.0.0.1
allow_anonymous false
password_file /mosquitto/config/passwd

正しい資格情報では subscriber に hello-auth が届きます。次に誤ったパスワードで publish します。

# mosquitto_pub -h 127.0.0.1 -u labuser -P wrong-pass -t lab/test -m nope
Connection error: Connection Refused: not authorised.
Error: The connection was refused.
client_exit=5

認証失敗はクライアント側に明確なメッセージと終了コード5で現れます。topic権限以前の接続段階で拒否されるため、メッセージが届かないのではなく接続そのものが拒否される、という見え方になります。

3. topic別ACLの適用とACL拒否

labuser に対し lab/allowed/# のみ readwrite を許可するACLを設定しました。

user labuser
topic readwrite lab/allowed/#

許可されたtopic(lab/allowed/x)では publish が通り、subscriber に ok が届きます。一方、許可していない lab/denied/x では挙動が分かれました。

  • denied への subscribe: deny_sub_exit=27(タイムアウトで何も受信せず終了)
  • denied への publish: クライアント側の終了コードは0

ここが認証失敗との大きな違いです。ACL拒否は接続を切るのではなく、対象topicのメッセージが静かに届かなくなります。publish 側は成功したように見えても、許可されていない宛先のメッセージはブローカーで破棄され、subscribe 側はタイムアウト(終了コード27)になります。「publishは成功しているのに値が来ない」という症状は、この静かな破棄が原因のことがあります。

4. retainあり/なしの新規購読者への到達

匿名構成に戻し、lab/state には -r 付き(retain)で、lab/ephemeral には retainなしで publish しました。

# mosquitto_pub -h 127.0.0.1 -t lab/state -m retained-value -r
# mosquitto_pub -h 127.0.0.1 -t lab/ephemeral -m plain-value

publish 後に、まだ購読していなかった新規 subscriber を起動して観測すると、結果は分かれました。

  • lab/state(retain): retained-value が即座に届く(終了コード0)
  • lab/ephemeral(retainなし): 何も届かずタイムアウト(終了コード27)

retain付きのメッセージは、後から接続した購読者にも最新の保持値として配信されます。状態通知には便利な一方、古い値が現在値として扱われる事故の原因にもなります。retainなしのメッセージは、購読していなかった期間のものは届きません。

5. persistence無効/有効の再起動挙動

retain値が再起動をまたいで残るかを確認しました。persistence false で lab/state に retain値を入れてからコンテナを停止・再作成すると、再起動後の新規購読者には何も届かず終了コード27でした。retain値は再起動で消えます。

次に persistence を有効化し、永続ボリュームをマウントします。

listener 1883 127.0.0.1
allow_anonymous true
persistence true
persistence_location /mosquitto/data/
autosave_interval 1

retain値を入れて停止すると、data ディレクトリに mosquitto.db が生成されました。再作成後の新規購読者には persisted-value が届き、終了コードは0でした。retain値が再起動後も復元されています。つまり「再起動しても直前の状態が見えてほしい」なら persistence と永続ボリュームの両方が必要で、どちらかが欠けると状態は失われます。

失敗・制約・再現条件

この検証では各コマンドの終了コードはいずれも0で、ブローカー自体のクラッシュは観測していません。ここで言う「失敗」は、認証拒否(client_exit=5)とACL拒否・retainなし・persistence無効による未到達(終了コード27)という、設計上の拒否や非到達を意図的に作って観測したものです。

制約として、通信はコンテナ内ループバックに限定しており、実際のLAN越しの接続やTLS、複数クライアントの同時負荷、Home Assistantなどの実機連携は検証範囲外です。資格情報はダミーで、実運用ではより強いパスワードと公開範囲の制御が前提になります。再現は、Docker入りの隔離VM上で同じ設定ファイルとコマンド列を順に実行すれば確認できます。検証後はコンテナとイメージ、隔離ディレクトリを削除し、環境を元に戻しています。コンテナ単位の作り直しで戻せるため、ロールバックは容易です。

常用前に確認したい最小チェック

自分の環境でMosquittoを常用MQTT基盤にする前に、最低限見ておくとよい点を、今回観測した挙動からまとめます。

  • 認証失敗はクライアント側に Connection Refused: not authorised と終了コード5で出る。届かないのではなく接続が拒否される。
  • ACL拒否はpublish/subscribeで見え方が違う。subscribeは未到達(タイムアウト)、publishは成功表示でも破棄され得る。
  • retain付きメッセージは新規購読者にも最新値として届く。状態には便利だが古い値の取り扱いに注意する。
  • persistenceと永続ボリュームの両方が揃って初めて、再起動後にretain値が残る。
  • 戻す手順はコンテナの再作成・破棄で完結する。実機投入前に隔離環境で同じ条件を試しておく価値がある。

便利なメッセージ中継として紹介する前に、届かない状態と残り続ける状態を小さく確認しておくと、センサーや自動化を増やしたあとの切り分けが楽になります。

この検証を回している環境

この検証は、自宅の常設ラボ(使い捨てVM/LXCを回す母艦+GPU+VLAN分離ネットワーク)で動かしています。使っている機材と選定理由、全体構成は1本にまとめています。

ラボ構成のまとめを見る →
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次