Amazon FreeRTOSでOTA(Over The Air)によるファームウェアの更新を試してみた
Contents
OTA Updateとは
OTA(Over The Air)は「無線で」との意味で OTA Update は最新ファームウェアへの更新を無線で行う機能の事だ。
IoT の場合は大量の端末をあちらこちら配置する事があるので、プログラムの入れ替えの際にそれぞれの端末を人が回りながらプログラムのアップデートをしていたのでは非常に効率が悪い。
その為、IoT 向けの OS には OTA Update の機能が組み込まれている事がある。
また更新データを安全(セキュア)に届けると共に悪意のあるプログラムを実行されない為に届いたデータが正しいものである事を検証する仕組みも必要だ。
Amazon FreeRTOS の OTA Update ではプライベートキーとコード署名証明書を使って接続認証を行っているのでセキュアな接続が確保されている。
また TLS (Transport Layer Security)上の MQTT(Message Queuing Telemetry Transport)プロトコルを使用するので TLS デバイス認証で届け先のデバイスが偽物で無いことを確認する。
前回の記事で ESP-32 DevKitC に Amazon FreeRTOS をインストールして AWS IoT Core にメッセージを送信するサンプルプログラムを動かしてみた。
詳しくは前回の記事を参照して欲しいのだが、デモプロジェクトの選択時に “OTA Updates” ライブラリを追加している。
このライブラリを追加しておくと OTA Update が比較的簡単に実現できる。
全体構成図
Amazon FreeRTOS の OTA Update の仕組みは以下の通り。
- 事前にプライベートキーとコード署名証明書を作成して AWS Certificate Manager に登録する
- パソコンでコンパイルした修正後更新ファームウェアをAWS のクラウド上のディスク(s3)に保存する
- AWS のコンソールから対象デバイス(グループ指定も可能)を指定してJOBを作成する
- ESP32上で動作しているOTA Updateエージェントが s3 から最新のモジュールを MQTT プロトコルでダウンロードしてファームウェアを入れ替える
事前準備
コード署名証明書を AWS Certificate Manager にインポートする際にAWS CLI(Command Line Interface)が必要になるので事前にこちらのページからインストールしておく。
64ビット、Windows版をインストールした。
初期ファームウェアの準備
署名検証用の証明書と OTA Update エージェントを組み込んだ更新前バージョンのファームウェアが動作している ESP32 DevkitC を準備する。
大まかな手順は以下の通り。
- s3 バケットの作成
- ロール・ポリシーの作成
- ユーザの作成とポリシーのアタッチ
- プライベートキーの作成
- コード署名証明書の作成
- AWS Certificate Manager にインポート
- FreeRTOS のコード署名へのアクセスを許可
- 証明書の変換とコピー
- コンパイル&Flush(書き込み)
尚、事前にこちらの記事の手順で AWS コンソールからFreeRTOS のダウンロードやパソコンでの ESP-IDF の環境構築、CMake環境の構築などが終了している前提とする。
ディレクトリ構造
前述の記事の手順で Amazon FreeRTOS をパソコン(Windows10)にダウンロードして以下のディレクトリに解凍している。
自分は D:¥GoogleDrive¥M2B¥FreeRTOS 配下に解凍したが任意のディレクトリで構わない。
├─d:
│ │
│ ├─GoogleDriveD
│ │ │
│ │ ├──M2B
│ │ │ │
│ │ │ ├──build ・・・ 空で作成しておく(buildファイル格納用)
│ │ │ │
│ │ │ ├──FreeRTOS ・・・ ここに解凍する
│ │ │ │ │
│ │ │ │ ├──demos ・・・デモ用アプリケーション
│ │ │ │ │
│ │ │ │ ├──device
│ │ │ │ │
│ │ │ │ ├──freertos_kernel ・・・ FreeRTOSのカーネル
│ │ │ │ │
│ │ │ │ ├──libraries ・・・ ライブラリー
│ │ │ │ │
│ │ │ │ ├──tests
│ │ │ │ │
│ │ │ │ ├──tools ・・・ ツール類(認証ファイルの変換ツール等)
│ │ │ │ │
│ │ │ │ ├──vendors ・・・ ESP-IDF環境
│ │ │ │ │
s3 バケットの作成
更新ファームウェア格納用の領域を AWS s3 上に作成する。
AWS の コンソールから s3サービスを表示して「バケットの作成」ボタンをクリックする。
- バケット名:esp-wroom-32-ota
- AWS リージョン:東京
を選択して下にスクロールする。
尚、バケット名は世界中で一意である必要があるので同じ名前でぶつかってしまった時は日付等を入れてユニークなバケット名にする。
“パブリックアクセスを全てブロック” を選択して下にスクロールする。
※別途ポリシーを作成して、そのポリシーを使用してアクセスする。
- バケットのバージョニング:有効にする
下にスクロールする。
タグは未設定(必要に応じて設定しても良い)、デフォルトの暗号化は無効で「バケットを作成」ボタンをクリックする。
バケットが作成された。
ロールの作成
続いてOTA Update のジョブで使用するロールを作成する。
このロールでファームウェアの更新を行うのでAmazon FreeRTOS Update や IAM、s3 へのアクセス権が必要になる。
AWS コンソールから IAMサービスページでロールを選択する。
「ロールの作成」ボタンをクリックする。
- 信頼されたエンティティの種類を選択:AWSサービス
を選択して下にスクロールする。
AWS サービスの一覧から IoT を選択する。
下のユースケースの一覧から IoT を選択して「次のステップ:アクセス権限」ボタンをクリックする。
IoT を選択した事によりデフォルトで、
- AWSIoTLogging
- AWSIoTRuleActions
- AWSIoTThingsRegistration
のポリシーがアタッチされているので「次のステップ:タグ」ボタンをクリックする。
タグは設定せずに(必要に応じて設定しても良い)「次のステップ:確認」ボタンをクリックする。
- ロール名:esp-32-ota-role
- 説明:Allows IoT to call AWS services on your behalf.
を入力して「ロールの作成」ボタンをクリックする。
ロールが作成された。
ポリシーのアタッチ
幾つか追加でポリシーをアタッチする必要がある。
まずは AmazonFreeRTOSOTAUpdate ポリシーをアタッチする。
今、作成したロール esp-32-ota-role を選択する。
「ポリシーをアタッチします」ボタンをクリックする。
ポリシーのフィルター欄に “AmazonFreeRTOSOTAUpdate” を入力して検索して、表示されるポリシーをチェックしたら「ポリシーのアタッチ」ボタンをクリックする。
追加で AmazonFreeRTOSOTAUpdate ポリシーがアタッチされた。
IAM アクセス許可の追加
更に IAM へのアクセス許可を追加する。
“+インラインポリシーの追加” をクリックする。
JSONタブをクリックする。
下記のポリシードキュメントを貼り付けて「ポリシーの確認」ボタンをクリックする。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:PassRole"
],
"Resource": "arn:aws:iam::AWSアカウントID:role/esp-32-ota-role"
}
]
}
AWSアカウントID | AWSのアカウントID(数字) |
esp-32-ota-role | 先程作成したロール名 |
- 名前:esp-32-ota-Policy
「ポリシーの作成」ボタンをクリックする。
名前を esp-32-ota-Policy にしたが、 esp-32-ota-iam-Policy とかにしておいた方が分かりやすかったかも知れない。
しかし記事中はこのままの名前で進める。
インラインポリシーが作成されてアタッチされた。
Amazon S3 アクセス許可の追加
更に s3 に対するアクセス許可を追加する。
尚、Amazon S3 バケット名が “afr-ota” で始まる場合は、先程アタッチした “AmazonFreeRTOSOTAUpdate” 管理ポリシーに必要なアクセス許可がすでに含まれているので下記の操作は不要だ。
自分は s3 バケット名を “esp-wroom-32-ota” としてしまったのでポリシーを追加する必要があった。
“+インラインポリシーの追加” をクリックする。
JSONタブをクリックする。
下記のポリシードキュメントを貼り付けて「ポリシーの確認」
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObjectVersion",
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::esp-wroom-32-ota/*"
]
}
]
}
esp-wroom-32-ota | 先程作成した s3 バケット名 |
- 名前:esp-32-s3-Policy
を入力して「ポリシーの作成」ボタンをクリックする。
ポリシーが作成されてアタッチされた。
OTAユーザの作成
OTA ユーザを作成する。
このユーザーは OTA update を実行する為のユーザで、 ESP32 の Amazon FreeRTOS から実行され、以下の権限(ポリシー)を持っている必要がある。
- ファームウェアの更新(更新プログラム)が保存されている S3 バケットにアクセスする
- AWS Certificate Manager に保存された証明書にアクセスする
- AWS IoT MQTT ベースのファイル配信機能にアクセスする
- FreeRTOS OTA Update にアクセスする
- AWS IoT ジョブにアクセスする
- IAM にアクセスする
- Code Signing for AWS IoT にアクセスする
- FreeRTOS ハードウェアプラットフォームを一覧表示する
AWS コンソールから IAMサービスに移動してユーザメニューから「ユーザを追加」ボタンをクリックする。
- ユーザ名:esp32otaupdate
- プログラムによるアクセス:チェック
「次のステップ:アクセス権限」ボタンをクリックする。
タグの追加は行わず(行っても良い)、「次のステップ:確認」ボタンをクリックする。
“このユーザーにはアクセス権限がありません” との警告メッセージは表示されるが無視して「ユーザの作成」ボタンをクリックする。
※権限はこの後に追加する。
ユーザが作成されるのでアクセスキーIDとシークレットアクセスキーを保存しておく。
ポリシーの作成&アタッチ
今、作成したユーザにポリシーを作成してアタッチする。
ユーザー名一覧から esp32otaupdate を選択する。
「アクセス権限の追加」ボタンをクリックする。
“既存のポリシーを直接アタッチ ” ー>「ポリシーの作成」ボタンをクリックする。
JSONタブを選択する。
以下のポリシー(コード)を貼り付けて「次のステップ:タグ」ボタンをクリックする。
{
"Version":"2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:ListAllMyBuckets",
"s3:CreateBucket",
"s3:PutBucketVersioning",
"s3:GetBucketLocation",
"s3:GetObjectVersion",
"s3:ListBucketVersions",
"acm:ImportCertificate",
"acm:ListCertificates",
"iot:*",
"iam:ListRoles",
"freertos:ListHardwarePlatforms",
"freertos:DescribeHardwarePlatform"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::esp-wroom-32-ota/*"
},
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::AWSアカウントID:role/esp-32-ota-role"
}
]
}
esp-wroom-32-ota | 先程作成した s3 バケット名 |
AWSアカウントID | AWSのアカウントID(数字) |
esp-32-ota-role | 先程作成したロール名 |
タグは追加せずに(任意)「次のステップ:確認」ボタンをクリックする。
- 名前:esp32otaupdate-Policy
下にスクロールする。
「ポリシーの作成」ボタンをクリックする。
先程の画面に戻って更新アイコンを押した後、先程作成したポリシー名 “esp32otaupdate-Policy” で検索して、一覧に表示されたらチェックをして「次のステップ:確認」ボタンをクリックする。
「アクセス権限の追加」ボタンをクリックする。
権限が追加された。
update時の認証の仕組み
ここからはローカルパソコン(Windows10)側の操作。
デバイス側(ESP32ーDevKitC)で最新のファームウェアをダウンロードした際に正しいイメージかどうかを確認するために秘密鍵とコード署名証明書を使って確認する。
つまりデバイス側が悪意のあるファームウェアを間違って反映してしまわないような仕組みの為にコード署名証明書を作成する。
今回はテスト目的で自己署名証明書とプライベートキーを作成するが、本番環境では既知の証明機関を介して証明書を購入する必要がある。
cert_config.txtの作成
以下の内容で D:¥GoogleDriveD¥M2B¥FreeRTOS¥OTA_Cert¥cert_config.txt ファイルをテキストエディターで作成する。
[ req ]
prompt = no
distinguished_name = my_dn
[ my_dn ]
commonName = メールアドレス
[ my_exts ]
keyUsage = digitalSignature
extendedKeyUsage = codeSigning
プライベートキーの作成
Windows10 の PowerShell から OpenSSL コマンドで ECDSA のコード署名プライベートキーを作成する。
D:¥GoogleDriveD¥M2B¥FreeRTOS¥OTA_Cert>で実行する
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -pkeyopt ec_param_enc:named_curve -outform PEM -out ecdsasigner.key
openssl | openssl コマンドを Windows10 の PowerShell から実行する インストールされていない時はこちらのページから Win64 OpenSSL 版をインストールする(msiがインストーラー版) |
genpkey | 秘密鍵の生成 |
-algorithm | RSA、DSA、DHなどが使用する公開鍵アルゴリズム。 RSA、RSA-PSS、EC、X25519、X448、ED25519、ED448を指定することができる。 EC(Elliptic Curve):楕円曲線アルゴリズムを指定した。 |
-pkeyopt | ec_paramgen_curve:P-256 を指定した。 楕円曲線アルゴリズムのパラメーターで P-256 曲線を指定している。 p = 2256 – 2224 + 2192 + 296 – 1 の式で表される(らしい) |
-pkeyopt | ec_param_enc:named_curve を指定した。 named_curve、explicit が指定出来る。 |
-outform | 出力フォーマット PEM、DER が指定出来る。 |
-out | プライベートキーの出力ファイル名を指定する。 ecdsasigner.key を指定した。 |
コード署名証明書の作成
先程作成したプライベートキー(ecdsasigner.key)をベースにして以下のコマンドでECDSA のコード署名証明書(ecdsasigner.crt)を作成する。
openssl req -new -x509 -config cert_config.txt -extensions my_exts -nodes -days 365 -key ecdsasigner.key -out ecdsasigner.crt
AWS Certificate Manager にインポート
AWS CLI でコード署名証明書、プライベートキー、および証明書チェーンを AWS Certificate Manager にインポートする。
事前に aws configure コマンドで先程作成したOTAユーザ(esp32otaupdate)のアクセスキーIDとシークレットアクセスキー、リージョンなどを設定する。
尚、 aws configure コマンドは一度設定するとパソコンを再起動しても前回の設定が保存されている。
aws configure
- AWS Access Key ID [None]: ユーザ作成時のアクセスキーIDを指定する
- AWS Secret Access Key [None]: ユーザ作成時のシークレットアクセスキーを指定する
- Default region name [None]: us-east-1
- Default output format [None]: json
を指定した。
続いて以下のコマンドで署名証明書、プライベートキー等を AWS Certificate Manager にインポートすると証明書の ARN(Amazon Resource Name)が返されるので保存しておく。
aws acm import-certificate --certificate fileb://ecdsasigner.crt --private-key fileb://ecdsasigner.key
acm | acm(AWS Certificate Manager)で証明書のインポート。 import-certificate を指定した |
–certificate | コード署名証明書 fileb://ecdsasigner.crt を指定した。 |
–private-key | プライベートキー fileb://ecdsasigner.key を指定した。 |
FreeRTOS のコード署名へのアクセスを許可
ファームウェア更新時にデジタル署名を行い更新データの信頼性をチェックする。
その為には更新用のアカウント(esp32otaupdate)に FreeRTOS のコード署名(Code Sigining for AWS IoT)へのアクセスを許可する必要がある。
AWS コンソールの IAMユーザから esp32otaupdate を選択する。
「アクセス権限の追加」ボタンをクリックする。
“既存のポリシーを直接アタッチ”ー>「ポリシー作成」ボタンをクリックする。
新しいタブが開くので”JSONタブ” をクリックする。
以下を貼り付けて「次のステップ:タグ」ボタンをクリックする。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"signer:*"
],
"Resource": "*"
}
]
}
タグは追加せずに(任意)「次のステップ:確認」ボタンをクリックする。
- 名前:esp32otaupdate-signer-Policy
を設定して「ポリシーの作成」ボタンをクリックする。
先程のタブに戻って “リフレッシュアイコン” をクリックすると先程作成したポリシー(esp32otaupdate-signer-Policy)が表示されるのでチェックを入れて「次のステップ:確認」ボタンをクリックする。
「アクセス権限の追加」ボタンをクリックする。
ポリシーが追加された。
OTA Updateのデモプログラム
以下のファイルを編集して OTA Update のデモプログラムが動作するようにする。
D:\GoogleDriveD\M2B\FreeRTOS\vendors\espressif\boards\esp32\aws_demos\config_files\aws_demo_config.h
上記のファイルに
#define CONFIG_OTA_UPDATE_DEMO_ENABLED
を定義する。
この AWS のサンプルプログラムは定義してある定数によって動作するプログラムが異なるように作られている。
D:\GoogleDriveD\M2B\FreeRTOS\demos\include\iot_demo_runner.h
にて実行するデモプログラムを振り分けている。
※ 111 行目で CONFIG_OTA_UPDATE_DEMO_ENABLED が定義されているかどうかチェックしている。
証明書のコピー
先程作成したコード署名証明書(ecdsasigner.crt)の中身を下記のファイルの Paste code signing certificate here. の位置にコピーする。
D:\GoogleDriveD\M2B\FreeRTOS\demos\include\aws_ota_codesigner_certificate.h
ただ注意しなくてはいけないのがそのままコピー&ペーストで貼り付けるとC言語のフォーマットに合っておらずその先の Build 時に証明書部分が認識されずに「認証ファイルの終了文字が無い」との理由で subcommand failed でエラーになってしまう。
証明書の変換
事前に証明書の変換ツールで変換する為、以下のファイルを(HTML)をダブルクリックしてブラウザで開く。
D:\GoogleDriveD\M2B\FreeRTOS\tools\certificate_configuration\PEMfileToCString.html
「ファイルを選択」ボタンでコード署名証明書(ecdsasigner.crt)を選択して、「Display Fromated PEM・・・」ボタンをクリックする。
ページ下部に変換後(行末の部分がC言語用に変換されている)の証明書が表示されるので、これを先程のファイルにコピー&ペーストする。
aws_ota_codesigner_certificate.h に貼り付けて保存する。
※最後の “; “(セミコロン)は必要なので注意。
CMake
Power shell を起動して FreeRTOS をインストールしたディレクトリに移動して以下のコマンドで build ファイルを作成する。
d:
cd .\GoogleDriveD\M2B\FreeRTOS\
cmake -DVENDOR=espressif -DBOARD=esp32_devkitc -DCOMPILER=xtensa-esp32 -G Ninja -S . -B ..\build\
cmake のパラメーターの説明については以前の記事を参照して欲しい。
Build
続いて Build ファイルを以下のコマンドでコンパイルする。
cmake --build ..\build\
Flush
正常にコンパイルされて aws_demos.bin が作成されたら パソコンと ESP32-DevkitC をUSB 接続して書き込む。
.\vendors\espressif\esp-idf\tools\idf.py flash -b 115200 -B ..\build\
パラメーターの説明については以前の記事を参照して欲しい。
新しい Window が開いて書き込みが終了したら「初期ファームウェアの準備」は終了。
ファームウェアの更新
続いてファームウェアが OTA で更新されるかを検証する。
ESP32-DevkitCの実行
更新前ファームウェア(Ver 092)がインストールされた ESP32-DevkitC に micro USB Type-B コネクターから給電をして動作させておく。
バージョンファイルの更新
下記のファイルを開いて現在のバージョンを変更する。
D:\GoogleDriveD\M2B\FreeRTOS\demos\include\aws_application_version.h
今のバージョンは 092 だったので 093 に変更して保存した。
再Build
続いて以下のコマンドで再Build すると Ver 093 の aws_demos.bin が作成される。
cmake --build ..\build\
AWSへのジョブ登録
OTA 更新用のジョブを作成する。
AWS のコンソールから IoT Core サービスのページに移動して管理ー>ジョブー>「ジョブを作成」ボタンをクリックする。
“FreeRTOS OTA更新ジョブを作成 ” を選択して「次へ」ボタンをクリックする。
OTA ジョブのプロパティ
- ジョブ名:esp32-ota-updatejob
説明及びタグは設定せず(設定は任意)に「次へ」ボタンをクリックする。
更新するデバイスで「モノのグループ」または「モノ」を選択する。
今回は、”ESP-WROOM-32-GGTest” と”モノ”を選択したのでピンポイントでそのモノ(デバイス)が更新される。
以前の記事のデバイスの登録で設定した名前を指定する。
プロトコルはセキュアな “MQTT ” を選択して下にスクロールする。
- 新しいファイルに署名します
を選択してコード署名プロファイルにて「新しいプロファイルの作成」ボタンをクリックする。
- プロファイル名:esp32_ota_profile
- デバイスのハードウェアプラットフォーム:ESP32-DevKitC
を設定して “新しいコード署名証明書のインポート” を選択して下にスクロールする。
- 証明書本文:コード署名証明書(ecdsasigner.crt)
- 証明書のプライベートキー:プライベートキー(ecdsasigner.key)
をそれぞれ選択して「インポート」ボタンを押すとアップーロードされるので下にスクロールする。
- デバイスのコード署名証明書のパス名:/OTA_Cert/auth.pem
を指定して「作成」ボタンをクリックする。
先程の画面に戻るとプロファイル(esp32_ota_profile)が作成されて選択されているので下にスクロールする。
“新しいファイルをアップロードします” を選択して「ファイルを選択」ボタンから再ビルドしたVer 093 の aws_demos.bin をアップロードする。
- S3 でのファイルのアップロード先:バケット名(s3://esp-wroom-32-ota)
- デバイス上のファイルのパス名:/device
を設定して下にスクロールする。
- ロール:esp-32-ota-role
最初に作成したロールを選択して「次へ」ボタンをクリックする。
ジョブの作成
設定が終了したのでジョブを作成する。
- ジョブ実行タイプ:選択したデバイスとグループ(スナップショット)にデプロイした後、ジョブは完了します
- ジョブ開始ロールアウト設定:未設定(オプション)
- ジョブの停止の設定:未設定(オプション)
- ジョブ実行タイムアウトの設定:未設定(オプション)
「ジョブの作成」ボタンをクリックする。
ジョブが作成されて OTA Update が実行される。
OTA Update
しばらく待っているとファームウェアの更新が自動で行われるので、ESP32-DevkitC のシリアルコンソールのログを確認する。
ジョブが実行されて MQTT プロトコルで 0.9.3 にバージョンアップされた事が分かる。
終わり
Amazon FreeRTOS の OTA Update デモプロジェクトで自動的に最新ファームウェアにアップデートされる事を確認できた。
事前のロールやポリシーの設定は複雑なのでちょっと面倒だが一度出来てしまえば OTA Update 自体はジョブを作成するだけで自動的に MQTT プロトコルで更新されるのでそれほど難しい操作が必要な訳でもない。
またプライベートキー、コード署名証明書で AWS Certificate Manager でのチェックが行われるのでセキュアな IoT デバイス環境が構築されるのはありがたい。
以上で今回の記事は終了とする。
この記事が何処かで誰かの役に立つことを願っている。
尚、当記事中の商品へのリンクはAmazonアソシエイトへのリンクが含まれています。Amazonのアソシエイトとして、当メディアは適格販売により収入を得ていますのでご了承ください。
最近のコメント