LoginSignup
6
1

More than 3 years have passed since last update.

【AWS IoT】Thing用の個別のクライアント証明書を使ってS3/DynamoDBにアクセスする

Last updated at Posted at 2019-08-25

はじめに

AWS IoTをはじめとし、多くのIoTシステムでは、各デバイスそれぞれに固有のクライアント証明書を発行し、これを利用してAuthentication/Authorizationを実現しているものと思います。
しかしながら、ほとんどのAWSサービスはクライアント証明書に対応しておらず、例えば特定のクライアント証明書を持つデバイスのみ、特定のS3 Bucketにアクセスさせたい、といったことはそのままでは実現できません。
しかしAuthorizing Direct Callsという仕組みを利用することで、これを実現することができるようになります。

Authorizing Direct Calls to AWS Services - AWS IoT
https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html

TL;DR

  • Authorizing Direct Callsとは、個別のクライアント証明書をTemporary Security Credentialsに変換する仕組み
  • Temporary Security Credentialsがあれば、(権限があれば)AWS APIを何でも実行できる
  • IoT Thingから見た場合、Certificate→IoT Policy→Role Alias→IAM Roleというかたちで権限情報が紐づく

検証した環境

  • AWSアカウントID: 123456789099
  • S3 Bucket:
    • S3 Bucket名: investigate-aws-iot
    • テスト対象ファイル名: s3://investigate-aws-iot/file_needs_retrieving.txt
  • IAM Role:
    • Role名: InvestigateAuthorizingDirectCallsRole
    • Role ARN: arn:aws:iam::123456789099:role/InvestigateAuthorizingDirectCallsRole
  • AWS IoT:
    • IoT Endpoint: abcdefgh123456.credentials.iot.ap-northeast-1.amazonaws.com
    • Role Alias:
      • Role Alias名: InvestigateIotRoleAlias
      • Role Alias ARN: arn:aws:iot:ap-northeast-1:123456789099:rolealias/InvestigateIotRoleAlias
    • Thing/Certificate/Policy
      • Thing: InvestigateAuthorizingDirectCallsThing
      • Certificate: c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e
      • Policy: InvestigateAuthorizingDirectCallsPolicy

作業概要

  1. S3 Bucketの準備
  2. IoT ThingからAssumeRoleするIAM Roleの作成
  3. AWS IoT Role Aliasの作成
  4. AWS IoT Thing/Certificate/Policyの準備
    1. Thingの作成
    2. IoT Policyの作成
    3. Certificateの作成
    4. Certificateのactivate
    5. CertificateへのPolicyのattach
    6. CertificateをThingにattach
  5. Authorizing Direct Callsの確認
    1. AWS IoTのEndpoint確認
    2. curlを実行し、Temporary Security Credentialsを取得する
    3. S3 Bucketからファイルを取得できることを確認する

各ステップの詳細

1. S3 Bucketの準備

手順詳細は省略しますが、下記のように、必要なS3 Bucketを作成し、取得対象のファイルを格納しておきます。

$ aws s3 ls
2019-08-21 15:14:05 investigate-aws-iot

$ aws s3 ls s3://investigate-aws-iot/
2019-08-21 15:16:09         11 file_needs_retrieving.txt

上記ファイルの内容は何でもよいです。今回は下記の内容でつくっています。

file_needs_retrieving.txt
retrieved


2. IoT ThingからAssumeRoleするIAM Roleの作成

本稿で確認するAuthorizing Direct Callsの仕組みは、最終的にThingはここで定義するIAM Roleの権限を取得することとなります。したがって、このRoleに必要なPermissionsを定義する必要があります。

Role名は上述の通り、「InvestigateAuthorizingDirectCallsRole」とします。

AWS IoTのサービスからAssumeRoleされる形になるため、Trust Relationshipで、「credentials.iot.amazonaws.com」からAssumeRoleできるようにしておきます。
またPermissionsとして、S3 Bucket一覧の取得と、対象S3 Bucketのオブジェクト取得に必要な権限を付与します。
それぞれ具体的には下記のようになります。

Trust Relationship
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
Permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:GetRole",
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::123456789099:role/InvestigateAuthorizingDirectCallsRole"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::investigate-aws-iot/*",
                "arn:aws:s3:::investigate-aws-iot"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "arn:aws:s3:::*"
        }
    ]
}

3. AWS IoT Role Aliasの作成

AWS IoTのRole Aliasを作成します。これはAWS Management Consoleからは実行できないようなので、AWS CLIから実行します。
Role Alias名は上述の通り「InvestigateIotRoleAlias」とします。
引数として、先ほど作成したIAM RoleのARNを渡します。

Role Aliasの作成
$ aws iot create-role-alias --role-alias InvestigateIotRoleAlias --role-arn arn:aws:iam::123456789099:role/InvestigateAuthorizingDirectCallsRole
{
    "roleAlias": "InvestigateIotRoleAlias",
    "roleAliasArn": "arn:aws:iot:ap-northeast-1:123456789099:rolealias/InvestigateIotRoleAlias"
}

4. AWS IoT Thing/Certificate/Policyの準備

