こちらのセクションではAWS IoT Core のフリートプロビジョニングの機能を使用して、初めて AWS IoT に接続するときにデバイス証明書とプライベートキーを生成し、AWS IoTからデバイスに対して安全に配信することができます。これによって製造時に個別の証明書をAWS IoTに登録する必要がありません。 初回接続時にLambda 関数を介してデバイスからのメッセージのペイロードを検証し、 ID を顧客の AWS アカウントに登録し、必要なすべてのアクセス許可とレジストリメタデータ(モノ、モノのグループなど)をデバイスに設定します。これはすべて、デバイスが AWS IoT Core に最初に接続したとき、またはデバイスが新しい認証情報または更新の必要があるときに自動的に行われるため、エンジニアの貴重な時間とリソースの節約にも繋がります。
フリートプロビジョニングでは、大きく分けて2つのプロビジョニング方法があります。このハンズオンではクレームによるプロビジョニングを行います。
ブートストラップ証明書を用いたアプローチと呼ばれることがあります。(このハンズオンでは4.5のLambdaでデバイスが送信するシリアルナンバーからデバイス情報を検証しています。)
エンドユーザーやインストール技術者などの信頼されたユーザーがモバイルアプリを使用してデプロイされた場所にデバイスを設定するときに、デバイスは AWS IoT に初めて接続します。
プロビジョニングテンプレートは、デバイスをプロビジョニングするときに実行する必要がある指示の詳細を示します。プロビジョニングテンプレートには、特定の証明書に関連付けるポリシー、デバイスレジストリのモノの名前、関連付けられた証明書をアクティブにするかどうかなどを含めることができます。詳細については、フリートプロビジョニングテンプレートのドキュメントをご覧ください。
ワークショップで利用するファイルをダウンロードして展開します。
cd
wget https://awsj-iot-handson.s3-ap-northeast-1.amazonaws.com/DeviceManagementWS/fleetProv.zip -O fleetProv.zip
unzip fleetProv.zip
また、必要なライブラリをインストールしておきます。
pip3 install pyfiglet
フリートプロビジョニングの作業用ディレクトリに移動します。
cd ~/fleetProv
AWS IoT はリソースベースのポリシーを使用して Lambda を呼び出すため、Lambda 関数を呼び出すための AWS IoT アクセス許可を与える必要があります。 以下は、Lambda に IoT アクセス許可を与える add-permission を使用する例です。
aws lambda create-function \
--region $REGION \
--function-name preProvFook-$NAME \
--zip-file fileb://lambda_function.zip \
--role $ARN_LAMBDA_ROLE \
--handler lambda_function.lambda_handler \
--runtime python3.8 \
--timeout 30 \
--memory-size 128
aws lambda add-permission \
--function-name preProvFook-$NAME \
--statement-id iot-permission \
--action lambda:InvokeFunction \
--principal iot.amazonaws.com
デバイスから送信されるペイロードを取得して、ブラック/ホワイトリストや内部の登録データベース、その他の内部リソースを使用して分析を行い、プロビジョニング要求を承認または拒否できます。このLambdaでは、シリアルナンバーが1~10000のデバイスに関しては承認して、プロビジョニング処理を行うことができます。 デバイスから送信された “provisioning-templates” に関する属性は、 Lambda のイベントの [“parameters”] タグから取得できます。 Lambdaが、プロビジョニング処理を行うためには、少なくともブール値の “allowProvisioning” ( True / False )を返す必要があります。
import json
provision_response = {'allowProvisioning': False}
def isBlacklisted(serial_number):
if serial_number >= 1 and serial_number <= 10000:
return True
else:
return False
#check serial against database of blacklisted serials
...
def lambda_handler(event, context):
# DISPLAY ALL ATTRIBUTES SENT FROM DEVICE
print("Received event: " + json.dumps(event, indent=2))
# Assume Device has sent a device_serial attribute
device_serial = event["parameters"]["SerialNumber"]
# Check serial against an isBlacklisted() function
if not isBlacklisted(device_serial):
provision_response["allowProvisioning"] = True
return provision_response
上記のLambdaをテンプレートに登録するため、hooks.jsonを編集します。
ARN_LAMBDA_FLEET=$(aws lambda get-function --function-name preProvFook-$NAME | jq -r '.Configuration.FunctionArn')
echo '{"targetArn" : "'$ARN_LAMBDA_FLEET'","payloadVersion" : "2020-04-01"}' >> hooks.json
create-provisioning-templateコマンドを使用して、事前プロビジョニングフックを追加したプロビジョニングテンプレートを作成します。
aws iot create-provisioning-template \
--template-name template-$NAME \
--provisioning-role-arn $ARN_IOT_PROVISIONING_ROLE \
--template-body file://template.json \
--pre-provisioning-hook file://hooks.json \
--enabled
デバイス証明書用の名前を設定します。
deviceCert=deviceFLEETCert
ブートストラップ用の証明書を作成します。certificateArn
をコピーします。
wget -O certs/root.ca.pem https://www.amazontrust.com/repository/AmazonRootCA1.pem
aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile certs/$deviceCert.crt \
--public-key-outfile certs/${deviceCert}_pub.key\
--private-key-outfile certs/${deviceCert}_private.key
ブートストラップ証明書用に制限付きポリシーを証明書にアタッチします。
ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account')
echo -e "{\n \"Version\":\"2012-10-17\",\n \"Statement\":[\n {\n \"Effect\":\"Allow\",\n \"Action\":[\n \"iot:Connect\"\n ],\n \"Resource\":[\n \"*\"\n ]\n },\n {\n \"Effect\":\"Allow\",\n \"Action\": [\n \"iot:Publish\",\n \"iot:Receive\"\n ],\n \"Resource\":[\n \"arn:aws:iot:$REGION:$ACCOUNT_ID:topic/\$aws/certificates/create/*\",\n \"arn:aws:iot:$REGION:$ACCOUNT_ID:topic/\$aws/provisioning-templates/template-$NAME/provision/*\"\n ]\n },\n {\n \"Effect\":\"Allow\",\n \"Action\":[\n \"iot:Subscribe\"\n ],\n \"Resource\":[\n \"arn:aws:iot:$REGION:$ACCOUNT_ID:topicfilter/\$aws/certificates/create/*\",\n \"arn:aws:iot:$REGION:$ACCOUNT_ID:topicfilter/\$aws/provisioning-templates/template-$NAME/provision/*\"\n ]\n }\n ]\n}" > bootStrapBody.json
aws iot create-policy --policy-name "bootstrapPolicy-$NAME" --policy-document file://bootStrapBody.json
aws iot attach-policy --policy-name "bootstrapPolicy-$NAME" --target [先程コピーしたcertificateArn]
接続情報を定義します。
echo -e "[SETTINGS]\nSECURE_CERT_PATH = /home/ec2-user\nROOT_CERT = fleetProv/certs/root.ca.pem\nCLAIM_CERT = fleetProv/certs/${deviceCert}.crt\nSECURE_KEY = fleetProv/certs/${deviceCert}_private.key\nIOT_ENDPOINT = $IOT_ENDPOINT\nPROVISIONING_TEMPLATE_NAME = template-$NAME" > config.ini
接続情報を確認します。
cat config.ini
デバイスをAWS IoT Coreに接続してFleet Provisioningを行います。
python3 main.py
以下のように出力されればFleet Provisioningは成功です。アクティベートした公式の証明書で接続して以前のbootstrap証明書では受け取ることのできなかった"openworld"というトピックからメッセージを受信しています。
[ec2-user@ip-192-168-128-14 fleetProv]$ python3 main.py
______ __ __
/ ____/ / / ___ ___ / /_
/ /_ / / / _ \ / _ \ / __/
/ __/ / / / __/ / __/ / /_
/_/ /_/ \___/ \___/ \__/
____ _ _ _
/ __ \_________ _ __(_)____(_)___ ____ (_)___ ____ _
/ /_/ / ___/ __ \ | / / / ___/ / __ \/ __ \/ / __ \/ __ `/
/ ____/ / / /_/ / |/ / (__ ) / /_/ / / / / / / / / /_/ /
/_/ /_/ \____/|___/_/____/_/\____/_/ /_/_/_/ /_/\__, /
/____/
____________________________________________________________
/_____/_____/_____/_____/_____/_____/_____/_____/_____/_____/
##### CONNECTING WITH PROVISIONING CLAIM CERT #####
##### SUCCESS. SAVING KEYS TO DEVICE! #####
##### CREATING THING ACTIVATING CERT #####
##### CERT ACTIVATED AND THING TestFleetPrefix_1261 CREATED #####
##### CONNECTING WITH OFFICIAL CERT #####
##### ACTIVATED AND TESTED CREDENTIALS (e1a30e7da6-private.pem.key, e1a30e7da6-certificate.pem.crt). #####
##### FILES SAVED TO /home/ec2-user #####
{'service_response': '##### RESPONSE FROM PREVIOUSLY FORBIDDEN TOPIC #####'}
CreateKeysAndCertificate
を呼び出して、AWS 認定権限により新しい証明書とプライベートキーを作成します。クレームによるデバイスのプロビジョニング方法 https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/provision-wo-cert.html
def on_message_callback(self, message):
""" Callback Message handler responsible for workflow routing of msg responses from provisioning services.
Arguments:
message {string} -- The response message payload.
"""
json_data = json.loads(message.payload)
# A response has been recieved from the service that contains certificate data.
if 'certificateId' in json_data:
self.logger.info('##### SUCCESS. SAVING KEYS TO DEVICE! #####')
print('##### SUCCESS. SAVING KEYS TO DEVICE! #####')
self.assemble_certificates(json_data)
# A response contains acknowledgement that the provisioning template has been acted upon.
elif 'deviceConfiguration' in json_data:
self.logger.info('##### CERT ACTIVATED AND THING {} CREATED #####'.format(json_data['thingName']))
print('##### CERT ACTIVATED AND THING {} CREATED #####'.format(json_data['thingName']))
self.rotate_certs()
else:
self.logger.info(json_data)
def rotate_certs(self):
"""Responsible for (re)connecting to IoTCore with the newly provisioned/activated certificate - (first class citizen cert)
"""
self.logger.info('##### CONNECTING WITH OFFICIAL CERT #####')
print('##### CONNECTING WITH OFFICIAL CERT #####')
self.cert_validation_test()
self.new_cert_pub_sub()
print("##### ACTIVATED AND TESTED CREDENTIALS ({}, {}). #####".format(self.new_key_name, self.new_cert_name))
print("##### FILES SAVED TO {} #####".format(self.secure_cert_path))