IoT Coreの証明書でIAM Roles Anywhere的なことをする（アリかもしれない）

[前回](https://qiita.com/moritalous/items/2fae4b3b17230b8931e2)、IAM Roles Anywhereの検証をしてとても良い機能だなと感じました。

https://qiita.com/moritalous/items/2fae4b3b17230b8931e2

そういえば、証明書を使った認証にIoT Coreがあったな🤔と思い出し、同じことができるかやってみました！試したところちょっとした違いがあり、IoT Coreでやるのもアリかもしれません。

## 手順

### IAMロールを作成

1. IoT Coreの認証情報プロバイダーが引き受けるIAMロールを作成します。
    信頼されたエンティティタイプを`カスタム信頼ポリシー`にし、信頼ポリシーに以下のJSONを入力します。

    ```json
    {
        "Version": "2012-10-17",
        "Statement": {
            "Effect": "Allow",
            "Principal": {
                "Service": "credentials.iot.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    }
    ```
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/4188bc9d-84fe-f23e-c311-b79871a3adbf.png)


1. 許可を追加で必要な権限を付与し`次へ`をクリックします。

1. ロールに名前をつけて保存します。（iot-roles-anywhere-role）としました。


### IoT ロールエイリアスを作成
1. IoT Coreの管理画面に移動し、管理メニュー -> セキュリティ -> ロールエイリアスを表示します。
    `ロールエイリアスを作成`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/61e1b4e7-643d-7dac-3040-a52eea43a849.png)

1. ロールエイリアスに名前をつけ、先程作成したロールを選択して、`作成`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/5f1d384a-e916-4d66-98e7-1406c5b3eb07.png)

作成したロールエイリアスのARNをメモしておきます。

### IoT ポリシーを作成

1. 管理メニュー -> セキュリティ -> ポリシーへ移動します。
    `ポリシーを作成`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/922a61be-4153-e3ae-997f-0f700aeaf49d.png)

1. ポリシー名を入力します。
    ポリシードキュメントは以下を入力して、`作成`をクリックします。
    * ポリシー効果： 許可
    * ポリシーアクション：iot:AssumeRoleWithCertificate
    * ポリシーリソース：作成したロールエイリアスのARN
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/91ff5607-99a8-2e71-6f9f-3dcd54cf091c.png)

### IoT モノのグループを作成

1. 管理メニュー -> すべてのデバイス -> モノのグループへ移動します。
    `モノのグループを作成`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/3376aa88-1ca6-7ef6-7b6d-79852ec6cd39.png)

1. `モノの静的グループを作成`を選択して、`次へ`をクリックします。

1. モノのグループ名を入力して、`モノのグループを作成`をクリックします。

1. 作成したグループを選択して、ポリシータブを選択。`ポリシーを管理`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/ce0354b8-9098-1b36-67dc-e6a9cc599644.png)

1. 先程作成したIoTポリシーを選択して、`ポリシーの更新`をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/056be8c5-1253-939c-4372-8952be190b28.png)


### IoT モノを作成

1. 管理メニュー -> すべてのデバイス -> モノへ移動します。
    `モノを作成`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8e97265c-ca2a-a173-092c-c12de5d313a0.png)

1. `1 つのモノを作成`を選択し、`次へ`をクリックします。

1. `モノの名前`を入力して、先ほど作成したモノのグループを選択。`次へ`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/7f64aed4-6bfb-abbb-56b7-5881529f6695.png)

1. `新しい証明書を自動生成 (推奨)`を選択して、`次へ`をクリックします。

1. ポリシーはグループに割り当てているので未選択のまま、モノを作成`をクリックします。

1. デバイス証明書、パブリックキーファイル、プライベートキーファイル、RSA 2048 ビットキー: Amazon ルート CA 1をダウンロードしてから、`続行`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b4c971e3-4be7-ecb2-9a40-e178c17ca158.png)


### 認証情報プロバイダーエンドポイントを取得する

```shell:CloudShellなど
aws iot describe-endpoint --endpoint-type iot:CredentialProvider
```
```
{
    "endpointAddress": "abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com"
}
```

## 一時認証情報を取得する

一時認証情報は認証情報プロバイダーエンドポイントにリクエストを送信して取得します。

```shell
export ENDPOINT=abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com
export ROLE_ALIAS_NAME=alias-for-roles-anywhere
export CERT_PATH=certificate.pem.crt
export PRIVATE_KEY_PATH=private.pem.key
export THING_NAME=client1
export CA_CERT_PATH=AmazonRootCA1.pem

