【小ネタ】AWS IoT(MQTT)へのアクセスでX.509証明書(秘密鍵)を使用しない場合もルート証明書は必須だった
1 はじめに
CX事業本部の平内(SIN)です。
今回、AWS IoT(MQTT)に、AccessKeyIDとSecretAccessKeyでアクセスして、少しハマってしまったので、覚書としてこれを記述しています。
結論から言うと、「X.509証明書と秘密鍵でアクセスしない場合でも、ルート証明書が必要」という事でした。ちなみに、この場合、MQTT over Websocket SigV4(ポート443)となります。
「当たり前だよ」って方には、未熟でほんとすいません。
2 X.509証明書ベース
AWS IoTのコンソールからモノ(Thing)を作成すると、1-Clickで証明書を発行することができます。
また、オンボードメニューから作成した場合にダウンロードできる接続キットは、X.509証明書(秘密鍵)を使用するものになっています。
AWSIoTMQTTClientを使用して、X.509証明書ベースでMQTTにアクセスする一般的なコードです。
AWSIoTMQTTClientのconfigureCredentialsでルート証明、秘密鍵及び、X.509証明書を設定して使用します。
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient clientId = "client_id" endPoint = "xxxxx-ats.iot.us-east-1.amazonaws.com" port = 8883 rootCA = "./root-CA.crt" privateKey = "./sample-private.key" certificate = "./sample-cert.pem" TOPIC = "topic" def main(): client = AWSIoTMQTTClient(clientId) client.configureEndpoint(endPoint, port) client.configureCredentials(rootCA, privateKey, certificate) client.configureAutoReconnectBackoffTime(1, 32, 20) client.configureOfflinePublishQueueing(-1) client.configureDrainingFrequency(2) client.configureConnectDisconnectTimeout(10) client.configureMQTTOperationTimeout(5) client.connect() client.subscribe(TOPIC, 1, callback) while True: time.sleep(5)
3 MQTT over Websocket SigV4
そして、AccessKeyIDとSecretAccessKeyでアクセスするパターンです。
認証情報をそのまま、デバイス側に置くことは推奨されていないので、Cosnitoのidentity poolを利用して、一時的な認証情報を取得しています。 AWSIoTMQTTClientのconfigureIAMCredentialsでアクセスキー、シークレットキー、及び、トークンを設定して使用します。
そして、今回、ハマった、configureCredentials(rootCA)によるルート証明書の設定です。
import boto3 import time from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient clientId = "client_id" endPoint = "xxxxx-ats.iot.us-east-1.amazonaws.com" port = 443 # Cognito経由の認証では、Websocket SigV4 しか使用できない rootCA = "./root-CA.crt" identityPoolId = 'ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' topic = "topic" def getAuthentication(identityPoolId): client = boto3.client('cognito-identity', 'ap-northeast-1') res = client.get_id(IdentityPoolId=identityPoolId) res = client.get_credentials_for_identity(IdentityId = res['IdentityId']) accessId = res['Credentials']['AccessKeyId'] secretKey = res['Credentials']['SecretKey'] token = res['Credentials']['SessionToken'] return (accessId, secretKey, token) def onSubscribe(client, userdata, message): print("message:{} topic:{}".format(message.payload, message.topic)) def main(): accessId, secretKey, token = getAuthentication(identityPoolId) # 一時的な認証情報を取得 client = AWSIoTMQTTClient(clientId, useWebsocket=True) # Websocket SigV4を利用 client.configureIAMCredentials(accessId, secretKey, token) client.configureCredentials(rootCA) # ルート証明書の設定が必要 client.configureEndpoint(endPoint, port) client.configureAutoReconnectBackoffTime(1, 32, 20) client.configureOfflinePublishQueueing(-1) client.configureDrainingFrequency(2) client.configureConnectDisconnectTimeout(10) client.configureMQTTOperationTimeout(5) client.connect() client.subscribe(TOPIC, 1, callback) while True: time.sleep(5) main()
4 最後に
エッジデバイスからのMQTTは、X.509証明書ベースで利用するのが普通だと思うのですが、MQTT以外にもAWSの他のリソースへのアクセス要件があったので、Cognitoのidentity poolを置くんだったら、MQTTもそれでアクセスしちゃえと思って、今回の作業になりました。
configureCredentials()を使用しないので、勝手にルート証明書も必要ないと思い込んで、Connect出来なくて悩みました。