MQTTゲートウェイ Fujiで証明書によるクライアント認証に対応したので、AWS IoTに繋いでみました。
<2016/2/16:追記:Fujiが1.0.0バージョンリリースとなり、この機能が正式に取り込まれていました>
以下の記事を大変参考にさせていただきました。ありがとうございます。
まずAWS IoT側でThingを作る
以下の情報が得られます
- Thingのcertificate
- 証明書のprivate_key
- 証明書のprivate_keyに対応するpublick_key
- MQTT topic
証明書の認証のルートCAはシマンテックサイトから取得します。
curl -o rootCA.pem https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
ところで、ThingsのshadowをupdateするにはどんなJSONを投げるのか?
API:http://docs.aws.amazon.com/iot/latest/developerguide//thing-shadow-mqtt.html
MQTTクライアントのmosquitto_sub, mosquitto_pubコマンドを使って動作確認してみます。
まず、結果をモニタするためのsubscribeをしておきます
mosquitto_sub -t "\$aws/things/RoomTemp/#" -h XXX.amazonaws.com -p 8883 --cafile rootCA.pem --cert UNIQUEID-certificate.pem.crt --key UNIQUEID-private.pem.key -d
Client mosqsub/hostname. sending SUBSCRIBE (Mid: 1, Topic: $aws/things/RoomTemp/#, QoS: 0)
ではJSONデータをpublishしてみましょう。
値だけのJSONをpublishしてみた
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update', ... (13 bytes))
{"value": 31}
すると、rejectを食らっていた
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update/rejected', ... (53 bytes))
{"code":400,"message":"Missing required node: state"}
desired
とreported
だけでもダメ。
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update', ... (59 bytes))
{ "desired": { "value": 30 }, "reported": { "value": 30 } }
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update/rejected', ... (53 bytes))
{"code":400,"message":"Missing required node: state"}
publishするJSONは以下の形式に従う必要があります。良い子はちゃんとドキュメントを読みましょう。
註:clientTokeとversionはオプション
{
"state": {
"desired": {
"attribute1": integer2,
"attribute2": "string2",
...
"attributeN": boolean2
},
"reported": {
"attribute1": integer1,
"attribute2": "string1",
...
"attributeN": boolean1
}
}
"clientToken": "token",
"version": version
}
成功例
desiredとreportedが異なる値にしてみました。
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update', ... (69 bytes))
{"state":{ "desired": { "value": 30 }, "reported": { "value": 31 } }}
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update/delta', ... (103 bytes))
{"version":4,"timestamp":1447815699,"state":{"value":30},"metadata":{"value":{"timestamp":1447815699}}}
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update/accepted', ... (197 bytes))
{"state":{"desired":{"value":30},"reported":{"value":31}},"metadata":{"desired":{"value":{"timestamp":1447815699}},"reported":{"value":{"timestamp":1447815699}}},"version":4,"timestamp":1447815699}
この結果、out of sync
の状態になります。
以下のようにdesired
とreported
が一致するとAWSのコンソールにShadow Status In sync
と表示されるようになります。
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update', ... (69 bytes))
{"state":{ "desired": { "value": 30 }, "reported": { "value": 30 } }}
Client mosqsub/hostname. received PUBLISH (d0, q0, r0, m0, '$aws/things/RoomTemp/shadow/update/accepted', ... (197 bytes))
{"state":{"desired":{"value":30},"reported":{"value":30}},"metadata":{"desired":{"value":{"timestamp":1447815776}},"reported":{"value":{"timestamp":1447815776}}},"version":5,"timestamp":1447815776}
fujiで繋げてみます
設定ファイル
[gateway]
name = "RoomTemp"
[[broker."aws/1"]]
host = "ABCDEFG.iot.ap-northeast-1.amazonaws.com"
port = 8883
topic_prefix = "aws"
tls = true
cacert = "/AWSThing/rootCA.pem"
client_cert = "/AWSThing/XXXXX-certificate.pem.crt"
client_key = "AWSThing/XXXXX-private.pem.key"
[device."RoomTemp"]
type = "dummy"
broker = "aws"
interval = 30
qos = 0
payload = "30"
実行結果
$ ./fuji -c awsthing.toml -d
INFO[0000] start fuji gateway
WARN[0000] status create error, no status found
INFO[0000] broker connecting to: tcp://ABCDEF.iot.ap-northeast-1.amazonaws.com:8883
INFO[0000] start dummy device
INFO[0000] client connected
DEBU[0030] message got: {aws/RoomTemp/RoomTemp/dummy/publish}
DEBU[0030] message published: {aws/RoomTemp/RoomTemp/dummy/publish}
知見、のような物
証明書が無効な場合、以下のようなエラーが出るらしい
ERRO[0000] Failed to start MQTT client: Network Error : remote error: unknown certificate
ERRO[0000] Failed to start MQTT client: Network Error : remote error: unknown certificate authority
どうやらwillの設定があるとうまく繋がらないらしい
もともとAWS IoTのシステムはWillをサポートせず、Shadowの状態として表現できるようなので特に問題では無いと思いますが、設定でwill_messageを指定したらつながらず、ちょっと悩みました(2015年12月下旬時点)