AWS IoT Coreの証明書を使ってS3にファイルをアップロードする

IoT Coreの証明書を使ってAWSのサービスを利用するための認証情報（アクセスキーIDとシークレットアクセスキー）を発行することができます。
IAMポリシーとうまく組み合わせることで、モノの名前のフォルダー配下だけにアクセスができる構成とすることができます。
設定ファイルをおいてみたりすると実用的ではないでしょうか。

```console
S3
└バケット
  ├デバイス001　　←他のデバイスのフォルダーは見れない
　│　└settiong.json
  ├デバイス002　　←自分のフォルダーは見れる
　│　└settiong.json
  ├デバイス003
　│　└settiong.json
　└デバイス004
　　　└settiong.json
```

参考サイト

* https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html
* https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-policies-s3.html

---

手順は結構長いですが、必要なリソースは以下のとおりです。

| サービス | 項目 | 内容 |
| --- | --- | --- |
| IoT Core | モノ |  |
| IoT Core | 証明書 | |
| IoT Core | ポリシー | ロールエイリアスに対する__iot:AssumeRoleWithCertificate__アクションが必要 |
| IoT Core | ロールエイリアス | 付与したいIAMロールを指定 |
| IAM | ポリシー |  |
| IAM | ロール | 信頼関係として__credentials.iot.amazonaws.com__サービスへの__sts:AssumeRole__アクションが必要 |
| S3 | バケット | |


こちらの名称で実施しました。

| サービス | 項目 | 名称 |
| --- | --- | --- |
| IoT Core | モノ | thing-3f21f372 |
| IoT Core | 証明書 | thing-3f21f372.cert.pem<br>thing-3f21f372.public.key<br>thing-3f21f372.private.key |
| IoT Core | ポリシー | thing-3f21f372-policy |
| IoT Core | ロールエイリアス | s3-bucket-4e8f4765-role-alias |
| IAM | ポリシー | s3-bucket-4e8f4765-policy |
| IAM | ロール | s3-bucket-4e8f4765-role-for-iot |
| S3 | バケット | bucket-4e8f4765 |


# リソース作成

## S3

### バケットの作成

```console
aws s3api create-bucket \
  --bucket bucket-4e8f4765 \
  --region ap-northeast-1 \
  --create-bucket-configuration LocationConstraint=ap-northeast-1
```

## IAM

### IAMポリシーの作成

ポリシー変数の`${credentials-iot:ThingName}`が、IoTのモノの名前になります。
使えるポリシー変数はこちら。

| ポリシー変数 |
| --- |
| credentials-iot:ThingName |
| credentials-iot:ThingTypeName |
| credentials-iot:AwsCertificateId |

IAMポリシーのJSONファイルを作成します。

```json:s3-bucket-4e8f4765-policy-document.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::bucket-4e8f4765",
            "Condition": {
                "StringLike": {
                    "s3:prefix": "${credentials-iot:ThingName}/*"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action":[
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:DeleteObject",
                "s3:DeleteObjectVersion"
            ],
            "Resource": "arn:aws:s3:::bucket-4e8f4765/${credentials-iot:ThingName}/*"
        }
    ]
}
```

```console
aws iam create-policy \
  --policy-name s3-bucket-4e8f4765-policy \
  --policy-document file://s3-bucket-4e8f4765-policy-document.json
```

### IAMロールの作成

信頼関係のポリシードキュメントを作成します。

```json:s3-bucket-4e8f4765-role-for-iot-policy-document.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

```console
aws iam create-role \
  --role-name s3-bucket-4e8f4765-role-for-iot \
  --assume-role-policy-document file://s3-bucket-4e8f4765-role-for-iot-policy-document.json
```

### IAMポリシーをIAMロールにアタッチ

IAMポリシーをIAMロールにアタッチ
（999999999999はアカウントIDです）

```console
aws iam attach-role-policy \
  --role-name s3-bucket-4e8f4765-role-for-iot \
  --policy-arn arn:aws:iam::999999999999:policy/s3-bucket-4e8f4765-policy
```

## IoT Core

### IoTロールエイリアスの作成

IAMロールに対するエイリアスを作成
（999999999999はアカウントIDです）

```console
aws iot create-role-alias \
  --role-alias s3-bucket-4e8f4765-role-alias \
  --role-arn arn:aws:iam::999999999999:role/s3-bucket-4e8f4765-role-for-iot
