MicroPythonの標準ライブラリのmqtt.simpleを使ってなぜAWS IoT Coreに接続できないのかずっと悩んでいた。
結果としては、許可されていないIDを指定して接続しようとして認証エラーになっていた。
client_id = 'basicPubSub' だと正常なのだが、当初は、IDを適当に 'esp32_0012'等を指定していてずっとエラーだった。(正確には無応答)
AWS IoT SDKのサンプルでは、IDとして'basicPubSub'を使っていて、このIDだと正常に接続できるのが分かった。
正常接続時のログ(CloudWatch) ("clientId": "basicPubSub")
{ "timestamp": "2022-04-21 14:40:06.824", "logLevel": "INFO", "traceId": "0eba8669-bd79-faf6-ff62-f7bf66cbae27", "accountId": "36000000000000074", "status": "Success", "eventType": "Subscribe", "protocol": "MQTT", "topicName": "topic_1", "clientId": "basicPubSub", "principalId": "16f31f1aaffde60f2ac508f99b9604b3aaf601c6531534fc99919d52aa83324f", "sourceIp": "2400:4150:5060:7900:dea6:32ff:fed3:72ee", "sourcePort": 60431 }
接続異常時のログ(認証エラー)( "clientId": "python1")
{ "timestamp": "2022-04-21 15:03:09.905", "logLevel": "ERROR", "traceId": "e11e02ca-342a-a573-7999-9adec3af4aad", "accountId": "360000000000074", "status": "Failure", "eventType": "Connect", "protocol": "MQTT", "clientId": "python1", "principalId": "16f31f1aaffde60f2ac508f99b9604b3aaf601c6531534fc99919d52aa83324f", "sourceIp": "2400:4150:5060:7900:dea6:32ff:fed3:72ee", "sourcePort": 56783, "reason": "AUTHORIZATION_FAILURE", "details": "Authorization Failure" }
しかし、id:basicPubSubってデフォルトで許可されているIDなんだろうか。。Client証明書のSubjectがbasicPubSubとか??
Client証明書を調べてみたが、そんな単純は話ではなかった。
$ openssl x509 -text -in cert.pem.crt Certificate: Data: Version: 3 (0x2) Issuer: OU = Amazon Web Services O=Amazon.com Inc. L=Seattle ST=Washington C=US Validity Not Before: Apr 18 23:26:09 2022 GMT Not After : Dec 31 23:59:59 2049 GMT Subject: CN = AWS IoT Certificate
IDの制約は証明書に紐づくPolicyで管理されているのが分かった(過去にそう理解していたが、設定の終わったAWS IoT Coreを使いまわししていたので忘れていた)
以下は証明書に紐づくPolicy、Policy内に接続許可IDを指定している(昔に設定したのだろうけど忘れていた)
上記Pocilyで動いているなら、basicPubSub以外に、sensor01~sensor03も使えるはず。
■おまけ
原因が分からずmqttライブラリをばらしてどこでエラーになるのか、一行ずつ叩いて調べていた。以下はmqttライブラリをばらして書いたMQTT Clientの例 (connectまで)
import usocket as socket import ussl ENDPOINT = 'a3bxxxxxxxkf7t-ats.iot.ap-northeast-1.amazonaws.com' PORT = 8883 client_id = 'basicPubSub' # OK CERT="certs/certificate.pem.crt" KEY="certs/private.pem.key" def msg_gen(id): clean_session = 1 keepalive = 60 buf = [0]*40 buf[0] = 0x10 buf[1] = len(id) + 10 + 2 buf[3] = 4 buf[4] = ord('M') buf[5] = ord('Q') buf[6] = ord('T') buf[7] = ord('T') buf[8] = 4 buf[9] = clean_session << 1 buf[10] = (keepalive >> 8) & 0xff buf[11] = keepalive & 0xff buf[13] = len(id) idx = 14 for chr in id: buf[idx] = ord(chr) idx += 1 return bytes(buf[0:idx]) with open(KEY, 'r') as f: key = f.read() with open(CERT, 'r') as f: cert = f.read() # SSL certificates. ssl_params={"cert": cert, "key": key, 'server_side': False} addr = socket.getaddrinfo(ENDPOINT, PORT)[0][-1] clean_session = 1 # set 0 or 1 sock = socket.socket() sock.connect(addr) sock = ussl.wrap_socket(sock, **ssl_params) sock.write(msg_gen(client_id)) print("===ret============") sock.read() print("==================")
■追記
なぜ使えるIDがbasicPubSubなのかをググっていたら、2020年の自分の記事に出くわした。アクセス許可ファイルに、id: basicPubSubが許可設定されているらしい。当時はAWS IoT Coreを使い始めた時期だったので、自分で設定したようだった。今となっては完全に忘れている。
AWS IoT Coreを使ってみる (デバイスからMQTTでPub/Sub) - chakokuのブログ(rev4)