Amazon Web Services ブログ

AWS IoT Core の認証プロバイダを使って IoT デバイスからセキュアに AWS サービスを利用する

こんにちは、プロトタイピングソリューションアーキテクトの市川です。

現在、様々なユースケースで IoT デバイスが AWS IoT Core を利用しています。ユースケースの中には AWS のサービスを直接利用したいという話もよく相談として受けます。

IoT デバイスのアプリケーションから AWS のサービスを利用する場合は、AWS 署名バージョン 4 形式 (SigV4) の AWS 認証情報を使用して呼び出すことができます。この署名を作成するためには、クレデンシャル情報(アクセスキー ID、シークレットアクセスキー)が必要になってきます。しかし、不特定多数が触る可能性がある IoT デバイスにこのクレデンシャル情報を持たせるのはセキュリティ上非常に危険な行為となり、どの様にクレデンシャル情報を管理するのが良いかという相談も同時に受けます。このような課題は AWS IoT Core で提供している「認証情報プロバイダ」という仕組みで解決することができます。

AWS IoT Core へ接続する際に利用する X.509 証明書を使って、特定の AWS Identity and Access Management (IAM) ロールの権限を持つ一時的なクレデンシャル情報を取得できる仕組みを「認証情報プロバイダ」という機能で提供しています。これまでは、この認証情報プロバイダを利用できる証明書は、 AWS IoT Core で発行した証明書と、AWS IoT Core に登録した認証局(CA)が発行した証明書が対象となっていましたが、昨年末に発表された機能の追加で、利用できる証明書にマルチアカウント登録証明書が追加されました。マルチアカウント証明書とは、AWS IoT Core に登録されていない CA によって発行された証明書を事前にマネージメントコンソールや CLI などを利用して登録できる仕組みです。

マルチアカウント登録証明書が利用されるユースケースとしては以下のようなものがあります。

  1. 生産時に証明書をデバイスに埋め込み、同じ証明書を使って異なる AWS アカウントで構築されている検証環境、本番環境のAWS IoT Core エンドポイントを使いたい
  2. セキュアエレメントに含まれている証明書を AWS IoT Core に登録してデバイスを接続したい

AWS Partner Device Catalog に掲載されている株式会社アットマークテクノArmadillo-IoT G4 の様に、デバイスに搭載されているセキュアエレメントの証明書を利用して AWS IoT Core に接続するようなケースでは、今までは AWS のサービスを直接利用する場合は工夫が必要でしたが、今回の発表でこのようなデバイスでも認証情報プロバイダを利用することで、セキュアに AWS のサービスと連携することが出来ます。株式会社アットマークテクノのブログ「 Armadillo-IoT G4 を用いた物体認識プログラムの動作確認と AWS 各サービスの利用による分析方法」という記事で Armadillo-IoT G4 でセキュアエレメントを使ったクレデンシャル認証プロバイダを利用する方法が紹介されていますのでぜひ御覧ください。記事の中で紹介されているユースケースでは以下のようなアーキテクチャを紹介されています。


この記事では Python のサンプルを使って認証情報プロバイダを利用する方法について紹介したいと思います。

事前準備

以下の手順では AWS CLI を利用して設定しますので IAM 、 AWS IoT Core の操作が可能な権限を持つ環境を用意してください。

Amazon S3 を操作できる IAM Role を作成

# クレデンシャル情報プロバイダを使って権限を引き受けたいRoleドキュメントを作成
cat << EOF > iot-assume-role-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
# IAM Roleの名前を変数に設定
S3_ALLOW_UPLOAD_ROLE_NAME=IoTDeviceUploadRole

# クレデンシャル情報プロバイダを使って権限を引き受けたいRoleを作成
aws iam create-role \
--role-name ${S3_ALLOW_UPLOAD_ROLE_NAME} \
--assume-role-policy-document file://iot-assume-role-policy.json

Amazon S3にアクセスできる権限を持つPolicyを作成

