AWS IoT 証明書で安全にデバイス内の画像をS3にアップロード

記事タイトルとURLをコピーする

こんにちは! サーバーワークスの松井です。

今回は、AWS IoT 証明書経由で安全にデバイス内にある画像をS3にアップロードする方法をご紹介します。

今回紹介する方法では、AWS Credentialをデバイスに配置しないでAWS環境に画像を送信できるので安全です。

デバイス内にある画像をS3にアップロードする方法としては、AWS IoT のロールエイリアスを使って、一時的にデバイスに権限を与え、AWS CLI経由でアップロードする方法もあります。

ロールエイリアスの使い方は、以下のブログをご参考ください。

blog.serverworks.co.jp

事前準備

・IoT Coreでのモノの作成

・デバイスへの証明書の配置

・デバイスに画像を配置

・python3.9が実行できる環境

・画像のアップロード先のS3バケットの作成

・S3へのPUT権限がついているpython環境のLambdaの作成

・Cloud9を作成

1. 画像送信プログラムを作成

Cloud9に画像送信プログラムを配置します。

以下のディレクトリ階層にてプログラムを作成していきます。

・
├── conf
│   └── setting.conf
├── main.py
├── settings.py
├── test.jpg

main.py

import requests
import json
import boto3
import settings
from boto3.session import Session 
 
cert_filepath = settings.CERT_PATH
pri_key_filepath = settings.PRIVATE_KEY_PATH
ca_filepath = settings.CA_FILE_PATH
endpoint = settings.IOT_CREDENTIAL_ENDPOINT
device_id = settings.DEVICE_ID




def main():
    # mqtt クライアント初期化
    mqtt_connection = init_mqtt()

       img_file = open("./test.jpg", 'rb').read()
       bin = base64.b64encode(img_file).decode("utf-8")
       data = {
           "img": bin,
       }

       send_to_iot_core(mqtt_connection, data)
 

def init_mqtt() -> mqtt.Connection:
    # mqtt クライアントを初期化する
    try:
        event_loop_group = io.EventLoopGroup(1)
        host_resolver = io.DefaultHostResolver(event_loop_group)
        client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver)

        mqtt_connection = mqtt_connection_builder.mtls_from_path(
            endpoint=settings.IOT_CREDENTIAL_ENDPOINT,
            cert_filepath=settings.CERT_PATH,
            pri_key_filepath=settings.PRIVATE_KEY_PATH,
            client_bootstrap=client_bootstrap,
            ca_filepath=settings.ROOT_CA_PATH,
            client_id=settings.DEVICE_ID,
            clean_session=False,
            keep_alive_secs=settings.KEEP_ALIVE,
        )

        # 接続開始
        connected_future = mqtt_connection.connect()

        # 利用可能になるまで待機
        connected_future.result()

        logger.info("mqtt connenction Successed")

        return mqtt_connection

    except Exception:
        logger.exception("Error")
        raise

if __name__ == "__main__":
    main()

settings.py

import os
import configparser
from logging import config
 
# 環境変数設定
conf = configparser.ConfigParser()
conf.read(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf/setting.conf'), encoding='UTF-8')

# DEVICE
DEVICE_ID =  conf['DEVICE']['DEVICE_ID']
CERT_PATH = conf['DEVICE']['CERT_PATH']
PRIVATE_KEY_PATH = conf['DEVICE']['PRIVATE_KEY_PATH']
ROOT_CA_PATH = conf['DEVICE']['ROOT_CA_PATH']

# AWS
IOT_CREDENTIAL_ENDPOINT = conf['AWS']['IOT_CREDENTIAL_ENDPOINT']

setting.conf

[DEVICE]
DEVICE_ID = xxx # デバイス名
CERT_PATH = xxx  # 証明書配置パス
PRIVATE_KEY_PATH = xxx  # 秘密鍵配置パス
ROOT_CA_PATH = xxx  # amazon CA1パス

[AWS]
IOT_CREDENTIAL_ENDPOINT = https://xxx.credentials.iot.ap-northeast-1.amazonaws.com

2 画像アップロード用Lambdaプログラムを作成

作成済みのLambdaを以下のコードに書き換えてデプロイし直します。

import json
import boto3
import base64
from base64 import b64decode
from decimal import Decimal
from io import BytesIO




bucket = "test-image-upload-bucket-1111"
key = "test.jpg"

def lambda_handler(event, context):
    for data in event["Records"]:
        # eventデータをデコード
        decoded_data = base64.b64decode(data["kinesis"]["data"]).decode()
        if decoded_data:
            json_payload = json.loads(decoded_data, parse_float=Decimal)
            # eventデータをデコード
            photo = json_payload["bin"]
            decode_photo = b64decode(photo)
            s3 = boto3.resource('s3')
            obj = s3.Object(bucket, key)
            obj.put(Body=BytesIO(decode_picture),ContentType="image/jpg")

3. プログラム実行

Cloud9でプログラムを実行します。

$ python main.py

メッセージのペイロードが128KBを超える画像は送信できないのでご注意ください。

IoT Core ログ

{
    "timestamp": "2022-xxxxxxxx",
    "logLevel": "ERROR",
    "traceId": "xxxxxxxx",
    "accountId": "xxxxxxxx",
    "status": "Failure",
    "eventType": "Publish-In",
    "protocol": "MQTT",
    "topicName": "xxxxxxxx",
    "clientId": "xxxxxxxx",
    "principalId": "xxxxxxxx",
    "sourceIp": "xxxxxxxx",
    "sourcePort": xxxxxxxx,
    "reason": "PAYLOAD_LIMIT_EXCEEDED",
    "details": "Message payload exceeds size limit for message type"
}

4. 画像を確認

S3に画像がアップロードされているかを確認してください。

S3からダウンロードした画像が、cloud9に配置していた画像と同じ用に表示されていたら成功です。

5. まとめ

すでにAWS IoT CoreにてAWS環境にデータを送信している方は、ほとんど同じ方法で画像もアップロードが可能なので画像も送信したいという方は是非試してください。

ただし、データ送信の制限は、AWS IoT Coreの送信制限に依存するの小規模容量画像を送信したいシーンでご利用してください。

ありがとうございました。