要約
- Mosquittoライブラリを使ってローカルにあるMQTTブローカに接続したい
- 以下の記事を参考にルートCA証明書とサーバ証明書・鍵を作った
- また以下の記事を参考にクライアント認証をしようとしたが、うまくいかなかった
- 最終的に
use_identity_as_username true
をmosquitto.confに追加する必要があることがわかった
証明書の作成
CA証明書
テスト目的なので自己署名
生成するときにいろいろ聞かれるが、CNはクライアント証明書と違う値にする必要がある
- 今回は空白にした
$ openssl req -new -x509 -days 365 -extensions v3_ca -keyout ca.key -out ca.crt
サーバ証明書
$ openssl genrsa -out server.key 2048
$ openssl req -out server.csr -key server.key -new
$ openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365
クライアント証明書
前述のとおりCNはCA証明書と違う値にする必要がある
- 今回はocalhostにした
$ openssl genrsa -out client.key 2048
$ openssl req -out client.csr -key client.key -new
$ openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
証明書の配置
CA証明書を/etc/mosquitto/ca_certificatesにコピー
$ ls -l
total 12
-rw-r--r-- 1 root root 1245 Apr 6 08:41 ca.crt
-rw------- 1 root root 1854 Apr 6 08:41 ca.key
-rw-r--r-- 1 root root 73 Oct 1 2023 README
サーバ証明書と鍵を/etc/mosquitto/certsにコピー
その際オーナーをchownでmosquittoに変更
$ ls -l
total 12
-rw-r--r-- 1 root root 130 Oct 1 2023 README
-rw-r--r-- 1 mosquitto mosquitto 1151 Apr 6 08:42 server.crt
-rw------- 1 mosquitto mosquitto 1704 Apr 6 08:42 server.key
クライアント証明書はクライアントアプリケーションと同じディレクトリに配置
mosquitto.confの設定
TLSを使う場合ポートは8883に設定
cafile, certfile, keyfileにそれぞれCA証明書、サーバ証明書、サーバ鍵のパスを設定
require_certificateをtrueにすることでクラインと認証を有効にする
listener 8883
cafile /etc/mosquitto/ca_certificates/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
require_certificate true
クライアントアプリケーションの実装
mosquitto_connect()の前にmosquitto_tls_set()を呼ぶ
- CA証明書のパスを指定していればcapathはNULLでもいい
- certfileとkeyfileにクライアント証明書を指定
- 今回クライアント鍵にパスワードを設定していないのでpw_callbackはNULLにした
mosquitto_tls_insecure_setをtrueにするとサーバ証明書のホストを確認する
- 自己署名CAなのでtrueにしておかないとcertificate verify failedになる
//省略しているが、各関数呼び出しのあとにエラーチェックしている
mosquitto_lib_init();
m_mosq = mosquitto_new("iot_device_hub", true, NULL);
mosquitto_tls_set(
m_mosq,
"/etc/mosquitto/ca_certificates/ca.crt",
NULL,
"client.crt",
"client.key",
NULL);
mosquitto_tls_insecure_set(m_mosq,true);
mosquitto_connect(m_mosq, "127.0.0.1", 8883, 60);
mosquitto_loop_start(m_mosq);
//この後にpublishなどを実行している
テスト結果
各関数を呼び出したときにエラーは返ってきていなかったが、すぐにDisconnectされてしまう
/var/log/mosquitto/mosquitto.logを見ると以下のような出力が出ていた
1743898855: New connection from 127.0.0.1:46826 on port 8883.
1743898855: Sending CONNACK to 127.0.0.1 (0, 5)
1743898855: Client <unknown> disconnected, not authorised.
ログを見るとunknownユーザが接続しようとして認証に失敗しているように見える
解決方法
mosquitto.confにuse_identity_as_username
を設定したら接続後Publishまですることができた
1743900176: New connection from 127.0.0.1:55378 on port 8883.
1743900176: New client connected from 127.0.0.1:55378 as iot_device_hub (p2, c1, k60, u'localhost').
1743900176: No will message specified.
1743900176: Sending CONNACK to iot_device_hub (0, 0)
1743900177: Received PUBLISH from iot_device_hub (d0, q0, r0, m0, 'iot_device_hub/sensor_data/bed_room/temperature', ... (18 bytes))
- copilotにこのログを見せたらuse_identity_as_usernameを設定するといいよと教えてくれて、確かにmanページにもそんなことが書いてあった
If require_certificate is true, the client must provide a valid certificate in order to connect successfully. In this case, the second and third options, use_identity_as_username and use_subject_as_username, become relevant. If set to true, use_identity_as_username causes the Common Name (CN) from the client certificate to be used instead of the MQTT username for access control purposes.
- ブログ記事は便利だがちゃんとマニュアルもしっかり読まないといけないと反省
- ただAPIドキュメント(https://mosquitto.org/api/files/mosquitto-h.html#mosquitto_tls_set) には書いていて欲しい気も
最終的なmosquitto.confの設定は以下の通り
listener 8883
allow_anonymous false
use_identity_as_username true
cafile /etc/mosquitto/ca_certificates/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key
require_certificate true