IoT Edgeの中で、ホスト(つまりDockerコンテナーの外側)で動作するコンポーネントとして、IoT Edgeセキュアデーモン(iotedged)とCLI(iotedge)がある。
これらはedgeletと呼ばれるネイティブコンポーネントで動作する(ちなみに、edgeletのほとんどはRustで記述されている。HSMと直接通信する部分のみC言語で記述されている)。
IoT Edgeセキュアデーモンは、以下の役割を持つコンポーネントである。
- HSM(Hardware Security Module)を抽象化する
- Dockerコンテナー内で動作するIoT Edgeのモジュールに対して、モジュールのアイデンティティに関わる情報のストレージを提供する
- IoT Edgeのモジュール間通信に必要なTLS証明書を管理する
- Docker(Moby)デーモンと通信してその機能をEdgeAgentに対して公開する
- edgeagentのブートストラップを行う
それに対し、CLIは、IoT Edge全体の管理やトラブルシューティングに必要なユーティリティツールである。
この文章は、1.0.9-rc1のコードベースを基にして記述されている。
ここでは、edgeletの中にあるRustのクレートの一覧を示す。 これらのクレートの概要を抑えておくことで、全体像が見えてくるだろう。順番は、大雑把にコアから周辺部(インターフェイス)に向けた順番になっている。
edgelet_coreクレート- コアとなる構造体やトレイトの定義。
managementクレート- 管理系のAPIの定義
workloadクレート- ワークロードAPIの定義
hsm_sysクレート- HSMのFFIとC言語実装
hsm_rsクレート- HSMのRust側ライブラリ
edgelet_hsmクレート- HSM関連のユーティリティ。
iothubserviceクレート- IoT Hub互換の構造体や列挙体の定義。
edgelet_iothubクレート- IoT Hubのデバイスレジストリを使用したアイデンティティマネージャーの実装
docker_rsクレート- Docker APIクライアント。
edgelet_dockerクレート- Dockerを使用したIoT Edgeセキュアデーモンランタイム実装。
edgelet_kubeクレート- Kubernetesを使用したIoT Edgeセキュアデーモンランタイム実装。
edgelet_httpクレート- HTTP APIのコアとなる構造体やトレイトの定義。
edgelet_http_mgmtクレートmanagementのHTTP APIとしての実装。
edgelet_http_workloadクレートworkloadのHTTP APIとしての実装。
provisioningクレート- マニュアルプロビジョニングロジック。
iotedgedクレート- IoT Edgeセキュアデーモンとしてのアプリケーションロジック。
iotedgeクレート- CLIとしてのアプリケーションロジック。
IoT Edgeを起動するとは、すなわちIoT Edgeセキュアデーモン(iotedged)を起動するということである。これは、以下のように動作する。
- ビルド時に構成された
unixモジュールまたはwindowsモジュールのrun()関数が呼び出される。 - アプリケーションの初期化シーケンスが実行される。
unixモジュールの場合、またはwindowsモジュールで環境変数IOTEDGE_RUN_AS_CONSOLEが存在する場合は以下のように動作する。appモジュールのinit()関数が呼び出され、以下を行う。logging::init()関数でロギングシステムを初期化する。Windowsの場合、use-event-logger引数が指定されている場合はlogging::init_win_log()を使用する。- 構成ファイルを
config-file引数で指定されたパスから読み込む。
- ルートモジュールの
Mainがビルド時に構成されたランタイム(edgelet_dockerモジュールのDockerModuleRuntimeまたはedgelet_kubeモジュールのKubeModuleRuntime)を指定してインスタンス化される。 Mainのrun_until()メソッドが呼び出される。signal::shutdown()を使用して、シグナルでシャットダウンできるようになっている。
- そうではない場合、つまりWindowsサービスの場合、以下のように動作する。
appモジュールのinit_win_svc_logging()が呼び出され、サービス起動時のエラーが記録されるようにする。windows_serviceクレートを使用して、Windowsサービスとして起動する。appモジュールのinit_win_svc()関数が呼び出され、構成ファイルをconfig-file引数で指定されたパスから読み込む。- Windowsサービスの状態を
Runningにする。 - ルートモジュールの
Mainがビルド時に構成されたランタイム(edgelet_dockerモジュールのDockerModuleRuntimeまたはedgelet_kubeモジュールのKubeModuleRuntime)を指定してインスタンス化される。 Mainのrun_until()メソッドが呼び出される。signal::shutdown()を使用して、シグナルでシャットダウンできるようになっている。さらに、このシグナルを受け取った時に、Windowsサービスの状態をStopPendingにする。
- 設定ファイル(
config.yaml)の内容のバリデーションを行う(Main::run_until())。- このとき、
provisioningの値がexternalの場合、環境変数IOTEDGE_EXTERNAL_PROVISIONING_ENDPOINTにconfig.yamlのendpointの値を設定する。
- このとき、
config.yamlで指定された値を環境変数に設定する(set_iot_edge_env_vars())。IOTEDGE_HOMEDIRにhomedirの値。certificatesがある場合、IOTEDGE_DEVICE_CA_CERTにdevice_ca_certの値(ファイルパス)、IOTEDGE_DEVICE_CA_PKにdevice_ca_pkの値(ファイルパス)、IOTEDGE_TRUSTED_CA_CERTSにtrusted_ca_certsの値(ファイルパス)を設定する。certificatesがない場合はクイックスタートモードになる。
config.yamlのprovisioningの値がdpsの場合、DPSの初期化を行う(set_iot_edge_env_vars())。attestationに応じた値を設定する。attestationがtpmまたはsymmetric_keyの場合、IOTEDGE_REGISTRATION_IDにregistration_idを設定する。attestationがx509の場合、IOTEDGE_REGISTRATION_IDにregistration_idを設定する(オプション)。また、IOTEDGE_DEVICE_IDENTITY_CERTにidentity_certの値(ファイルパスまたはURI)、IOTEDGE_DEVICE_IDENTITY_PKにidentity_pkの値(ファイルパスまたはURI)を設定する。
- HSMを初期化する。
edgeletという名前でインメモリストアを初期化する(hsm_client_crypto_init->edge_hsm_client_store_create->create_store)。- デバイスCA証明書を環境変数
IOTEDGE_DEVICE_CA_CERT、IOTEDGE_DEVICE_CA_PK、IOTEDGE_TRUSTED_CA_CERTSで指定されたパスから読み込む。これらが設定されていない場合は、90日間有効な証明書を生成する。(hsm_client_crypto_init->edge_hsm_client_store_create->hsm_provision->hsm_provision_edge_ca_certificates) - 環境変数
IOTEDGE_DEVICE_IDENTITY_CERTとIOTEDGE_DEVICE_IDENTITY_PKがあれば、それらからデバイスID証明書のパスを取得し、HSMに格納する(hsm_client_crypto_init->edge_hsm_client_store_create->hsm_provision->hsm_provision_edge_id_certificate)
- HSMライブラリのバージョンチェックをおこなう。
1.0.2でない場合はエラー終了。 - マスター暗号化キーを作成する。
edgelet-masterという名前でキーを作成する(edge_hsm_client_create_master_encryption_key->edge_hsm_client_store_insert_encryption_key)- 既存のキーを読み込む。
- キーのファイルパスを構築する。具体的には、以下のディレクトリ名またはパスを連結したファイルパスに、拡張子
.enc.keyを付けたもの。(build_enc_key_file_path)- ホームディレクトリ。具体的には環境変数
IOTEDGE_HOMEDIRの値。ない場合は/var/lib/iotedgeまたは%ProgramData%\iotedge\ enc_keys- キー名のSHA256ダイジェストをBASE64エンコードした文字列
- ホームディレクトリ。具体的には環境変数
- キーを読み込む(
load_encryption_key_from_file)
- キーのファイルパスを構築する。具体的には、以下のディレクトリ名またはパスを連結したファイルパスに、拡張子
- 既存のキーがなければ、暗号化キーを生成する(
generate_encryption_key)。- OpenSSLを初期化する。
- 32バイトのランダムなキーを生成する。
- 生成したキーを保存する。ファイルパスは前述のとおり(
save_encryption_key_to_file)
- hyper clientを初期化する。X509のDPSが構成されている場合はHSMからデバイスID証明書を取得し、Hyper Clientに設定する。(
prepare_httpclient_and_identity_data) - ホームディレクトリに
cacheサブディレクトリを作成する。 - ホームディレクトリの
hybrid_idサブディレクトリを使用して、X509のDPSの設定を行う(prepare_master_hybrid_identity_key)- DPSのX509認証構成場合、ハイブリッドアイデンティティ鍵を取得または作成する(
get_or_create_hybrid_identity_key)。- キーを読み取る(
get_hybrid_identity_key_inner)。- ファイル
{ホームディレクトリ}/hybrid_id/iotedge_hybrid_keyの内容を鍵として読み取る。 - ファイル
{ホームディレクトリ}/hybrid_id/iotedge_hybrid_ivの内容をIVとして読み取る。 - IVの長さが16バイトであることを検証する。
- クライアントIDを
$iotedgeにして、IVを渡して、暗号APIのdecryptを呼び出して鍵を復号する。 - 復号した鍵(マスターキー)の長さが32バイトであることを確認する。
- ファイル
- キーの取得に失敗した場合、ハイブリッドアイデンティティ鍵を作成する(
create_hybrid_identity_key)。- ディレクトリ
{ホームディレクトリ}/hybrid_id/を作成する。 - 暗号APIの
get_random_bytesを呼び出し、32バイトのマスターキーを生成する。 - 暗号APIの
get_random_bytesを呼び出し、16バイトのIVを生成する。 - クライアントIDを
$iotedgeにして、IVを渡して、暗号APIのencryptを呼び出して鍵を暗号化する。 - ファイル
{ホームディレクトリ}/hybrid_id/iotedge_hybrid_keyに暗号化した鍵を書き込む。 - ファイル
{ホームディレクトリ}/hybrid_id/iotedge_hybrid_ivにIVを書き込む。 - マスターキーを返す。
- ディレクトリ
- キーを読み取る(
- DPSでない場合、
{ホームディレクトリ}/hybrid_id/以下を削除する。
- DPSのX509認証構成場合、ハイブリッドアイデンティティ鍵を取得または作成する(
- デバイスのプロビジョニングを行う。
manualの場合(manual_provision())- 接続文字列をパースする。
- キーストアとして
edge_coreクレートのMemoryKeyStoreを初期化する。 - プロビジョニングを実施する(
provisioningクレートのManualProvisioning::provision())- キーストア(
MemoryKeyStore)にデバイスのprimaryアイデンティティ鍵として接続文字列のデバイスキーを保存する(activate_identity_key)。
- キーストア(
DerivedKeyStoreをMemoryKeyStoreから取り出したデバイスのprimaryアイデンティティ鍵で初期化する。
externalの場合config.yamlのprovisioning.endpointを使用して、ExternalProvisioningClient構造体(edgelet_http_exgternal_provisioning)を初期化する。- 内部には
external_provisioningクレートのAPIClient構造体をラップしている。さらに、そのAPIClient構造体は、external_provisioningクレートのapisモジュールのExternalProvisioningApiClient構造体をラップしている。 provisioning.endpointのベースパスが保存される。
- 内部には
ExternalProvisioningClientをラップするExternalProvisioning構造体(provisioningクレート)を初期化する。- キーストアとして
MemoryKeyStoreを初期化する。 - プロビジョニングを実行する(
ExternalProvisioningClient::provision()){basePath}/device/provisioninginformation?api-version=2019-04-10にGETリクエストを実行する。- 結果の
credentialを解析し、AuthTypeとCredentialSource列挙体の値に変換する。authTypeがsymmetric-keyの場合:sourceがpayloadの場合:keyをBase64でデコードする。- キーストア(
MemoryKeyStore)にデバイスキーprimaryとして保存する(activate_identity_key)。
hsmの場合:- 何もしない。
- それ以外の場合:エラー
- それ以外の場合:エラー
- プロビジョニングの結果の
credentials.authTypeがsymmetricKeyであることを検証する。x509の場合はエラーとする。credentials.symmetricKey.keyがある場合:keyのコピーをMemoryKeyとして初期化し、DervicedKeyStoreを初期化する。
credentials.symmetricKey.keyがない場合:TpmKeyStore構造体を初期化する。TpmKeyStoreからデバイスのprimaryキーを取得し、そのキーでDerivedKeyStoreを初期化する。
dpsの場合config.yamlのprovisioning.attestationがtpmの場合:- DPSによるプロビジョニングを実行する(
dps_tpm_provision)。- TPMインターフェイス(
hsm_rsクレートのTpm構造体)を初期化する。- TPM初期化処理を呼び出す(
hsm_client_tpm_init@hsm_client_tpm_select.c->hsm_client_tpm_device_init@hsm_client_tpm_device)。なお、実装は空。 - インターフェイス取得処理を呼び出す(
hsm_client_tpm_interface@hsm_client_tpm_select.c->hsm_client_tpm_device_interface@hsm_client_tpm_device.c)。 azure-iot-hsm-cライブラリのAPIを使用して、TPMクライアント情報を初期化する(hsm_client_tpm_create@hsm_client_tpm_device.c->initialize_tpm_device)。
- TPM初期化処理を呼び出す(
- TPM(HSM)ライブラリのバージョンチェックをおこなう。
1.0.2でない場合はエラー終了。 - TPMのEK(Endosement Key)を取得する(
Tpm::get_ek()) - TPMのSRK(Storage Root Key)を取得する(
Tpm::get_srk()) - DPSクライアント(
provisioningクレートのDpsTpmProvisioning構造体)をHyper Client、config.yamlのglobal_endpoint、scope_id、registration_id、APIバージョン2018-11-01、TPMから取得したEKとSRKで初期化する。 - DPS用のキーストア(
edgelet_hsmクレートのTpmKeyStore構造体)を初期化する。 - バックアップファイル
{ホームディレクトリ}/cache/provisioning_backup.jsonを使用し、DpsTpmProvisioningをラップするBackupProvisioning構造体provisioningクレート)を初期化する。 - プロビジョニングを行う(
BackupProvisioning::provision())。DpsTpmProvisioning::provision()を呼び出す。DpsClientをDpsAuthKind::TpmとEK、SRKで初期化する。DpsClient::register()を呼び出す。- TPM固有の登録処理を実行する(
register_with_tpm_auth)。/{scopeId}/registrations/{registrationId}/registerに対して、registrationId、tpm.endorsementKey、tpm.storageRootKeyを持つJSONをボディにして、PUTリクエストを送信する。結果は401になるはず。- TPMチャレンジ鍵を取得する(
get_tpm_challenge_key)- レスポンスの
authenticationKeyをバイト列にデコードし、キーストア(TpmKeyStore)に、デバイスのparimaryアイデンティティ鍵として保存する(activate_identity_key)。- 実装としては、
hsm_client_tpm_activate_identity_key@hsm_client_tpm_device.c。
- 実装としては、
- キーストア(
TpmKeyStore)から、デバイスのparimaryアイデンティティ鍵を取得する(get)。- 実体はTPMのラッパーである
TpmKeyが返る。
- 実体はTPMのラッパーである
- レスポンスの
scopeId、registrationId、チャレンジ鍵からDpsTokenSourceを初期化する。- 引数と
DpsTokenSourceをもとに、もう一度/{scopeId}/registrations/{registrationId}/registerに対して、registrationId、tpm.endorsementKey、tpm.storageRootKeyを持つJSONをボディにして、先ほど作成したDpsTokenSourceで生成するSASトークンを使用して、PUTリクエストを送信する(get_operation_id())。- ここで、SASトークンの署名には、
DpsTokenSourceのチャレンジ鍵(TpmKey)のSign::signトレイトメソッドの実装が使用される。これは、hsm_rsクレートのTpm::sign_with_identity()メソッドに処理を委譲し、hsm_client_tpm_sign_data@hsm_client_tpm_device経由でTPMによる署名が行われる。
- ここで、SASトークンの署名には、
- キーストア(
TpmKeyStore)からデバイスのprimaryアイデンティティ鍵を取得する。 - 取得したキーを使用して、
DpsTokenSourceを初期化する。 - DPSの結果を構築する(
get_device_registration_result)。- 先ほど構築した
DpsTokenSourceで生成するSASトークンを使用して、/{scopeId}/registrations/{registrationId}/operations{operationId}にGETリクエストを送信し(operationIdはPUTの結果に含まれている)、DPSの結果を取得する(get_operation_status)。
- 先ほど構築した
- TPM固有の後処理を実行する。
- DPSの戻り値の
authentication_keyをバイト列にデコードし、キーストア(TpmKeyStore)に、デバイスのparimaryアイデンティティ鍵として保存する(activate_identity_key)。
- DPSの戻り値の
- DPS処理の戻り値を応答から生成する(
get_device_info)。
- TPM固有の登録処理を実行する(
ProvisioningResultを構築する。device_id:DPSから返されたデバイスID。hub_name:DPSから返されたIoT Hubの名前。reconfigure:常にReprovisioningStatus::InitialAssignmentsha256_thumbprint:常にNonecredentials:常にNone
- 結果をバックアップファイルに記録する。
TpmKeyStoreからデバイスのprimary鍵を取り出し、DervicedKeyStoreを初期化する。
- TPMインターフェイス(
- DPSによるプロビジョニングを実行する(
config.yamlのprovisioning.attestationがsymmetric_keyの場合:- DPSによるプロビジョニングを実行する(
dps_symmetric_key_provision)。- キーストア(
edgelet_coreクレートのMemoryKeyStore構造体)を初期化する。 config.yamlのattestation.symmetric_keyをバイト列にデコードし、デバイスのprimaryキーとしてキーストアに格納する(activate_identity_key)。- DPSクライアント(
provisioningクレートのDpsSymmetricKeyProvisioning構造体)をHyper Client、config.yamlのglobal_endpoint、scope_id、registration_id、APIバージョン2018-11-01で初期化する。 - バックアップファイル
{ホームディレクトリ}/cache/provisioning_backup.jsonを使用し、DpsSymmetricKeyProvisioningをラップするBackupProvisioning構造体provisioningクレート)を初期化する。 - プロビジョニングを行う(
BackupProvisioning::provision())。DpsSymmetricKeyProvisioning::provision()を呼び出す。DpsClientをDpsAuthKind::SymmetricKeyで初期化する。DpsClient::register()を呼び出す。- 対象鍵固有の登録処理を実行する(
register_with_symmetric_key_auth)。- キーストア(
MemoryKeyStore)からチャレンジ鍵として対称鍵を取得する(get_symmetric_challenge_key) scopeId、registrationId、チャレンジ鍵からDpsTokenSourceを初期化し、DpsClientに設定する。/{scopeId}/registrations/{registrationId}/registerに対して、registrationIdを持つJSONをボディにして、PUTリクエストを送信する。
- キーストア(
- キーストア(
MemoryKeyStore)からデバイスのprimaryアイデンティティ鍵を取得する。 - 取得したキーを使用して、
DpsTokenSourceを初期化する。 - DPSの結果を構築する(
get_device_registration_result)。- 先ほど構築した
DpsTokenSourceで生成するSASトークンを使用して、/{scopeId}/registrations/{registrationId}/operations{operationId}にGETリクエストを送信し(operationIdはPUTの結果に含まれている)、DPSの結果を取得する(get_operation_status)。
- 先ほど構築した
- DPS処理の戻り値を応答から生成する(
get_device_info)。
- 対象鍵固有の登録処理を実行する(
ProvisioningResultを構築する。device_id:DPSから返されたデバイスID。hub_name:DPSから返されたIoT Hubの名前。reconfigure:DPSから返されたsubstatusの値。ない場合はReprovisioningStatus::InitialAssignment。sha256_thumbprint:常にNonecredentials:常にNone
- 結果をバックアップファイルに記録する。
MemoryKeyStoreからデバイスのprimary鍵を取り出し、DervicedKeyStoreを初期化する。
- キーストア(
- DPSによるプロビジョニングを実行する(
config.yamlのprovisioning.attestationがx509の場合:- DPSによるプロビジョニングを実行する(
dps_x509_provision)。- キーストア(
edgelet_coreクレートのMemoryKeyStore構造体)を初期化する。 - ハイブリッドアイデンティティ鍵をデバイスの
primaryキーとしてキーストアに格納する(activate_identity_key)。 - DPSクライアント(
provisioningクレートのDpsX509Provisioning構造体)をHyper Client、config.yamlのglobal_endpoint、scope_id、registration_id(未指定の場合は証明書のCN)、APIバージョン2018-11-01で初期化する。 - バックアップファイル
{ホームディレクトリ}/cache/provisioning_backup.jsonを使用し、DpsTpmProvisioningをラップするBackupProvisioning構造体provisioningクレート)を初期化する。 - プロビジョニングを行う(
BackupProvisioning::provision())。DpsX509Provisioning::provision()を呼び出す。DpsClientをDpsAuthKind::X509で初期化する。DpsClient::register()を呼び出す。- X509固有の登録処理を実行する(
register_with_x509_auth)。/{scopeId}/registrations/{registrationId}/registerに対して、registrationIdを持つJSONをボディにして、PUTリクエストを送信する。認証はHyper Clientに設定されたデバイスID証明書による。
- DPSの結果を構築する(
get_device_registration_result)。/{scopeId}/registrations/{registrationId}/operations{operationId}にGETリクエストを送信し(operationIdはPUTの結果に含まれている)、DPSの結果を取得する(get_operation_status)。認証はHyper Clientに設定されたデバイスID証明書による。
- DPS処理の戻り値を応答から生成する(
get_device_info)。
- X509固有の登録処理を実行する(
ProvisioningResultを構築する。device_id:DPSから返されたデバイスID。hub_name:DPSから返されたIoT Hubの名前。reconfigure:DPSから返されたsubstatusの値。ない場合はReprovisioningStatus::InitialAssignment。sha256_thumbprint:常にNonecredentials:常にNone
- 結果をバックアップファイルに記録する。
DerivedKeyStoreを初期化する(prepare_derived_hybrid_key)。MemoryKeyStoreからデバイスのprimary鍵を取り出す。{hubName}/devices/{deviceId}/{x509CertThumbprint}という文字列を、取得した鍵を使用してHMACSHA256アルゴリズムで署名してダイジェストを取得する。- ダイジェストを
MemoryKeyとしてDervicedKeyStoreを初期化する。
- キーストア(
- デバイスID証明書のX509サムプリントを起動引数に渡す。
- DPSによるプロビジョニングを実行する(
- 起動する
- ランタイムの
make_runtimeメソッドを呼び出す。DockerModuleRuntimeの場合config.yamlからDocker APIのURIとネットワーク名、IPv6設定、IPAM設定を取得する。- Docker APIの
network listで既存のネットワークがあるかどうかを確認する。 - ネットワークがないならば、Docker APIの
network createでネットワークを作成する。
- DPSで再プロビジョニングが指定された場合、再プロビジョニングを行う。
- 構成が前回起動時から変更されていないかをチェックする(
check_settings_state)- ホームディレクトリの
cacheサブディレクトリにあるsettings_stateのSHA256ダイジェストと、config.yamlの内容およびX509サムプリントを連結した値のSHA256ダイジェストを比較する。 - ダイジェストが異なっている場合、またはワークロードCA証明書の署名元であるデバイスCA証明書有効期限が残り5分未満の場合、ランタイム構成の再構成を行う(
reconfigure)。- ランタイムAPI
remove_allを呼び出して、全てのモジュールを削除する。 - ホームディレクトリの
cacheサブディレクトリを削除する。 - HSMでワークロードCA証明書を再生成する。
- ランタイムAPI
- ホームディレクトリの
- ワークロードデータを初期化する。プロビジョニングの結果からIoT Hubの名前とデバイスID、デバイスID証明書の最大有効期限(2時間)、サーバー証明書の最大有効期限(90日)を設定する。
- APIの実行を開始する(
start_api)。なお、これは- IoT HubへのHTTPS接続用の
DeviceClientを初期化する。 - IoT Hubベースのモジュールアイデンティティ管理用の
HubIdentityManagerを初期化する。 - サーバー証明書用のパラメーター
CertificateProperties(有効期限90日、サーバー証明書、CNはiotedged)を使用して、CertificateManagerを初期化する。 - サーバー証明書の期限切れ対応処理のタイマーを設定する(
CertificateManager::schedule_expiration_timer)。サーバー証明書の期限が切れると、各APIは一度シャットダウンされ、start_apiがもう一度実行される。期限は、サーバー証明書の期限までの猶予時間の95%。 edgelet_http_mangaement::serverモジュールのManagementServiceを作成し、マネジメントAPIを開始する。edgelet_http_workload::serverモジュールのWorkloadServiceを作成し、マネジメントAPIを開始する。Watchdog経由で$edgeAgentモジュールを作成、実行する。内部的には、レジストリAPIpullとランタイムAPIcreate、startを呼び出して開始される(start_runtime)config.yamlのagentを読み込む。agent.envに指定された環境変数に対して以下のように値をマージする(build_env)。IOTEDGE_IOTHUBHOSTNAMEにプロビジョニングの結果のIoT Hubホスト名。EDGEDEVICEHOSTNAMEにconfig.yamlのhostnameの値。IOTEDGE_DEVICEIDにプロビジョニングの結果のデバイスID。IOTEDGE_MODULEIDに$edgeAgent。IOTEDGE_WORKLOADURIにconfig.yamlのconnect.workload_uriの値。IOTEDGE_MANAGEMENTURIにconfig.yamlのconnect.management_uriの値。IOTEDGE_AUTHSCHEMEにsasToken。Modeにiotedged。configy.yamlのagent.envに指定された値。IOTEDGE_APIVERSIONに2019-01-30。
Watchdog::run_untilを呼び出す(処理はstart_watchdog)。- 60秒ごとに状態を監視する(監視内容は
check_runtime)。- EdgeAgentの状態を取得する(
get_edge_runtime_mod)。- ランタイムAPIの
list()を呼び出し、その結果をフィルタリングする。 - 状態に応じて以下のいずれかを行う。
- モジュールが存在し、状態が
runingであれば、情報ログを出力する。 - モジュールが存在し、状態が
runningでなければ、情報ログを出力し、ランタイムAPIのstart()を呼び出して開始する。 - モジュールが存在しないならば、作成する(
create_and_start)- 情報ログを出力する。
- アイデンティティマネージャーAPIの
getを呼び出し、IoT Hubから$edgeAgentの情報を取得する。 - アイデンティティマネージャーAPIの
updateを呼び出し、$edgeAgentに認証情報を設定する。 - 環境変数
IOTEDGE_MODULEGENERATIONIDに生成IDを設定する。 imagePullPolicyがonCreateならば、レジストリAPIのpull()を呼び出す。- ランタイムAPIの
create()を呼び出す。 - ランタイムAPIの
start()を呼び出す。
- モジュールが存在し、状態が
- ランタイムAPIの
- EdgeAgentの状態を取得する(
- 60秒ごとに状態を監視する(監視内容は
- IoT HubへのHTTPS接続用の
- ランタイムの
レスポンスボディ
{
"required": ["hubName", "deviceId", "credentials"],
"properties": {
"hubName": {
"type": "string",
"description": "IoT Hubのホスト名。"
},
"deviceId": {
"type": "string",
"description": "IoT HubでのデバイスのID。"
},
"credentials": {
"type": "object",
"required": ["authType", "source"],
"properties": {
"authType": {
"type": "string",
"enum": ["symmetric-key", "x509"],
"description": "使用される認証資格情報の種類を示します。"
},
"source": {
"type": "string",
"enum": ["payload"],
"description": "認証資格情報のソースを示します。"
},
"key": {
"type": ["string", "null"],
"default": null,
"description": "認証に使用されるキー。'authType'が'symmetric-key'で、'source'が'payload'の場合にのみ指定されます。"
},
"identityCert": {
"type": ["string", "null"],
"default": null,
"description": "アイデンティティ証明書。'authType'が'x509'で'source'が'payload'の場合はPEMフォーマットのバイト列となり、'authType'が'x509'で'source'が'hsm'の場合は証明書への参照となるはずです。"
},
"identityPrivateKey": {
"type": ["string", "null"],
"default": null,
"description": "アイデンティティ証明書の秘密鍵。'authType'が'x509'で'source'が'payload'の場合はPEMフォーマットのバイト列となり、'authType'が'x509'で'source'が'hsm'の場合は秘密鍵への参照となるはずです。"
}
}
}
}
}IoT Edgeセキュアデーモンは、IoT Edgeモジュール向けにRESTライクなAPIを公開している。モジュールは、Unix Domain Socket経由のHTTPS通信で、JSONを使用したAPI呼び出しを実行できる。
IoT EdgeセキュアデーモンのAPIの認証と認可は、PIDベースで行われている。その概要は以下のとおり。
- Unix Domain Socketから呼び出し元のPIDを取得する。
edgelet_http::authenticationモジュールにより、認証が行われる。
Policy::AnonymousなAPIの場合、AuthId::Anyになる。Policy::ModuleなAPIの場合、ランタイムのauthenticateに処理を委譲する。Pid::Noneならば、AuthId::Noneになる。Pid::Anyならば、AuthId::Anyになる。Pid::Value(pid)ならば、モジュール名による認証が行われる。呼び出し元のモジュールIDのPIDをランタイムのlistAPIで取得し、一致していればAuthId::Value(module)、そうでなければAuthId::Noneになる。
edgelet_http::authorizeモジュールにより、認可が行われる。
Policy::AnonymousなAPIの場合、常に認可は成功する。Policy::ModuleなAPIの場合、AuthIdを見て、AuthId::Anyならば認可は成功する。AuthId::Value(module)の場合、認証済みモジュールが呼び出し元モジュール名と一致(認証処理でこの呼び出し先モジュール名からAuthId::Value(module)を作成しているのだから、これは常に一致するはず)すれば認可は成功する。それ以外の場合、HTTP404になる。
edgelet_http_mgmt クレートにAPI、edgelet_coreクレートにトレイト、実装はedgelet_dockerクレートにあるランタイムAPIとレジストリAPIである(将来的にはedgelet_kubeクレートにも実装ができるだろう)。
GET /modules?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- レスポンスボディ
- 後述
レスポンスボディ
{
"type": "array",
"items": {
"type" : "object",
"properties": {
"id": {
"type": "string",
"description": "システムが生成した一意な識別子。"
},
"name": {
"type": "string",
"description": "モジュールの名前。"
},
"type": {
"type": "string",
"description": "モジュールの種類。"
},
"config": {
"type": "object",
"required": ["settings"],
"properties": {
"settings": {
"type": "object",
"properties": {
"image": {
"type": "string"
},
"imageHash": {
"type": ["string", "null"],
"default": null
},
"createOptions": {
"type": ["object", "null"],
"description": "DockerのCreate APIに渡す引数。https://docs.docker.com/engine/api/v1.40/#operation/ContainerCreate を参照してください。"
},
"auth": {
"type": ["object", "null"],
"default": null,
"properties": {
"username": {
"type": ["string", "null"],
"default": null
},
"password": {
"type": ["string", "null"],
"default": null
},
"email": {
"type": ["string", "null"],
"default": null
},
"serveraddress": {
"type": ["string", "null"],
"default": null
}
}
}
}
},
"env": {
"type": ["array", "null"],
"default": null,
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
},
"status": {
"startTime": {
"type": "string"
},
"exitStatus": {
"type": "object",
"properties": {
"exitTime": {
"type": "string"
},
"statusCode": {
"type": "string"
}
}
},
"runtimeStatus": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
}
}IoT Edgeモジュールの情報を列挙する。ランタイムAPIlist_with_detailsを呼び出す。
なお、Dockerベースの実装におけるstatusの値は、Dockerのcontainer inspect APIの戻り値のプロパティの値である。詳細は以下のとおり。
| APIの戻り値のプロパティ | Dockerのcontainer inspect APIの戻り値のプロパティ |
|---|---|
startTime |
startedAt |
exitStatus.exitTime |
finishedAt |
exitStatus.statusCode |
exitCode |
runtimeStatus.status |
statusとexitCodeから算出された値。詳細後述。 |
runtimeStatus.description |
status |
runtimeStatus.statusはDockerのcontainer inspect APIの戻り値の値に応じて以下のようになる。
Dockerのcontainer inspect APIの戻り値のstatusプロパティの値|Dockerのcontainer inspect APIの戻り値のexitCodeの値|runtimeStatus.statusの値
running|(任意)|running
created|(任意)|stopped
paused|(任意)|stopped
restring|(任意)|stopped
removing|0|stopped
removing|0以外|failed
dead|0|stopped
dead|0以外|failed
exited|0|stopped
exited|0以外|failed
(上記以外)|(任意)|unknown
POST /modules?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgentCreateCommand
リクエストボディ
{
"required": ["name", "type", "config"],
"properties": {
"name": {
"type": "string",
"description": "モジュールの名前。"
},
"type": {
"type": "string"
},
"config": {
"type": "object",
"required": ["settings"],
"properties": {
"settings": {
"type": "object",
"properties": {
"image": {
"type": "string"
},
"imageHash": {
"type": ["string", "null"],
"default": null
},
"createOptions": {
"type": ["object", "null"],
"description": "DockerのCreate APIに渡す引数。https://docs.docker.com/engine/api/v1.40/#operation/ContainerCreate を参照してください。"
},
"auth": {
"type": ["object", "null"],
"default": null,
"properties": {
"username": {
"type": ["string", "null"],
"default": null
},
"password": {
"type": ["string", "null"],
"default": null
},
"email": {
"type": ["string", "null"],
"default": null
},
"serveraddress": {
"type": ["string", "null"],
"default": null
}
}
}
}
},
"env": {
"type": ["array", "null"],
"default": null,
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
},
"imagePullPolicy": {
"type": ["string", "null"],
"default": null,
"enum": ["on-create", "never"]
}
}
}レスポンスボディ
{
"properties": {
"id": {
"type": "string",
"description": "システムが生成した一意な識別子。"
},
"name": {
"type": "string",
"description": "モジュールの名前。"
},
"type": {
"type": "string",
"description": "モジュールの種類。"
},
"config": {
"type": "object",
"required": ["settings"],
"properties": {
"settings": {
"type": "object",
"properties": {
"image": {
"type": "string"
},
"imageHash": {
"type": ["string", "null"],
"default": null
},
"createOptions": {
"type": ["object", "null"],
"description": "DockerのCreate APIに渡す引数。https://docs.docker.com/engine/api/v1.40/#operation/ContainerCreate を参照してください。"
},
"auth": {
"type": ["object", "null"],
"default": null,
"properties": {
"username": {
"type": ["string", "null"],
"default": null
},
"password": {
"type": ["string", "null"],
"default": null
},
"email": {
"type": ["string", "null"],
"default": null
},
"serveraddress": {
"type": ["string", "null"],
"default": null
}
}
}
}
},
"env": {
"type": ["array", "null"],
"default": null,
"items": {
"type": "object",
"properties": {
"key": {
"type": "string"
},
"value": {
"type": "string"
}
}
}
}
}
},
"status": {
"startTime": {
"type": "string"
},
"exitStatus": {
"type": "object",
"properties": {
"exitTime": {
"type": "string"
},
"statusCode": {
"type": "string"
}
}
},
"runtimeStatus": {
"type": "object",
"properties": {
"status": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
}IoT Edgeモジュールを作成する。
imagePullPolicyがonCreateの場合はレジストリAPIpullを呼び出す。- ランタイムAPI
createを呼び出す。
GET /modules/{name}?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)name:モジュールの名前(URLエンコード済み)
IoT Edgeモジュールの情報を取得する。現状は何もしないで空の結果を返す。
PUT /modules/{name}?api-version={apiVersion}[&start=true]
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
create moduleを参照
- レスポンスボディ
create moduleを参照
- 呼び出し元
$edgeAgentUpdateCommand
アップデート。クエリ文字列でstartを指定すると同時に起動する。
- ランタイムAPI
removeを呼び出す。 imagePullPolicyがonCreateの場合はレジストリAPIpullを呼び出す。- ランタイムAPI
createを呼び出す。 startが指定されている場合は、ランタイムAPIstartを呼び出す。
POST /modules/{name}/prepareupdate?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2019-01-30)
- リクエストボディ
create moduleを参照
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgentUpdateCommand
アップデートの準備を行う。具体的には、imagePullPolicyがonCreateの場合はレジストリAPIpullを呼び出す。
DELETE /modules/{name}?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgentRemoveCommand
IoT Edgeモジュールを削除する。引数はモジュールの名前。
ランタイムAPIremoveを呼び出す。
POST /modules/{name}/start?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgentStartCommand
IoT Edgeのモジュールの開始を行う。引数は対象のモジュール名。ランタイムAPIstartを呼び出す。
POST /modules/{name}/stop?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgentUpdateCommandStopCommand
IoT Edgeのモジュールの停止を行う。引数は対象のモジュール名。ランタイムAPIstopを呼び出す(wait_before_kill引数にはNoneを渡す)。
POST /modules/{name}/restart?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgentRestartCommand
IoT Edgeのモジュールの再起動を行う。引数は対象のモジュール名。ランタイムAPIrestartを呼び出す。
POST /modules/{name}/logs?api-version={apiVersion}&tail={(all|integer)}&follow={bool}&since={integer}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)tail:末尾何行を対象にするか。既定値はall。follow:ログを返した後もDocker APIとの接続を維持する場合はtrue。既定値はfalsesince:開始タイムスタンプ(Unixタイムスタンプ)。既定値は0。
- リクエストボディ
- なし
- レスポンスボディ
- Dockerログのストリーム。
モジュールのログを取得する。引数はモジュール名とその他のオプション群。ランタイムAPIlogsを呼び出す。
GET /identities?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- 後述
レスポンスボディ
{
"type": "array",
"items": {
"type": "object",
"properties": {
"moduleId": {
"type": "string"
},
"managedBy": {
"type": "string"
},
"generationId": {
"type": "string"
},
"authType": {
"type": "string"
}
}
}
}モジュールアイデンティティを列挙する。アイデンティティマネージャーのlistAPIを呼び出し、その結果を整形して返す。
POST /identities?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
リクエストボディ
{
"required": ["moduleId"],
"properties": {
"moduleId": {
"type": "string",
"description": "作成するモジュールアイデンティティのID。"
},
"managedBy": {
"type": ["string", "null"],
"default": null,
"description": "作成するモジュールアイデンティティを管理するモジュールのID。"
}
}
}レスポンスボディ
{
"type": "object",
"properties": {
"moduleId": {
"type": "string",
"description": "このモジュールアイデンティティのID。"
},
"managedBy": {
"type": "string",
"description": "このモジュールアイデンティティを管理するモジュールアイデンティティのID。"
},
"generationId": {
"type": "string",
"description": "アイデンティティマネージャーがこのモジュールアイデンティティに付与したID。"
},
"authType": {
"type": "string",
"enum": ["none", "sas", "x509"],
"description": "このモジュールアイデンティティの認証種別。"
}
}
}モジュールアイデンティティを作成する。引数としてスペックを受け取る。アイデンティティマネージャーのロックを取り、createAPIを呼び出し、その結果を整形して返す。
なお、現在authTypeは常にsasになる。
PUT /identities/{name}?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
create identityと同じ
リクエストボディ
{
"required": ["moduleId"],
"properties": {
"moduleId": {
"type": "string",
"description": "更新対象のモジュールアイデンティティのID。"
},
"generationId": {
"type": "string",
"description": "モジュールアイデンティティのシステム側生成ID。"
},
"managedBy": {
"type": ["string", "null"],
"default": null,
"description": "モジュールアイデンティティを管理するモジュールのID。"
}
}
}モジュールアイデンティティを更新する。引数としてスペックを受け取る。アイデンティティマネージャーのロックを取り、updateAPIを呼び出す。
DELETE /identities/{name}?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- なし
モジュールアイデンティティを削除する。引数としてモジュール名を受け取る。アイデンティティマネージャーのロックを取り、deleteAPIを呼び出す。
GET /systeminfo?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- 後述
レスポンスボディ
{
"properties": {
"osType": {
"type": "string",
"enum": ["linux", "windows"],
"description": "ランタイムが認識しているOSの種別。"
},
"architecture": {
"type": "string",
"enum": ["386", "amd64", "arm", "arm64"],
"description": "ランタイムが認識しているCPUのアーキテクチャ。"
},
"version": {
"type": "string",
"description": "IoT Edgeセキュアデーモンのバージョン情報。"
}
}
}システム情報を取得する。ランタイムのsystem_infoAPIを呼び出し、結果を整形して返す。システム情報とは、OSの種類、CPUアーキテクチャ、バージョン。
OSの種別とCPUアーキテクチャは、現在のDockerの実装ではGo言語の実装に依存している。詳細は https://golang.org/doc/install/source#environment を参照のこと。
バージョンは、ビルド時にビルドツールによって埋め込まれるバージョンで、${VERSION} (${BUILD_SOURCEVERSION})の形式。VERSIONとBUILD_SOURCEVERSIONは環境変数である。VERSIONはversion.txtの値。BUILD_SOURCEVERSIONはCIツール(おそらくはAzure Pipeline)が埋め込むgitのコミットハッシュと思われる。
モジュール間認証に関わるAPI群である。
GET /modules?api-version={apiVersion}
マネジメントAPIのGET /modulesと同一である。
POST /modules/{name}/genid/{genid}/sign?api-version={apiVersion}
- 認証/認可
- IoT Edgeモジュールからのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
リクエストボディ
{
"required": ["keyId", "algo", "data"],
"properties": {
"keyId": {
"type": "string",
"description": "署名を行うキーの名前。"
},
"algo": {
"type": "string",
"description": "使用する署名アルゴリズム。"
},
"data": {
"type": "string",
"format": "base64",
"description": "署名対象のデータ。"
}
}
}レスポンスボディ
{
"properties": {
"digest": {
"type": "string",
"format": "base64"
}
}
}データを指定されたキーで署名して返す。なお、リクエストボディのalgoは無視される。
- キーIDにURLコンポーネント
genidを連結する。 - キーストア(
KeyStoreトレイトオブジェクト、実装はedgelet_coreクレートのDerivedKeyStore構造体)から、モジュールID(URLコンポーネントname)と先ほど連結したキーIDを渡し、キー(MemoryKeyトレイトオブジェクト、実装はedgelet_coreクレートのMemoryKey構造体)のsignメソッドを呼び出す。アルゴリズムはHMACSHA256で、対象はBase64デコードしたリクエストボディ。このHMAC処理はRustのhmacクレートの実装を使用する。DervicedKeyStoreは、以下のようにして都度MemoryKeyを生成する。- モジュールIDとキー名を連結したもの(デバイスに対するキーを求められた場合はキー名のみ)をHMACSHA256で署名する(この署名処理は
MemoryKeyの実装によるので、Rustのhmacクレートの実装である)。この時使用するキーは、デバイス接続文字列のデバイスキーである。
- モジュールIDとキー名を連結したもの(デバイスに対するキーを求められた場合はキー名のみ)をHMACSHA256で署名する(この署名処理は
- すなわち、署名処理とは、「
{moduleId}{keyId}{genId}に対してデバイス接続文字列のキーをキーとしてHMACSHA256署名したもの」をキーとして、任意のデータをHMACSHA256署名することである。
- 結果をBase64エンコードする。
呼び出し元:
ModuleClient(Device SDK for .NET)
POST /modules/{name}/genid/{genid}/decript?api-version={apiVersion}
}
- 認証/認可
- IoT Edgeモジュールからのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgentEncryptionProvider- バックアップ配置マニフェストの復号
- RocksDB内の配置マニフェストの復号
リクエストボディ
{
"required": ["ciphertext", "initializationVector"],
"properties": {
"ciphertext": {
"type": "string",
"format": "base64",
"descritpion": "復号対象のデータ。"
},
"initializationVector": {
"type": "string",
"format": "base64",
"description": "データの復号に使用される初期化ベクター。"
}
}
}レスポンスボディ
{
"properties": {
"plaintext": {
"type": "string",
"format": "base64",
"descritpion": "Base64エンコードされた、復号後のデータ。"
}
}
}暗号化されたデータを復号する。暗号化APIのdecryptを呼び出す。その引数で渡すクライアントIDは{name}{genId}である。
POST /modules/{name}/genid/{genid}/encrypt?api-version={apiVersion}
}
- 認証/認可
- IoT Edgeモジュールからのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgentEncryptionProvider- バックアップ配置マニフェストの暗号化
- RocksDB内の配置マニフェストの暗号化
リクエストボディ
{
"required": ["plaintext", "initializationVector"],
"properties": {
"plaintext": {
"type": "string",
"format": "base64",
"descritpion": "暗号化対象のデータ。"
},
"initializationVector": {
"type": "string",
"format": "base64",
"description": "データの暗号化に使用される初期化ベクター。"
}
}
}レスポンスボディ
{
"properties": {
"ciphertext": {
"type": "string",
"format": "base64",
"descritpion": "Base64エンコードされた、暗号化後のデータ。"
}
}
}データを暗号化する。暗号化APIのencryptを呼び出す。その引数で渡すクライアントIDは{name}{genId}である。
POST /modules/{name}/certificate/identity?api-version={apiVersion}
- 認証/認可
- IoT Edgeモジュールからのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
リクエストボディ
{
"properties": {
"expiration": {
"type": ["string", "null"],
"format": "date-time",
"default": null,
"descritpion": "証明書の有効期限の日時(ISO 8601)。"
}
}
}レスポンスボディ
{
"properties": {
"privateKey": {
"type": "object",
"description": "",
"required": ["type"],
"properties": {
"type": {
"type": "string",
"description": "キーの形式を示します(PEMフォーマットのバイト列か参照)。"
},
"ref": {
"type": ["string", "null"],
"description": "秘密鍵への参照。"
},
"bytes": {
"type": ["string", "null"],
"format": "base64",
"description": "PEMフォーマットのバイト列をBase64エンコードしたもの。"
}
}
},
"certificate": {
"type": "string",
"format": "base64",
"descritpion": "Base64エンコードされた、証明書とそのチェインを含むPEMフォーマットのバイト列。"
},
"expiration": {
"type": "string",
"format": "date-time",
"descritpion": "証明書の有効期限の日時(ISO 8601)。"
}
}
}指定した情報でクライアントID証明書を作成して返す。
WorkloadConfigトレイトの実装(iotedgedクレートのWorkloadData構造体)から、クライアント証明書(CertificateType::Client)用の証明書の最大有効期限(2時間の固定値)を取得する。- クライアントID証明書のエイリアスを
{モジュールID}identityという名前で構築する。 - クライアントID証明書のSAN(Subject Alt Name)となる
URI: azureiot://{IoT Hubの名前}/devices/{デバイスID}/modules/{モジュールID}という文字列を構築する。 - 入力の
expirationが最大有効期限を越えていないことを検証する。これはクライアントID証明書の有効期限となる。 - 入力
name(モジュールID)が空でないことを検証する。これはクライアントID証明書のCNになる。 - 証明書APIの
destroy_certificateを呼び出し、エイリアスに一致する証明書を破棄する。 - 有効期限、CN(モジュールID)、
CertificateType::Client、エイリアス、SAN、CertificateIssuer::DefaultCaを使用して、証明書APIのcreate_certificateを呼び出し、クライアントID証明書を作成する。 - 応答を構築する。証明書のPEM形式のデータを
certicate、有効期限をRFC3339形式でexpirationにマッピングする。さらに、秘密鍵がref形式であればprivateKey.typeにrefを指定してprivateKey.refにその値を指定する。そうではなく、秘密鍵がbytes形式であればprivateKey.typeにbytesを指定してprivateKey.bytesにその値を指定する。
POST /modules/{name}/genid/{genid}/certificate/server?api-version={apiVersion}
- 認証/認可
- IoT Edgeモジュールからのみ呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
リクエストボディ
{
"required": ["commonName", "expiration"],
"properties": {
"commonName": {
"type": "string",
"description": "サブジェクトの共通名(CN)。"
},
"expiration": { "type": "string",
"format": "date-time",
"descritpion": "証明書の有効期限の日時(ISO 8601)。"
}
}
}レスポンスボディ
{
"properties": {
"privateKey": {
"type": "object",
"description": "",
"required": ["type"],
"properties": {
"type": {
"type": "string",
"description": "キーの形式を示します(PEMフォーマットのバイト列か参照)。"
},
"ref": {
"type": ["string", "null"],
"description": "秘密鍵への参照。"
},
"bytes": {
"type": ["string", "null"],
"format": "base64",
"description": "PEMフォーマットのバイト列をBase64エンコードしたもの。"
}
}
},
"certificate": {
"type": "string",
"format": "base64",
"descritpion": "Base64エンコードされた、証明書とそのチェインを含むPEMフォーマットのバイト列。"
},
"expiration": {
"type": "string",
"format": "date-time",
"descritpion": "証明書の有効期限の日時(ISO 8601)。"
}
}
}指定した情報でサーバー証明書を作成して返す。
WorkloadConfigトレイトの実装(iotedgedクレートのWorkloadData構造体)から、サーバー証明書(CertificateType::Server)用の証明書の最大有効期限(90日の固定値)を取得する。- サーバー証明書のエイリアスを
{モジュールID}{生成ID}serverという名前で構築する。 - 入力の
expirationが最大有効期限を越えていないことを検証する。 - 入力
commonName(モジュールID)が空でないことを検証する。 - サーバー証明書のSAN(Subject Alt Name)文字列を構築する。
DNS: {モジュールID}という文字列を構築する。これを1番目のエントリとする。- モジュールIDは、以下のようにDNSエントリとして妥当な値に変換する。
- モジュールIDの先頭から、ASCIIの英字でない文字を全て取り除く。
- その結果のモジュールIDの末尾から、ASCIIの英数字でない文字を全て取り除く。
- その結果のモジュールIDを全て小文字化する。
- その結果のモジュールIDからASCII英数字でもASCIIハイフンでもない文字を全て取り除く。
- その結果の先頭から63文字までを採用する。
- モジュールIDは、以下のようにDNSエントリとして妥当な値に変換する。
DNS: {commonName}の値を末尾に追加する。- これらを
,で連結する。
- 証明書APIの
destroy_certificateを呼び出し、エイリアスに一致する証明書を破棄する。 - 有効期限、CN(
commonName)、CertificateType::Server、エイリアス、SAN、CertificateIssuer::DefaultCaを使用して、証明書APIのcreate_certificateを呼び出し、クライアントID証明書を作成する。。 - 応答を構築する。証明書のPEM形式のデータを
certicate、有効期限をRFC3339形式でexpirationにマッピングする。さらに、秘密鍵がref形式であればprivateKey.typeにrefを指定してprivateKey.refにその値を指定する。そうではなく、秘密鍵がbytes形式であればprivateKey.typeにbytesを指定してprivateKey.bytesにその値を指定する。
GET /trust-bundle?api-version={apiVersion}
}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version:APIのバージョン(既定値は2018-06-28)
- リクエストボディ
- なし
- レスポンスボディ
- 後述
レスポンスボディ
{
"properties": {
"certificate": {
"type": "string",
"format": "base64",
"descritpion": "Base64エンコードされた、信頼済み証明書群を含むPEM形式のバイト列。"
}
}
}証明書APIのget_trust_bundleを呼び出す。
edgelet_dockerクレートのruntimeモジュールのDockerModuleRuntime構造体のModuleRuntimeトレイトを実装するメソッドとして提供される。
fn create(&self, module: ModuleSpec<DockerConfig>) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>- 環境変数を調整する。具体的には、スペックの
create_optionsのenvと、スペック自身のenvをマージする。 - ラベルを調整する。スペックの
create_optionsのlabelsに対して、キーnet.azure-devices.edge.owner、値Microsoft.Azure.Devices.Edge.Agentを追加する。
- これによって、IoT Edgeモジュールかどうかを判別する(たとえば
listAPI)。
- 調整した環境変数、ラベル、そして
imageの値をマージしたcreate_optionsを作成し、Docker APIのcontainer createを呼び出す。
fn get(&self, id: &str) -> Box<dyn Future<Item = (DockerModule<UrlConnector>, ModuleRuntimeState), Error = Error> + Send>実装は、Docker APIを使用したcontainer inspectの結果から、イメージID、コンテナーのPID、開始時刻、終了時刻、終了コード、docker inspect結果のステータスに応じた値が入る。
- コンテナーのステータスが
created、paused、restartingの場合stopped - コンテナーのステータスが
runningの場合、running - コンテナーのステータスが
removing、dead、exitedの場合、終了コードが0ならstopped、それ以外ならfailed。 - コンテナーのステータスが上記以外の場合は
unknown
fn system_info(&self) -> Box<dyn Future<Item = CoreSystemInfo, Error = Error> + Send>Docker APIを使用したsystem system_infoを行う。このとき、os_typeとarchitectureそれぞれについて、返されなかった場合はUnknownに置き換える。
fn list(&self) -> Box<dyn Future<Item = Vec<DockerModule<UrlConnector>>, Error = Error> + Send>Docker APIを使用したcontainer listを行い、その結果について、ラベルにキーnet.azure-devices.edge.owner、値Microsoft.Azure.Devices.Edge.Agentを持つもののみにフィルタリングする。結果を DockerModule にマッピングし、返す。
fn list_with_details(&self) -> Box<dyn Stream<Item = (DockerModule<UrlConnector>>, ModuleRuntimeState), Error = Error> + Send>list()メソッドを呼び出す。- 結果を
Streamに変換する。 - 個々の結果となる
DockerModule<UrlConnector>のruntime_state()を呼び出し、タプルにする。- 内部的にはDocker APIの
container inspectが実行される。
- 内部的にはDocker APIの
- エラーとなったモジュールについては除外する。
fn remove(&self, id: &str) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>Docker APIを使用したcontainer deleteを行う。このとき、remove volumesにはfalse、forceにはtrue、remove linkにはfalseを渡す。
fn start(&self, id: &str) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>Docker APIを使用したcontainer startを行う。
fn stop(&self, id: &str, wait_before_kill: Option<Duration>) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>Docker APIを使用したcontainer stopを行う。このとき、待機時間として、引数wait_before_killが指定されていればその時間、指定されてないならば固定値10秒を渡す。
fn restart(&self, id: &str) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>Docker APIを使用したcontainer restartを行う。このとき、待機時間として、固定値10秒を渡す。
fn logs(&self, id: &str, options: &LogOptions) -> Box<dyn Future<Item = Logs, Error = Error> + Send>Docker APIを使用したcontainer logsを行う。引数は以下のとおり。
|仮引数|値|
|id|対象モジュールID(コンテナーID)|
|follow|followオプションの値|
|stdout|true|
|stderr|true|
|since|sinceオプションの値|
|timestamps|false|
|tail|tailオプションの値|
fn registry(&self) -> &ModuleRegistryModuleRegistryトレイトオブジェクトしてDockerModuleRuntime自身を返す。
fn remove_all(&self) -> Box<dyn Future<Item = (), Error = Self::Error> + Send>edgelet_dockerクレートのruntimeモジュールのDockerModuleRuntime構造体のModuleRegistryトレイトを実装するメソッドとして提供される。
fn pull(&self, config: &DockerConfig) -> Box<dyn Future<Item = (), Error = Error> + Send>Docker APIを使用してimage createを行う。
fn remove(&self, name: &str) -> Box<dyn Future<Item = (), Error = Error>>Docker APIを使用してimage deleteを行う。
edgelet_iothubクレートのルートモジュールのHubIdentityManager構造体のIdentityManagerトレイトを実装するメソッドとして提供される。
fn create(&mut self, id: IdentitySpec) -> Box<dyn Future<Item = HubIdentity, Error = Error> + Send>引数として、module_id、generation_id、managed_byを受け取る。
- IoT HubのAPI(
PUT /devices/{deviceID}/modules/{moduleID})をmodule_id、managed_byを渡して呼び出し、モジュールを作成する。 KeyStoreの実装(現在はedgelet_coreクレートのDerivedKeyStore)により、プライマリキーとセカンダリキーを生成する。キー名はprimary{generationID}とsecondary{generationID}。値は、モジュールIDとキー名を連結したものを、デバイスのプライマリキーをキーにしてHMACSHA256を取った結果(これはMemoryKeyのsignメソッド実装による)。- IoT HubのAPI(
PUT /devices/{deviceID}/modules/{moduleID})を呼び出し、認証種別をSASにし、キーに先ほど生成した値を設定する。
fn update(&mut self, id: IdentitySpec) -> Box<dyn Future<Item = HubIdentity, Error = Error> + Send>KeyStoreの実装(現在はedgelet_coreクレートのDerivedKeyStore)により、プライマリキーとセカンダリキーを生成する。キー名はprimary{generationID}とsecondary{generationID}。値は、モジュールIDとキー名を連結したものを、デバイスのプライマリキーをキーにしてHMACSHA256を取った結果(これはMemoryKeyのsignメソッド実装による)。- IoT HubのAPI(
PUT /devices/{deviceID}/modules/{moduleID})を呼び出し、認証種別をSASにし、キーに先ほど生成した値を設定し、それ以外は引数で受け取った値を設定する。
fn list(&self) -> Box<dyn Future<Item = Vec<HubIdentity>, Error = Error> + Send>- IoT HubのAPI(
GET /devices/{deviceID}/modules/)を呼び出し、結果を返す。
fn get(&mut self, id: IdentitySpec) -> Box<dyn Future<Item = HubIdentity, Error = Error> + Send>- IoT HubのAPI(
GET /devices/{deviceID}/modules/{moduleId})を呼び出し、結果を返す。
fn delete(&mut self, id: IdentitySpec) -> Box<dyn Future<Item = (), Error = Error> + Send>- IoT HubのAPI(
DELETE /devices/{deviceID}/modules/{moduleId})を呼び出し、結果を返す。
edgelet_hsmクレートのcryptoモジュールのCrypto構造体が実装する、edgelet_coreクレートのDecrypt、Encryptトレイトとして定義される。
実装は、hsm_rsクレートのCrypto構造体である(これをArcとして内部に保持するedgelet_hsmクレートのCrypto構造体がAPIとして公開される)。
fn encrypt(&self, client_id: &[u8], plaintext: &[u8], initialization_vector: &[u8]) -> Result<Buffer, Error>指定された平文を、指定されたクライアントID用の鍵で暗号化して返す。なお、戻り値のBufferはHSMのC言語側で管理しているメモリのラッパーであり、それを開放するためのDropトレイトを実装している。
- 引数をC言語とのFFI用にマーシャリングする。
- C言語で記述されたHSMの実装を呼び出す(
edge_hsm_client_encrypt_data@edge_hsm_client_crypto.c)。- HSMの初期化状態と引数をチェックする。
- 内部APIを呼び出す(
encrypt_data)。- ネイティブストアAPIを呼び出し、暗号化用の鍵をストアから取り出す(
edge_hsm_client_open_key@edge_hsm_client_store.c)。種別はHSM_KEY_ENCRPTION、鍵の名前はedgelet-master。- HSMの初期化状態と引数をチェックする。
- 鍵を取得する(
open_key)。- 鍵の存在チェックを行う。
- インメモリの鍵を検索する(
key_exists->get_key) - 存在しない場合、ファイルから鍵を読み取る(
load_encryption_key_from_file)- 鍵名からそのファイルパスを構築する(
build_enc_key_file_path)。- 鍵名のSHA256ダイジェストを計算し、Base64エンコードし、さらに
+を-に、\と=を_に置換する。 - エイリアスの先頭から、ASCII英数、
_、-を最大32文字抽出する。 {ホームディレクトリ}/enc_keys/{2の結果}{1の結果}.enc.keyが結果である。
- 鍵名のSHA256ダイジェストを計算し、Base64エンコードし、さらに
- ファイルの内容を読み取る。
- インメモリストアに挿入する(
put_key)。
- 鍵名からそのファイルパスを構築する(
- インメモリの鍵を検索する(
- インメモリの鍵を取得する(
get_key)。 - 暗号化用の鍵情報である
ENC_KEY構造体を作成し、そのハンドル(KEY_HANDLE)を返す(create_encryption_key@edge_enc_openssl_key.c)。このとき、ハンドルに関連する各メソッド(関数ポインター)はedge_enc_openssl_key.c内の関数になる。
- 鍵の存在チェックを行う。
- 暗号化を行う(
edge_hsm_client_key_encrypt@edge_hsm_key_interface.c)。- 引数をチェックする。
- 暗号化処理を実行する(
key_encrypt@hsm_key_interface.h->enc_key_encrypt@edge_enc_openssl_key.c)。CIPHER_VERSION_V1を使用して、OpenSSLの暗号化関数を呼び出す(encrypt@edge_enc_openssl_key.c)。- なお、クライアントIDはAAD(Additional Authenticated Data)として使用される。
- 鍵情報ハンドルをクローズする(
edge_hsm_client_close_key@edge_hsm_client_store.c)。- HSMの初期化状態と引数をチェックする。
- 鍵情報のメモリを解放する(
key_destroy@hsm_key_interface.h->enc_key_destroy@edge_enc_openssl_key.c)
- ネイティブストアAPIを呼び出し、暗号化用の鍵をストアから取り出す(
fn decrypt(&self, client_id: &[u8], ciphertext: &[u8], initialization_vector: &[u8]) -> Result<Buffer, Error>指定された暗号文を、指定されたクライアントID用の鍵で復号して返す。なお、戻り値のBufferはHSMのC言語側で管理しているメモリのラッパーであり、それを開放するためのDropトレイトを実装している。
- 引数をC言語とのFFI用にマーシャリングする。
- C言語で記述されたHSMの実装を呼び出す(
edge_hsm_client_decrypt_data@edge_hsm_client_crypto.c)。- HSMの初期化状態と引数をチェックする。
- 内部APIを呼び出す(
decrypt_data)。- ネイティブストアAPIを呼び出し、暗号化用の鍵をストアから取り出す(
edge_hsm_client_open_key@edge_hsm_client_store.c)。種別はHSM_KEY_ENCRPTION、鍵の名前はedgelet-master。- HSMの初期化状態と引数をチェックする。
- 鍵を取得する(
open_key)。- 鍵の存在チェックを行う。
- インメモリの鍵を検索する(
key_exists->get_key) - 存在しない場合、ファイルから鍵を読み取る(
load_encryption_key_from_file)- 鍵名からそのファイルパスを構築する(
build_enc_key_file_path)。- 鍵名のSHA256ダイジェストを計算し、Base64エンコードし、さらに
+を-に、\と=を_に置換する。 - エイリアスの先頭から、ASCII英数、
_、-を最大32文字抽出する。 {ホームディレクトリ}/enc_keys/{2の結果}{1の結果}.enc.keyが結果である。
- 鍵名のSHA256ダイジェストを計算し、Base64エンコードし、さらに
- ファイルの内容を読み取る。
- インメモリストアに挿入する(
put_key)。
- 鍵名からそのファイルパスを構築する(
- インメモリの鍵を検索する(
- インメモリの鍵を取得する(
get_key)。 - 復号用の鍵情報である
ENC_KEY構造体を作成し、そのハンドル(KEY_HANDLE)を返す(create_encryption_key@edge_enc_openssl_key.c)。このとき、ハンドルに関連する各メソッド(関数ポインター)はedge_enc_openssl_key.c内の関数になる。
- 鍵の存在チェックを行う。
- 復号を行う(
edge_hsm_client_key_decrypt@edge_hsm_key_interface.c)。- 引数をチェックする。
- 復号処理を実行する(
key_decrypt@hsm_key_interface.h->enc_key_decrypt@edge_enc_openssl_key.c)。CIPHER_VERSION_V1を使用して、OpenSSLの復号関数を呼び出す(decrypt@edge_enc_openssl_key.c)。- なお、クライアントIDはAAD(Additional Authenticated Data)として使用される。
- 鍵情報ハンドルをクローズする(
edge_hsm_client_close_key@edge_hsm_client_store.c)。- HSMの初期化状態と引数をチェックする。
- 鍵情報のメモリを解放する(
key_destroy@hsm_key_interface.h->enc_key_destroy@edge_enc_openssl_key.c)
- ネイティブストアAPIを呼び出し、暗号化用の鍵をストアから取り出す(
edgelet_hsmクレートのcryptoモジュールのCrypto構造体が実装する、edgelet_coreクレートのCreateCertificateトレイトと、GetTrustBundleトレイトとして定義される。
実装は、hsm_rsクレートのCrypto構造体である(これをArcとして内部に保持するedgelet_hsmクレートのCrypto構造体がAPIとして公開される)。
fn create_certificate(&self, properties: &CertificateProperties) -> Result<HsmCertificate, Error>- 入力プロパティ
CertificatePropertiesをC言語とのFFI用にマーシャリングする。 - C言語で記述されたHSMの実装を呼び出す(
edge_hsm_client_create_certificate@edge_hsm_client_crypto.c)。- HSMの初期化状態と引数をチェックする。
- ネイティブストアAPIを呼び出し、証明書を作成する(
edge_hsm_client_store_create_pki_cert@edge_hsm_client_store)。- 初期化状態と引数をチェックする。
- ファイルに出力済みの証明書があればそれをロードする(
load_if_cert_and_key_exist_by_alias)。- エイリアスから証明書と秘密鍵のファイルパスを構築する(
build_cert_file_paths)。- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
+を-に、\と=を_に置換する。 - エイリアスの先頭から、ASCII英数、
_、-を最大32文字抽出する。 {ホームディレクトリ}/certs/{2の結果}{1の結果}.cert.pemと{ホームディレクトリ}/cert_keys/{2の結果}{1の結果}.key.pemが結果である。
- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
- 証明書の内容を検証する(
verify_certificate_helper)。 - インメモリストアにPKI証明書管理情報を挿入する(
edge_hsm_client_store_insert_pki_cert->put_pki_cert)。
- エイリアスから証明書と秘密鍵のファイルパスを構築する(
- ファイルがなければ、作成処理本体を呼び出す(
edge_hsm_client_store_create_pki_cert_internal)。- 初期化状態と引数をチェックする。
- エイリアスから証明書と秘密鍵のファイルパスを構築する(
build_cert_file_paths)。- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
+を-に、\と=を_に置換する。 - エイリアスの先頭から、ASCII英数、
_、-を最大32文字抽出する。 {ホームディレクトリ}/certs/{2の結果}{1の結果}.cert.pemと{ホームディレクトリ}/cert_keys/{2の結果}{1の結果}.key.pemが結果である。
- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
generate_pki_cert_and_key_helperをkey_propsにNULLを渡して呼び出す。なお、シリアル番号はrand()の値。(generate_pki_cert_and_key@edge_pki_openssl.c)。- 引数を検証する。
- 秘密鍵を生成する(
generate_cert_key)。- 発行者証明書がない場合、
PKI_KEY_PROPS.key_type(とPKI_KEY_PROPS.ec_curve_name)に合わせたキーをOpenSSLのAPIを使用して、発行者証明書がある場合、その公開鍵に合わせた秘密鍵を生成する。- RSA鍵の長さは、CA証明書の場合4096バイト、そうでない場合8192バイト。
- ファイルに書き出す(
write_private_key_file)。
- 発行者証明書がない場合、
- 証明書を生成する(
generate_evp_certificate)。- X509証明書の各種プロパティを設定する(
cert_set_core_properties、cert_set_expiration、cert_set_extensions、cert_set_subject_fields_and_issuer、cert_set_key_id_extensions) - 秘密鍵で署名する。
- ファイルに書き出す(
write_certificate_file)。
- X509証明書の各種プロパティを設定する(
- インメモリストアにPKI証明書管理情報を挿入する(
put_pki_cert)
- ネイティブストアAPIを呼び出し、証明書を取得する(
edge_hsm_client_store_get_pki_cert@edge_hsm_client_store.c)。- 内部APIを呼び出す(
get_cert_info_by_alias)。- 初期化状態と引数チェック。
- インメモリの証明書管理情報を取り出す(
get_pki_cert)。 - 証明書データを読み取る(
prepare_cert_info_handle)。- 秘密鍵を読み取る。
- 証明書ファイルを読み取る。
- 証明書構造体を構築する(
certificate_info_create)。
- 内部APIを呼び出す(
fn destroy_certificate(&self, alias: String) -> Result<(), Error>エイリアスに一致する証明書を破棄する。
- C言語で記述されたHSMの実装を呼び出す(
edge_hsm_client_destroy_certificate@edge_hsm_client_crypto.c)。- HSMの初期化状態と引数をチェックする。
- ネイティブストアAPIを呼び出し、証明書を削除する(
edge_hsm_client_store_remove_pki_cert@edge_hsm_client_store.c)。- エイリアスをキーにして証明書情報を削除する内部処理を呼び出す(
remove_cert_by_alias@edge_hsm_client_store.c)。- 初期化状態と引数をチェックする。
- 削除処理の本体を呼び出す(
remove_if_cert_and_key_exist_by_alias@edge_hsm_client_store.c) 。- エイリアスから証明書と秘密鍵のファイルパスを構築する(
build_cert_file_paths)。- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
+を-に、\と=を_に置換する。 - エイリアスの先頭から、ASCII英数、
_、-を最大32文字抽出する。 {ホームディレクトリ}/certs/{2の結果}{1の結果}.cert.pemと{ホームディレクトリ}/cert_keys/{2の結果}{1の結果}.key.pemが結果である。
- エイリアスのSHA256ダイジェストを計算し、Base64エンコードし、さらに
- 証明書と秘密鍵のファイルを削除する。ファイルがない場合は無視する。
- インメモリストアからPKI証明書管理情報を削除する(
remove_pki_cert)。
- エイリアスから証明書と秘密鍵のファイルパスを構築する(
- エイリアスをキーにして証明書情報を削除する内部処理を呼び出す(
fn get_trust_bundle(&self) -> Result<HsmCertificate, Error>信頼済み証明書のチェーンを返す。
- C言語で記述されたHSMの実装を呼び出す(
edge_hsm_client_crypto_get_certificate@edge_hsm_client_crypto.c)。- HSMの初期化状態と引数をチェックする。
- ネイティブストアAPIを呼び出し、証明書を取得する(
edge_hsm_client_store_get_pki_trusted_certs@edge_hsm_client_store.c)。- HSMの初期化状態と引数をチェックする。
- メモリ内に保存されている信頼済み証明書の情報を取得する(
prepare_trusted_certs_info)。 - 信頼済み証明書の証明書ファイルを読み取り、連結する(
certificate_info_create@hsm_utils.c)。 - 結果を
CERT_INFO_DATA構造体に格納し、そのハンドル(CERT_INFO_HANDLE)を返す(certificate_info_create@hsm_utils.c)。
iotedge クレイトで実装されている。
listモジュール。
- ランタイムAPIの
list_with_detailsを呼び出す。 - ステータスをわかりやすくする。
Stoppedの場合、終了時間(Unixタイムスタンプ)を人間が読みやすい形のどのくらい前の時間かに変えて(chrono-humarizeクレートを使用)表示。Failedの場合、終了コードと、人間が読みやすい形のどのくらい前の時間かに変えた(chrono-humarizeクレートを使用)終了時間(Unixタイムスタンプ)を表示。Runningの場合、開始時間(Unixタイムスタンプ)を人間が読みやすい形のどのくらい前の時間かに変えて(chrono-humarizeクレートを使用)表示。
logsモジュール。引数としてfollow、tail、sinceを受け取る。
- ランタイムAPIの
logsを呼び出す。 - 結果を表示する。
restartモジュール。引数としてidを受け取る。
- ランタイムAPIの
restartを呼び出す。 - 再起動したモジュールのIDを表示する。
versionモジュール。
- クレート名とバージョン情報(
systeminfoと同じ値)を表示する。
checkモジュールとそのサブモジュール群。
(別途記述)