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 CertificateIDの制約は証明書に紐づく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)