From 35321bbbedf8617bf1c03672cff8fe119d15f53f Mon Sep 17 00:00:00 2001 From: nox-msft Date: Thu, 5 Dec 2024 13:34:36 -0800 Subject: [PATCH 1/7] Ensure to use the download and extensions folder in du-config.json if specified. --- .../src/adu_core_interface.c | 3 +- src/utils/config_utils/src/config_utils.c | 29 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/agent/adu_core_interface/src/adu_core_interface.c b/src/agent/adu_core_interface/src/adu_core_interface.c index f1620001b..9f989dac8 100644 --- a/src/agent/adu_core_interface/src/adu_core_interface.c +++ b/src/agent/adu_core_interface/src/adu_core_interface.c @@ -453,9 +453,10 @@ void OrchestratorUpdateCallback( } // Ensure update to latest rootkey pkg, which is required for validating the update metadata. - workFolder = workflow_get_root_sandbox_dir(workflowData->WorkflowHandle); #ifdef ADUC_BUILD_SNAP workFolder = ADUC_DOWNLOADS_FOLDER; + #else + workFolder = workflow_get_root_sandbox_dir(workflowData->WorkflowHandle); #endif if (workFolder == NULL) { diff --git a/src/utils/config_utils/src/config_utils.c b/src/utils/config_utils/src/config_utils.c index 6ed92e236..51ec1e7e9 100644 --- a/src/utils/config_utils/src/config_utils.c +++ b/src/utils/config_utils/src/config_utils.c @@ -445,24 +445,33 @@ bool ADUC_ConfigInfo_Init(ADUC_ConfigInfo* config, const char* configFolder) config->dataFolder = ADUC_JSON_GetStringFieldPtr(config->rootJsonValue, CONFIG_ADU_DATA_FOLDER); } - if (!EnsureDataSubFolderSpecifiedOrSetDefaultValue( - config->rootJsonValue, - CONFIG_ADU_DOWNLOADS_FOLDER, - &config->downloadsFolder, - config->dataFolder, - DOWNLOADS_PATH_SEGMENT)) + config->downloadsFolder = ADUC_JSON_GetStringFieldPtr(config->rootJsonValue, CONFIG_ADU_DOWNLOADS_FOLDER); + + if (config->downloadsFolder == NULL) { - goto done; + if (!EnsureDataSubFolderSpecifiedOrSetDefaultValue( + config->rootJsonValue, + CONFIG_ADU_DOWNLOADS_FOLDER, + &config->downloadsFolder, + config->dataFolder, + DOWNLOADS_PATH_SEGMENT)) + { + goto done; + } } - if (!EnsureDataSubFolderSpecifiedOrSetDefaultValue( + config->extensionsFolder = ADUC_JSON_GetStringFieldPtr(config->rootJsonValue, CONFIG_ADU_EXTENSIONS_FOLDER); + if (config->extensionsFolder == NULL) + { + if (!EnsureDataSubFolderSpecifiedOrSetDefaultValue( config->rootJsonValue, CONFIG_ADU_EXTENSIONS_FOLDER, &config->extensionsFolder, config->dataFolder, EXTENSIONS_PATH_SEGMENT)) - { - goto done; + { + goto done; + } } // Since we're only allow overriding of 'extensions' folder, let's populate all extensions sub-folders. From bf1179de9717cbd35f3c68b6d548cf026837fbc6 Mon Sep 17 00:00:00 2001 From: nox-msft Date: Thu, 5 Dec 2024 13:53:13 -0800 Subject: [PATCH 2/7] Add unit test for downloadsFolder setting --- .../config_utils/tests/config_utils_ut.cpp | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/src/utils/config_utils/tests/config_utils_ut.cpp b/src/utils/config_utils/tests/config_utils_ut.cpp index e8257255a..524d1eb13 100644 --- a/src/utils/config_utils/tests/config_utils_ut.cpp +++ b/src/utils/config_utils/tests/config_utils_ut.cpp @@ -341,6 +341,42 @@ static const char* validConfigWithOverrideFolder = R"(])" R"(})"; +static const char* validConfigWithUbuntuCoreDownloadsFolder = + R"({)" + R"("schemaVersion": "1.1",)" + R"("aduShellTrustedUsers": ["adu","do"],)" + R"("manufacturer": "device_info_manufacturer",)" + R"("model": "device_info_model",)" + R"("downloadTimeoutInMinutes": 1440,)" + R"("aduShellFolder": "/usr/mybin",)" + R"("dataFolder": "/var/lib/adu/mydata",)" + R"("downloadFolder": "/var/lib/deviceupdate-agent-downloads",)" + R"("extensionsFolder": "/var/lib/adu/myextensions",)" + R"("compatPropertyNames": "manufacturer,model",)" + R"("agents": [)" + R"({ )" + R"("name": "host-update",)" + R"("runas": "adu",)" + R"("connectionSource": {)" + R"("connectionType": "AIS",)" + R"("connectionData": "iotHubDeviceUpdate")" + R"(},)" + R"("manufacturer": "Contoso",)" + R"("model": "Smart-Box")" + R"(},)" + R"({)" + R"("name": "leaf-update",)" + R"("runas": "adu",)" + R"("connectionSource": {)" + R"("connectionType": "string",)" + R"("connectionData": "HOSTNAME=...")" + R"(},)" + R"("manufacturer": "Fabrikam",)" + R"("model": "Camera")" + R"(})" + R"(])" + R"(})"; + static const char* invalidConfigContentDownloadTimeout = R"({)" R"("schemaVersion": "1.1",)" @@ -618,7 +654,7 @@ TEST_CASE_METHOD(GlobalMockHookTestCaseFixture, "ADUC_ConfigInfo_Init Functional CHECK(config->refCount == 0); } - SECTION("User folders from du-config file") + SECTION("Uses folders from du-config file") { REQUIRE(mallocAndStrcpy_s(&g_configContentString, validConfigWithOverrideFolder) == 0); ADUC::StringUtils::cstr_wrapper configStr{ g_configContentString }; @@ -641,4 +677,30 @@ TEST_CASE_METHOD(GlobalMockHookTestCaseFixture, "ADUC_ConfigInfo_Init Functional ADUC_ConfigInfo_ReleaseInstance(config); CHECK(config->refCount == 0); } + + // NOTE: Ensure that the downloadsFolder is set correctly, if specified in du-configjson. + // (Instead of a 'downloads' sub-folder of the dataFolder ) + SECTION("Specify Ubunto Core agent downloads folder in du-config file") + { + REQUIRE(mallocAndStrcpy_s(&g_configContentString, validConfigWithUbuntuCoreDownloadsFolder) == 0); + ADUC::StringUtils::cstr_wrapper configStr{ g_configContentString }; + const ADUC_ConfigInfo* config = ADUC_ConfigInfo_GetInstance(); + CHECK_THAT(config->aduShellFolder, Equals("/usr/mybin")); + +#if defined(WIN32) + CHECK_THAT(config->aduShellFilePath, Equals("/usr/mybin/adu-shell.exe")); +#else + CHECK_THAT(config->aduShellFilePath, Equals("/usr/mybin/adu-shell")); +#endif + + CHECK_THAT(config->dataFolder, Equals("/var/lib/adu/mydata")); + CHECK_THAT(config->extensionsFolder, Equals("/var/lib/adu/myextensions")); + CHECK_THAT(config->extensionsComponentEnumeratorFolder, Equals("/var/lib/adu/myextensions/component_enumerator")); + CHECK_THAT(config->extensionsContentDownloaderFolder, Equals("/var/lib/adu/myextensions/content_downloader")); + CHECK_THAT(config->extensionsStepHandlerFolder, Equals("/var/lib/adu/myextensions/update_content_handlers")); + CHECK_THAT(config->extensionsDownloadHandlerFolder, Equals("/var/lib/adu/myextensions/download_handlers")); + CHECK_THAT(config->downloadsFolder, Equals("/var/lib/deviceupdate-agent-downloads")); + ADUC_ConfigInfo_ReleaseInstance(config); + CHECK(config->refCount == 0); + } } From 586a5a8e1645d98fde456d8eab23aafa1f9c000c Mon Sep 17 00:00:00 2001 From: Nox Date: Thu, 5 Dec 2024 22:17:35 +0000 Subject: [PATCH 3/7] Fix config_utils_unit_test --- src/utils/config_utils/tests/config_utils_ut.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/config_utils/tests/config_utils_ut.cpp b/src/utils/config_utils/tests/config_utils_ut.cpp index 524d1eb13..f2549196d 100644 --- a/src/utils/config_utils/tests/config_utils_ut.cpp +++ b/src/utils/config_utils/tests/config_utils_ut.cpp @@ -350,7 +350,7 @@ static const char* validConfigWithUbuntuCoreDownloadsFolder = R"("downloadTimeoutInMinutes": 1440,)" R"("aduShellFolder": "/usr/mybin",)" R"("dataFolder": "/var/lib/adu/mydata",)" - R"("downloadFolder": "/var/lib/deviceupdate-agent-downloads",)" + R"("downloadsFolder": "/var/lib/deviceupdate-agent-downloads",)" R"("extensionsFolder": "/var/lib/adu/myextensions",)" R"("compatPropertyNames": "manufacturer,model",)" R"("agents": [)" @@ -678,9 +678,10 @@ TEST_CASE_METHOD(GlobalMockHookTestCaseFixture, "ADUC_ConfigInfo_Init Functional CHECK(config->refCount == 0); } + // NOTE: Ensure that the downloadsFolder is set correctly, if specified in du-configjson. // (Instead of a 'downloads' sub-folder of the dataFolder ) - SECTION("Specify Ubunto Core agent downloads folder in du-config file") + SECTION("Ubunto Core DO downlad.") { REQUIRE(mallocAndStrcpy_s(&g_configContentString, validConfigWithUbuntuCoreDownloadsFolder) == 0); ADUC::StringUtils::cstr_wrapper configStr{ g_configContentString }; @@ -704,3 +705,4 @@ TEST_CASE_METHOD(GlobalMockHookTestCaseFixture, "ADUC_ConfigInfo_Init Functional CHECK(config->refCount == 0); } } + From fa595605c1979d8d0f107db9729a2ad195c96bb9 Mon Sep 17 00:00:00 2001 From: nox-msft Date: Thu, 5 Dec 2024 15:08:08 -0800 Subject: [PATCH 4/7] Add intructions on how to specify the downloadsFolder in du-config.json for Ubuntu Core agent --- snap/local/README.md | 74 ++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/snap/local/README.md b/snap/local/README.md index 986b7498e..b3d36cc58 100644 --- a/snap/local/README.md +++ b/snap/local/README.md @@ -16,7 +16,7 @@ Visit [Ubuntu Core Official Page](https://ubuntu.com/core) for more information. ## Inside Ubuntu Core -The Ubuntu Core architecture overview diagram below depicts the delivery of the kernel, boot assets, runtime environment, applications, and device enablement capabilities as snaps. These snaps are managed by the snap daemon (snapd) and the daemon itself is packaged as a snap. +The Ubuntu Core architecture overview diagram below depicts the delivery of the kernel, boot assets, runtime environment, applications, and device enablement capabilities as snaps. These snaps are managed by the snap daemon (snapd) and the daemon itself is packaged as a snap. Visit [Ubuntu Core](https://ubuntu.com/core/docs/uc20/inside) page for more details. ![Inside Ubuntu Core - Diagram](./assets/du-agent-snap-inside-ubuntu-core.svg) @@ -24,7 +24,7 @@ Visit [Ubuntu Core](https://ubuntu.com/core/docs/uc20/inside) page for more deta > Credit: the diagram above is adapted from [Inside Ubuntu Core](https://ubuntu.com/core/docs/uc20/inside) diagram. ## Device Update Agent Snap for Ubuntu Core -The Device Update Agent, also known as the DU Agent, is a crucial `application` snap specifically designed for Ubuntu Core. Its primary purpose is to manage updates and ensure the security and reliability of devices that run on this operating system. +The Device Update Agent, also known as the DU Agent, is a crucial `application` snap specifically designed for Ubuntu Core. Its primary purpose is to manage updates and ensure the security and reliability of devices that run on this operating system. ### Device Update Agent and Dependencies @@ -48,14 +48,14 @@ In a nutshell, `snaps` are isolated from other snaps and the rest of the system Overall, the snap user and group security model in Ubuntu Core provides a high degree of isolation and security for individual applications, while still allowing them to access the resources they need to function properly. -For more information, see [Security Policy and Sandboxing of Snapcraft](https://snapcraft.io/docs/security-policy-and-sandboxing), a snap is run inside a isolated sandbox, so the user accessing Device Update Agent will always be `root`. +For more information, see [Security Policy and Sandboxing of Snapcraft](https://snapcraft.io/docs/security-policy-and-sandboxing), a snap is run inside a isolated sandbox, so the user accessing Device Update Agent will always be `root`. #### DU Agent Snap User and Group Key differences between Device Update Agent and Device Update Agent Snap: | | Device Upate Agent | Device Update Agent Snap | |---|---|---| -| user id| adu | snap_aziotdu | +| user id| adu | snap_aziotdu | | group id| adu | snap_aziotdu | ### How DU Agent Acquire the IoT Hub Connection Information @@ -80,7 +80,7 @@ This specifies the principal information for the DU Agent Module, including its When connecting snaps with interfaces, the snaps are typically connected with the default user or "system" user. However, it is possible to connect snaps with a specific user ID by using the `--classic` and `--username` options with the snap connect command. For example, to connect to Azure Identity Service with a specific user ID called "snap_aziotdu", you would use the following command: -`snap connect --classic --username=snap_aziotdu deviceupdate-agent:AIS-interface azureIdentityService-snap:AIS-interface` +`snap connect --classic --username=snap_aziotdu deviceupdate-agent:AIS-interface azureIdentityService-snap:AIS-interface` This will connect the two snaps using the "snap_aziotdu" user ID, allowing the snaps to communicate with each other as that user. It is important to note that using the `--classic` and `--username` options with the snap connect command can have security implications, as it allows the connected snaps to access each other's data and resources as the specified user. Therefore, it should only be used if necessary and with caution. @@ -190,19 +190,19 @@ layout: ``` -> Note that above DU's `_plug_name_:deviceupdate-agent-downloads` identifier must match DO's `_slot_name_:deviceupdate-agent-downloads` identifier +> Note that above DU's `_plug_name_:deviceupdate-agent-downloads` identifier must match DO's `_slot_name_:deviceupdate-agent-downloads` identifier To connect, use following commands: ```shell # Required connections for content downloading using Delivery Optimization snap. -sudo snap connect deviceupdate-agent:do-port-numbers deliveryoptimization-client:do-port-numbers +sudo snap connect deviceupdate-agent:do-port-numbers deliveryoptimization-agent:do-port-numbers -sudo snap connect deviceupdate-agent:do-configs deliveryoptimization-client:do-configs +sudo snap connect deviceupdate-agent:do-configs deliveryoptimization-agent:do-configs -sudo snap connect deliveryoptimization-client:deviceupdate-agent-downloads deviceupdate-agent:deviceupdate-agent-downloads +sudo snap connect deliveryoptimization-agent:deviceupdate-agent-downloads deviceupdate-agent:deviceupdate-agent-downloads -# Required interface for Snapd RestFul requests. +# Required interface for Snapd RestFul requests. sudo snap connect deviceupdate-agent:snapd-control ``` @@ -210,13 +210,13 @@ sudo snap connect deviceupdate-agent:snapd-control ### Azure Identity Service Integration To connect to Azure Identity Service, install AIS with this command `sudo snap install --edge azure-iot-identity`, wiring it up with the following command, ```shell -sudo snap connect azure-iot-identity:log-observe +sudo snap connect azure-iot-identity:log-observe -sudo snap connect azure-iot-identity:system-observe +sudo snap connect azure-iot-identity:system-observe -sudo snap connect azure-iot-identity:tpm +sudo snap connect azure-iot-identity:tpm ``` -Here is the process of configuring AIS using a `config.toml` file and creating a `testing.toml` file for cloud identity creation. +Here is the process of configuring AIS using a `config.toml` file and creating a `testing.toml` file for cloud identity creation. Use the following commands: @@ -224,16 +224,16 @@ Use the following commands: sudo snap set azure-iot-identity raw-config="$(cat /path/to/config.toml)" ``` ```shell -$ sudo cat testing.toml -[[principal]] -uid = -name = "testing" -idtype = ["module"] +$ sudo cat testing.toml +[[principal]] +uid = +name = "testing" +idtype = ["module"] -sudo chown root:root testing.toml -sudo chmod 0600 testing.toml +sudo chown root:root testing.toml +sudo chmod 0600 testing.toml -sudo mv testing.toml /var/snap/azure-iot-identity/current/shared/config/aziot/identityd/config.d/testing.toml +sudo mv testing.toml /var/snap/azure-iot-identity/current/shared/config/aziot/identityd/config.d/testing.toml ``` then use the following command to connect Device Update and AIS: @@ -244,7 +244,7 @@ sudo snap connect deviceupdate-agent:identity-service azure-iot-identity:identit Verify that connections are ok: ```shell -$ snap connections deviceupdate-agent +$ snap connections deviceupdate-agent Interface Plug Slot Notes content[deviceupdate-agent-downloads] deliveryoptimization-client:deviceupdate-agent-downloads deviceupdate-agent:deviceupdate-agent-downloads manual @@ -392,12 +392,32 @@ $ sudo systemctl restart snap.deviceupdate-agent.deviceupdate-agent To configure the DU Agent, you can use `deviceupdate-agent.set-config` command. - This command decodes and saves base64 encoded configuration data to a file with the given name in the `\$SNAP_DATA/config` directory. + This command decodes and saves base64 encoded configuration data to a file with the given name in the `\$SNAP_DATA/config` directory. - The script takes two required options - the name of the config file and the base64 encoded data - and saves the data to the specified file. + The script takes two required options - the name of the config file and the base64 encoded data - and saves the data to the specified file. > NOTE | Only two config file names are allowed: `du-config.json` and `du-diagnostics-config.json`. + > IMPORTANT | In order to communicate with deliveryoptimization-agent snap, the downloadsFolder must be pointed to '/var/lib/deviceupdate-agent-downloads'. This can be accomplished by setting "downloadsFolder" value in du-config.json to "/var/lib/deviceupdate-agent-downloads". See example below... + ```json +{ + "schemaVersion": "1.1", + "aduShellTrustedUsers": [ + "snap_aziotdu", + "snap_aziotdo" + ], + "iotHubProtocol": "mqtt", + "manufacturer": "Contoso", + "model": "Virtual-Vacuum", + "downloadsFolder": "/var/lib/deviceupdate-agent-downloads", + "agents": [ + { + ... + } + ] + + ``` + | Options | Arguments | Description | |---|---|---| |-c, --config-file | \ | Specifies the name of the configuration file to be created or updated.

