AWS IoT 調査 (途中)

コーヒーポットや冷蔵庫など、人は昔からいろんな物をインターネットに接続してきました。ネットの黎明期ならグローバルアドレスを使ってデバイス同士を直接通信されるだけで良かったですが、今どきのネットに繋げるには色々問題があります。例えば。。。

* 経路の問題。家庭内のデバイスはグローバルアドレスを持たないため直接デバイスに通信を送る事が出来ない。
* 安全の問題。ニセのデバイスを見分けたい。

[Amazon IoT とは](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/what-is-aws-iot.html) この問題に対する今どきの回答です。

* 経路の問題を解決するために、Amazon IoT サーバで中継する。
* 安全の問題を解決するために X.509 クライアント証明書や SigV4 (アマゾン製の署名) を使う。

AWS IoT は、通信方式として MQTT という物を使っています。ざっくり言うと、トピックというキーに対して複数のクライアントがテキスト(JSON)を送ったり受け取ったりして通信します。ここでは `topic_fruit` という名前のトピックを使う例を挙げます。

## 証明書

Amazon IoT にアクセスするためには証明書が必要です。証明書を使う事でクライアントごとに適切な権限を設定出来ます。証明書は自分で作って登録することも、AWS に作ってもらう事も出来ます。証明書を AWS に作ってもらうには `create-keys-and-certificate` コマンドを使います。

証明書の内容は標準出力に返りますが、オプションでファイルに出力すると便利です。
    
* --certificate-pem-outfile: 証明書のファイル名 あとで使う
* --public-key-outfile: 公開鍵のファイル名
* --private-key-outfile: 秘密鍵のファイル名 あとで使う
* --set-as-active: 証明書には ACTIVE と INACTIVE の状態があります。すぐ使うのでこのオプションで ACTIVE にしておきます。

```
aws iot create-keys-and-certificate --set-as-active \
    --certificate-pem-outfile apple.pem \
    --public-key-outfile apple.pub.key \
    --private-key-outfile apple.pri.key
    
{
    "certificateArn": "arn:aws:iot:ap-northeast-1:xxx:cert/170e8aa97e49b1db662d3675d728b7a35f171821158a08a4096065cbda14e5a1", 
...
```

出力の `certificateArn` の値で証明書を特定します。

## Policy

証明書に権限を設定するには `create-policy` を使ってPolicy を作成し、証明書と関連付けます。[AWS IoT ポリシー](https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/iot-policies.html) に詳しい設定方法が書いてあります。

Action の意味は以下の通り:
        
* `iot:Publish`: あるトピックを送信する権利
  * Resource に `arn:aws:iot:リージョン名:アカウント:topic/トピック名` のようにトピックを指定する。
  * 下の例では topic_fruit にメッセージを送れるようになっている。
* `iot:Receive`: あるトピックを受信する権利 イマイチ何のためにあるのか分からなかった。
  * Resource に `arn:aws:iot:リージョン名:アカウント:topic/トピック名` のようにトピックを指定する。
* `iot:Connect`: AWS IoT に接続する権利
  * Resource に `arn:aws:iot:リージョン名:アカウント:client/クライアントID` のようにクライアント ID を指定する。
  * 下の例では client_fruit というクライアント ID が接続出来るようになっている。
* `iot:Subscribe`: あるトピックを Subscribe する権利
  * Resource に `arn:aws:iot:リージョン名:アカウント:topicfilter/トピックフィルタ名` のようにトピックフィルタを指定する。
  * 下の例では topic_fruit に来たメッセージを受け取れるようになっている。

```
aws iot create-policy --policy-name fruit-policy --policy-document '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:xxx:topic/topic_fruit"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:xxx:client/client_fruit"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Subscribe"
      ],
      "Resource": [
        "arn:aws:iot:ap-northeast-1:xxx:topicfilter/topic_fruit"
      ]
    }
  ]
}'
```

Policy には、更新した後で簡単に戻せるよう Policy version という物があります。Policy を更新するには、まず新しい Policy version を作ってからそれを default にするという手順を取ります。

