デバイスSDKとか、デバイスExplorerとか、謎テクノロジーは嫌なんじゃー!という方にお送りする Azure IoT Hubをmosquittoで使う方法 をご紹介します
ここで解説すること、しないこと
- すること;
- セキュリティトークン(SASトークン)を利用した mosquitto_pub/_sub によるメッセージの送受信
- しないこと;
- X.509証明書を利用した送受信
必要なもの
- mosquitto コマンド (Ubuntuなら
apt install mosquitto-clients
で入ります) - python(とか)
- SASトークンを生成するためのヘルパコマンドを実行する
最終的な mosquitto_pub/_sub コマンド
下記の条件の場合における mosquitto_sub は後述の通りです
これを組み立てるのに必要な情報や手続きを解説します
- IoT Hub名 :
testing-iothub0
- デバイス ID :
dev0
$ mosquitto_pub -d -q 1 --capath /etc/ssl/certs/ -V mqttv311 -p 8883 \
-h testing-iothub0.azure-devices.net \
-i dev0 \
-u "testing-iothub0.azure-devices.net/dev0/api-version=2016-11-14" \
-P "SharedAccessSignature sr=<snip>&skn=<snip>&sig=<snip>&se=1498222991" \
-t "devices/dev0/messages/events/"
-m '{"v":1}'
$ mosquitto_sub -d -q 1 --capath /etc/ssl/certs/ -V mqttv311 -p 8883 \
-h testing-iothub0.azure-devices.net \
-i dev0 \
-u "testing-iothub0.azure-devices.net/dev0/api-version=2016-11-14" \
-P "SharedAccessSignature sr=<snip>&skn=<snip>&sig=<snip>&se=1498222991" \
-t "devices/dev0/messages/devicebound/#"
Azure IoT Hub を作成する
今回 testing-iothub0
という名前の Azure IoT Hub を作成します
新規作成から IoT Hub を探します
必要事項を入力して、作成します。無料Tierを利用しましょう
作成は3分くらいで完了します
IoT Hub デバイスを作成する
IoT Hub では登録されたデバイスから通信できるようになるため、デバイスの追加を行う必要があります
今回は dev0
というデバイス名を持つデバイスを作成します
IoT Hub の管理ページから Device Explorer を選択し [追加] より dev0
として保存します
SASトークンを生成する
SASはShared Access Signatureの略で、文字列的には SharedAccessSignature ...
から始まる文字列になります
ドキュメント中にでは「セキュリティトークン」や「トークン」とだけ書かれていたり、何気にややこしい奴ですが、実態は SASトークン です
これは手で生成するのが難しいので、生成するヘルパープログラムを用意したほうがいいでしょう
サンプルは Azure IoT Hubセキュリティのページ に掲載されています
下記コードは、上記で掲載されているPythonコードに、コマンドラインから使いやすいようにoptparseを加えたものです
## generate_sas_token.py
from base64 import b64encode, b64decode
from hashlib import sha256
from time import time
from urllib import quote_plus, urlencode
from hmac import HMAC
def generate_sas_token(uri, key, policy_name, expiry=3600):
ttl = time() + expiry
sign_key = "%s\n%d" % ((quote_plus(uri)), int(ttl))
print sign_key
signature = b64encode(HMAC(b64decode(key), sign_key, sha256).digest())
rawtoken = {
'sr' : uri,
'sig': signature,
'se' : str(int(ttl))
}
if policy_name is not None:
rawtoken['skn'] = policy_name
return 'SharedAccessSignature ' + urlencode(rawtoken)
from optparse import OptionParser
parser = OptionParser()
parser.add_option("-u", "--uri", dest="uri", help="URI", metavar="URI")
parser.add_option("-k", "--key", dest="key", help="KEY", metavar="KEY")
parser.add_option("-c", "--policy", dest="policy_name", help="Policy Name", metavar="POLICY_NAME")
parser.add_option("-a", "--age", dest="age", help="Age of Token (sec)", metavar="AGE", default=3600)
(options, args) = parser.parse_args()
print generate_sas_token(options.uri, options.key, options.policy_name, int(options.age))
$ python generate_sas_token.py --help
Usage: generate_sas_token.py [options]
Options:
-h, --help show this help message and exit
-u URI, --uri=URI URI
-k KEY, --key=KEY KEY
-c POLICY_NAME, --policy=POLICY_NAME
Policy Name
-a AGE, --age=AGE Age of Token (sec)
-
--uri
- リソース URI:
{IoT hub name}.azure-devices.net/devices/{device id}
という文字列。たとえば、testing-iothub0.azure-devices.net/devices/dev1
- リソース URI:
-
--key
- 共有アクセスキー(署名キーとも書かれてる): 「デバイスの詳細」に記載された プライマリ キー (対称キーとも書かれてる)
-
--policy
- 共有アクセスポリシー名: デバイスの詳細に記載されたプライマリキーを利用する場合は、指定不要
-
--age
- 有効期限: 生成するトークンの有効期限を秒で指定。60 => 1分間有効なトークンが生成される。デフォルト3600秒
$ python generate_sas_token.py \
-u testing-iothub0.azure-devices.net/devices/dev0 \
-k Z/yan+rwy0gqSQuKpPdCX7+w/F0HruLB+U4YjWAcz0w=
SharedAccessSignature sr=testing-iothub0.azure-devices.net%2Fdevices%2Fdev0&sig=rYvmWTuFt8YyTPDodLuYVLta962QCFLJJuo1SL4fB1w%3D&se=1498295991
共有アクセスキーの拾い方
Device Explorer から当該デバイスを選ぶと表示されます
"共有アクセスポリシー"に割り当てられた"共有アクセスキー"
本稿では、デバイス毎に割り当てられた"共有アクセスキー"を使いました
IoT Hubには 共有アクセスポリシーに割り当てられた"共有アクセスキー" ってのもあります
ややこしい!
この 共有アクセスポリシーに割り当てられた"共有アクセスキー" とは、作成したIoT Hub内すべてのデバイスすべてに同じようにアクセスできるマスターキー相当です
このマスターキー相当である 共有アクセスポリシーに割り当てられた"共有アクセスキー" を利用する場合は generate_sas_token
が以下のように変わります
(この場合、デバイス毎に割り当てられた共有アクセスキーは使用しません)
$ python generate_sas_token.py \
-u testing-iothub0.azure-devices.net/devices/dev0 \
-k TEOLRugIC/ocpimvE05R3HSeSxhJ9FHNJI9KfdBCG/A= \
-c device
SharedAccessSignature sr=testing-iothub0.azure-devices.net%2Fdevices%2Fdev0&skn=device&sig=%2Fip%2F9kwV4U1Mdo2BG%2BuNpO44D26sLjRtNcXOFu6n3gk%3D&se=1498293853
device という文字列は 共有アクセスポリシー の中のポリシー名です。キーは詳細ページに表示されています
デバイス自体は作る(登録)する必要があるため、基本的にはデバイス毎の共有アクセスキーを使うのが良いでしょう
mosquitto の実行
おつかれさまでした、ここまでの作業で ようやっと mosquitto を実行することができます
-
--capath
はOSのCertificateファイルが格納されているディレクトリを書くようにしてください。Ubuntu ならば/etc/ssl/cert
です - IoT Hubは MQTT 3.1.1 接続のみサポートしているため
-V mqttv311
は必須です -
-h
-i
-u``-P
-t
それぞれについては詳細は Azure IoT Hub の MQTTサポート / MQTTプロトコルの直接使用 を見てください
メッセージ送信
$ mosquitto_pub -d -q 1 --capath /etc/ssl/certs/ -V mqttv311 -p 8883 \
-h testing-iothub0.azure-devices.net \
-i dev0 \
-u "testing-iothub0.azure-devices.net/dev0/api-version=2016-11-14" \
-P "SharedAccessSignature sr=<snip>&skn=<snip>&sig=<snip>&se=1498222991" \
-t "devices/dev0/messages/events/"
-m '{"v":1}'
メッセージ受信
$ mosquitto_sub -d -q 1 --capath /etc/ssl/certs/ -V mqttv311 -p 8883 \
-h testing-iothub0.azure-devices.net \
-i dev0 \
-u "testing-iothub0.azure-devices.net/dev0/api-version=2016-11-14" \
-P "SharedAccessSignature sr=<snip>&skn=<snip>&sig=<snip>&se=1498222991" \
-t "devices/dev0/messages/devicebound/#"
mosquitto_sub しているクライアントに対して、クラウド側からメッセージpushのテスト
デバイスの詳細ページから Message Body を入力して [Send Message] します
subscribeの様子
Client dev0 sending CONNECT
Client dev0 received CONNACK
Client dev0 sending SUBSCRIBE (Mid: 1, Topic: devices/dev0/messages/devicebound/#, QoS: 1)
Client dev0 received SUBACK
Subscribed (mid: 1): 1
Client dev0 sending PINGREQ
Client dev0 received PINGRESP
Client dev0 received PUBLISH (d0, q1, r0, m2, 'devices/dev0/messages/devicebound/%24.to=%2Fdevices%2Fdev0%2Fmessages%2FdeviceBound', ... (4 bytes))
Client dev0 sending PUBACK (Mid: 2)
Hi!!
Azure IoT Hubの注意点
pub時のペイロードフォーマットはJSONのみ
どうやらそういうことらしいです
MQTT同士でのpub/subはできない模様
IoT Hub はパブリッシャーとサブスクライバー間の汎用メッセージング ブローカーではないため って書いてあるとおり、mosquitto_subでsubscribeしておきつつ、mosquitto_pubをしても、メッセージの受信ができません
Azure IoT HubはMQTTブローカとして使うな!というMSさんのメッセージなのでしょう
(AWS IoTはできますね。。。)
ちなみにHTTP RESTでも同じ模様
AMQPSならできるっぽい (tcpdumpしたらamqpsだった)
"共有アクセスキー" のセカンダリーキー
ストレージアカウントはセキュリティの観点からアクセスキーを一定期間でローテーションすることを目的として、プライマリとセカンダリのキーが発行されます
具体的な運用としては、アクセスに使うキーをプライマリからセカンダリに変更し、その間にプライマリのキーを再生成してから元に戻す(そのあとセカンダリも再生成する)といった形です
しかしIoT Hubのデバイスや共有アクセスポリシーに割り当てられた共有アクセスキーは再生成をする方法が無いようです
(少なくともWebコンソールからはできない)
なんのためにあるのか、いまいちわかりません。。。
HTTP REST
HTTP RESTでも基本的には操作は同じです
メッセージ送信
$ curl -v -X POST \
-H "Authorization: SharedAccessSignature sr=ma2iot-aeM6Xeel.azure-devices.net%2Fdevices%2Fdev0&sig=qlbXcpKVzYU%2B4Yfs21Yr%2BeLxedv4a2EkoW3bQRhHnZQ%3D&se=1498435678" \
-H "Content-Type: application/json"
-d "event message!"
"https://testing-iothub0.azure-devices.net/devices/dev0/messages/events/?api-version=2016-11-14"
メッセージ受信
$ curl -v -X GET \
-H "Authorization: SharedAccessSignature sr=ma2iot-aeM6Xeel.azure-devices.net%2Fdevices%2Fdev0&sig=qlbXcpKVzYU%2B4Yfs21Yr%2BeLxedv4a2EkoW3bQRhHnZQ%3D&se=1498435678" \
"https://testing-iothub0.azure-devices.net/devices/dev0/messages/devicebound/?api-version=2016-11-14"
あとがき
SASトークンが大変