The only valid values for the config file name are \"du-config.json\" and \"du-diagnostics-config.json\".| @@ -426,8 +446,8 @@ Key differences between Device Update Agent and Device Update Agent Snap: | | Device Upate Agent | Device Update Agent Snap | |---|---|---| -| user id| adu | snap_aziotdu | +| user id| adu | snap_aziotdu | | group id| adu | snap_aziotdu | | downloads folder | /var/lib/adu/downloads | $SNAP_DATA/data/downloads | | configs file | /etc/adu/du-config.json | $SNAP_DATA/configs/du-config.json| -| logs folder | /var/log/adu | $SNAP_DATA/log +| logs folder | /var/log/adu | $SNAP_DATA/log From bcc7fa6162e78f0ec934b09a856996a1608f9117 Mon Sep 17 00:00:00 2001 From: Nox-MSFT <55153324+Nox-MSFT@users.noreply.github.com> Date: Thu, 5 Dec 2024 15:15:51 -0800 Subject: [PATCH 5/7] Update snap/local/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- snap/local/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/local/README.md b/snap/local/README.md index b3d36cc58..10476e961 100644 --- a/snap/local/README.md +++ b/snap/local/README.md @@ -398,7 +398,7 @@ To configure the DU Agent, you can use `deviceupdate-agent.set-config` command. > NOTE | Only two config file names are allowed: `du-config.json` and `du-diagnostics-config.json`. - > IMPORTANT | In order to communicate with deliveryoptimization-agent snap, the downloadsFolder must be pointed to '/var/lib/deviceupdate-agent-downloads'. This can be accomplished by setting "downloadsFolder" value in du-config.json to "/var/lib/deviceupdate-agent-downloads". See example below... + > IMPORTANT | In order to communicate with deliveryoptimization-agent snap, the downloadsFolder must point to '/var/lib/deviceupdate-agent-downloads'. This can be accomplished by setting "downloadsFolder" value in du-config.json to "/var/lib/deviceupdate-agent-downloads". See example below... ```json { "schemaVersion": "1.1", From 39f34f86e87b18927be10f776ccb0a12d70c85e8 Mon Sep 17 00:00:00 2001 From: Nox Date: Wed, 11 Dec 2024 17:31:07 +0000 Subject: [PATCH 6/7] Fix memory leak in OrchestratorUpdateCallback (SNAP BUILD only) --- src/utils/config_utils/tests/config_utils_ut.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/config_utils/tests/config_utils_ut.cpp b/src/utils/config_utils/tests/config_utils_ut.cpp index f2549196d..9b570adae 100644 --- a/src/utils/config_utils/tests/config_utils_ut.cpp +++ b/src/utils/config_utils/tests/config_utils_ut.cpp @@ -681,7 +681,7 @@ TEST_CASE_METHOD(GlobalMockHookTestCaseFixture, "ADUC_ConfigInfo_Init Functional // NOTE: Ensure that the downloadsFolder is set correctly, if specified in du-configjson. // (Instead of a 'downloads' sub-folder of the dataFolder ) - SECTION("Ubunto Core DO downlad.") + SECTION("Ubuntu Core DO download") { REQUIRE(mallocAndStrcpy_s(&g_configContentString, validConfigWithUbuntuCoreDownloadsFolder) == 0); ADUC::StringUtils::cstr_wrapper configStr{ g_configContentString }; From cf05ffbca271ba36a10a903b2be5df2b34b77e29 Mon Sep 17 00:00:00 2001 From: Nox Date: Wed, 11 Dec 2024 17:33:03 +0000 Subject: [PATCH 7/7] Fix memory leak in OrchestratorUpdateCallback (SNAP BUILD only) --- src/agent/adu_core_interface/src/adu_core_interface.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/agent/adu_core_interface/src/adu_core_interface.c b/src/agent/adu_core_interface/src/adu_core_interface.c index 9f989dac8..3c5a1877e 100644 --- a/src/agent/adu_core_interface/src/adu_core_interface.c +++ b/src/agent/adu_core_interface/src/adu_core_interface.c @@ -453,11 +453,8 @@ void OrchestratorUpdateCallback( } // Ensure update to latest rootkey pkg, which is required for validating the update metadata. - #ifdef ADUC_BUILD_SNAP - workFolder = ADUC_DOWNLOADS_FOLDER; - #else workFolder = workflow_get_root_sandbox_dir(workflowData->WorkflowHandle); - #endif + if (workFolder == NULL) { Log_Error("workflow_get_root_sandbox_dir failed"); @@ -514,9 +511,7 @@ void OrchestratorUpdateCallback( STRING_delete(rootKeyPackageFilePath); workflow_free_string(rootKeyPkgUrl); workflow_free_string(workflowId); -#ifndef ADUC_BUILD_SNAP workflow_free_string(workFolder); -#endif STRING_delete(jsonToSend); free(jsonString);