AWS IoTのX.509証明書を使ってAssumeRoleする

IoTデバイスからAWSのサービスに繋ぐ時に困る事の一つに、デバイスからのAWSのクレデンシャルの取得があります。

最近、AWS IoTのX.509証明書を使ってhttpリクエストをする事で、AssumeRoleして一時クレデンシャルを取得する事ができるようになったとの事なので試しました。
[Authorizing Direct Calls to AWS Services - Security and Identity for AWS IoT](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/authorizing-direct-aws.html)

現状だと、公式のドキュメントにはサンプルコードはありませんでした。[フォーラム](https://forums.aws.amazon.com/thread.jspa?messageID=817558&tstart=0)に試し方が書いてあったので、これに従います。

次の4ステップで確認できます。

  1. Thing+Certificateを作る
  2. デバイスに渡したいIAMRoleを作る
  3. RoleAliasを作る
  4. 一時クレデンシャルを取得するエンドポイントを取得する
  5. 接続チェック！

# 1. Thing+Certificateを作る
ウィザードに従ってThingとCertificateを作って下さい。Certificateは一旦「One-click certificate creation (recommended)」で作ります。CertificateにattachするPolicyは以下の内容にします。`iot:AssumeRoleWithCertificate`というactionを付与すると良いそうです。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:AssumeRoleWithCertificate"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
```
作成後は証明書、秘密鍵、ルート証明書をダウンロードしておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/55128/394ce37d-b6fb-ae5a-d88f-c14e54613a2f.png)

# 2. デバイスに渡したいIAMRoleを作る
デバイスに渡したいIAM Roleを作ります。Permissionの指定はお好きに設定してください。
Trust Relationshipは以下の内容にします。Management Consoleから作る時は一旦適当なServiceを割り当てた後に修正してください。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "credentials.iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```
# 3. RoleAliasを作る
RoleAliasとは、AWS IoTで追加された概念で、デバイスからIAMRoleをリクエストする時にARNのハードコードをさけてRoleのアップデートをしやすくするために追加されたようです。
> The device which is going to make direct calls to AWS Services must know what role ARN to use when connecting to AWS IoT. But hard-coding the role ARN is not a good solution because you would have to update the device anytime the role ARN changes. A better solution is to create a role alias that points to the role ARN and use that on your device. If the role ARN changes, you can update the role alias and no change is required on the device. Role aliases are created using the CreateRoleAlias API. This API takes the following parameters:

AWS IoTの"Secure > Role Aliases"タブからRoleAliasesを作成します。今回は`cert-cred-test-role-alias`という名前にしています。roleには先ほど作ったIAMRoleを指定してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/55128/22638362-fc9b-d68b-266c-6e664d7d769e.png)

# 4. 一時クレデンシャル用エンドポイントを取得する
一時クレデンシャルを取得するためのエンドポイントはCLIから取得する事ができます。このエンドポイントはMQTT用のエンドポイントは別です。

```shell-session

$ aws iot describe-endpoint --endpoint-type=iot:CredentialProvider
{
    "endpointAddress": "xxxxxxxxx.credentials.iot.us-west-2.amazonaws.com"
}
```

# 5. 接続チェック！
```shell-session

$ wget --private-key=./1234567-private.pem.key \
       --certificate=./1234567-certificate.pem.crt \
       --ca-directory=./AmazonRootCA1.pem \
       https://xxxxxxxxx.credentials.iot.us-west-2.amazonaws.com:443/role-aliases/cert-cred-test-role-aliases/credentials
$ cat credentials
> (クレデンシャルが見えます)
```

:tada:

ちなみにPythonから取得する時はこんな感じ。

```python3
import requests

endpoint = 'https://xxxxxxxx.credentials.iot.us-west-2.amazonaws.com:443'
role_alias = 'cert-cred-test-role-alias'
cert_id = '1234567'

r = requests.get(
    f'{endpoint}/role-aliases/{role_alias}/credentials',                                                                                                                                                        
    cert=(f'./{cert_id}-certificate.pem.crt', f'./{cert_id}-private.pem.key')
)
print(r.status_code)
print(r.text)

```