AWS IoT JSON形式のテンプレートを使ってデバイスを自動登録する (Just-in-Time Provisioning)

AWS IoT ジャストインタイムプロビジョニング Just-in-Time Provisioning (JITP) によるデバイス自動登録
2020.04.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、hagiwaraです。 AWS IoTでデバイス(モノ)を初回接続時に自動登録することができます。その方法が2種類あります。Just-in-Time Registration (JITR)とJust-in-Time Provisioning (JITP)です。今回は後者のJITPを試してみました。 前者のJITRについては、高橋さんのブログを参照してください。

AWS IoTを使用しているので「デバイス」ではなく「モノ」という単語を使うべきとも思いましたが、個人的に「モノ」が脳内でゲシュタルト崩壊?してしまったので、「デバイス」で統一しています。

JITRとJITPの違い

両者の異なる点は、デバイスをAWS Lambdaを使用して登録するのか、JSON形式のプロビジョニング用テンプレートを元に登録するのかという点です。CA証明書および検証用証明書をAWSに設定する点は同じです。

上の図は、オフィシャルブログに記載されているものを元に記載しました。 どちらを採用するかの判断は、Lambdaで細かいカスタマイズをする必要がある場合はJITR、テンプレートで事足りる場合はJITPといった感じでしょうか。

セットアップ

JITPの設定を行なっていきます。

1. 独自のCA証明書および検証用証明書の作成

ここは、JITRと同様です。

1-1 CA証明書作成

# キーペア作成
openssl genrsa -out rootCA.key 2048

# 証明書作成
CA_SUBJ="/C=JP/ST=Tokyo/L=Tokyo/O=MyOrg/OU=MyOU/CN=RootCA"
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem -subj "$CA_SUBJ"

1-2 検証用証明書作成

# キーペア作成
openssl genrsa -out verificationCert.key 2048

# AWS IoTのユーザーCA登録コード取得
CODE=`aws iot get-registration-code --output text`

# コモンネームに登録コードを設定してCSRを作成
VC_SUBJ="/C=JP/ST=Tokyo/L=Tokyo/O=MyOrg/OU=verification/CN=${CODE}"
openssl req -new -key verificationCert.key -out verificationCert.csr -subj "$VC_SUBJ"

# 証明書を作成
openssl x509 -req -in verificationCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out verificationCert.pem -days 500 -sha256

2. デバイスを自動登録するロールの作成

JITPを行うIAM Roleを作成します。

2-1 IAM ロール一覧

IAM の画面から [ロールの作成] を選択。

2-2 信頼されたエンティティの種類およびユースケースの選択

信頼されたエンティティはAWSサービス、ユースケースにIoTを選択して次へ。


2-3 アクセス権限

必要に応じアクセス権限の境界を設定しますが、今回はなにもせずデフォルトのまま次へ。

2-4 タグ

お好みでタグを追加し次へ。

2-5 確認

ロール名を入力し作成。今回はJITPRoleという名前にしました。

3. デバイスにアタッチするポリシーの作成

テンプレートからポリシーを作成することも可能ですが、そうするとデバイスごとに別のポリシーが作成されてしまいます。今回は共通で使用するポリシーをあらかじめ作成しておきます。

AWS IoT Coreコンソール画面をブラウザで開き、[安全性] → [ポリシー] → [作成] → [アドバンストモード] を選択し、以下を入力して作成しました。

名前 : jitp-things-policy

ステートメント :

{
"Version": "2012-10-17",
"Statement": [
{
"Action": "iot:Connect",
"Resource": "arn:aws:iot:ap-northeast-1:<アカウントID>:client/${iot:Connection.Thing.ThingName}",
"Effect": "Allow"
},
{
"Action": [
"iot:Publish",
"iot:Receive"
],
"Resource": "arn:aws:iot:ap-northeast-1:<アカウントID>:topic/$aws/things/${iot:Connection.Thing.ThingName}/shadow/*",
"Effect": "Allow"
},
{
"Action": "iot:Subscribe",
"Resource": "arn:aws:iot:ap-northeast-1:<アカウントID>:topicfilter/$aws/things/${iot:Connection.Thing.ThingName}/shadow/*",
"Effect": "Allow"
}
]
}

4. テンプレートの作成

4-1 プロビジョニングの設定をテンプレートに記述

以下のようなテンプレートを作成しました。 パラメータの内容についてこちらを参照してください。ポイントはThingNameにデバイス証明書のコモンネームを代入している点と、先ほど作成したポリシーを指定しているところです。 今回は設定していませんが、タイプやグループも指定可能です。その場合はあらかじめそれらを作成しておく必要があります。

{
"Parameters": {
"AWS::IoT::Certificate::CommonName": { "Type": "String" },
"AWS::IoT::Certificate::Country": { "Type": "String" },
"AWS::IoT::Certificate::Id": { "Type": "String" }
},
"Resources": {
"thing": {
"Type": "AWS::IoT::Thing",
"Properties": {
"ThingName": { "Ref": "AWS::IoT::Certificate::CommonName" },
"AttributePayload": { "version": "v1", "country": { "Ref": "AWS::IoT::Certificate::Country" } }
}
},
"certificate": {
"Type": "AWS::IoT::Certificate",
"Properties": {
"CertificateId": { "Ref": "AWS::IoT::Certificate::Id" },
"Status": "ACTIVE"
}
},
"policy": {
"Type": "AWS::IoT::Policy",
"Properties": {
"PolicyName": "jitp-things-policy"
}
}
}
}

