chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

mqtt通信エラーの原因・・mqttライブラリでAWS IoT Coreに接続できないのは、許可されていないIDを設定していたため

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)