Azure IoT EdgeデバイスをX.509証明書を使って自動プロビジョニングする

IoT EdgeのデバイスをIoT Hubと通信できるようにする（＝プロビジョニングする）ためには、大きく二つの構成方法があります。

- 手動プロビジョニング
    - IoT Hubへの接続文字列を個々のデバイスに埋め込んで通信を行う。
    - 手軽に対応できるため、少量のデバイスでソリューションを構築する際に便利。

- 自動プロビジョニング
    - Device Provisioning ServiceというPaaSのサービスを別途使用して行う。
    - デバイスを一括して、IoT Hubに自動登録できるため、大量のデバイスを扱う際は便利。
    - デバイス側で、トラステッドプラットフォームモジュール（TPM）が必要。
   
IoT Edgeデバイスの自動プロビジョニングを実行する場合、現状はデバイス側でTPMが必要とされていますが、[プレビュー機能](https://github.com/Azure/iotedge/blob/master/doc/rc/how-to-auto-provision-x509-certificates.md)にて X.509証明書を使った構成方法も提供されています。Raspberry Piのように標準でTPMをサポートしていないデバイスでIoT Edgeを使用する場合に有用な機能と思われますので、試した手順をまとめてみました。

なお、IoT Edgeでない通常のIoTデバイスであれば、以前よりX.509証明書を使った自動プロビジョニングが利用可能となっています。

自動プロビジョニングの詳細については、[こちら](https://docs.microsoft.com/ja-jp/azure/iot-dps/concepts-auto-provisioning)を参照ください。

# 手順概要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/d18a0ba1-f4c1-7283-fa22-81343ecef0ef.png)


# 1) IoT Hub + Device Provisioning Serviceの設定

以下の手順に従って、IoT HubとIoT Hub Device Provisioning Service（以降DPS）を作成し、それらをリンクさせます。

[クイック スタート:Azure Portal で IoT Hub Device Provisioning Service を設定する](https://docs.microsoft.com/ja-jp/azure/iot-dps/quick-setup-auto-provision)

# 2) IoT Edgeの構成
今回は、Azure Marketplaceから既にIoT Edgeランタイム構成済のUbuntu仮想マシンをデプロイします。
（クラウド上の仮想マシンをIoT Edgeデバイスと見立てます）

[Azure portal](https://portal.azure.com/) にサインインし、[+ リソースの作成] ボタンを選択し、 [Marketplace を検索] フィールドに「Azure IoT Edge on Ubuntu」と入力します。
検索結果の [Azure IoT Edge on Ubuntu] を選択し、[作成] を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/cd245a8e-ced7-ff07-6946-01232bb4efd8.png)

[仮想マシンの作成] でご自身の環境に応じて各項目を適宜指定し、[確認および作成] より仮想マシンを [作成] します。
- [仮想マシン名] は任意の名前を指定
- [認証の種類] は [パスワード] を選択し、任意の [ユーザー名] [パスワード] を指定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/d92da44f-2b48-05c3-162d-9ca8827b4668.png)

デプロイ完了後、仮想マシンのIPアドレスを固定します。
（※必須作業ではありませんが、SSH利用時の利便性のため、アドレスを固定するのが目的です）

仮想マシンの画面を開き、[パブリックIPアドレス] のアドレスをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/d094a6cc-326f-267c-3b9c-59b94a59ec68.png)

[静的] を選択し、[保存]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/046f39cf-624b-a1fd-6797-76020ccd1a06.png)

# 3) IoT Edgeランタイムの更新
SSHにて、作成したIoT Edgeデバイスにログインします。
`iotedge version` コマンドにて、デバイスで実行されているセキュリティ デーモンのバージョンを確認します。

```
testuser001@IoTEdgeVM001:~$ iotedge version
iotedge 1.0.8 (208b2204fd30e856d00b280112422130c104b9f0)
```

バージョン1.0.8が導入されていますが、X.509による自動プロビジョニング機能は 1.0.9-rc2 以降で提供されているため、IoT Edgeランタイムのバージョンを更新します。

