AWS IoT Coreまでの通信ってどうなっているの?【TCP/TLS/MQTT】
記事を書こうと思ったきっかけ
これまでにAWSIoTCoreを使用したMQTTパブリッシュやサブスクライブの実装経験があったが、ここ最近、基本情報技術者の勉強をしているうちに、「そういえばTCP・TLS・MQTT」など無意識に使っていて中身を理解できていないなと思った。
そこで今回の記事では、TCP・TLS・MQTTについて簡単に振り返りつつ、実際にWiresharkを使用してパケットをキャプチャして通信の流れを把握してみたいと思う。
まずは、各プロトコルの概要をざっと振り返ってみました。
TCPについて
まずは、TCPについて簡単にまとめです。
TCPとは
Transmission Control Protocol の略で、OSI参照モデルのトランスポート層で伝送を管理するプロトコル。
1対1の通信で、相手に都度確認を取りながら行う信頼性のある通信。
TCPハンドシェイク
TCPではデータのやり取りを開始する前に、スリーウェイハンドシェイクという動作を行います。
- SYN: クライアントがサーバーに接続要求を送る
- SYN-ACK: サーバーが接続要求を承認し、応答を返す
- ACK: クライアントが応答を確認し、接続が確立される
この手順により、通信相手が存在し、データ転送の準備ができていることを確認します。
TLSについて
次に、TLSについて簡単にまとめです。
TLSとは
TLS(Transport Layer Security)は、データの暗号化と通信の安全性を確保するためのプロトコルであり、主にHTTPSや今回使用するMQTT over TLSなどで使用される。
TLS接続
TLS通信の基本的な流れは以下の通り。
- Client Hello: クライアントがTLSの通信開始をサーバーに通知
- Server Hello: サーバーがTLSのバージョンや暗号方式を通知
- 証明書交換: サーバーが証明書を送信し、クライアントが検証
- 鍵交換: 共通鍵を生成し、安全な通信を確立
- 暗号化通信開始: 双方が暗号化されたデータをやり取りする
MQTTについて
次に、MQTTについて簡単にまとめです。
MQTTとは
MQTT(Message Queuing Telemetry Transport)は、軽量なメッセージングプロトコルであり、IoTデバイスなどのリソースが限られた環境で使用される。
通信モデルはパブリッシュ/サブスクライブ方式が採用されている。
パブリッシャー: メッセージをブローカー(サーバー)に送信
ブローカー: メッセージを受け取り、適切なサブスクライバーに配信
サブスクライバー: ブローカーを通じてメッセージを受信
今回で言うと、後述のPythonスクリプトを実行するクライアントがパブリッシャーで、AWSIoTCoreがブローカーに当たる。
実際にAWSIoTCoreにパブリッシュしてみた
AWSIoTCoreの設定
AWSIoTCoreの構築方法は、以前Qiitaにてまとめているので、必要時はこちらを参照いただけると幸いです。
(※この記事で紹介するスクリプトをそのまま使える環境ではありませんので、あくまで参考として。)
実装
Pythonで簡単にのサンプルプログラムを作成しました。
大まかな流れは以下。
- 接続
- MQTTパブリッシュ
- 切断
from awscrt import mqtt
from awsiot import mqtt_connection_builder
import json
# 実際の使用環境に合わせて設定するデータ群
input_endpoint = '**********.iot.ap-northeast-1.amazonaws.com'
input_port = 8883
input_cert = '**********.cert.pem'
input_key = '**********.private.key'
input_ca = 'root-CA.crt'
input_clientId = 'basicPubSub'
input_topic = 'sdk/test/python'
def on_connection_interrupted(connection, error, **kwargs):
print("Connection interrupted. error: {}".format(error))
def on_connection_resumed(connection, return_code, session_present, **kwargs):
print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present))
if __name__ == '__main__':
mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=input_endpoint,
port=input_port,
cert_filepath=input_cert,
pri_key_filepath=input_key,
ca_filepath=input_ca,
on_connection_interrupted=on_connection_interrupted,
on_connection_resumed=on_connection_resumed,
client_id=input_clientId,
clean_session=False,
keep_alive_secs=30
)
print("Connecting to endpoint with client ID")
connect_future = mqtt_connection.connect()
# Future.result() waits until a result is available
connect_future.result()
print("Connected!")
message = "test message"
print("Publishing message to topic '{}': {}".format(input_topic, message))
message_json = json.dumps(message)
mqtt_connection.publish(
topic=input_topic,
payload=message_json,
qos=mqtt.QoS.AT_LEAST_ONCE)
# Disconnect
print("Disconnecting...")
disconnect_future = mqtt_connection.disconnect()
disconnect_future.result()
print("Disconnected!")
スクリプトを実行し、無事にAWS IoTCoreまでパブリッシュされると、以下のような結果になる。
mqtt_packet_test % python publish.py
Connecting to endpoint with client ID
Connected!
Publishing message to topic 'sdk/test/python': test message
Disconnecting...
Disconnected!
WireSharkでパケットをキャプチャ
ここからはWiresharkを使って、実際のパケットをキャプチャした結果を簡単にまとめました。
Wiresharkとは
Wiresharkとは、LAN(ローカルエリアネットワーク)を通じたデータ通信について、特定のデバイスに流れているトラフィックの情報量などを可視化できるパケットキャプチャツールです。
まずは全体
上記のサンプルスクリプトを実行すると、以下のようなパケットがキャプチャできた。
ちなみにWiresharkをそのまま使用すると、他のパケットが大量に出てしまい、欲しい部分が見にくくなったのでフィルタ機能を使用して、MQTTに使用される最低限のパケットだけ見れるようにしました。
フィルタの指定条件は以下。
同じ通信内容でIPv6とIPv4が同時に表示されたので、IPv4に絞って流れがわかりやすいように調整しました。
TCP部分
始めの3行でTCPの3ウェイハンドシェイクが確認できました。
TLS部分
赤枠の部分で以下のTLSコネクションのやり取りが行われていることが確認できました。
- Client Hello
- Server Hello
- Certificate
- Client Key Exchange
- Encrypted Handshake Message
MQTT部分
Application Dataの部分から実際のMQTT通信が行われていることが確認できました。
Application DataのTCP Payloadを見てみると、TLSにより暗号化されて見えなくなっているようです。
セッション終了部分
Encrypted AlertからRSTまでのあたりでセッション終了のやり取りをしていることが確認できた。
まとめ
WireSharkを使用したのは初めてだったのだが、「TCPハンドシェイクっていうのがあるんだ〜」みたいなざっくりの理解から、実際の通信をキャプチャして確認することで解像度が上がる感じがしたので、ネットワーク初心者にとっていい経験になりそう。
それぞれのパケットの中身のデータを細かく見たわけではないので、もっと細かいところまで見れるように経験値を積んでいきたいと思う。
そしてHTTPSやその他のプロトコルの通信ができないみたいな時も、パケットを確認してどこまで通信ができていてどこで止まってるか等確認することでトラブルシューティングにも活用できたらと思う。
Discussion