curl \
 --cert ${CERT_PATH} \
 --key ${PRIVATE_KEY_PATH} \
 -H "x-amzn-iot-thingname: ${THING_NAME}" \
 --cacert ${CA_CERT_PATH} \
 https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials
```
```json
{
  "credentials": {
    "accessKeyId": "...",
    "secretAccessKey": "...",
    "sessionToken": "...",
    "expiration": "2023-10-21T09:45:03Z"
  }
}
```

### credential_process の仕組みに合わせる

もうちょいです。

認証情報プロバイダーエンドポイントの出力をcredential_processのフォーマットに変換します。

```json:credential_processのフォーマット
{
  "Version": 1,
  "AccessKeyId": "an AWS access key",
  "SecretAccessKey": "your AWS secret access key",
  "SessionToken": "the AWS session token for temporary credentials", 
  "Expiration": "ISO8601 timestamp when the credentials expire"
}
```

* Versionを追加
* 各キーの先頭を大文字にする

先程の出力をパイプで`jq`に繋いで変換します。

```shell
export ENDPOINT=abcdefghijklm.credentials.iot.ap-northeast-1.amazonaws.com
export ROLE_ALIAS_NAME=alias-for-roles-anywhere
export CERT_PATH=certificate.pem.crt
export PRIVATE_KEY_PATH=private.pem.key
export THING_NAME=client1
export CA_CERT_PATH=AmazonRootCA1.pem

curl \
 --cert ${CERT_PATH} \
 --key ${PRIVATE_KEY_PATH} \
 -H "x-amzn-iot-thingname: ${THING_NAME}" \
 --cacert ${CA_CERT_PATH} \
 https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials \
 | jq '.credentials | {"Version":1, "AccessKeyId":.accessKeyId, "SecretAccessKey":.secretAccessKey, "SessionToken":.sessionToken, "Expiration":.expiration}'

```
```json
{
  "Version": 1,
  "AccessKeyId": "...",
  "SecretAccessKey": "...",
  "SessionToken": "...",
  "Expiration": "2023-10-21T09:52:49Z"
}
```

いい感じです。
うまく行ったのでこいつをシェルスクリプトにして保存します。

```shell:credential-helper.sh
#!/bin/sh
ENDPOINT=ce4kc59ulqnzy.credentials.iot.ap-northeast-1.amazonaws.com
ROLE_ALIAS_NAME=alias-for-roles-anywhere
CERT_PATH=certificate.pem.crt
PRIVATE_KEY_PATH=private.pem.key
THING_NAME=client1
CA_CERT_PATH=AmazonRootCA1.pem

curl \
 --cert ${CERT_PATH} \
 --key ${PRIVATE_KEY_PATH} \
 -H "x-amzn-iot-thingname: ${THING_NAME}" \
 --cacert ${CA_CERT_PATH} \
 https://${ENDPOINT}/role-aliases/${ROLE_ALIAS_NAME}/credentials \
 | jq '.credentials | {"Version":1, "AccessKeyId":.accessKeyId, "SecretAccessKey":.secretAccessKey, "SessionToken":.sessionToken, "Expiration":.expiration}'
```

最後に`.aws/config`に登録します。

```text:.aws/config
[profile iotrolesanywhere]
credential_process = sh <<credential-helper.shのフルパス指定>>
```

AWSコマンドを実行します。

```console
aws sts get-caller-identity --profile iotrolesanywhere
```
```
{
    "UserId": "...",
    "Account": "...",
    "Arn": "arn:aws:sts::...:assumed-role/iot-roles-anywhere-role/..."
}
```

やったぜ

## IAM Roles Anywhereとの違い

良いと思った点
* IoT Coreだと証明証が無料でマネージド！
    IAM Roles AnywhereではAWS Private CA（有料）もしくは自前管理のプライベートCAが必要
* 証明書の失効も簡単！

残念な点
* なんでIoTやねん感
    * しっかりルールを明文化して管理しないと後々困りそう


## 参考

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-sourcing-external.html

https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html


## おまけ

AWS発行のIoT Coreの証明書に使われているRootCA証明書をIAM Roles Anywhereで使おうとするとエラーになりました😝

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/a207401d-17a5-3672-56eb-578fd95ac9c0.png)