AWS IoTとHTTPプロキシ経由で接続する

# やりたいこと
* AWS IoTにHTTPプロキシ経由で接続したい
* 解決方法
    * 単純にHostとportを指定して接続、といけば良いのですが、TLS相互認証を利用したMQTTSでの接続はV2のSDKではいまのところサポートされてません。なので、SigV4の一時認証を使ってMQTT over WebSocket での接続を行います。
* もう少し詳しく書くと、
    * Device SDK (V2)では、プロキシをサポートしている。
    * ただし、MQTT over WebSocketで使用する必要があり、TLS相互認証ではなく、Sigv4での認証が必要になる。
    * そのためクライアント証明書を使って、AWS IoTのクレデンシャルプロバイダーから、SigV4の一時クレデンシャル取得しそれを使う
    * MQTT Pub/Sub可能なポリシーがattachされたロールのクレデンシャルを発行すると、このクレデンシャルを使ってMQTT over WebSocketが可能になる。

https://aws.amazon.com/blogs/security/how-to-eliminate-the-need-for-hardcoded-aws-credentials-in-devices-by-using-the-aws-iot-credentials-provider/
こちらの記事を参考に実施していく。この例だとDynamoDBへのアクセスを許可しているが、それをiot coreへのアクセスに変更すればよいので、流れは同様になる。ちなみに同じ方法でS3などへのアクセスも可能。

## Thingの作成
クレデンシャル発行時の認証には、クライアント証明書を用いる。IoT Coreはクライアント証明書の有効性を検証する。
証明書に対して、クライアントから指定されたrole-aliasに対して `AssumeRoleWithCertificate`の権限が付与されていれば、STSからTokenを取得する。
なので、証明書には、以下のようなポリシーをattachする。

```json:alicethermostatpolicy.json
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "iot:AssumeRoleWithCertificate",
    "Resource": "arn:aws:iot:us-east-1:<your_aws_account_id>:rolealias/${iot:Connection.Thing.ThingTypeName}-iot-access-role-alias",
    "Condition": {
      "StringEquals": {
        "iot:Connection.Thing.Attributes[Owner]": "Alice",
        "iot:Connection.Thing.ThingTypeName": "Thermostat"
      },
      "Bool": {
        "iot:Connection.Thing.IsAttached": "true"
      }
    }
  }
}
```

aws iotの設定は以下の通り。

```sh

aws iot create-thing-type --thing-type-name Thermostat

aws iot create-thing --thing-name MyHomeThermostat --thing-type-name Thermostat --attribute-payload "{\"attributes\": {\"Owner\":\"Alice\"}}"

aws iot create-keys-and-certificate --set-as-active \
    --certificate-pem-outfile    certificate.pem \
    --public-key-outfile         public_key.pem  \
    --private-key-outfile        private_key.pem \
    --query certificateArn

# <certificate-arn> が出力される

aws iot attach-thing-principal --thing-name MyHomeThermostat --principal <certificate-arn>

aws iot create-policy --policy-name AliceThermostatPolicy --policy-document file://alicethermostatpolicy.json

aws iot attach-policy --policy-name AliceThermostatPolicy --target <certificate-arn>
```

## IAM の設定
IAM Roleで、"credentials.iot.amazonaws.com"　からのsts:AssumeRole を許可する。また、このロールに対して、attachするポリシーでは、実際にクライアントが操作したいアクションを定義する。以下で、ロールとポリシーを作成し、ポリシーをロールにattachしている。
最後にロールエイリアスを作成し、IoT Coreが指定するRoleを設定する。クライアントは後ほどこのロールエイリアスに対してクレデンシャルをリクエストする。

```sh
aws iam create-role --role-name iot-access-role --assume-role-policy-document file://trustpolicyforiot.json
aws iam create-policy --policy-name accesspolicyforiot --policy-document file://accesspolicyforiot.json
aws iam attach-role-policy --role-name iot-access-role --policy-arn arn:aws:iam::<your_aws_account_id>:policy/accesspolicyforiot

aws iot create-role-alias --role-alias Thermostat-iot-access-role-alias --role-arn arn:aws:iam::<your_aws_account_id>:role/　iot-access-role --credential-duration-seconds 3600
```

```json:accesspolicyforiot.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "iot:*",
            "Resource": "*"
        }
    ]
}
```

一旦全許可してます。

## クレデンシャル取得のリクエストを投げる

クレデンシャル取得用のエンドポイントに対して、取得したい操作権限が紐付いているロールエイリアスを指定しリクエストを投げる。

```sh
aws iot describe-endpoint --endpoint-type iot:CredentialProvider

{
    "endpointAddress": "<your_aws_account_specific_prefix>.credentials.iot.us-east-1.amazonaws.com"
}

curl --cert certificate.pem  --key private_key.pem -H "x-amzn-iot-thingname: MyHomeThermostat" https://<your_aws_account_specific_prefix>.credentials.iot.us-east-1.amazonaws.com/role-aliases/Thermostat-iot-access-role-alias/credentials
```

IoT Coreでの認証・認可がOKであれば、IoT Coreはクライアントの代わりに、STSに対してAssumeRoleを呼び出し、Tokenの取得を行う。
そこで得られたクレデンシャルをレスポンスとしてクライアントに返却する。
クライアントでは、環境変数に情報を入れておく。

```sh

export AWS_ACCESS_KEY_ID=<access-key-id>
export AWS_SECRET_ACCESS_KEY=<secret-access-key>
export AWS_SESSION_TOKEN=<session-token>
```

## WebSocketとプロキシ接続

以前書いた[こちらの記事](https://qiita.com/tatsuhiroiida/items/bb9e801702ed0e641fa1) のような手段でセットアップする。証明書やThing情報は上記で作ったものを使う。

```sh
python pubsub.py --endpoint <your_aws_account_specific_prefix>-ats.iot.us-east-1.amazonaws.com --topic my/topic --use-websocket 
```
引数に、 `--use-websocket` を指定している。クレデンシャルを使うため、ここでは証明書や鍵情報は不要になる。
この方法だと、HTTP Proxy経由での接続も可能になるメリットがある。
プロキシを使う場合は、 `--proxy-host proxy.foo.com --proxy-port 1234` のように指定すればOK.