4-2 テンプレートとロールを統合

上記テンプレートを文字列にしてtemplateBodyに、先ほど作成したロールをroleArnに設定したファイルを作成します。今回はprovisioning-template.jsonという名前で作成しました。

{
"templateBody": "{\"Parameters\":{\"AWS::IoT::Certificate::CommonName\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::Country\":{\"Type\":\"String\"},\"AWS::IoT::Certificate::Id\":{\"Type\":\"String\"}},\"Resources\":{\"thing\":{\"Type\":\"AWS::IoT::Thing\",\"Properties\":{\"ThingName\":{\"Ref\":\"AWS::IoT::Certificate::CommonName\"},\"AttributePayload\":{\"version\":\"v1\",\"country\":{\"Ref\":\"AWS::IoT::Certificate::Country\"}}}},\"certificate\":{\"Type\":\"AWS::IoT::Certificate\",\"Properties\":{\"CertificateId\":{\"Ref\":\"AWS::IoT::Certificate::Id\"},\"Status\":\"ACTIVE\"}},\"policy\":{\"Type\":\"AWS::IoT::Policy\",\"Properties\":{\"PolicyName\":\"jitp-things-policy\"}}}}",
"roleArn": "arn:aws:iam::<アカウントID>:role/JITPRole"
}

JSON→文字列の変換は、ブラウザのコンソールから雑に... JSON.stringify({templateBody:JSON.stringify(JSON.parse(`&lt;ここにコピペ&gt;`))})

5. CA証明書およびテンプレートの登録

上記で作成したCAを登録し、自動登録の設定を追加します。

# AWSに独自CA証明書を登録
aws iot register-ca-certificate --ca-certificate file://rootCA.pem --verification-cert file://verificationCert.pem

# 以下、上記で返却される証明書ID(certificateId)を使用

# 登録した証明書をアクティブに変更
aws iot update-ca-certificate --certificate-id <証明書ID> --new-status ACTIVE

# テンプレートを指定して自動登録を有効化
aws iot update-ca-certificate --certificate-id <証明書ID> --new-auto-registration-status ENABLE --registration-config file://provisioning-template.json

以上で設定は終了です、次はデバイスの登録を行なっていきます。

デバイスを接続する

先ほど作成した独自のCA証明書から、デバイスごとにデバイス証明書を作成し、それを使用して接続します。 AWS IoT は登録されていない証明書でのアクセスがあった場合、その証明書を検証します。登録されたCAに基づくものであれば、自動で証明書の登録を行い、テンプレートの内容に従ってデバイスの登録を行います。

1. デバイスごとにデバイス証明書を作成

MyJITPThingという名前にして証明書を作成しました。接続に使用する証明書にはCA証明書を含める必要があるので、デバイス証明書とCA証明書を統合します。

# 名前
THING_NAME="MyJITPThing"

# キーペア作成
openssl genrsa -out deviceCert.key 2048

# コモンネームに名前を設定してCSRを作成
DE_SUBJ="/C=JP/ST=Tokyo/L=Tokyo/O=MyOrg/OU=device/CN=${THING_NAME}"
openssl req -new -key deviceCert.key -out deviceCert.csr -subj "$DE_SUBJ"

# 証明書作成
openssl x509 -req -in deviceCert.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out deviceCert.crt -days 365 -sha256

# デバイス証明書とCA証明書を統合
cat deviceCert.crt rootCA.pem > deviceCertAndCACert.crt

2. AWSサーバー接続用の証明書を取得

AWSサーバー認証用の証明書をダウンロードしておきます。AWS ドキュメント

curl https://www.amazontrust.com/repository/AmazonRootCA1.pem > root.cert

3. AWS IoT に接続する

mosquitto_sub を使って未登録の証明書で接続してみます。

END_POINT=`aws iot describe-endpoint --endpoint-type iot:Data-ATS --output text`
mosquitto_sub --cafile root.cert \
--cert deviceCertAndCACert.crt \
--key deviceCert.key \
-h $END_POINT \
-p 8883 \
-q 1 \
-i MyJITPThing \
--tls-version tlsv1.2 \
-t '$aws/things/MyJITPThing/shadow/update/delta' \
-d

以下のような感じで出力されました、無事接続はできてそうです。

$ mosquitto_sub --cafile root.cert --cert deviceCertAndCACert.crt --key deviceCert.key -h $END_POINT -p 8883 -q 1 -i MyJITPThing --tls-version tlsv1.2 -t '$aws/things/MyJITPThing/shadow/update/delta' -d
Client MyJITPThing sending CONNECT
Client MyJITPThing sending CONNECT
Client MyJITPThing sending CONNECT
Client MyJITPThing sending CONNECT
Client MyJITPThing sending CONNECT

3. 確認

コンソールからも無事登録されてることが確認できました!

記載はしていませんが、詳細画面から自動登録された証明書と、前もって作成しておいたポリシーがアタッチされていることが確認できます。

まとめ

以上、テンプレートを使用したデバイス登録を試してみました。デバイス登録時の設定がテンプレートで事足りる場合は、Lambdaを実装しなくていいのでこちらのほうがよさそうです。

参考 URL

ジャストインタイムのプロビジョニング Setting Up Just-in-Time Provisioning with AWS IoT Core AWS IoTの証明書自動登録でクライアント証明書の運用を楽にする