42
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Azure IoT Hubを生MQTTS(mosquitto)やHTTP RESTで使う方法

Last updated at Posted at 2017-06-25

デバイス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 を探します

azure-iot-hub-0.png

必要事項を入力して、作成します。無料Tierを利用しましょう

azure-iot-hub-1.png

作成は3分くらいで完了します

IoT Hub デバイスを作成する

IoT Hub では登録されたデバイスから通信できるようになるため、デバイスの追加を行う必要があります
今回は dev0 というデバイス名を持つデバイスを作成します

IoT Hub の管理ページから Device Explorer を選択し [追加] より dev0 として保存します

azure-iot-hub-2.png

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
  • --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 から当該デバイスを選ぶと表示されます

azure-iot-hub-3.png

"共有アクセスポリシー"に割り当てられた"共有アクセスキー"

本稿では、デバイス毎に割り当てられた"共有アクセスキー"を使いました
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 という文字列は 共有アクセスポリシー の中のポリシー名です。キーは詳細ページに表示されています

azure-iot-hub-5.png

デバイス自体は作る(登録)する必要があるため、基本的にはデバイス毎の共有アクセスキーを使うのが良いでしょう

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] します

azure-iot-hub-4.png

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トークンが大変

参考資料;

42
27
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?