AWS IoTのクイック接続を利用してPythonとMosquittoからIoT CoreとMQTT通信してみた

AWS IoTのクイック接続を利用してPython SDKとMosquittoからMQTT通信し、作成されるリソースを確認しました
2024.01.04

AWS IoTのクイック接続を利用すると、わずか15分でデバイスからSDKを使ってAWS IoT CoreとのMQTT通信できます。

このウィザードを利用し、MacBook上からPython SDKでAWS IoT CoreとMQTT通信し、作成されたリソースを流用してMosquittoクライアントからもMQTT通信してみます。

合わせて、関連するAWS IoTリソースも確認します。

前提

Mac上で以下のクライアントからAWS IoT CoreにMQTT通信します

  • Python SDK
  • mosquitto_pub 2.0.18

クイック接続でデバイスを簡単接続

AWS IoTのクイック接続方法は簡単です。 AWSコンソールのIoTトップページから「デバイスを接続」をクリックし、ウィザードに従ってポチポチするだけです。

Step 1 : デバイスを準備する

Macターミナルからデバイスデータエンドポイントにpingできることを確認します。 このエンドポイントは、AWSアカウント x リージョンの組み合わせごとに存在します。

同一リージョンに、複数のエンドポイントを持てないことに注意しましょう。

$ ping a1b2c3-ats.iot.ap-northeast-1.amazonaws.com
PING a1b2c3-ats.iot.ap-northeast-1.amazonaws.com (54.168.193.76): 56 data bytes
64 bytes from 54.168.193.76: icmp_seq=0 ttl=245 time=26.501 ms
64 bytes from 54.168.193.76: icmp_seq=1 ttl=245 time=25.398 ms
64 bytes from 54.168.193.76: icmp_seq=2 ttl=245 time=25.524 ms
^C
--- a1b2c3-ats.iot.ap-northeast-1.amazonaws.com ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 25.398/25.808/26.501/0.493 ms

Step 2 : デバイスを登録して保護する

AWS IoTに接続するデバイスをモノ(Thing)と表現します。

モノに名前をつけましょう。

Step 3 : プラットフォームと SDK を選択します

プラットフォームとSDKをクライアント環境に合わせます。

今回は以下の組み合わせを選択しました。

  • Platform : Linux / macOS
  • SDK : Python

Step 4 : 接続キットをダウンロード

Zip形式の接続キット(connect_device_package.zip)をダウンロードします。

$ unzip -d connect_device_package connect_device_package.zip
Archive:  connect_device_package.zip
 extracting: connect_device_package/macbook.cert.pem
 extracting: connect_device_package/macbook.public.key
 extracting: connect_device_package/macbook.private.key
 extracting: connect_device_package/macbook-Policy
 extracting: connect_device_package/start.sh

$ cd connect_device_package

$ ls -1
TutorialTestThing-Policy
TutorialTestThing.cert.pem
TutorialTestThing.private.key
TutorialTestThing.public.key
start.sh

認証に利用されるX.509クライアント証明書を確認すると、2049年末まで有効な証明書がAmazon Trust Services(ATS)から発行されていることがわかります。

TutorialTestThing.cert.pem