動作確認対象のデバイス(Thing)を作成し、個別のクライアント証明書を発行し、Policyを紐づけます。
このあたりの作業は、通常AWS IoTを利用する上では当たり前のように実施していると思いますので、できる限り省略します。

4-1. Thingの作成

普通にThingを作成します。何も特別なことはありません。

Thingの作成
$ aws iot create-thing --thing-name InvestigateAuthorizingDirectCallsThing
{
    "thingArn": "arn:aws:iot:ap-northeast-1:123456789099:thing/InvestigateAuthorizingDirectCallsThing",
    "thingName": "InvestigateAuthorizingDirectCallsThing"
}

4-2. IoT Policyの作成

AWS IoTにはIoT Policyという概念があります。これは各クライアント証明書に紐づけるもので、当該クライアント証明書が紐づけられたThingに付与する権限を指定します。
本稿で必要な権限は、先ほど作成したRole Aliasに対する「iot:AssumeRoleWithCertificate」となります。

まずは下記JSONファイルを作成します。ここでは「iot_policy.json」とします。

iot_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:AssumeRoleWithCertificate",
      "Resource": "arn:aws:iot:ap-northeast-1:123456789099:rolealias/InvestigateIotRoleAlias"
    }
  ]
}

上記ファイルを指定して、IoT Policyを作成します。

IoT Policyの作成
$ aws iot create-policy --policy-name InvestigateAuthorizingDirectCallsPolicy --policy-document file://iot_policy_document.json
{
    "policyName": "InvestigateAuthorizingDirectCallsPolicy",
    "policyArn": "arn:aws:iot:ap-northeast-1:123456789099:policy/InvestigateAuthorizingDirectCallsPolicy",
    "policyDocument": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"iot:AssumeRoleWithCertificate\",\n      \"Resource\": \"arn:aws:iot:ap-northeast-1:123456789099:rolealias/InvestigateIotRoleAlias\"\n    }\n  ]\n}\n",
    "policyVersionId": "1"
}

4-3. Certificateの作成

クライアント証明書と鍵ペアを作成し、それぞれ個別のファイルに保存します。

まずはiot create-keys-and-certificateを実行し、結果をファイルに出力します。

クライアント証明書と秘密鍵の作成
$ aws iot create-keys-and-certificate > keys-and-cert.txt
$ cat keys-and-cert.txt
{
    "certificateArn": "......",
    "certificatePem": "-----BEGIN CERTIFICATE-----\n......\n-----END CERTIFICATE-----\n",
    "keyPair": {
        "PublicKey": "-----BEGIN PUBLIC KEY-----\n......\n-----END PUBLIC KEY-----\n",
        "PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\n......\n-----END RSA PRIVATE KEY-----\n"
    },
    "certificateId": "c231554173......"
}

certificateIdの先頭10文字を取得します。これはファイル名などに用います。

証明書IDの先頭10文字取得
$ cat keys-and-cert.txt | jq -r '.certificateId[0:10]'
c231554173

証明書と秘密鍵を個別のファイルに保存します。公開鍵は単体では特に必要ありません。

クライアント証明書と秘密鍵の取り出し
$ cat keys-and-cert.txt | jq -r '.certificatePem' > c231554173-certificate.pem.crt
$ cat keys-and-cert.txt | jq -r '.keyPair.PrivateKey' > c231554173-private.pem.key

4-4. Certificateのactivate

Certificateは作成直後は「INACTIVE」の状態になっていますので、これを「ACTIVE」に変更します。

クライアント証明書をactivateする
$ aws iot update-certificate --certificate-id c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e --new-status "ACTIVE"

iot describe-crtificateコマンドで、クライアント証明書のステータスを確認することができます。

クライアント証明書のステータスを確認する
$ aws iot describe-certificate --certificate-id c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e
{
    "certificateDescription": {
        "certificateArn": "arn:aws:iot:ap-northeast-1:123456789099:cert/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e",
        "status": "ACTIVE",
        "certificateId": "c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e",
        "lastModifiedDate": 1566750890.409,
        "certificatePem": "-----BEGIN CERTIFICATE-----\n......\n-----END CERTIFICATE-----\n",
        "transferData": {},
        "ownedBy": "123456789099",
        "creationDate": 1566750089.245
    }
}

4-5. CertificateへのPolicyのattach

クライアント証明書にIoT Policyを紐づけます。

aws iot attach-principal-policy
$ aws iot attach-principal-policy --policy-name InvestigateAuthorizingDirectCallsPolicy --principal arn:aws:iot:ap-northeast-1:123456789099:cert/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e

IoT Policyに紐づけられたクライアント証明書は、aws iot list-policy-principalsコマンドを実行することで確認できます。

aws iot list-policy-principals
$ aws iot list-policy-principals --policy-name InvestigateAuthorizingDirectCallsPolicy
{
    "principals": [
        "arn:aws:iot:ap-northeast-1:123456789099:cert/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e"
    ]
}

4-6. CertificateをThingにattach

クライアント証明書をThingに紐づけます。

