💨
IoTデバイスから直接いろんなAWSサービスにアクセスするのが便利
Credential Provider
Credential Providerを使うと、タイトルのことができる。これはかなり便利。アイデアとしては以下のような感じ。
- AWS IoTに接続するデバイスは、一般的に証明書と秘密鍵をもってクライアント認証をしてIoT Coreに接続している。
- デバイスはIoT Coreだけじゃなくて他のAWSサービス(例:S3)にアクセスしたい時もある。Sig V4形式の認証情報でアクセスできるが、その場合はアクセスキーとシークレットキーが必要。デバイスにそいう情報は置きたくない。
- デバイスはクライアント証明書をもってるんだからそれで認証・認可してしまおう。
以下に具体的な方法を記載する。
IAMロールを作る
Trust Policy trust-policy.json
を作っておく。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "credentials.iot.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Roleを作って、それにTrust Policyをアタッチ
aws iam create-role \
--role-name s3_role \
--assume-role-policy-document file://trust-policy.json
# 確認
aws iam get-role --role-name s3_role
S3アクセス用の Policy を作っておく。
s3-access-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "*"
}
]
}
上記 Policy を使って、s3_access_policy
を作成する。
export S3_POLICY_ARN=$(aws iam create-policy \
--policy-name s3_access_policy \
--policy-document file://s3-access-policy.json \
--query Policy.Arn \
--output text)
PolicyをRole s3_role
にアタッチ
aws iam attach-role-policy \
--role-name s3_role \
--policy-arn $S3_POLICY_ARN
# 確認
aws iam list-attached-role-policies --role-name s3_role
RoleのARNを取得
export S3_ROLE_ARN=$(aws iam list-roles --query "Roles[?RoleName=='s3_role'].Arn" --output text)
ロールエイリアスを作成
デバイスがトークンをリクエストするときに、ロールを直接指定してしまうと、たとえばロール名が変わったときにファームウェア側のアップグレードが必要になるなど面倒なことになるので、ロールにエイリアスを作って、デバイス側からはロール名の変更が見えないようにしようというもの[1]。
aws iot create-role-alias \
--role-alias s3_role_alias \
--role-arn $S3_ROLE_ARN
export S3_ROLE_ALIAS_ARN=$(aws iot describe-role-alias \
--role-alias s3_role_alias \
--query roleAliasDescription.roleAliasArn \
--output text)
echo $S3_ROLE_ALIAS_ARN
作成したロールエイリアスが証明書での認証後にでAssume RoleできるようにIoTポリシーのファイルを作る。
cat << END > iot-policy.json
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": "iot:AssumeRoleWithCertificate",
"Resource": "$S3_ROLE_ALIAS_ARN"
}
}
END
iot_policy
を作成
aws iot create-policy \
--policy-name iot_policy \
--policy-document file://iot-policy.json
Thingを作る
Thingを作る
aws iot create-thing --thing-name thing_x
{
"thingName": "thing_x",
"thingArn": "arn:aws:iot:ap-northeast-1:<ACCOUNT_ID>:thing/thing_x",
"thingId": "d62b589c-77d0-48a5-b71d-5be6c844667f"
}
クライアントの証明書と秘密鍵を取得する
export CERTIFICATE_ARN=$(aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile "./device.pem.crt" \
--public-key-outfile "./public.pem.key" \
--private-key-outfile "./private.pem.key" \
--query "certificateArn" --output text)
証明書にThingとIoT Policyを紐づける
aws iot attach-thing-principal --thing-name thing_x --principal $CERTIFICATE_ARN
aws iot attach-policy --target $CERTIFICATE_ARN --policy iot_policy
IoT Coreのルート証明書を取得
wget -q https://www.amazontrust.com/repository/AmazonRootCA1.pem
トークンをリクエストしてみる
トークンをリクエストするエンドポイントを取得する。エンドポイントタイプは iot:CredentialProvider
である。
export ENDPOINT_NAME=$(aws iot describe-endpoint \
--endpoint-type iot:CredentialProvider \
--query endpointAddress \
--output text)
# 確認
echo $ENDPOINT_NAME
トークンの取得
curl --cert device.pem.crt \
--key private.pem.key \
-H "x-amzn-iot-thingname: thing_x" \
--cacert AmazonRootCA1.pem \
https://$ENDPOINT_NAME/role-aliases/s3_role_alias/credentials
{"credentials":{"accessKeyId":"ADIASYRA4ZVER27QKYXQ","secretAccessKey":"rxvDAueXJKu9S4JUGP32a/zqfQzP8yV6C9aQGxsT","sessionToken":"IQoJb3JpZ2luX2VjEKb//////////wEaDmFwLW5vcnRoZWFzdC0xIkYwRAIgI5yspZ554ktQM0fUUYTyksJKfuFF7v43TBH5I4IqPZwCIAMWEs2wMXudF6qBWpaExVnmRo3fmx3n4BBgFiVDsCmWKsMDCND//////////wEQARoMNTMzNzE4NjcwNjY1IgyuC6Gnjd4LN+WSExgqlwNiqF8PE4XL3SXmKdri2sSKpnAzAlFMHAmHcFDQJ+3HflCyWDJ8JtdF5IMu2f1h/GTi5WmwuurIXl55D25NNbL/JpJ60T0YE1B0vzPV8I94vxFquMsazlamzO58mGyi5hHbR7PJfkiajGMe9hUG982+EUA1rYvRq0bEmNJRLJzWaXFiOmk0MCSnIf9JJT0fC9zkF8mRRaKuxTixX/sjCMUtTVKFA1PLPwR891RFGAe7ruYvqQCKhzmy60EVrAPNsFKIr3AaHLJayyzvq9nA/oLJG3epLsSciiuXfDgSg7oV3aoQ6HiACpBv4voBv/bnm6d0sBuD1byOmifxptQ/iav/skA43sfstO6SA8svZoWoUfp9gPIUD5iMWwmRUhnAIlPwtJapepIkyR+MP7nOPo1BKTwQyKW0A+/d69SUTsg26rgroy1f5yKBxk/xquj961MQnBBXlY09M7Ah0v3POx9S/F6xBi9ElDsNIz1Ya997tvOldbEvEm8Z2ztZ5cYEjq5kZGLe6JTRPPXg7QeUmqiin1Lb/0+33zCnl/2pBjqXAd7/yxIqIbU63rdR6OvXCy0x3n62iRqRN+Uduq7rpdQ/R9p4h+oimXiKNVKapLAwzKOX1PkpSilc8O4tiLk9zhFKuGfpd0/gzrG6+2Es4QrBLsbAfunwtcu7ZtnuGxqDk2OkKkITFn8f3sRBaHpL4sx7/Xg5AZs3Z9R32IhzZj0df7BMHPzw0uJzfW0GI86QOhrXUcKZemg=","expiration":"2023-10-30T07:22:31Z"}}%
S3にアップロードしてみる
例えば、デバイス上のファイルをS3にアップロードするRubyクライアントコード[2]
require 'net/http'
require 'json'
require 'aws-sdk-s3'
ENDPOINT_URL = ENV["ENDPOINT_NAME"]
THING_NAME = "thing_x"
ROLE_ALIAS = "s3_role_alias"
uri = URI.parse("https://#{ENDPOINT_URL}/role-aliases/#{ROLE_ALIAS}/credentials")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.key = OpenSSL::PKey::RSA.new(File.read("private.pem.key"))
http.cert = OpenSSL::X509::Certificate.new(File.read("device.pem.crt"))
http.ca_file = 'AmazonRootCA1.pem'
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(uri.request_uri)
request["x-amzn-iot-thingname"] = THING_NAME
response = http.request(request)
if response.code.to_i != 200
puts "Error: #{response.code} - #{response.message}"
exit 1
end
credential_data = JSON.parse(response.body)
Aws.config.update({
region: 'ap-northeast-1',
credentials: Aws::Credentials.new(
credential_data['credentials']['accessKeyId'],
credential_data['credentials']['secretAccessKey'],
credential_data['credentials']['sessionToken']
)
})
s3 = Aws::S3::Resource.new
obj = s3.bucket('2heozhb6').object('file_y')
obj.upload_file('./file_y')
参考
- AWS IoT Core認証情報プロバイダーを使用して、AWSサービスの直接呼出しを認証
- AWS IoT Core の認証プロバイダを使って IoT デバイスからセキュアに AWS サービスを利用する
- 同様のことが、最近は閉域(VPCE)でもできるようになっているので、別途試したい。 AWS IoT Core Credential Provider supports Virtual Private Cloud endpoints
Discussion