$ openssl x509 -in TutorialTestThing.cert.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            d8:3a:40:6b:4f:a1:5c:0f:b8:b5:59:ac:47:89:82:99:7e:39:70:12
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: OU=Amazon Web Services O=Amazon.com Inc. L=Seattle ST=Washington C=US
        Validity
            Not Before: Jan  3 12:57:37 2024 GMT
            Not After : Dec 31 23:59:59 2049 GMT
        Subject: CN=AWS IoT Certificate
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:99:b9:c6:15:ff:7b:7f:a9:c4:f2:27:12:0b:1b:
                    ...
                    53:d4:48:c7:ce:18:46:45:46:36:fa:f7:8e:16:26:
                    99:f1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Authority Key Identifier:
                49:A8:81:2F:38:35:3B:E2:DE:A7:BA:F6:68:5E:44:A1:44:87:9F:5C
            X509v3 Subject Key Identifier:
                C4:97:88:41:6F:F6:5D:70:B9:10:C4:85:56:1B:5E:97:C7:42:04:D3
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        69:9c:3c:37:7d:36:c0:e8:51:d8:73:3b:8f:ef:22:40:1b:0d:
        ...
        d3:3e:be:25:93:8a:db:53:33:28:57:c5:eb:b5:92:d6:62:ba:
        ec:6a:19:5a
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIVANg6QGtPoVwPuLVZrEeJgpl+OXASMA0GCSqGSIb3DQEB
...
hBPdYtsoKTfFmzxO0pA2RkVjloDGhzfg0z6+JZOK21MzKFfF67WS1mK67GoZWg==
-----END CERTIFICATE-----

Step 5 : 接続キットを実行

インストラクション通りにクライアントプログラムを実行すると、AWSコンソールから、サブスクライブしたメッセージを確認できます。

$ chmod +x start.sh

$ ./start.sh

...

Running pub/sub sample application...
Connecting to a1b2c3-ats.iot.ap-northeast-1.amazonaws.com with client ID 'basicPubSub'...
Connection Successful with return code: 0 session present: True
Connected!
Subscribing to topic 'sdk/test/python'...
Subscribed with 1
Sending messages until program killed
Publishing message to topic 'sdk/test/python': Hello World!  [1]
Received message from topic 'sdk/test/python': b'"Hello World!  [1]"'
Publishing message to topic 'sdk/test/python': Hello World!  [2]
Received message from topic 'sdk/test/python': b'"Hello World!  [2]"'
...

Python Publisherスクリプトを確認

クイック接続では以下の通信を行います

  • TLS通信
  • MQTTプロトコル
  • X.509クライアント証明書ベースの認証
  • 8883ポート

起動するstart.sh の実態は AWS IoT Coreと通信するPython スクリプト(aws-iot-device-sdk-python-v2/samples/pubsub.py)です。

$ tail -n 1 start.sh
python3 aws-iot-device-sdk-python-v2/samples/pubsub.py --endpoint a1b2c3-ats.iot.ap-southeast-2.amazonaws.com --ca_file root-CA.crt --cert macbook.cert.pem --key macbook.private.key --client_id basicPubSub --topic sdk/test/python --count 0