Policy version の作成には [`create-policy-version`](https://docs.aws.amazon.com/cli/latest/reference/iot/create-policy-version.html) を使います。`--set-as-default` を指定すると作成と同時に default に設定されるため、一つのコマンドで Policy を更新出来ます。

```
aws iot create-policy-version --set-as-default --policy-name fruit-policy --policy-document '...'
```

Policy version を default にしたり削除するには以下のようなコマンドを使います。マニュアルによると最大 5 個までの Policy version しか作れないらしいので古いのは削除します。

```
aws iot set-default-policy-version --policy-name fruit-policy --policy-version-id 3 # 3 番の policy version を default にする。
aws iot delete-policy-version --policy-name fruit-policy --policy-version-id 3 # 3 番の policy version を削除する
```

## Policy と証明書を関連付ける。

`attach-policy` を使って Policy と証明書を関連付けます。これでこの証明書を使うクライアントに権限が設定されます。

```
aws iot attach-policy --policy-name fruit-policy --target arn:aws:iot:ap-northeast-1:xxx:cert/44969003adb95c46a537454b64a7262b0d8a7363435f36093d363e5961952a40
```

## Curl でメッセージを Publish する

[Amazon IoT で使えるプロトコル](https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html) は幾つかあります。ここでは REST API を使ってメッセージを送信してみます。まず `describe-endpoint` で Endpoint を確認します。

```
aws iot describe-endpoint
{
    "endpointAddress": "xxx.iot.ap-northeast-1.amazonaws.com"
}
```

REST API には https://Endpoint:8443/topics/トピック名 でアクセスします。

事前に [AWS Console > IoT](https://console.aws.amazon.com/iot) > Test のテスト用 MQTT Client で topic_fruit を subscribe しておきます。

まず、root-CA が必要なのでダウンロードしておきます。

```
curl https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem > root-CA.crt
```

以下のコマンドで topic_fruit にメッセージを送ります。

```
curl -v --tlsv1.2 \
    --cacert root-CA.crt \
    --cert apple.pem \
    --key apple.pri.key \
    -d "{ \"message\": \"Hello from Curl at $(date)\" }" \
    https://xxx.iot.ap-northeast-1.amazonaws.com:8443/topics/topic_fruit?qos=1
```

AWS Console に以下のように出たら送信は成功しています。

```
{
  "message": "Hello from Curl at Mon Aug 27 18:33:10 JST 2018"
}
```

REST API の仕様は [Publish](https://docs.aws.amazon.com/ja_jp/iot/latest/apireference/API_iotdata_Publish.html) に記載されています。

## Policy と証明書の削除

Policy を削除するには事前に証明書との関連付けを解除します。証明書を削除するには事前に INACTIVE 状態に設定し、Policy との関連付けを解除します。ここでは 1. Policy 2. 証明書 の順に削除してゆきます。

Policy に関連付けられた証明書を表示

```
aws iot list-targets-for-policy --policy-name fruit-policy
{
    "targets": [
        "arn:aws:iot:us-east-1:xxx:cert/48f6bfe6619c05fab43587c69dce323bd3c6c43b465511e8a467c23393364cb6"
    ]
}
```

Policy と証明書の関連を解除。

```
aws iot detach-policy --policy-name fruit-policy --target arn:aws:iot:us-east-1:xxx:cert/48f6bfe6619c05fab43587c69dce323bd3c6c43b465511e8a467c23393364cb6
```

Policy を削除。

```
aws iot delete-policy --policy-name fruit-policy
```

証明書を INACTIVE にしてから削除する。

```
aws iot update-certificate --new-status INACTIVE --certificate-id 48f6bfe6619c05fab43587c69dce323bd3c6c43b465511e8a467c23393364cb6
aws iot delete-certificate --certificate-id 48f6bfe6619c05fab43587c69dce323bd3c6c43b465511e8a467c23393364cb6
```

## メッセージを Subscribe する

次に topic_fruit トピックを Subscribe します。とりあえず [http://mosquitto.org/](mosquitto) というツールを使います。

```
brew install mosquitto
```

[mosquitto_sub](http://mosquitto.org/man/mosquitto_sub-1.html) を以下のように使って Amazon IoT の MQTT より topic_fruit を受信します。

```
$ mosquitto_sub -d --id client_fruit --url mqtts://xxx.iot.ap-northeast-1.amazonaws.com:8883/topic_fruit --cafile root-CA.crt --cert apple.pem --key apple.pri.key
Client client_fruit sending CONNECT
Client client_fruit received CONNACK (0)
Client client_fruit sending SUBSCRIBE (Mid: 1, Topic: topic_fruit, QoS: 0)
Client client_fruit received SUBACK
Subscribed (mid: 1): 0
Client client_fruit received PUBLISH (d0, q0, r0, m0, 'topic_fruit', ... (64 bytes))
{ "message": "Hello from Curl at Tue Aug 28 13:57:24 JST 2018" }
```

## Thing の利用

(途中)


## 参考

* [AWS IoT Message BrokerのMQTTでpub/subをやってみた #reinvent](https://dev.classmethod.jp/cloud/aws/pub-sub-with-aws-iot-over-mqtt/)