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::InitialAssignment
sha256_thumbprint
:常にNone
credentials
:常に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
:常にNone
credentials
:常に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
:常にNone
credentials
:常に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をランタイムのlist
APIで取得し、一致していれば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
)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgent
CreateCommand
リクエストボディ
{
"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
を参照
- 呼び出し元
$edgeAgent
UpdateCommand
アップデート。クエリ文字列で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
を参照
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgent
UpdateCommand
アップデートの準備を行う。具体的には、imagePullPolicy
がonCreate
の場合はレジストリAPIpull
を呼び出す。
DELETE /modules/{name}?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version
:APIのバージョン(既定値は2018-06-28
)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgent
RemoveCommand
IoT Edgeモジュールを削除する。引数はモジュールの名前。
ランタイムAPIremove
を呼び出す。
POST /modules/{name}/start?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version
:APIのバージョン(既定値は2018-06-28
)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgent
StartCommand
IoT Edgeのモジュールの開始を行う。引数は対象のモジュール名。ランタイムAPIstart
を呼び出す。
POST /modules/{name}/stop?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version
:APIのバージョン(既定値は2018-06-28
)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgent
UpdateCommand
StopCommand
IoT Edgeのモジュールの停止を行う。引数は対象のモジュール名。ランタイムAPIstop
を呼び出す(wait_before_kill
引数にはNone
を渡す)。
POST /modules/{name}/restart?api-version={apiVersion}
- 認証/認可
- 匿名呼び出し可能
- クエリパラメーター
api_version
:APIのバージョン(既定値は2018-06-28
)
- リクエストボディ
- なし
- レスポンスボディ
- なし
- 呼び出し元
$edgeAgent
RestartCommand
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
。既定値はfalse
since
:開始タイムスタンプ(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"
}
}
}
}
モジュールアイデンティティを列挙する。アイデンティティマネージャーのlist
APIを呼び出し、その結果を整形して返す。
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": "このモジュールアイデンティティの認証種別。"
}
}
}
モジュールアイデンティティを作成する。引数としてスペックを受け取る。アイデンティティマネージャーのロックを取り、create
APIを呼び出し、その結果を整形して返す。
なお、現在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。"
}
}
}
モジュールアイデンティティを更新する。引数としてスペックを受け取る。アイデンティティマネージャーのロックを取り、update
APIを呼び出す。
DELETE /identities/{name}?api-version={apiVersion}
- 認証/認可
- Edge Agentのみ呼び出し可能
- クエリパラメーター
api_version
:APIのバージョン(既定値は2018-06-28
)
- リクエストボディ
- なし
- レスポンスボディ
- なし
モジュールアイデンティティを削除する。引数としてモジュール名を受け取る。アイデンティティマネージャーのロックを取り、delete
APIを呼び出す。
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_info
APIを呼び出し、結果を整形して返す。システム情報とは、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
)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgent
EncryptionProvider
- バックアップ配置マニフェストの復号
- 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
)
- リクエストボディ
- 後述
- レスポンスボディ
- 後述
- 呼び出し元
$edgeAgent
EncryptionProvider
- バックアップ配置マニフェストの暗号化
- 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モジュールかどうかを判別する(たとえば
list
API)。
- 調整した環境変数、ラベル、そして
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) -> &ModuleRegistry
ModuleRegistry
トレイトオブジェクトして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
モジュールとそのサブモジュール群。
(別途記述)