# Policyドキュメントを作成
cat << EOF > s3-access-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": "*"
    }
  ]
}
EOF

# ポリシーを作成し、変数にARNを設定
S3_UPLOAD_POLICY_ARN=$(aws iam create-policy \
--policy-name ${S3_ALLOW_UPLOAD_ROLE_NAME}_Policy \
--policy-document file://s3-access-policy.json \
--query Policy.Arn \
--output text) 

# 変数の確認
echo ${S3_UPLOAD_POLICY_ARN}

Policy を Role に紐付け

# 作成したPolicyをRoleに紐付け
aws iam attach-role-policy \
--role-name ${S3_ALLOW_UPLOAD_ROLE_NAME} \
--policy-arn ${S3_UPLOAD_POLICY_ARN}

# RoleのARNを変数に設定
S3_ALLOW_UPLOAD_ROLE_ARN=$(aws iam get-role \
--role-name ${S3_ALLOW_UPLOAD_ROLE_NAME} \
--query Role.Arn \
--output text)

# 変数の確認
echo ${S3_ALLOW_UPLOAD_ROLE_ARN}

AWS IoT Core の Role Alias を作成

認証情報プロバイダから引き受ける Role を指定する必要がありますが、引き受けたい Role の名前は IoT デバイスのアプリケーションで知っておく必要があります。しかし、引き受けたい Role が将来的に変更になるような場合に、デバイス側で保持している情報を変更するには OTA(Over-The-Air) などを利用してアプリケーションを変更する必要が出てきますが、 IoT デバイスの利用状況によっては OTA が難しいことがあります。 AWS IoT Core では Role の Alias (別名)を作成することで、将来的に Role を変更することになっても、クラウド側の設定のみを変えることで対応できるように、 Alias を利用しています。

# Alias名を変数に設定
S3_UPLOAD_ROLE_ALIAS_NAME=${S3_ALLOW_UPLOAD_ROLE_NAME}_Alias

# 変数の確認
echo ${S3_UPLOAD_ROLE_ALIAS_NAME}

# Role Aliasを作成
aws iot create-role-alias \
--role-alias ${S3_UPLOAD_ROLE_ALIAS_NAME} \
--role-arn ${S3_ALLOW_UPLOAD_ROLE_ARN}

# Role AliasのARNを変数に設定
S3_UPLOAD_ROLE_ALIAS_ARN=$(aws iot describe-role-alias \
--role-alias ${S3_UPLOAD_ROLE_ALIAS_NAME} \
--query roleAliasDescription.roleAliasArn \
--output text)

# 変数の確認
echo ${S3_UPLOAD_ROLE_ALIAS_ARN}

Role Alias を作成する際に一時的なクレデンシャルの有効期限(–credential-duration-seconds)を指定することが出来ます。デフォルトは1時間ですが、最大で12時間を指定することが可能です。

Assume Role ができるIoT Policy の作成

# IoT Policyのドキュメントを作成
cat << EOF > iot-policy.json
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "iot:AssumeRoleWithCertificate",
    "Resource": "${S3_UPLOAD_ROLE_ALIAS_ARN}"
  }
}
EOF

# IoT Policy名を設定
IOT_POLICY_NAME=AllowResumeIoTPolicy

# IoT Policyを作成
aws iot create-policy \
--policy-name ${IOT_POLICY_NAME} \
--policy-document file://iot-policy.json

IoT デバイスが AWS IoT Core とメッセージングもする場合は、この IoT Policy にメッセージングに必要な権限も付与します。

IoT デバイスで利用する証明書を登録

証明書 (iot_device.crt) はセキュアエレメントから取得したものか、ご自身で作成したものを登録します。仮のものを作成する場合はこちらの手順を参考に作成してください。

# CA 無しで証明書を登録
aws iot register-certificate-without-ca \
--status ACTIVE \
--certificate-pem file://iot_device.crt > registered.json

