AWS IoTにRubyのMQTTクライアントから接続
ここを参考に進める
前提
- EC2にVM立てておく。これをThingとして使う。
- AWS CLIが使える
-
jq
を入れておく -
ruby
を入れておく
概要
設定の流れとしては以下のようになる。作業のほとんどは安全な接続を確立するためのセキュリティ関連の設定である。デバイスを接続するためにmTLSを使うので証明書やキーの設定が必要になる。
-
Thing
とCertificate
とPolicy
の3つのオブジェクトを作ってcertificateArn
で関連付け -
Certificate
から証明書やキーのファイルを得る - AWSからルート証明書をダウンロード
- 得たファイルをMQTTクライアントで使う
AWSのルート証明書AmazonRootCA1.pem
はサーバー(IoT Core)認証に使う。ブラウザがWebブラウズの際にTLSでサーバー認証を行うがあれと同じ。一方でdevice.pem.crt
はクライアント認証に使う。
EC2のVMでの設定
この部分はやや長いのでスクリプトにしました
Thing毎にディレクトリを作成する。あとでいくつかファイルを置く。
mkdir t1 && cd t1
Thing t1
をつくる
aws iot create-thing --thing-name t1
{
"thingArn": "arn:aws:iot:us-west-2:<account-id>:thing/t1",
"thingName": "t1",
"thingId": "769f4f7b-190f-489g-a261-e84e184d99a6"
}
ちなみにARNは以下のように定義されるらしい
arn:partition:service:region:account-id:resourcetype/resource
以下で作ったThingを確認できる
aws iot list-things
Amazon 認証機関 (CA) 証明書のコピーをダウンロード
wget https://www.amazontrust.com/repository/AmazonRootCA1.pem
クラウド側にプライベートキー、パブリックキー、および X.509 証明書ファイルを作成。以下のコマンドの出力に後に必要な証明書やキーがはいっている。一部はオプションでファイルに書き出しつつ、全体をcerts-and-keys.json
に書いている。
aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile "./device.pem.crt" \
--public-key-outfile "./public.pem.key" \
--private-key-outfile "./private.pem.key" \
> certs-and-keys.json
作った証明書をthingに対応づける
aws iot attach-thing-principal \
--thing-name t1 \
--principal $(jq -r .certificateArn certs-and-keys.json)
Policyのファイルをつくる。Policyは複数のThingで共通のものになると思うので、t1
より一段上のディレクトリにでも置いておく。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Subscribe",
"iot:Receive",
"iot:Connect"
],
"Resource": [
"*"
]
}
]
}
上記のJSONを使ってポリシーp1
を作る
aws iot create-policy \
--policy-name "p1" \
--policy-document "file://../policy.json"
証明書にポリシーを関連づける
aws iot attach-policy --policy-name p1 --target $(jq -r .certificateArn certs-and-keys.json)
Ruby MQTT クライアント
各種証明書とキーを使う。デバイスが接続するエンドポイントは複数あるが、MQTTのやりとりなどデータプレーンのエンドポイントは以下で得ることができる。
aws iot describe-endpoint --endpoint-type iot:Data-ATS
クライアントのサンプルはこんな感じ。
require 'rubygems'
require "mqtt"
require "json"
msg = JSON.generate({
"d" => {
"test_data" => 10
}
})
endpoint = `aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress`.chomp
MQTT::Client.connect(
host: endpoint,
port: 8883,
ssl: true,
cert_file: "device.pem.crt",
key_file: "private.pem.key",
ca_file: "AmazonRootCA1.pem") do |client|
client.publish("topic/test", msg)
end
require "mqtt"
endpoint = `aws iot describe-endpoint --endpoint-type iot:Data-ATS | jq -r .endpointAddress`.chomp
MQTT::Client.connect(
host: endpoint,
port: 8883,
ssl: true,
cert_file: "device.pem.crt",
key_file: "private.pem.key",
ca_file: "AmazonRootCA1.pem") do |client|
client.subscribe("topic/test")
topic,message = client.get
p [topic, message]
end
sub.rb
を実行した状態でpub.rb
を動かすとsub側でメッセージを確認できる
$ ruby sub.rb
$ ["topic/test", "{\"d\":{\"test_data\":10}}"]
QoSの設定
Method: MQTT::Client#subscribe
String: subscribe to one topic with QoS 0
Array: subscribe to multiple topics with QoS 0
Hash: subscribe to multiple topics where the key is the topic and the value is the QoS level
For example:
client.subscribe( 'a/b' )
client.subscribe( 'a/b', 'c/d' )
client.subscribe( ['a/b',0], ['c/d',1] )
client.subscribe( 'a/b' => 0, 'c/d' => 1 )
Discussion