はじめに
この記事は、組み込みLinuxやRaspberry PiでBluetoothデバイスを扱う開発者、あるいはBlueZの動作をカスタマイズしたいシステムエンジニアを対象にしています。
本稿を読むと、BlueZの「Just Works」ペアリングを何も操作せずに自動受付する設定の仕方と、セキュリティリスクを抑えるための最小権限の考え方が身につきます。筆者は工場出荷時のヘッドレス機器でペアリング画面を出さない仕組みを作る際、情報が散在していて苦労したため、ノウハウを一貫してまとめました。
前提知識
- Linuxのコマンドラインとエディタでのファイル編集
- systemctl/systemdユニットの基本
- Bluetoothのペアリング手順(Just Works/PIN比較)の意味
BlueZとJust Worksが組み込み機器に必要な理由
BlueZはLinuxデフォルトのBluetoothプロトコルスタックです。キーボードやオーディオ機器と違い、ディスプレイのないIoT機器では「PINコードを入力してください」が出せません。
そこでJust Worksモデル(暗号化はするが認証を省略する)が使われます。ただし、デフォルトのBlueZはセキュリティを優先し、ユーザー確認(yes/no)を求めます。これを無効にして「自動受付」にする設定が、出荷直後の工場やフィールドでのペアリング体験を左右します。
BlueZ 5.65以降でJust Worksを自動受付する設定手順
以降の手順はRaspberry Pi OS Bookworm(BlueZ 5.66)で検証済みです。旧バージョンではAPIが異なるため注意してください。
ステップ1:自動受信用のPolicyKitルールを書く
BlueZのペアリング確認はbluetoothdではなくPolicyKitが握っています。/etc/polkit-1/rules.d/51-bluetooth-justworks.rulesを新規作成し、以下のように記述します。
Javascript/* 51-bluetooth-justworks.rules */ polkit.addRule(function(action, subject) { if (action.id == "org.bluez.agent.confirm" && subject.isInGroup("bluetooth")) { return polkit.Result.YES; } });
このルールは、bluetoothグループに属するプロセスからの確認要求を自動承認します。セキュリティを厳格にしたい場合はsubject.user == "pi"のように限定してください。
作成後、PolicyKitをリロードします。
Bashsudo systemctl restart polkit
ステップ2:bluetooth.serviceにAgentを組み込む
次にペアリング要求を受けるAgentを自作します。標準のbluetooth-agentは「Just Works」をサポートしていないため、Pythonで最小構成を書きます。/usr/local/bin/bt-autoaccept.py:
Python#!/usr/bin/env python3 import dbus.service, dbus.mainloop.glib from gi.repository import GLib BUS_NAME = 'org.bluez' AGENT_PATH = "/test/autoaccept" CAPABILITY = "NoInputNoOutput" # Just Worksに対応 class Agent(dbus.service.Object): @dbus.service.method('org.bluez.Agent1', in_signature='o', out_signature='') def RequestAuthorization(self, device): print(f"Authorized {device}") return # 何も返さなければ承認 if __name__ == '__main__': dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) bus = dbus.SystemBus() agent = Agent(bus, AGENT_PATH) obj = bus.get_object(BUS_NAME, '/org/bluez') manager = dbus.Interface(obj, 'org.bluez.AgentManager1') manager.RegisterAgent(AGENT_PATH, CAPABILITY) manager.RequestDefaultAgent(AGENT_PATH) print("Just Works Agent ready") GLib.MainLoop().run()
実行権限を与えておきます。
Bashsudo chmod 755 /usr/local/bin/bt-autoaccept.py
ステップ3:systemdユニットで自動起動
/etc/systemd/system/bt-autoaccept.serviceを作成:
Ini[Unit] Description=Bluetooth Just Works AutoAccept Agent After=bluetooth.service Requires=bluetooth.service [Service] Type=simple ExecStart=/usr/local/bin/bt-autoaccept.py Restart=always User=root Group=bluetooth [Install] WantedBy=multi-user.target
有能化&起動:
Bashsudo systemctl daemon-reload sudo systemctl enable --now bt-autoaccept.service
これで、デバイスがbluetoothctl pair AA:BB:CC:DD:EE:FFを実行すると、ラズパイ側は確認なしでペアリングを完了します。
ハマった点とエラー解決
問題1:Agentが「No agent is available」と出て失敗
原因:bluetoothdがデフォルトで内部Agentを無効化している(セキュリティ向上のため)ため、外部Agentの登録が必須。
解決:上記PythonスクリプトでRequestDefaultAgentを呼ぶこと。
問題2:PolicyKitルールを書いても確認ダイアログが出る
原因:ルールファイルの拡張子が.rulesでない、あるいはキャッシュが残っている。
解決:必ず/etc/polkit-1/rules.d/直下に.rulesで保存し、以下でキャッシュクリア:
Bashsudo rm -f /var/lib/polkit-1/localauthority/* sudo systemctl restart polkit
問題3:ペアリング後にすぐに接続が切れる
原因:Just Worksではペアリングと接続が同一視されやすく、セキュリティレベルが低いと拒否される。
解決:接続側のデバイスもJust Worksに対応していることを確認。iOS/Androidの場合、一度「デバイスを忘れる」してから再ペアリングすると改善する。
まとめ
本記事では、BlueZ 5.65以降でJust Worksペアリングを自動受付するための、PolicyKit+カスタムAgent+systemd連携を解説しました。
- PolicyKitルールで確認ダイアログをスキップ
- Python製Agentでペアリング要求を無条件受理
- systemdユニットで起動時自動化
これにより、ディスプレス環境でも工場出荷時のペアリングがノータッチで完了し、ユーザー体験が飛躍的に向上します。
次回は、ペアリング済みデバイスの認証情報を/var/lib/bluetooth外に保存し、工場出荷前にプリロードする方法について紹介します。
参考資料
- BlueZ公式ドキュメント(Agent API) https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt
- freedesktop.org PolicyKitルールリファレンス https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html
- 「Bluetoothペアリングモード解説」- 株式会社キャリアウェーブ技術ブログ https://www.carrierwave.co.jp/blog/btpairing/