```

### モノの作成

```console
aws iot create-thing --thing-name thing-3f21f372
```

### 証明書の作成

```console
aws iot create-keys-and-certificate \
  --certificate-pem-outfile "thing-3f21f372.cert.pem" \
  --public-key-outfile "thing-3f21f372.public.key" \
  --private-key-outfile "thing-3f21f372.private.key" \
  --set-as-active
```

### AWS IoT Core ポリシーの作成

```json:thing-3f21f372-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:AssumeRoleWithCertificate",
      "Resource": "arn:aws:iot:ap-northeast-1:999999999999:rolealias/s3-bucket-4e8f4765-role-alias"
    }
  ]
}
```

```console
aws iot create-policy \
  --policy-name thing-3f21f372-policy \
  --policy-document file://thing-3f21f372-policy.json
```

### 証明書にAWS IoT Core ポリシーのアタッチ

```console
aws iot attach-policy \
  --policy-name thing-3f21f372-policy \
  --target "arn:aws:iot:ap-northeast-1:999999999999:cert/5c3453ecc18e5e6b8f3cf89a6c1d68f62a5b08855d13c61b37c987e3442f70f5"
```

### モノに証明書をアタッチ

```console
aws iot attach-thing-principal \
  --thing-name thing-3f21f372 \
  --principal arn:aws:iot:ap-northeast-1:999999999999:cert/5c3453ecc18e5e6b8f3cf89a6c1d68f62a5b08855d13c61b37c987e3442f70f5
```

リソースの準備は以上です。

# 認証情報の取得方法

認証情報の取得は、SDKではなく、curlコマンドで行います。（curl以外でももちろん可能です）

## エンドポイントの取得

使用するエンドポイントはアカウントごとに異なります。
あとで使うので環境変数に入れておきます。

```
export endpoint=`aws iot describe-endpoint --endpoint-type iot:CredentialProvider | jq .endpointAddress | tr -d '"'`
```

## 認証情報の取得

以下のパラメーターでリクエストを送信します。

| パラメータ | 設定値 |
| --- | --- |
| --cert | 証明書ファイル |
| --key | プライベートキー |
| x-amzn-iot-thingname | モノの名称。<br>これだけHTTPヘッダーで指定です |

```console
curl --cert ./thing-3f21f372.cert.pem \
  --key ./thing-3f21f372.private.key \
  -H "x-amzn-iot-thingname: thing-3f21f372" \
  https://${endpoint}/role-aliases/s3-bucket-4e8f4765-role-alias/credentials
```

```console:結果
{
  "credentials": {
    "accessKeyId": "ASIA3.....",
    "secretAccessKey": "fjKhv..........",
    "sessionToken": "IQoJb.....",
    "expiration": "YYYY-MM-DDThh:mm:ssZ"
  }
}
```

# S3へのアクセス

## 認証情報を環境変数にセット

認証情報を環境変数にセットします。

```console
credentials=`curl --cert ./thing-3f21f372.cert.pem \
  --key ./thing-3f21f372.private.key \
  -H "x-amzn-iot-thingname: thing-3f21f372" \
  https://${endpoint}/role-aliases/s3-bucket-4e8f4765-role-alias/credentials`

export AWS_ACCESS_KEY_ID=`echo $credentials | jq .credentials.accessKeyId | tr -d '"'`
export AWS_SECRET_ACCESS_KEY=`echo $credentials | jq .credentials.secretAccessKey | tr -d '"'`
export AWS_SESSION_TOKEN=`echo $credentials | jq .credentials.sessionToken | tr -d '"'`
```

## S3にアップロード

```console
[cloudshell-user@ip-10-0-129-6 ~]$ aws s3 cp test.txt s3://bucket-4e8f4765/thing-3f21f372/test.txt
upload: ./test.txt to s3://bucket-4e8f4765/thing-3f21f372/test.txt
```

無事アップロードできました。

## ファイルのリスト取得

```console
[cloudshell-user@ip-10-0-129-6 ~]$ aws s3 ls s3://bucket-4e8f4765/thing-3f21f372/
2021-03-28 06:42:12          5 test.txt
```

## 他のフォルダーへのアクセスできないことを確認

自分のフォルダー以外はアクセスできません

```console
[cloudshell-user@ip-10-0-129-6 ~]$ aws s3 ls s3://bucket-4e8f4765/thing-xxxxxxxx/

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
```

バケット直下もアクセスできないことを確認

```console
[cloudshell-user@ip-10-0-129-6 ~]$ aws s3 cp test.txt s3://bucket-4e8f4765/test.txt
upload failed: ./test.txt to s3://bucket-4e8f4765/test.txt An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
```