aws iot attach-thing-principal
$ aws iot attach-thing-principal --thing-name InvestigateAuthorizingDirectCallsThing --principal arn:aws:iot:ap-northeast-1:123456789099:cert/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e

たクライアント証明書に紐づけられたThingは、aws iot list-principal-thingsコマンドを実行することで確認できます。

aws iot list-principal-things
$ aws iot list-principal-things --principal arn:aws:iot:ap-northeast-1:123456789099:cert/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e
{
    "things": [
        "InvestigateAuthorizingDirectCallsThing"
    ]
}

5. Authorizing Direct Callsの確認

5-1. AWS IoTのEndpoint確認

AWS IoTはそのサービス提供用に、各AWSアカウント・リージョンごとに専用のEndpointを用意しています。aws iot describe-endpointコマンドを実行して、当該Endpointのドメイン名を取得できます。

aws iot iot-describe-endpoint
$ aws --profile playground iot describe-endpoint --endpoint-type iot:CredentialProvider
{
    "endpointAddress": "abcdefgh123456.credentials.iot.ap-northeast-1.amazonaws.com"
}

5-2. curlを実行し、Temporary Security Credentialsを取得する

上記で取得したEndpointに対して、Thingのクライアント証明書・秘密鍵を用いてHTTPSリクエストを発行することで、Temporary Security Credentialsを取得することができます。

まず、AWSのRootCA局証明書が必要になりますので、これをダウンロードしておきます。

AmazonRootCA1証明書の保存
$ curl -O https://www.amazontrust.com/repository/AmazonRootCA1.pem

下記のように、curlの引数として以下の情報を渡すことで、Temporary Security Credentialsを取得することができます。

  • クライアント証明書とその秘密鍵
  • RootCA局証明書
  • Thing名を「x-amzn-iot-thingname」ヘッダに指定
$ curl -v --cert ./c231554173-certificate.pem.crt --key ./c231554173-private.pem.key \
  --cacert AmazonRootCA1.pem \
  -H "x-amzn-iot-thingname: InvestigateAuthorizingDirectCallsThing" \
  https://abcdefgh123456.credentials.iot.ap-northeast-1.amazonaws.com/role-aliases/InvestigateIotRoleAlias/credentials \
  | jq

上記が成功すると、下記のようなJSONの出力を得ることができます。この情報を用いて、S3やDynamoDBその他AWSリソースにアクセスすることが可能です。権限は、最初の方で作成したIAM Role「InvestigateAuthorizingDirectCallsRole」のPermissionsが付与されます。

{
  "credentials": {
    "accessKeyId": "ASIA................",
    "secretAccessKey": "................................",
    "sessionToken": "................................................",
    "expiration": "2019-08-25T18:26:25Z"
  }
}

5-3. S3 Bucketからファイルを取得できることを確認する

上記で取得したTemporary Security Credentialsを用いて、実際にS3 Bucketにアクセスできることを確認します。

まず、先ほど取得したAccessKeyId/SecretAccessKey/SessionTokenを、環境変数にセットします。

$ export AWS_ACCESS_KEY_ID=ASIA................
$ export AWS_SECRET_ACCESS_KEY=................................
$ export AWS_SESSION_TOKEN=................................................

なお、AWS CLIからTemporary Security Credentialsを利用する方法について、詳細は下記ドキュメントに記載があります。

Using Temporary Security Credentials to Request Access to AWS Resources - AWS Identity and Access Management
https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html#using-temp-creds-sdk-cli

次に、STSのGetCallerIdentity APIを実行し、APIの発行主体がIAM Role「InvestigateAuthorizingDirectCallsRole」になっていることを確認します。

$ aws sts get-caller-identity
{
    "Account": "123456789099",
    "UserId": "AROA............:c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e",
    "Arn": "arn:aws:sts::123456789099:assumed-role/InvestigateAuthorizingDirectCallsRole/c231554173c80cdb46da694a21ec92b060b07d27883214edf7ac1573843f2a3e"
}  

最後に、S3 Bucketからファイルを取得できることを確認します。

$ aws s3 ls s3://investigate-aws-iot
2019-08-21 15:16:09         11 file_needs_retrieving.txt

$ aws s3 cp s3://investigate-aws-iot/file_needs_retrieving.txt ./
download: s3://investigate-aws-iot/file_needs_retrieving.txt to ./file_needs_retrieving.txt

おわりに

これで、Thing個別のクライアント証明書を用いて、AWSリソースにアクセスできるようになりました。

AWSの基本的な考え方として、各AWSリソースにアクセスする際には、AWS Credentials(AccessKeyId/SecretAccessKey)か、Temporary Security Credentials(AccessKeyId/SecretAccessKey/SessionToken)を用いて、Signature V4で署名してAWS APIを発行する、という方針があると思います。
Cognito Federated Identitiesもそうですが、何らか操作する主体を特定できる情報があった場合、最終的にTemporary Security Credentialsに変換する、という考え方が根底にあるかと思います。

そのため、たとえクライアント証明書があったところで、これを用いて直接AWS APIを発行できるようになる可能性はゼロと考えた方がよいでしょう。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1