そのために

  • AWS IoT Client SDK for Python v2 ダウンロード(python3 -m pip install ./aws-iot-device-sdk-python-v2)
  • 認証局の証明書のダウンロード(curl https://www.amazontrust.com/repository/AmazonRootCA1.pem > root-CA.crt)

などを行っています。

一度 start.sh を呼び出したあとであれば、Pythonスクリプトをそのまま呼び出しても動作します。

$ python3 aws-iot-device-sdk-python-v2/samples/pubsub.py \
  --endpoint a1b2c3-ats.iot.ap-northeast-1.amazonaws.com \
  --ca_file root-CA.crt \
  --cert TutorialTestThing.cert.pem \
  --key TutorialTestThing.private.key \
  --client_id basicPubSub \
  --topic sdk/test/python \
  --count 0

パラメーター名からそれぞれが何を表しているのかは、自明です。

MQTTにPublishするこのPythonプログラムをmosquitto_pubに移植します。

Pythonスクリプトをmosquitto_pubに移植

Eclipse MosquittoはオープンソースのMQTTブローカーです。

MacOSからは $ brew install mosquitto でインストールします。

mosquitto_pub を使い、Publish用クライアントプログラムと同じ通信手順でAWS IoT Coreと通信するのが以下のコマンドです。

$ mosquitto_pub -d \
  --host a1b2c3-ats.iot.ap-northeast-1.amazonaws.com \
  --cafile root-CA.crt \
  --cert TutorialTestThing.cert.pem \
  --key TutorialTestThing.private.key \
  --id basicPubSub \
  --topic "sdk/test/python" \
  --message '{  "message": "Hello from mosquitto_pub"}' 

Client basicPubSub sending CONNECT
Client basicPubSub received CONNACK (0)
Client basicPubSub sending PUBLISH (d0, q0, r0, m1, 'sdk/test/python', ... (41 bytes))
Client basicPubSub sending DISCONNECT

動作確認しやすいように、デバッグオプション(-d)も渡しています。

クイック接続で利用するAWSリソースを確認

最後に、今回のクイック接続で利用した関連リソースを確認します。

AWS IoT Coreのデータプレーンエンドポイント

IoTメッセージを送受信する AWS IoT CoreのデータプレーンエンドポイントはAWSアカウントxリージョンの単位で存在します。

同じリージョンに複数のエンドポイントを用意できない、言い換えると、異なるシステムも同じエンドポイントを利用しないといけない点にご注意ください。

次のAPIからもエンドポイントを確認できます。

$ aws iot describe-endpoint --endpoint-type iot:Data-ATS
{
    "endpointAddress": "a1b2c3-ats.iot.ap-northeast-1.amazonaws.com"
}

モノ(Thing)

AWS IoTに接続するデバイスをモノ(Thing)と表現します。

コンソールでは「AWS IoT > 管理 > モノ」から確認できます。

モノの認証

モノの認証を担うのがX.509クライアント証明書です。モノと関連付けられています。

コンソールでは 「AWS IoT > セキュリティ > 証明書」 から確認できます。

クライアント接続では、「接続キットをダウンロード」のタイミングで証明書、パブリックキー、プライベートキーのセットをダウンロードしました。

コンソールからモノを新規登録する際にも、これら一式が生成されます。

また、証明書を発行する認証局の証明書は次のURLから取得できます。

https://www.amazontrust.com/repository/AmazonRootCA1.pem

モノの認可

モノの認可を担うのがポリシーです。証明書と関連付けられ、IAM PolicyのAWS IoT版とみなせます。

コンソールでは、AWS IoT > セキュリティ > ポリシー から確認できます。

クイック接続では以下のアクションが許可されています。

  • iot:Publish
  • iot:Receive
  • iot:PublishRetain
  • iot:Subscribe
  • iot:Connect

iot:Connect を例に取ると、以下の通りリソースが制限されています。

    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:123456789012:client/sdk-java",
        "arn:aws:iot:ap-northeast-1:123456789012:client/basicPubSub",
        "arn:aws:iot:ap-northeast-1:123456789012:client/sdk-nodejs-*"
      ]
    }

このclient が何かというと、 --client_id basicPubSub--id basicPubSub で渡しているクライアントIDです。

試しに許可されていないクライアントID(dummy)を渡すと、Error: The connection was lost. というエラーが発生しました。

$ mosquitto_pub -d \
  --host a1b2c3-ats.iot.ap-northeast-1.amazonaws.com \
  --id dummy \
  ...

Client dummy sending CONNECT
Error: The connection was lost.

クライアントIDでclient/sdk-nodejs-* にマッチする sdk-nodejs-dummyと名乗ると、成功します。

$ mosquitto_pub -d \
  --host a1b2c3-ats.iot.ap-northeast-1.amazonaws.com \
  --id sdk-nodejs-dummy \
  ...

Client sdk-nodejs-dummy sending CONNECT
Client sdk-nodejs-dummy received CONNACK (0)
Client sdk-nodejs-dummy sending PUBLISH (d0, q0, r0, m1, 'sdk/test/python', ... (41 bytes))
Client sdk-nodejs-dummy sending DISCONNECT

モノ/証明書/ポリシーの関係

モノと証明書は多:多の関係であり、証明書とポリシーも多:多の関係です。

証明書の詳細ページからは、紐づいているポリシーとモノの一覧を確認できます。

参考