[Azure IoT Edgeリリース](https://github.com/Azure/azure-iotedge/releases)の一覧より、1.0.9-rc2 以降のバージョンである **1.0.9-rc4** の [Asset] セクションを展開します。
（※**-rc**がついているバージョンは、安定版の前のリリース候補(RC)バージョンを表します）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/9c610fbe-419d-07bc-3ea7-f5613a4c9278.png)

IoT Edgeデバイスのアーキテクチャーに対応する **libiothsm-std** ファイルを探し、リンクのアドレスをコピーします。
（今回はamd64のUbuntu 16.04を使用しているため、**libiothsm-std_1.0.9_rc4-1_ubuntu16.04_amd64.deb**のリンクをコピーします）

コピーしたリンクを使用して、そのバージョンの hsmlib をインストールします。

```
curl -L https://github.com/Azure/azure-iotedge/releases/download/1.0.9-rc4/libiothsm-std_1.0.9_rc4-1_ubuntu16.04_amd64.deb -o libiothsm-std.deb && sudo dpkg -i ./libiothsm-std.deb
```

IoT Edge デバイスのアーキテクチャに対応する iotedge ファイルを探し、リンクのアドレスをコピーします。
（今回はamd64のUbuntu 16.04を使用しているため、**iotedge_1.0.9_rc4-1_ubuntu16.04_amd64.deb**のリンクをコピーします）

コピーしたリンクを使用して、そのバージョンの IoT Edge セキュリティ デーモンをインストールします。

```
curl -L https://github.com/Azure/azure-iotedge/releases/download/1.0.9-rc4/iotedge_1.0.9_rc4-1_ubuntu16.04_amd64.deb -o iotedge.deb && sudo dpkg -i ./iotedge.deb
```

インストール成功後、新しいバージョンの config.yaml 導入するかどうか聞かれますが、**Y or I** にて置き換えを選択します。

```
Configuration file '/etc/iotedge/config.yaml'
 ==> Modified (by you or by a script) since installation.
 ==> Package distributor has shipped an updated version.
   What would you like to do about it ?  Your options are:
    Y or I  : install the package maintainer's version
    N or O  : keep your currently-installed version
      D     : show the differences between the versions
      Z     : start a shell to examine the situation
 The default action is to keep your current version.
*** config.yaml (Y/I/N/O/D/Z) [default=N] ? Y
Installing new version of config file /etc/iotedge/config.yaml ...
```


`iotedge version` コマンドにて、デバイスで実行されているセキュリティ デーモンのバージョンが更新されていることを確認します。

```
testuser001@IoTEdgeVM001:~$ iotedge version
iotedge 1.0.9~rc4
```

# 4) 証明書の生成

Linuxコンピューター上にて、テスト用の証明書を生成します。
（※この手順で生成される証明書はテストのみを目的としておりますので、運用環境では使用しないでください）

テスト証明書の生成に使用できるスクリプトが含まれるAzure IoT Edge gitリポジトリーをクローンします。

```
git clone https://github.com/Azure/iotedge.git
```

任意の作業ディレクトリーを作成し移動します。すべての証明書ファイルと鍵ファイルがこのディレクトリに作成されます。

```
mkdir work
cd work
```

クローンしてきたgitリポジトリーから作業ディレクトリーに、構成ファイルとスクリプトファイルをコピーします。

```
cp ../iotedge/tools/CACertificates/*.cnf .
cp ../iotedge/tools/CACertificates/certGen.sh .
```

以下コマンドを使用して、ルート CA 証明書と中間証明書を作成します。

```
./certGen.sh create_root_and_intermediate
```

以下マンドを使用して、IoT Edge デバイス ID 証明書と秘密鍵を作成します。`<name>` は任意のデバイス ID で置き換えます。

```
./certGen.sh create_edge_device_identity_certificate "<name>"
```

以下に証明書と秘密鍵が生成されます。