IoT Policy を登録した証明書に紐付け

# 変数に証明書のIDとARNを設定
CERTIFICATE_ID=`jq -r ".certificateId" registered.json`
CERTIFICATE_ARN=`jq -r ".certificateArn" registered.json`

# 証明書に IoT Policyを登録
aws iot attach-policy --target $CERTIFICATE_ARN --policy $IOT_POLICY_NAME

Thingを作成して証明書を紐付け

THING_NAME=Thing1

# Thingの作成
aws iot create-thing \
  --thing-name $THING_NAME
  
# Thingに証明書を紐付け
aws iot attach-thing-principal \
  --thing-name $THING_NAME \
  --principal $CERTIFICATE_ARN

認証情報プロバイダのエンドポイントを確認


ENDPOINT_NAME=$(aws iot describe-endpoint \
--endpoint-type iot:CredentialProvider \
--query endpointAddress \
--output text)

# 認証情報プロバイダのエンドポイントを確認
echo $ENDPOINT_NAME

以上で事前準備が完了です。


認証情報プロバイダを利用する

認証情報プロバイダに対して HTTP リクエストを送ることで、上記の図の1〜6の処理が行われ、一時的なクレデンシャルが取得できます。

curl --cert iot_device.crt \
--key iot_device.key \
-H "x-amzn-iot-thingname: $THING_NAME" \
--cacert AmazonRootCA1.pem \
https://$ENDPOINT_URL/role-aliases/${S3_UPLOAD_ROLE_ALIAS_NAME}/credentials

上記のコマンドで、以下のような JSON データが返却されます。

{
    "credentials":{
        "accessKeyId":"xxx",
        "secretAccessKey":"xxxx",
        "sessionToken":"xxx",
        "expiration":"YYYY-MM-GGTHH:mm:SSZ"
    }
}

IoT デバイスではこの様に一時的な認証情報を取得し AWS SDK 等を使って AWS のサービスを直接呼び出すことが出来ます。以下の Python プログラムの例では、クレデンシャルを取得して Amazon S3 にデータを保存しています。ENDPOINT_NAME は、先の手順で確認した認証情報プロバイダのエンドポイントを指定し、ファイルに保存して実行してみてください。

import boto3
import json
import http.client
import ssl
import sys

context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.load_cert_chain("iot_device.crt", "iot_device.key")
context.load_verify_locations("AmazonRootCA1.pem")
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True

headers={
    "x-amzn-iot-thingname": "Thing1"
}

conn = http.client.HTTPSConnection("ENDPOINT_NAME", port=443, context=context)
conn.request("GET", "/role-aliases/IoTDeviceUploadRole_Alias/credentials", headers=headers)
response = conn.getresponse()
if response.status != 200:
  print("status:{} reason:{}".format(response.status, response.reason))
  sys.exit(1)

credential_data = json.loads(response.read())


s3 = boto3.resource('s3')
boto3.resource('s3', 
  aws_access_key_id=credential_data['credentials']['accessKeyId'], 
  aws_secret_access_key=credential_data['credentials']['secretAccessKey'],
  aws_session_token=credential_data['credentials']['sessionToken'])

bucket = s3.Bucket('your s3 bucket name') # 利用できるバケット名を指定
bucket.upload_file('./some_file', "some_file") # アップロードできるファイルを指定

まとめ

いかがでしたでしょうか。認証情報プロバイダを利用することで、IoT デバイスから直接 AWS のサービスを利用することができることが理解できたかと思います。この仕組を利用することで、データの種類によってデータの送信先を AWS IoT Core やそれ以外のサービスと柔軟に変更したいといったユースケースの課題をセキュアな方法で解決することが出来ます。


著者

市川 純
プロトタイピングソリューションアーキテクト
AWS では IoT に関連するプロトタイピングを支援する、プロトタイピング ソリューション アーキテクトとして、お客様の IoT 関連案件を支援しています。