`<work_dir>/certs/iot-edge-device-identity-<name>.cert.pem`
`<work_dir>/private/iot-edge-device-identity-<name>.key.pem`

# 5) DPS登録の追加

生成された証明書を使用して、DPS に個別の登録を作成します。

Azure portalで、DPSのインスタンスに移動し、[設定] > [登録を管理します] > [個別の登録の追加] を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/dc465988-2ce6-120a-85d4-680edc0296dd.png)

以下の通り指定し、[保存] をクリックします。
- [メカニズム] で [X.509] が選択されてることを確認
- [プライマリ証明書の.pem ファイルまたは.cer ファイル] で先ほど作成した証明書を指定
　　`<work_dir>/certs/iot-edge-device-identity-<name>.cert.pem`
- [IoT Hub のデバイス ID] に、必要に応じてデバイスIDを指定
- [IoT Edge デバイス] で [True] を選択
- [デバイス ツインの初期状態] に、必要に応じてタグ値を追加
- [エントリの有効化] で [有効] に設定されていることを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/3cfa7138-0fda-a457-0677-c8796db0c5aa.png)

保存が完了したら、DPSの [概要] の画面に戻って、[スコープID] の値を控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/f346f54e-6018-29f8-3158-dcc663b6e77b.png)


# 6) 自動プロビジョニングの実行

SSHにてIoT Edgeデバイスにログインし、構成ファイルを開きます。

```
sudo vi /etc/iotedge/config.yaml
```

手動プロビジョニングに関する行をコメントアウトします。

```
# Manual provisioning configuration
# provisioning:
#   source: "manual"
#   device_connection_string: "<ADD DEVICE CONNECTION STRING HERE>"
```

X.509による自動プロビジョニングに関する行のコメントアウトを解除し、以下項目にそれぞれ適切な値を指定します。
- scope_id： 先の手順で確認したDPSのスコープIDを指定
- registration_id：（オプション）登録IDを指定
- identity_cert: デバイスID証明書のファイルパスを指定（`file://<path>`形式にて指定）
- identity_pk: デバイスID秘密鍵のファイルパスを指定（`file://<path>`形式にて指定）

例）

```
# DPS X.509 provisioning configuration
provisioning:
  source: "dps"
  global_endpoint: "https://global.azure-devices-provisioning.net"
  scope_id: "0ne000C3E88"
  attestation:
    method: "x509"
    registration_id: "iotedgedev001"
    identity_cert: "file:///home/testuser001/work/certs/iot-edge-device-identity-iotedgedev001.cert.pem"
    identity_pk: "file:///home/testuser001/work/private/iot-edge-device-identity-iotedgedev001.key.pem"
```

以下コマンドにてIoT Edgeランタイムを再起動します。

```
sudo systemctl restart iotedge
```

`systemctl status iotedge`コマンドにてステータスが **active (running)** になっていることを確認します。

```
estuser001@IoTEdgeVM001:~$ systemctl status iotedge
● iotedge.service - Azure IoT Edge daemon
   Loaded: loaded (/lib/systemd/system/iotedge.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-02-18 13:54:34 UTC; 5min ago
     Docs: man:iotedged(8)
 Main PID: 27344 (iotedged)
    Tasks: 8
   Memory: 3.3M
      CPU: 1.629s
   CGroup: /system.slice/iotedge.service
           mq27344 /usr/bin/iotedged -c /etc/iotedge/config.yaml
```

ログの確認方法

```
journalctl -u iotedge --no-pager --no-full
```

稼働中のモジュールの確認方法

```
iotedge list
```

Azure portalで、IoT Hubのインスタンスに移動し、[デバイスの自動管理] > [IoT Edge] を開き、該当のIoT EdgeデバイスがDPSによって自動プロビジョニングされていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/575879/6c18d20e-e74f-ef59-9599-10aaf4596bbd.png)

これで、IoT EdgeデバイスとIoT Hubの通信が行えるようになりました。