Making IoT Hub management optional by default and configurable in favor of RC/DC channel (#733)

This commit is contained in:
Marius Niculescu 2024-06-27 16:56:51 -07:00 коммит произвёл GitHub
Родитель e16a6c68b3
Коммит 35c689b071
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
17 изменённых файлов: 1487 добавлений и 440 удалений

Просмотреть файл

@ -32,7 +32,7 @@ cmake --version
gcc --version
```
Install and configure the *Azure IoT Identity Service (AIS)* package as described at [azure.github.io/iot-identity-service/](https://azure.github.io/iot-identity-service/).
For IoT Hub management, you can install and configure the *Azure IoT Identity Service (AIS)* package as described at [azure.github.io/iot-identity-service/](https://azure.github.io/iot-identity-service/).
### Build
@ -60,16 +60,10 @@ Source | Destination | Description
[src/adapters/pnp/daemon/osconfig.service](src/adapters/pnp/daemon/osconfig.service) | /etc/systemd/system/osconfig.service | The service unit for the OSConfig Agent
[src/platform/daemon/osconfig-platform.service](src/platform/daemon/osconfig-platform.service) | /etc/systemd/system/osconfig-platform.service | The service unit for the OSConfig Platform
[src/adapters/pnp/daemon/osconfig.toml](src/adapters/pnp/daemon/osconfig.toml) | /etc/aziot/identityd/config.d/osconfig.toml | The OSConfig Module configuration for AIS
[src/modules/securitybaseline/](src/modules/securitybaseline/) | /usr/lib/osconfig/securitybaseline.so | The SecurityBaseline module binary
[src/modules/deviceinfo/](src/modules/deviceinfo/) | /usr/lib/osconfig/deviceinfo.so | The DeviceInfo module binary
[src/modules/configuration/](src/modules/configuration/) | /usr/lib/osconfig/configuration.so | The Configuration module binary
[src/modules/commandrunner/](src/modules/commandrunner/) | /usr/lib/osconfig/commandrunner.so | The CommandRunner module binary
[src/modules/firewall/](src/modules/firewall/) | /usr/lib/osconfig/firewall.so | The Firewall module binary
[src/modules/adhs/](src/modules/adhs/) | /usr/lib/osconfig/adhs.so | The Azure Device Health Services (ADHS) module binary
[src/modules/deliveryoptimization/](src/modules/deliveryoptimization/) | /usr/lib/osconfig/deliveryoptimization.so | The Delivery Optimization (DO) module binary
[src/modules/hostname/](src/modules/hostname/) | /usr/lib/osconfig/hostname.so | The HostName module binary
[src/modules/networking/](src/modules/networking/) | /usr/lib/osconfig/networking.so | The Networking module binary
[src/modules/tpm/](src/modules/tpm/) | /usr/lib/osconfig/tpm.so | The TPM module binary
[src/modules/configuration/](src/modules/configuration/) | /usr/lib/osconfig/configuration.so | The Configuration module binary
[src/modules/securitybaseline/](src/modules/securitybaseline/) | /usr/lib/osconfig/securitybaseline.so | The SecurityBaseline module binary
### Enable and start OSConfig for the first time
@ -122,6 +116,16 @@ Only the root user can view these log files.
OSConfig can be configured via `/etc/osconfig/osconfig.json`. After changing this configuration file, restart OSConfig to apply the configuration changes. Only the root user can view or edit this configuration file.
### Enabling management via IoT Hub
Originally OSConfig was developed with the IoT Hub management channel by default and always enabled. Currently, this managament channel is by default disabled. You can enable it via the OSConfig general configuration file at `/etc/osconfig/osconfig.json`. Edit there the integer value named "IotHubManagement" to a non-zero value:
```json
{
"IotHubManagement": 0
}
```
### Adjusting the reporting interval
OSConfig periodically reports device data at a default time period of 30 seconds. This interval period can be adjusted between 1 second and 86,400 seconds (24 hours) via the OSConfig general configuration file at `/etc/osconfig/osconfig.json`. Edit there the integer value named "ReportingIntervalSeconds" to a value between 1 and 86400:
@ -132,7 +136,7 @@ OSConfig periodically reports device data at a default time period of 30 seconds
}
```
This same interval is also used for RC/DC and GitOps DC processing.
This interval is used for RC/DC, GitOps DC, and IoT Hub processing.
### Enabling logging of system commands executed by OSConfig for debugging purposes

Просмотреть файл

@ -1,6 +1,5 @@
Azure OSConfig - North Star Architecture
========================================
Author: [MariusNi](https://github.com/MariusNi)
# 1. Introduction
@ -35,55 +34,55 @@ This diagram shows the current North Star architecture of OSConfig. In this diag
## 3.1. Introduction
The OSConfig agent is a thin client running as a daemon (Linux background service) and implements several adapters: a watcher for the Reported Configuration/Desired Configuration (RC/DC), a watcher for GitOps and a PnP agent for [IoT Hub](https://learn.microsoft.com/en-us/azure/iot-hub/).
The OSConfig Agent is a thin client running as a daemon (Linux background service) and it's implementing the following adapters: a Watcher for the Reported Configuration/Desired Configuration (RC/DC), a Watcher for GitOps and a PnP Agent for [IoT Hub](https://learn.microsoft.com/en-us/azure/iot-hub/).
The agent communicates with the OSConfig Management Platform over the Management Platform Interface (MPI) IPC REST API.
The OSConfig Agent communicates with the OSConfig Management Platform over the Management Platform Interface (MPI) REST API.
<img src="assets/agent.png" alt="OSConfig Agent" width=50%/>
The agent is completely decoupled from the platform and the modules. A new module can be installed, and, optionally, PnP interface(s) published as part of the OSConfig Model and that will be enough to make OSConfig use the respective module, without the need to recompile the agent or the platform.
The agent is completely decoupled from the platform and the modules. A new module can be installed, and, optionally, PnP interface(s) published as part of the OSConfig Model and that will be enough to make OSConfig use it, without the need to recompile the agent or the platform.
## 3.2. RC/DC Watcher
The RC/DC Watcher monitors the Desired Configuration (DC) file and acts on detected file changes by making MpiSetDesired calls to the Management Platform. The Watcher also makes MpiGetReported calls to the Management Platform and writes the reported configuration to the Reported Configuration (RC) file.
The RC/DC Watcher monitors the Desired Configuration (DC) file and acts on file changes detected there by making MpiSetDesired calls to the OSConfig Management Platform. The RC/DC Watcher also makes periodic MpiGetReported calls to the OSConfig Management Platform, updating the Reported Configuration (RC) file.
The RC/DC files are written in JSON and follow the [MIM schema](../src/modules/schema/mim.schema.json).
To protect against unauthorized access, the RC/DC files are restricted to root user read and write access only.
To protect against unauthorized access, the RC/DC files are restricted to root user for both read and write.
The RC/DC files by default reside under `/etc/osconfig/` as `/etc/osconfig/osconfig_desired.json` (the DC file) and `/etc/osconfig/osconfig_reported.json` (the RC file).
## 3.3. GitOps Watcher
The RC/DC Watcher clones a Git repository, monitors the GitOps Desired Configuration (DC) file and acts on detected file changes by making MpiSetDesired calls to the Management Platform.
The GitOps Watcher clones a Git repository and branch containing a Desired Configuration (DC) file, monitors the cloned DC file and when it detects changes it makes MpiSetDesired calls to the OSConfig Management Platform.
The GitOps DC file by default is named `osconfig_desired.json`, is located in the root of the repository and is locally cloned at `/etc/osconfig/gitops/osconfig_desired.json`.
The GitOps DC file by default is named `osconfig_desired.json`, is located in the root of the Git repository and is locally cloned at `/etc/osconfig/gitops/osconfig_desired.json`.
The GitOps DC file is written in JSON and follows the [MIM schema](../src/modules/schema/mim.schema.json).
The Git clone is automatically deleted when the GitOps Watcher terminates. While active, the cloned DC file is protected for root user access only.
The Git clone is automatically deleted when the GitOps Watcher terminates. While active, the cloned DC file has restricted access for root user only.
## 3.4. AIS Client
The agent uses HTTP helper APIs from the [Azure IoT PnP C SDK](https://github.com/Azure/azure-iot-sdk-c) to make GET and POST requests to the [Azure Identity Services (AIS)](https://azure.github.io/iot-identity-service/) (identity, key, certificates) to build the device/module identity to connect to the IoT Hub with.
For connecting to IoT Hub, the OSConfig Agent uses HTTP helper APIs from the [Azure IoT PnP C SDK](https://github.com/Azure/azure-iot-sdk-c) to make GET and POST requests to the [Azure Identity Services (AIS)](https://azure.github.io/iot-identity-service/) (identity, key, certificates) to build the device/module identity necessary for IoT Hub connection.
## 3.5. IoT Hub Client
The agent's IoT Hub Client uses the IoT Client APIs from the [Azure IoT PnP C SDK](https://github.com/Azure/azure-iot-sdk-c) to connect to the [IoT Hub](https://learn.microsoft.com/en-us/azure/iot-hub/), receive Desired Twin updates, and make Reported Twin updates.
The Agent's IoT Hub Client uses the IoT Client APIs from the [Azure IoT PnP C SDK](https://github.com/Azure/azure-iot-sdk-c) to connect to the [IoT Hub](https://learn.microsoft.com/en-us/azure/iot-hub/), receive Desired Twin updates, and make Reported Twin updates.
The IoT Hub Client does not attempt to parse the property values.
## 3.6. Desired and Reported Twins
The twins start empty and gradually get filled in with content (desired, from the remote authority and reported, from the device).
The Twins start empty and gradually get filled in with content (desired, from the remote authority and reported, from the device).
When the agent starts, it receives the full Desired Twin and dispatches that to the platform. From there on, incremental changes of the Desired Twin are communicated to agent, one (full or partial) property at a time.
When the OSConfig Agent starts, it receives the full Desired Twin and dispatches that to the OSConfig Managament Platform. From there on, incremental changes of the Desired Twin are communicated to the agent, one (full or partial) property at a time.
In the opposite direction, the OSConfig agent periodically updates the Reported Twin with one property value at a time, reading via the platform from the modules.
In the opposite direction, the OSConfig Agent periodically updates the Reported Twin with one property value at a time, reading via the platform from the modules.
## 3.7. MPI Client
The agent implements an MPI Client and it uses it to make Management Platform Interface (MPI) calls to the platform as IPC REST API calls over HTTP and the Unix Domain Sockets (UDS).
The OSConfig Agent links to the common MPI Client library and it uses it to make Management Platform Interface (MPI) calls to the OSConfig Platform as IPC REST API calls over HTTP and Unix Domain Sockets (UDS).
# 4. OSConfig Management Platform
@ -91,22 +90,22 @@ The agent implements an MPI Client and it uses it to make Management Platform In
The OSConfig Management Platform runs in its own daemon process. The platform communicates with the management authority adapters (the OSConfig Agent, the Universal NRP, etc) over the Management Platform Interface (MPI) REST API.
The platform communicates to the OSConfig Management Modules over the Management Modules Interface (MMI) REST API.
The platform communicates to the OSConfig Management Modules over the Management Modules Interface (MMI) API.
<img src="assets/platform.png" alt="OSConfig Management Platform" width=60%/>
The platform includes the following main components:
- Management Platform Interface (MPI): MPI as an IPC REST API over HTTP and UDS. This is the main adapter interface.
- Watcher: monitors for desired configuration file for updates. This is the other adapter interface, for both agentless and agent management authorities.
- Orchestrator: receives, orchestrates, serializes requests for modules into the Modules Manager. The management authorities and their adapters can make their own MPI requests. The Orchestrator's role is to orchestrate between local and remote management authorities and also help orchestrate for each authority following the Module Interface Model (MIM) contracts and for PnP without modifying passing-through requests in either direction.
- Modules Manager: receives serialized requests over the MPI C API, dispatches the requests to Module Hosts over the MMI REST API.
- MMI Client: makes MMI REST API calls to Module Hosts over HTTP and UDS.
- Management Platform Interface (MPI): MPI as an IPC REST API over HTTP and UDS. This is the main interface for adapters.
- Modules Manager: receives serialized requests over the MPI C API, dispatches the requests to modules over the MMI API.
- MMI Client: makes MMI API calls to modules.
The platform also includes several utility libraries which are shared with all OSConfig components, including adapters and modules:
- Logging: file and console circular logging library.
- CommonUtils: various utility APIs useful for accesing and working with the Linux OS.
- MpiClient: client for the MPI REST API
- Asb: the implementation of the Azure Security Baseline, shared among the Universal NRP adapter and the SecurityBaseline module.
The platform is completely decoupled from the adapters and the modules. The platform can function without any particular adapter. New modules can be installed without changing or recompiling the platform.
@ -117,9 +116,7 @@ The Management Platform Interface (MPI) provides a way for the OSConfig Manageme
The MPI has two different implementations:
- REST API over Unix Domain Sockets (UDS) for inter-process communication (IPC) with the adapters.
- C API for internal in-process communication between the Orchestrator and the Modules Manager.
The MPI REST API is implemented as a Linux Static Library (.a), either alone or combined with the Orchestrator, linked into the platform's main binary, and exporting the REST API over Unix Domain Sockets (UDS). The adapter MPI clients make such MPI REST API calls over HTTP.
- C API for internal in-process communication between the MPI REST API server and the Modules Manager.
MPI REST API calls include GET (MpiGet, MpiGetReported) and POST (MpiSet, MpiSetDesired).
@ -152,9 +149,7 @@ This format is following the MIM JSON payload schema described in the [OSConfig
## 4.3. Orchestrator
The Orchestrator receives management requests from adapters over the Management Platform Interface (MPI) IPC REST API. The Orchestrator combines the requests in a serial sequence that it feeds into the Module Manager to dispatch the requests to the respective Management Modules.
The Orchestrator can be implemented as a Linux Static Library (.a) either alone or combined with the MPI and linked into the platform's main binary.
The Orchestrator receives management requests from Adapters over the Management Platform Interface (MPI) IPC REST API. The Orchestrator combines the requests in a serial sequence that it feeds into the Module Manager to dispatch the requests to the respective Management Modules.
<img src="assets/orchestration.png" alt="Orchestrator" width=50%/>
@ -172,7 +167,7 @@ What the Orchestrator does not do:
- Retry failed requests on its own. The Management Modules can internally retry, the upper OSConfig stack, including the Orchestrator, should not automatically retry.
- Create new or modify requests in a way that is incompatible with the MIM contracts with Management Modules.
For PnP:
For IoT Hub and the Digital Twins:
- The Orchestrator cannot change midway in the platform desired requests from the Twins and cannot change reported requests to the Twins - doing so violates the Twins-MIM contract.
- Once a desired object is set on the Twin, that object gets automatically re-applied to the device until it is corrected on the Twin by the remote authority. OSConfig can reject a desired payload but cannot correct that payload on the Twin. The device cannot change Desired Twin content.
@ -181,9 +176,7 @@ Special OSConfig Management Modules that implement more complex scenarios and on
## 4.4. Modules Manager
The Modules Manager receives serialized MPI C API requests from the Orchestrator and dispatches the requests over MMI to the appropriate Management Modules in the order they arrive. Also, the Modules Manager returns the modules responses to the Orchestrator.
The Modules Manager is implemented as a Linux Static Library (.a) exporting the MPI C API.
The Modules Manager receives serialized MPI C API requests from the MPI REST API server and/or from the Orchestrator and dispatches the requests over MMI to the appropriate Management Modules. The Modules Manager returns the responses from the modules to the MPI REST API server and/or the Orchestrator.
<img src="assets/modulesmanager.png" alt="Modules Manager" width=40%/>
@ -196,7 +189,7 @@ What the Module Manager does:
- Maintains an internal list of loaded modules/forked Module Hosts.
- (only when modules are loaded in-process by manager) Persists state and if a particular module crashes too many times, decide to not load that module anymore and fail any MPI calls for.
- When two or more Modules implementing the same components are present, decides which module to load/fork using version info reported by the modules.
- Keeps a module loaded according to the respective module configuration either for the entire duration of the platform process or may decides to unload that module/stop the forked Module Host after a period of inactivity and reload/re-fork when a new request arrives.
- Keeps a module loaded according to the respective module configuration either for the entire duration of the platform process or may decides to unload that module/stop the forked module Host after a period of inactivity and reload/re-fork when a new request arrives.
- Keeps track of multiple parallel client MPI sessions.
What the Module Manager does not do:
@ -229,7 +222,7 @@ Like the MPI, the MMI has two different implementations:
- REST API over Unix Domain Sockets (UDS) for inter-process communication (IPC) between the Management Platform (the Modules Manager) and the Module Hosts.
- C API for internal in-process communication between the Modules Hosts and their modules.
The MMI REST API allows modules to be directly called by any management authority if needed. This is also the main channel of communication of modules with the OSConfig Management Platform. This API is implemented as a Linux Static Library (.a) linked into the Module Host binary and exporting the REST API over Unix Domain Sockets (UDS). The MMI Client make such MPI REST API calls over HTTP.
The MMI REST API allows Modules to be directly called by any management authority Adapter if needed. This is also the main channel of communication of modules with the OSConfig Management Platform.
REST API calls include: GET (MmiGet, MmiGetInfo) and POST (MmiSet).
@ -249,7 +242,7 @@ The Downloader can be a Dynamically Linked Shared Object library (.so) provided
The OSConfig Management Platform and the Management Modules may need to persist data locally to be available across device reboot.
To make it easier for module developers, OSConfig can provide a Secure Store storage component either as a Dynamically Linked Shared Object library (.so) loaded by the OSConfig Platform or running in its own separate daemon process.
To make it easier for Module developers, OSConfig can provide a Secure Store storage component either as a Dynamically Linked Shared Object library (.so) loaded by the OSConfig Platform or running in its own separate daemon process.
The store can be accessed over a Store REST API modeled after the MPI REST API. The Store API can store and retrieve MIM settings per MIM components and MIM objects, per management authority, and can also add audit logging, to record who did what and when. A Store Client API can be provided as a static library.
@ -267,7 +260,7 @@ For more details on the Logging library and its use by modules see the [OSConfig
## 4.10. Telemetry
OSConfig has its own Telemetry via its product information submitted to the IoT Hub. OpenTelemetry is a potential future path.
OSConfig has limited Telemetry via its product information submitted to the IoT Hub. OpenTelemetry is a potential future path.
# 5. OSConfig Management Modules
@ -277,7 +270,7 @@ Each Management Module typically implements one OS configuration function. OSCon
<img src="assets/modules.png" alt="Management Modules" width=80%/>
Modules are implemented as Dynamically Linked Shared Object libraries (.so). Each Module runs loaded into its own Module Host process, isolated from the other modules. The Module Hosts implement the Management Module Interface (MMI) as an IPC REST API and make MMI C API calls into their hosted modules.
Modules are implemented as Dynamically Linked Shared Object libraries (.so). Each module runs loaded either in the platform process, or into its own Module Host process, isolated from the other modules. The Module Hosts implement the Management Module Interface (MMI) as an IPC REST API and make MMI C API calls into their hosted modules.
In summary, each module must:
@ -293,17 +286,17 @@ For more information about modules and MIM see the [OSConfig Management Modules]
## 5.2. Module Interface Model (MIM)
The Module Interface Model (MIM) is a PnP/DTDL-agnostic model that is composed of components, objects, and settings. A module that can be described by a set of components, objects and settings can be translated to PnP and DTDL and still be generic enough so it could be reused for other platforms.
The Module Interface Model (MIM) is composed of MIM components, MIM objects, and MIM settings. A module that can be described by a set of components, objects and settings can be translated to PnP and DTDL and still be generic enough so it could be reused for other managament platforms.
The Module Management Interface (MMI) is used to transport desired and reported object payloads (to and from the module) that follow the MIM.
This model assumes a declarative style of communication between the upper layer and the module, where the desired and reported configuration of the device is communicated in bulk (for PnP this configuration being stored on the Twins), not a procedural style with multi-step negotiation.
The MIM assumes a declarative style of communication between the upper layer and the module, where the desired and reported configuration of the device is communicated in bulk (for PnP this configuration being stored on the Twins), not a procedural style with multi-step negotiation.
For more information about the MIM see the [OSConfig Management Modules](modules.md) specification.
## 5.3. OSQuery Module
The OSQuery Module implements an adapter for [OSQuery](https://www.osquery.io/). This module can support multiple MIM components and PnP interfaces that are read-only (all reported, no desired configuration) and leverage OSQuery to retrieve the respective configuration from the device. To be decided.
The OSQuery Module implements an adapter for [OSQuery](https://www.osquery.io/). This module can support multiple MIM components and PnP interfaces that are read-only (all reported, no desired configuration) and leverage OSQuery to retrieve the respective configuration from the device.
## 5.5. Orchestrator Module
@ -313,11 +306,11 @@ A new kind of OSConfig Management Module can be added: an Orchestrator Module lo
# 6. OSConfig Universal Native Resource Provider (NRP)
The OSConfig Universal Native Resource Provider (NRP) adapter links OSConfig to the [Azure Automanage Machine Configuration (MC)](https://learn.microsoft.com/en-us/azure/governance/machine-configuration/).
The OSConfig Universal Native Resource Provider (NRP) Adapter links OSConfig to the [Azure Automanage Machine Configuration (MC)](https://learn.microsoft.com/en-us/azure/governance/machine-configuration/).
Using MC and the OSConfig Universal NRP, we can create Azure Policies that automatically target for compliance audit or remediation all Linux Arc devices in a particular Azure subscription or Azure resource group.
Using MC and the OSConfig Universal NRP, we can create Azure Policies that automatically target for compliance audit or remediation all Linux Arc devices in a particular Azure subscription and Azure resource group.
The Universal NRP is used with the SecurityBaseline module to audit and remediate the [Azure Security Baseline for Linux](https://learn.microsoft.com/en-us/azure/governance/policy/samples/guest-configuration-baseline-linux). In the future the Universal NRP could be used for other scenarios.
The Universal NRP can audit and remediate the the [Azure Security Baseline for Linux](https://learn.microsoft.com/en-us/azure/governance/policy/samples/guest-configuration-baseline-linux) either by itself (acting as 'OSConfig for MC') or via the SecurityBaseline module. In the future the Universal NRP could be used for other scenarios.
<img src="assets/5_guestconfig.png" alt="OSConfig NRP" width=70%/>

Двоичные данные
docs/assets/8_osconfig_di.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 556 KiB

Просмотреть файл

@ -1,16 +1,15 @@
OSConfig Management Modules
===========================
Author: [MariusNi](https://github.com/MariusNi)
# 1. Introduction
Azure OSConfig is a modular security configuration stack for Linux Edge devices.
Azure OSConfig is a modular security configuration stack for Linux Edge devices. OSConfig supports multi-authority device management over Azure and Azure Portal/CLI, GitOps, as well as local management.
OSConfig contains a set of Adapters, a Management Platform (including a Modules Manager) and several Management Modules.
The main way of contributing to and extending OSConfig is via developing new Management Modules.
Each Management Module typically implements one device OS configuration function. OSConfig isolates the module from the management authority protocols (such as: Digital Twins Definition Language). OSConfig communicates with the module via the Module Interface Model (MIM) and the Management Module Interface (MMI) API that each module implements. The module developer is not required to learn Azure technologies like MC, PnP, DTDL, DPS, AIS, etc. and instead can focus on designing the MIM and implement the module. If needed, the MIM can then be easily translated to DTDL or other formats with minimal changes.
Each Management Module typically implements one device OS configuration function. OSConfig isolates the module from the management authority protocols (such as: Digital Twins Definition Language, etc). OSConfig communicates with the module via the module Interface Model (MIM) and the Management Module Interface (MMI) API that each module implements. The module developer is not required to learn Azure technologies like MC, PnP, DTDL, DPS, AIS, etc. and instead can focus on designing the MIM and implement the module. If needed, the MIM can then be easily translated to DTDL or other formats with minimal changes.
This document describes the OSConfig Management Modules and it's meant to serve as a guide for the design and development of such modules.
@ -26,7 +25,7 @@ This diagram shows the overall OSConfig North Star architecture. Not all compone
## 3.1. Introduction
This section describes the Module Interface Model (MIM).
This section describes the module Interface Model (MIM).
The MIM describes the device configuration the module can perform and defines the valid payload for Management Module Interface (MMI) Get/Set API calls.
@ -36,46 +35,46 @@ The MIM is composed by one or more MIM components, each component containing one
MIM can be directly translated to DTDL: MIM components can be translated to PnP interfaces, MIM objects can be translated to PnP properties, MIM settings can be translated to PnP property values. Such DTDL (obtained from MIM translation) is guaranteed to be supported by OSConfig. In general, MIM can always be translated to DTDL but DTDL cannot always be translated to MIM.
A MIM can also be translated to other object models. For example, a MIM could be translated to a C++ class framework (each component becoming a namespace, each object a class, each setting a class member).
A MIM can also be translated to other object models. For example, a MIM could be translated to a C++ class framework (each Component becoming a namespace, each Object a class, each Setting a class member).
This model assumes a declarative style of communication between the upper layers and the module, where the desired and reported configuration of the device is communicated at once (for PnP this configuration being stored on the Digital Twin), not a procedural style with multi-step negotiation.
This model assumes a declarative style of communication between the upper management layers and the module, where the desired and reported configuration of the device is communicated at once (for PnP this configuration being stored on the Digital Twin), not a procedural style with multi-step negotiation.
- Declarative style: the model describes the desired state (what), the platform decides how to get there.
- Procedural style: the model relies on programmatic step-by-step negotiation of what and how.
- Declarative style: the Model describes the desired state (what), the Platform decides how to get there.
- Procedural style: the Model relies on programmatic step-by-step negotiation of What and How.
For PnP, the Twins start empty and gradually get filled in with content (desired, from the remote operator and reported, from the device). When the OSConfig starts, it receives the full desired Twin and dispatches that to modules. From there on, incremental changes of the desired Twin are communicated to OSConfig (and the Modules), one (possibly partial, just the changed settings) object at a time. In the opposite direction, OSConfig periodically updates the reported Twin with one MIM object at a time, reading from the modules. Modules can also have their own MIM-specified actions to request the update of a reported MIM object (example: refreshCommandStatus Action for CommandRunner.commandArguments to update CommandRunner.commandStatus for a particular command).
For PnP, the Twins start empty and gradually get filled in with content (desired, from the remote operator and reported, from the device). When the OSConfig starts, it receives the full desired Twin and dispatches that to modules. From there on, incremental changes of the desired Twin are communicated to OSConfig (and the modules), one (possibly partial, just the changed settings) object at a time. In the opposite direction, OSConfig periodically updates the reported Twin with one MIM object at a time, reading from the modules. Modules can also have their own MIM-specified actions to request the update of a reported MIM object (example: refreshCommandStatus Action for CommandRunner.commandArguments to update CommandRunner.commandStatus for a particular command).
### 3.1.1. MIM Components
A MIM Component captures one specific OS configuration function, such as for example: running shell commands, configuring Wi-Fi, managing certificates, etc.
MIM components can be translated to PnP interfaces (or to C++ namespaces etc).
MIM Components can be translated to PnP interfaces (or to C++ namespaces, etc).
Modules in general only need to contain only one MIM component. Sometimes, a second MIM component could be needed. For example, for Wi-Fi one MIM component could be Wi-Fi Configuration and a second MIM component in support of the first but still separate could be Wi-Fi Certificates.
Modules in general only need to contain only one MIM component each. Sometimes, a second MIM component could be needed. For example, for Wi-Fi one MIM component could be Wi-Fi configuration and a second MIM component in support of the first but still separate could be Wi-Fi Certificates.
Each MIM component is defined by and contains a set of MIM objects. Each new version of a MIM component must have a name that's different from the previous versions of the same MIM component.
The names of MIM components must be PascalCased, with the first letter of each word capitalized.
Example of an existing OSConfig MIM component: commandRunner.
Example of an existing OSConfig MIM Component: commandRunner.
### 3.1.2. MIM Objects
A MIM object captures an OS configuration state or function. For example: result of a command, a new Wi-Fi Profile, etc.
MIM objects can be translated to PnP properties (or to C++ classes etc).
MIM objects can be translated to PnP properties (or to C++ classes, etc).
There are two types of PnP properties:
- Readable, holding Reported Twin values. These properties are exclusively updated from the device and seen as read-only from the IoT Hub.
- Writeable, holding Desired Twin values. These properties are exclusively updated from the IoT Hub. The device can reject an update request but cannot update such property.
Same as PnP properties, the MIM objects (together with the settings they contain) are unidirectional, either reported or desired, not both. For writeable settings, desired objects normally may be paired with reported objects. For read-only settings there can be only reported objects. There also could be desired objects without reported counterparts. Once the desired and reported objects are modeled, the module must adhere to this model (report reported, accept desired) and respect it.
Like the PnP properties, the MIM objects (together with the Settings they contain) are uni-directional, either reported or desired, not both. For writeable settings, desired objects normally may be paired with reported objects. For read-only settings there can be only reported objects. There also could be desired objects without reported counterparts. Once the desired and reported objects are modeled, the module must adhere to this model (report reported, accept desired) and respect it.
When the desired MIM object is distinctly different from its reported MIM object the two objects can be linked together (to be able to reference each other) via a common MIM setting. For example, the CommandRunner OSConfig component's desired commandArguments object is linked to the matching reported commandStatus object via the commandId MIM setting that both contain.
When the desired MIM object is distinctly different from its reported MIM object the two objects can be linked together (to be able to reference each other) via a common MIM setting. For example, the CommandRunner OSConfig Component's desired commandArguments Object is linked to the matching reported commandStatus Object via the commandId MIM setting that both contain.
Each MIM object contains one or multiple MIM settings, either in a single instance or in multiple instances, as a array object.
An array MIM object is a MIM object with its list of MIM settings repeated a variable number of times as items into an array.
An array MIM object is a MIM Object with its list of MIM settings repeated a variable number of times as items into an array.
When a MIM object contains just one MIM setting, that object and setting share the same name and for PnP are translated to a simple PnP property.
@ -160,7 +159,7 @@ Example of an array MIM object:
### 3.1.3 MIM Settings
MIM Settings translate to PnP property values of following types supported by both DTDL and OSConfig:
MIM settings translate to PnP property values of following types supported by both DTDL and OSConfig:
- Character string (UTF-8)
- Integer
@ -172,7 +171,7 @@ MIM Settings translate to PnP property values of following types supported by bo
- Map of strings
- Map of integers
Same as objects, settings can be either reported or desired. All MIM settings within a MIM object share the same parent object's type (either reported or desired).
Same as objects, settings can be either reported or desired. All MIM Settings within a MIM Object share the same parent object's type (either reported or desired).
The names of MIM settings and setting values must be camelCased, with the first letter lowercase and the first letter of each word after the first uppercase.
@ -271,7 +270,7 @@ Example of an MIM setting of enumeration of strings type:
}
```
Example of a MIM seting of array of strings type:
Example of a MIM setting of array of strings type:
```JSON
{
@ -283,7 +282,7 @@ Example of a MIM seting of array of strings type:
}
```
Example of a MIM seting of map of strings type:
Example of a MIM setting of map of strings type:
```JSON
{
@ -302,7 +301,7 @@ Example of a MIM seting of map of strings type:
}
```
## 3.2. Describing the Module Interface Model (MIM)
## 3.2. Describing the module Interface Model (MIM)
MIM names (for components, objects, settings and setting values, including map key names) may only contain the characters 'a'-'z', 'A'-'Z', '0'-'9', and '_' (underscore), and must match the following regular expression:
@ -314,117 +313,13 @@ Value types can be "object", "string", "integer", "boolean", "enum" (enumeration
MIM names for components must be PascalCased, while for object, settings and setting values must be camelCased.
The number of MIM components, objects and settings translated to PnP affect the size of the device's Twin, which is limited. Thus it is important to try to describe a new module with the smallest possible number and size of MIM components, objects, and settings.
The number of MIM components, objects and settings translated to PnP affect the size of the device's Twin, which is limited. Thus, for IoT Hub and Digital Twins, it is important to try to describe a new module with the smallest possible number and size of MIM components, objects, and settings. There is no such limitation for the other management authority channels.
### 3.2.1. Describing the MIM via lists and tables
A MIM can be described in JSON.
The model is composed by a list of components, several lists (one for each component) of desired or reported objects, several lists (one for each object) of settings, and one table per seting describing that setting.
### 3.2.1. MIM JSON
1. List the component(s) for the module. A module should have only one or two components.
1. For each component, list the readable and writeable object(s) contained within this component.
1. For each object, list the setting(s) contained within this object.
1. For each object, answer if this is an array object (where all settings repeat a variable number of times as items into an array) or not.
1. Describe each setting with answers to the following questions:
1. What is the setting name? The name of the setting, in camelCase.
1. What is the access: desired or reported for the setting?
1. What does the setting? A short description of the setting (what the setting does, in few words).
1. What is the value type for the setting? Answer can be: boolean, integer, enumeration of integers or strings, array of integers or strings, map of integers or strings, character string (UTF-8).
1. What are the supported values? Describe supported values, including minimum and maximums if any, valid enumeration values, maximum length for string if any, etc.
1. Does this setting need to link two objects together (desired and reported), and if so, how?
1. Does this setting depend on any other setting in this or another object? List what.
1. Is this setting required or optional? Can this setting be skipped at run time to be reported/updated?
Example: desired setting action of commandArguments:
Question | Answer
-----|-----
Component | CommandRunner
Object | commandArguments
Setting | action
Access | Desired (no matching reported setting)
Description | The action for this command
Value type | Enumeration of integers
Supported values | 0 (none), 1 (reboot), 2 (shutdown), 3 (runCommand), 4 (refreshCommandStatus), 5 (cancelCommand)
Links two objects | No
Relation to other settings | runCommand runs the command described by the arguments setting value of this object. refreshCommandStatus requests the commandStatus object of this component to be refreshed for the commandId setting value of this object.
Required | Yes
Example: setting commandId links desired object commandArguments and reported object commandStatus:
Question | Answer
-----|-----
Component | CommandRunner
Object | commandArguments
Setting | commandId
Access | Desired (matching commandStatus.commandId as reported)
Description | The identifier for this command, as provided with the command request
Value type | String
Supported values | Any UTF-8 character string up to 256 bytes long.
Links two objects | Yes. commandArguments with commandStatus. The setting value identifies a command and links this desired commandArguments object with the reported commandStatus object.
Relation to other settings | None
Required | Yes
Question | Answer
-----|-----
Component | CommandRunner
Object | commandStatus
Setting | commandId
Access | Reported
Description | The identifier for this command, originally provided via a commandArguments object, now reported back to indicate the command this result (status) is for.
Value type | String
Supported values | Any UTF-8 character string up to 256 bytes long.
Links two objects | Yes. commandArguments with commandStatus. The setting value identifies a command and links this reported commandStatus object with the respective desired commandArguments object.
Relation to other settings | None
Required | Yes
Example: fictive setting fooPolicy, both desired and reported, optional:
Question | Answer
-----|-----
Component | FooPolicies
Object | desiredFooPolicies
Setting | fooPolicy
Access | Desired (matching ReportedFooPolicies.fooPolicy as reported)
Description | A fictive desired policy
Value type | String
Supported values | Any UTF-8 character string up to 256 bytes long.
Links two objects | No
Relation to other settings | Related to reported reportedFooPolicies.fooPolicy
Required | No
Question | Answer
-----|-----
Component | FooPolicies
Object | reportedFooPolicies
Setting | fooPolicy
Access | Reported
Description | A fictive reported policy
Value type | String
Supported values | Any UTF-8 character string up to 256 bytes long.
Links two objects | No
Relation to other settings | Related to (set via) desiredFooPolicies.fooPolicy
Required | No
Example: fictive setting ReadOnlyFooPolicy which is only reported and is required:
Question | Answer
-----|-----
Component | FooPolicies
Object | reportedFooPolicies
Setting | readOnlyFooPolicy
Access | Reported
Description | A fictive read-only reported policy
Value type | String
Supported values | Any UTF-8 character string up to 256 bytes long.
Links two objects | No
Relation to other settings | None
Required | Yes
A MIM can be completely described by lists and tables as described in this section and also in JSON.
### 3.2.2. MIM JSON
Each module must have its own MIM JSON saved to the [src/modules/mim/](../src/modules/mim/) in a JSON file with the same name as the module SO binary.
Each Module must have its own MIM JSON saved to the [src/modules/mim/](../src/modules/mim/) in a JSON file with the same name as the module SO binary.
The MIM JSON schema is at [src/modules/schema/mim.schema.json](../src/modules/schema/mim.schema.json).
@ -633,7 +528,7 @@ Sample MIM JSON:
}
```
A partial MIM JSON example of a fictional firewall configuration component including an array object, an array of strings and a map of strings simple objects:
A partial MIM JSON example of a fictional firewall configuration component including an array object, an array of strings and a map of strings simple Objects:
```JSON
{
@ -703,12 +598,12 @@ A partial MIM JSON example of a fictional firewall configuration component inclu
```
Other MIM JSON examples:
- CommandRunner: two MIM objects, commandArguments (desired) and commandStatus (reported), linked together by a common setting, commandId: [CommandRunner MIM](../src/modules/mim/commandrunner.json)
- CommandRunner: two MIM objects, commandArguments (desired) and commandStatus (reported), linked together by a common Setting, commandId: [CommandRunner MIM](../src/modules/mim/commandrunner.json)
- Tpm: three simple reported MIM objects, each containing a single setting: [Tpm MIM](../src/modules/mim/tpm.json)
### 3.2.3 Serialized MIM payload at run-time
The following would be the payload serialized at runtime for the entire desired or reported MIM (wrapping the object values that the MMI handles):
The following is the payload serialized at runtime for the entire desired or reported MIM (wrapping the object values that the MMI handles):
```
{"ComponentName":{"objectName":[{"stringSettingName":"some value","integerValueName":N,"booleanValueName":true|false,"integerEnumerationSettingName":N,"stringEnumerationSettingName":"enumStringValue","stringArraySettingName":["stringArrayItemA","stringArrayItemB","stringArrayItemC"],"integerArraySettingName":[A,B,C],"stringMapSettingName":{"mapKeyX":"X","mapKeyY":"Y","mapKeyZ":"Z"},"integerMapSettingName":{"mapKeyX":X,"mapKeyY":Y,"mapKeyZ":Z}},{...}]},{"objectNameZ":{...}}},{"ComponentNameY":{...}}
@ -770,9 +665,9 @@ A simplified diagram shows the desired and reported configuration requests excha
<img src="assets/desiredreported.png" alt="OSConfig Configuration Data" width=70%/>
Each module must be a Linux Dynamically Linked Shared Object library (.so) implementing the MMI. The MMI transports the MIM object payloads of settings for the module component(s).
Each Module must be a Linux Dynamically Linked Shared Object library (.so) implementing the MMI. The MMI transports the MIM object payloads of settings for the module component(s).
For the current version OSConfig continues to run as a single process, with modules loaded in-proc. In a future version OSConfig could split in two processes, one for the Agent and the other for the Platform. In a further future version modules could be isolated to run each into their own processes, via an executable shell provided by OSConfig.
For the first version of OSConfig, there was a single process, with modules loaded in-proc. In the current version of OSConfig there are two processes, one for the agent and the other for the platform and the later loads the modules in-proc. In a further future version modules could be isolated to run each into their own processes, via an executable shell provided by OSConfig.
In general, any process can load a module and communicate to it over the MMI. The module developer shall not assume that the module will be loaded by a certain process or that will be only invoked over PnP and IoT Hub.
@ -840,9 +735,9 @@ void MmiClose(MMI_HANDLE clientSession);
## 4.4. MmiSet
MmiSet takes as input arguments a handle returned by MmiOpen, the name of the component (for OSConfig this will be the name of the PnP interface/component, e.g. "CommandRunner"), the name of the object (for OSConfig this will be the be PnP property name, e.g. "commandArguments"), the desired object payload formatted as JSON and not null terminated UTF-8 character string and the length (size) in bytes of the JSON payload (without null terminator). The module can use the clientSession handle (module specific, could be a C structure or C++ class) to give context to the call or can ignore it.
MmiSet takes as input arguments a handle returned by MmiOpen, the name of the Component (e.g. "CommandRunner"), the name of the Object (e.g. "commandArguments"), the desired Object payload formatted as JSON and not null terminated UTF-8 character string and the length (size) in bytes of the JSON payload (without null terminator). The module can use the clientSession handle (module specific, could be a C structure or C++ class) to give context to the call or can ignore it.
The objectName and payload must must match a desired MIM object from the componentName MIM component and present in the module's MIM. There can only be one single MIM object per MmiSet call. Modules must not accept MmiSet calls that are not following their MIM precisely.
The objectName and payload must must match a desired MIM Object from the componentName MIM component and present in the module's MIM. There can only be one single MIM Object per MmiSet call. Modules must not accept MmiSet calls that are not following their MIM precisely.
```C
int MmiSet(
@ -867,17 +762,17 @@ The payload argument contains a JSON formatted, not null terminated UTF-8 string
- Boolean payload example: ```"true"```
- Complex payload example combining all the above as fields into same object payload: ```"{"valueOne":123,"valueTwo":"This is a test.","valueThree":true}"``` where "valueOne", "valueTwo" and "valueThree" are the respective field names.
OSConfig will not attempt to parse and validate the payload and payloadSizeBytes arguments. It is the responsability of the respective module to do this and return errors if appropriate. Modules must also validate the clientSession, componentName and objectName arguments against invalid values.
OSConfig will not attempt to parse and validate the payload and payloadSizeBytes arguments. It is the responsability of the respective Module to do this and return errors if appropriate. Modules must also validate the clientSession, componentName and objectName arguments against invalid values.
The maximum size of payload will be limited to the size specified via MmiOpen if that's a non-zero value (0 meaning unlimited). For OSConfig the payload of the PnP requests translated into MMI calls must be size limited (the size of the Twin, the Azure clone of the device, is limited) and a 4KB (4,096 bytes) limit was chosen but this may change in the future. Other platforms calling into the module may not have such a limitation. If needed, the module must enforce this maximum limit and reject MmiSet calls with payloadSizeBytes greater than this maximum value (unless than maximum value is 0, unlimited).
MmiSet may be called with the same payload several times. The module must be able to handle these calls either by reapplying the desired payload or detect when the respective desired configuration was already applied and in that case return MMI_OK without reapplying the payload and without logging errors.
MmiSet may be called with the same payload several times. The Module must be able to handle these calls either by reapplying the desired payload or detect when the respective desired configuration was already applied and in that case return MMI_OK without reapplying the payload and without logging errors.
## 4.5. MmiGet
MmiGet takes as input arguments a handle returned by MmiOpen, the name of the component, the name of the object, and returns via output arguments the reported object payload formatted as JSON (same format as for MmiSet), the size of value size and MMI_OK if success, NULL, 0 and an error code defined in errno.h if failure. On success, the caller requests the module to free the memory for the JSON payload with MmiFree.
MmiGet takes as input arguments a handle returned by MmiOpen, the name of the Component, the name of the Object, and returns via output arguments the reported Object payload formatted as JSON (same format as for MmiSet), the size of value size and MMI_OK if success, NULL, 0 and an error code defined in errno.h if failure. On success, the caller requests the module to free the memory for the JSON payload with MmiFree.
The objectName and payload must must match a reported MIM object from the componentName MIM component and present in the module's MIM. There can only be one single MIM object per MmiGet call. Modules must not return to MmiGet payloads that are not following their MIM precisely.
The objectName and payload must must match a reported MIM object from the componentName MIM Component and present in the module's MIM. There can only be one single MIM Object per MmiGet call. Modules must not return to MmiGet payloads that are not following their MIM precisely.
```C
int MmiGet(
@ -888,13 +783,13 @@ int MmiGet(
int* payloadSizeBytes);
```
Modules must validate on input the clientSession, componentName and objectName arguments against invalid values. For example, a module that implements a single MIM component with just one MIM reported object needs to validate that the requested component and object names are supported ones in order not to report their reported object data for a different component or object.
Modules must validate on input the clientSession, componentName and objectName arguments against invalid values. For example, a module that implements a single MIM Component with just one MIM reported Object needs to validate that the requested component and object names are supported ones in order not to report their reported Object data for a different Component or Object.
If the module has no data to report the module must still return valid payload that contains default values (such as empty strings). The module must not return MMI_OK with null payload or payload size of 0.
## 4.6. MmiFree
Frees memory allocated by module for the payload returned to MmiGetInfo and MmiGet:
Frees memory allocated by Module for the payload returned to MmiGetInfo and MmiGet:
```C
void MmiFree(MMI_JSON_STRING payload);
@ -902,7 +797,7 @@ void MmiFree(MMI_JSON_STRING payload);
# 5. Installation
Modules are installed as Dynamically Linked Shared Object libraries (.so) under /usr/lib/osconfig/. Each module reports its version at runtime via MmiGetInfo.
Modules are installed as Dynamically Linked Shared Object libraries (.so) under /usr/lib/osconfig/. Each Module reports its version at runtime via MmiGetInfo.
Reported MIM objects for the module are registered via the OSConfig general configuration file at /etc/osconfig/osconfig.json.
@ -937,9 +832,16 @@ Once the module's SO binary is copied to /usr/lib/osconfig/ and the reported obj
sudo systemctl kill -s SIGHUP osconfig.service
```
or simply:
```
sudo systemctl restart osconfig
```
# 6. Persistence, Retry, Wait
Modules may need to save data to files to persist across device restart. Any persisted files must be access-restricted to be written by root account only and must be deleted when no longer needed. The modules must not save to such files any personal, user or device, identifying data. The files may be encrypted (encryption is optional).
Modules may need to save data to files to persist across device restart. Any persisted files must be access-restricted to be written by root account only and must be deleted when no longer needed. The Modules must not save to such files any personal, user or device, identifying data. The files may be encrypted (encryption is optional).
A future version of OSConfig may provide modules with a Storage utility that could be used by Modules instead of their own persistence files.
@ -947,13 +849,13 @@ Modules may also need to retry failed OS configuration operations or wait. Persi
# 7. Orchestration
The OSConfig Platform will orchestrate the requests received from the various adapters (including the OSConfig PnP Agent) and management authorities into an ordered sequences of MMI calls that will be communicated to each module to execute.
OSConfig orchestrates the requests received from the various management authority adapters into an ordered sequences of MMI calls that are communicated to the respective modules to execute.
# 8. Versioning
Modules must report their version via MmiGetInfo. OSConfig will use this version to decide which module to load in case that multiple versions of the same module are present under /usr/lib/osconfig/.
Modules must report their version via MmiGetInfo. OSConfig will use this version to decide which Module to load in case that multiple versions of the same Module are present under /usr/lib/osconfig/.
Each module must be compliant with its Module Interface Model (MIM). New versions of the Modules keeping the same MIM must increment their version numbers. When the MIM needs to change, the component names can be changed and/or components with new names be added.
Each Module must be compliant with its Module Interface Model (MIM). New versions of the modules keeping the same MIM must increment their version numbers. When the MIM needs to change, the component names can be changed and/or components with new names be added.
# 9. Packaging
@ -965,9 +867,9 @@ Each module is responsible of its own telemetry. The modules must not emit any p
# 11. Logging
Modules can log via the logging library provided by OSConfig to log files under /var/log/ where all other OSConfig logs are found. To help the device administrator collect all OSCOnfig logs the module should use a log name that matches the rest of the OSConfig logs: ```/var/log/osconfig_*modulename*.log```.
Modules should log via the logging library provided by OSConfig to log files under /var/log/ where all other OSConfig logs are found. To help the device administrator collect all OSCOnfig logs the module should use a log name that matches the rest of the OSConfig logs: ```/var/log/osconfig_*modulename*.log```.
Modules can log MIM component names and MIM object names. Modules must not log MIM setting values unless full logging is enabled. In general, the modules must not log any personal, user or device, identifying (PII) data.
Modules can log MIM component names and MIM object names. Modules must not log MIM Setting values unless full logging is enabled. In general, the modules must not log any personal, user or device, identifying (PII) data.
## 11.1. Logging library
@ -1007,13 +909,13 @@ sudo systemctl kill -s SIGHUP osconfig.service
Modules can log as necessary informational and error traces during all MMI calls except MmiGet: MmiOpen, MmiClose, MmiFree and MmiSet.
During MmiGet modules should only log in case of error (no information traces) and only when full logging is enabled (when IsFullLoggingEnabled returns true). This is because MmGet is periodically called and same traces can fill the module log, obscuring other traces.
During MmiGet the modules should only log in case of error (no information traces) and only when full logging is enabled (when IsFullLoggingEnabled returns true). This is because MmGet is periodically called and same traces can fill the module's log, obscuring other traces.
# 12. Handling multiple client sessions
OSConfig Modules can be invoked to execute multiple sessions in parallel. Each MmiOpen opens a new session with that client. Each MmiClose closes one session (other sessions may remain open). At any time during a module instance life there may be multiple clients connected to the module and there may be multiple open sessions from each client.
The code for a module can be split into a static library and a shared object (SO) dynamic library. This section describes one optional pattern which can help a module to support multiple sessions.
The code for a Module can be split into a static library and a shared object (SO) dynamic library. This section describes one optional pattern which can help a module to support multiple sessions.
## 12.1. Module Static Library
@ -1094,7 +996,7 @@ The Module Test Recipe is fed into the OSConfig's ModulesTest utility to be exec
# 14.1. Introduction
Modules can work invoked by any client over their MMI. When a module works with the rest of the OSConfig stack and in particular with the OSConfig PnP Agent, in order to allow remote management over Azure and IoT Hub, the module needs to have one or more PnP interfaces (one for each MIM component) published via the public [OSConfig DTDL Model](https://github.com/Azure/iot-plugandplay-models/tree/main/dtmi/osconfig). This allows applications such as [Azure IoT Explorer](https://github.com/Azure/azure-iot-explorer) to display personalized user interface to help the operator request desired and reported configuration with the respective module at the other end.
Modules can work invoked by any client over their MMI. When a module works with the rest of the OSConfig stack and in particular with the OSConfig PnP Agent, in order to allow remote management over Azure and IoT Hub, the module needs to have one or more PnP interfaces (one for each MIM component) published via the public [OSConfig DTDL Model](https://github.com/Azure/iot-plugandplay-models/tree/main/dtmi/osconfig). This allows applications such as [Azure IoT Explorer](https://github.com/Azure/azure-iot-explorer) to display personalized user interface to help the operator request desired and reported configuration with the respective Module at the other end.
# 14.2. Translating MIM to DTDL
@ -1103,11 +1005,11 @@ MIM can be directly translated to [Digital Twins Definition Language (DTDL)](htt
MIM | DTDL | Notes
-----|-----|-----
MIM component | PnP interface | Each MIM component can be translated to a [PnP interface](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#interface). For example the [CommandRunner MIM component](../src/modules/mim/commandrunner.json) component is translated to the [CommandRunner PnP interface](https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/osconfig/commandrunner-2.json).
Complex MIM object (containing multiple MIM settings) | Complex PnP property of object type | Each complex MIM object can be translated to a complex [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property) with the same name. For example CommandRunner.commandArguments in [CommandRunner MIM](../src/modules/mim/commandrunner.json) and [CommandRunner PnP interface](https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/osconfig/commandrunner-2.json).
Simple MIM object (containing a single MIM setting) | Simple PnP property | Each simple MIM object can be translated to a simple [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property) with the same name. For example Tpm.TpmVersion in [Tpm MIM](../src/modules/mim/tpm.json) and [Tpm PnP interface](https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/osconfig/tpm-1.json).
Desired MIM object | Writeable (also called read-write) PnP property | Each desired MIM object can be translated to a writeable (read-write) [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property).
Reported MIM object | Read-only PnP property | Each desired MIM object can be translated to a read-only [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property).
MIM setting | PnP property value | Each MIM setting can be translated to a PnP property value with the same name and value type.
Complex MIM Object (containing multiple MIM settings) | Complex PnP property of object type | Each complex MIM object can be translated to a complex [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property) with the same name. For example CommandRunner.commandArguments in [CommandRunner MIM](../src/modules/mim/commandrunner.json) and [CommandRunner PnP interface](https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/osconfig/commandrunner-2.json).
Simple MIM Object (containing a single MIM setting) | Simple PnP property | Each simple MIM object can be translated to a simple [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property) with the same name. For example Tpm.TpmVersion in [Tpm MIM](../src/modules/mim/tpm.json) and [Tpm PnP interface](https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/osconfig/tpm-1.json).
Desired MIM Object | Writeable (also called read-write) PnP property | Each desired MIM object can be translated to a writeable (read-write) [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property).
Reported MIM Object | Read-only PnP property | Each desired MIM object can be translated to a read-only [PnP property](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#property).
MIM Setting | PnP property value | Each MIM setting can be translated to a PnP property value with the same name and value type.
MIM enum | DTDL enum | MIM enums of integers (for MIM Settings) can be translated to [DTDL enums](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#enum) of the same type.
MIM array | DTDL array | MIM arrays of strings and integers (for MIM Settings) or objects (for MIM Objects) can be translated to [DTDL arrays](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#array) of the same types.
MIM map | DTDL map | MIM maps of string and integers (for MIM Settings) can be translated to [DTDL maps](https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md#map) of the same types.

Просмотреть файл

@ -1,12 +1,11 @@
Azure Device OS Configuration (OSConfig) - Roadmap
==================================================
Author: [MariusNi](https://github.com/MariusNi)
# 1. Introduction
Azure OSConfig is a modular security configuration stack for Linux Edge devices. OSConfig supports multi-authority device management over Azure and Azure Portal/CLI, GitOps, as well as local management.
This document describes the development roadmap of the OSConfig project.
This document describes the development roadmap of the Azure OSConfig project.
During any of these stages OSConfig can be extended by adding new [OSConfig Management Modules](modules.md).
@ -44,8 +43,7 @@ The modules become detached Shared Object (.so) libraries, partners can develop
OSConfig continues to run as a monolithic process and can accept requests from agentless management authorities like OOBE. More module scenarios are enabled (TPM, etc.)
- The Watcher is introduced. OSConfig can accept requests from Local Management Authorities (like OOBE).
- The Orchestrator is introduced to orchestrate input from multiple authorities.
- The RC/DC Watcher is introduced. OSConfig can accept requests from Local Management Authorities (like OOBE).
- Modules continue to work unchanged, same as in previous release, as Dynamically Loaded Shared Object libraries exporting the MMI C API.
- The Modules Manager is introduced and loads the module libraries in-proc.
- Tpm and other new Management Modules can appear (not shown in diagram).
@ -55,7 +53,7 @@ OSConfig continues to run as a monolithic process and can accept requests from a
# 4. Detached Platform (codename Copper)
The concept of Adapters is introduced. The Management Platform runs in its own process. OSConfig can accept request from both local and remote management authorities.
The concept of OSConfig Adapters is introduced. The Management Platform runs in its own daemon process. OSConfig can accept request from both local and remote management authorities.
- The OSConfig separates the Agent and the Management Platform into two separate daemon processes.
- The IPC REST API over Unix Domain Sockets (UDS) and HTTP for MPI is introduced.
@ -67,16 +65,28 @@ The concept of Adapters is introduced. The Management Platform runs in its own p
# 5. GitOps, Azure Policy, Security Baseline (codename Germanium)
The Universal NRP and the Security Modules can be used to audit and remediate the Linux Security Baseline over Azure Policy and AutoManage Machine Configuration.
The Universal NRP and the SecurityBaseline Module can be used to audit and remediate the Linux Security Baseline over Azure Policy and AutoManage Machine Configuration.
- A new OSConfig Universal NRP is added for Azure Policy and Machine Configuration.
- A new GitOps DC Watcher is added for accepting desired configuration from a Git repository.
- A new OSConfig Universal NRP is added for Azure Policy and Machine Configuration.
- A new SecurityModule module implements the Azure Security Baseline for Linux.
- Modules continue to work unchanged, same as in previous releases, as Dynamically Loaded Shared Object libraries exporting the MMI C API. The Platform loads these libraries in-proc.
<img src="assets/7_osconfig_ge.png" alt="Germanium" width=70%/>
# 6. SSH Posture Control, ASB v2, OSConfig for MC, IoT Hub becomes optional (codename Dilithium)
- A new Azure Policy for SSH Posture Control is published, running via the OSConfig Universal NRP.
- A new Azure Security Baseline (ASB) for Linux v2 implementation is added into the separate ASB libraries.
- The Universal NRP and the SecurityBaseline module both link to the same ASB libraries.
- The concept of 'OSConfig for MC'is introduced, where the Universal NRP implements this speacialized, targeted OSConfig, inside of a single NRP.
- The RC/DC channel becomes by default enabled, while the IoT Hub channel becomes by default disabled.
- The SecurityBaseline module works with the RC/DC Watcher for executing ASB v2 when invoked by local managamenet authorities.
- Modules continue to work as Dynamically Loaded Shared Object libraries exporting the MMI C API. The Platform loads these libraries in-proc.
<img src="assets/8_osconfig_di.png" alt="Dilithium" width=70%/>
# 6. Isolated Modules - the current North Star (codename Krypton)
# 7. Isolated Modules - the current North Star (codename Krypton)
Main target scenario: OSConfig Management Modules run isolated in their own processes, Azure Policy and GitHub

Просмотреть файл

@ -0,0 +1,145 @@
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:osconfig:configuration;4",
"@type": "Interface",
"displayName": "Configuration",
"description": "Provides functionality to remotely manage OSConfig configuration on device",
"contents": [
{
"@type": "Property",
"name": "desiredRefreshInterval",
"schema":"integer",
"writable": true
},
{
"@type": "Property",
"name": "desiredLocalManagementEnabled",
"schema":"boolean",
"writable": true
},
{
"@type": "Property",
"name": "desiredFullLoggingEnabled",
"schema":"boolean",
"writable": true
},
{
"@type": "Property",
"name": "desiredCommandLoggingEnabled",
"schema":"boolean",
"writable": true
},
{
"@type": "Property",
"name": "desiredIotHubManagementEnabled",
"schema":"boolean",
"writable": true
},
{
"@type": "Property",
"name": "desiredIotHubProtocol",
"schema": {
"@type": "Enum",
"valueSchema": "string",
"enumValues": [
{
"name": "auto",
"enumValue": "auto"
},
{
"name": "mqtt",
"enumValue": "mqtt"
},
{
"name": "mqttWebSocket",
"enumValue": "mqttWebSocket"
}
]
},
"writable": true
},
{
"@type": "Property",
"name": "desiredGitManagementEnabled",
"schema": "boolean",
"writable": true
},
{
"@type": "Property",
"name": "desiredGitBranch",
"schema": "string",
"writable": true
},
{
"@type": "Property",
"name": "modelVersion",
"schema":"integer",
"writable": false
},
{
"@type": "Property",
"name": "refreshInterval",
"schema":"integer",
"writable": false
},
{
"@type": "Property",
"name": "localManagementEnabled",
"schema":"boolean",
"writable": false
},
{
"@type": "Property",
"name": "fullLoggingEnabled",
"schema":"boolean",
"writable": false
},
{
"@type": "Property",
"name": "commandLoggingEnabled",
"schema":"boolean",
"writable": false
},
{
"@type": "Property",
"name": "iotHubManagementEnabled",
"schema":"boolean",
"writable": false
},
{
"@type": "Property",
"name": "iotHubProtocol",
"schema": {
"@type": "Enum",
"valueSchema": "string",
"enumValues": [
{
"name": "auto",
"enumValue": "auto"
},
{
"name": "mqtt",
"enumValue": "mqtt"
},
{
"name": "mqttWebSocket",
"enumValue": "mqttWebSocket"
}
]
},
"writable": false
},
{
"@type": "Property",
"name": "gitManagementEnabled",
"schema": "boolean",
"writable": false
},
{
"@type": "Property",
"name": "gitBranch",
"schema": "string",
"writable": false
}
]
}

Просмотреть файл

@ -0,0 +1,33 @@
{
"@context": "dtmi:dtdl:context;2",
"@id": "dtmi:osconfig:deviceosconfiguration;20",
"@type": "Interface",
"displayName": "Device OS Configuration",
"contents": [
{
"schema": "dtmi:osconfig:adhs;1",
"@type": "Component",
"name": "Adhs"
},
{
"schema": "dtmi:osconfig:commandrunner;3",
"@type": "Component",
"name": "CommandRunner"
},
{
"schema": "dtmi:osconfig:configuration;4",
"@type": "Component",
"name": "Configuration"
},
{
"schema": "dtmi:osconfig:deviceinfo;5",
"@type": "Component",
"name": "DeviceInfo"
},
{
"schema": "dtmi:osconfig:securitybaseline;2",
"@type": "Component",
"name": "SecurityBaseline"
}
]
}

Просмотреть файл

@ -0,0 +1,898 @@
{
"properties": {
"displayName": "Azure Security Baseline for Linux policy",
"policyType": "Custom",
"mode": "Indexed",
"description": "This policy audits the Azure Security Baseline for Linux",
"metadata": {
"category": "Guest Configuration",
"version": "1.0.0.0",
"requiredProviders": [
"Microsoft.GuestConfiguration"
],
"guestConfiguration": {
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSecurityBaselinePolicy.zip",
"contentHash": "DF222B0DB9E2A4A5AFC19475130BC2097E22158FC4B11028756775FFCDF1A62B",
"configurationParameter": {
"accessPermissionsForSshdConfig": "Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue",
"ignoreHosts": "Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue",
"logLevel": "Ensure that the SSH LogLevel is configured;DesiredObjectValue",
"maxAuthTries": "Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue",
"allowUsers": "Ensure that the allowed users for SSH access are configured;DesiredObjectValue",
"denyUsers": "Ensure that the denied users for SSH are configured;DesiredObjectValue",
"allowGroups": "Ensure that the allowed groups for SSH are configured;DesiredObjectValue",
"denyGroups": "Ensure that the denied groups for SSH are configured;DesiredObjectValue",
"hostBasedAuthentication": "Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue",
"permitRootLogin": "Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue",
"permitEmptyPasswords": "Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue",
"clientAliveCountMax": "Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue",
"clientAliveInterval": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"loginGraceTime": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"messageAuthenticationCodeAlgorithms": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"banner": "Ensure that the SSH warning banner is configured;DesiredObjectValue",
"permitUserEnvironment": "Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue",
"ciphers": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue"
}
}
},
"parameters": {
"IncludeArcMachines": {
"type": "string",
"metadata": {
"displayName": "Include Arc connected machines",
"description": "By selecting this option, you agree to be charged monthly per Arc connected machine.",
"portalReview": "true"
},
"allowedValues": [
"true",
"false"
],
"defaultValue": "false"
},
"effect": {
"type": "string",
"metadata": {
"displayName": "Effect",
"description": "Configures between remediation (DeployIfNotExists), audit-only (AuditIfNotExists), or disabled (Disabled) policy execution mode. Default is AuditIfNotExists"
},
"allowedValues": [
"DeployIfNotExists",
"AuditIfNotExists",
"Disabled"
],
"defaultValue": "AuditIfNotExists"
},
"accessPermissionsForSshdConfig": {
"type": "string",
"metadata": {
"displayName": "Access permissions for sshd_config",
"description": "File access permissions for /etc/ssh/sshd_config. Default is '600'"
},
"defaultValue": "600"
},
"ignoreHosts": {
"type": "string",
"metadata": {
"displayName": "Ignore rhosts and shosts",
"description": "Whether to ignore per-user .rhosts and .shosts files during HostbasedAuthentication. Default is 'yes'"
},
"defaultValue": "yes"
},
"logLevel": {
"type": "string",
"metadata": {
"displayName": "Log verbosity level",
"description": "The verbosity level for the sshd logging. Default is 'INFO'"
},
"defaultValue": "INFO"
},
"maxAuthTries": {
"type": "string",
"metadata": {
"displayName": "Maximum number of authentication attempts",
"description": "The maximum number of authentication attempts permitted per connection. Default is '6'"
},
"defaultValue": "6"
},
"allowUsers": {
"type": "string",
"metadata": {
"displayName": "Allowed users for SSH",
"description": "List of users to be allowed to connect with SSH. Default is all authenticated users ('*@*')"
},
"defaultValue": "*@*"
},
"denyUsers": {
"type": "string",
"metadata": {
"displayName": "Denied users for SSH",
"description": "List of users to be denied to connect with SSH. Default is 'root'"
},
"defaultValue": "root"
},
"allowGroups": {
"type": "string",
"metadata": {
"displayName": "Allowed groups for SSH",
"description": "List of user groups to be allowed to connect with SSH. Default is all groups ('*')"
},
"defaultValue": "*"
},
"denyGroups": {
"type": "string",
"metadata": {
"displayName": "Denied groups for SSH",
"description": "List of user groups to be denied to connect with SSH. Default is 'root'"
},
"defaultValue": "root"
},
"hostBasedAuthentication": {
"type": "string",
"metadata": {
"displayName": "Host-based authentication",
"description": "Host-based authentication. Default is 'no'"
},
"defaultValue": "no"
},
"permitRootLogin": {
"type": "string",
"metadata": {
"displayName": "Whether root can log in using ssh",
"description": "Whether root can log in using ssh. Default is 'no'"
},
"defaultValue": "no"
},
"permitEmptyPasswords": {
"type": "string",
"metadata": {
"displayName": "Whether the server allows login to accounts with empty password strings",
"description": "Whether the server allows login to accounts with empty password strings. Default is 'no'"
},
"defaultValue": "no"
},
"clientAliveCountMax": {
"type": "string",
"metadata": {
"displayName": "The number of client alive messages which may be sent without sshd receiving any messages back from the client",
"description": "The number of client alive messages which may be sent without sshd receiving any messages back from the client. Default is '0'"
},
"defaultValue": "0"
},
"clientAliveInterval": {
"type": "string",
"metadata": {
"displayName": "Timeout interval in seconds after which if no data has been received from the client, sshd will send a message to request a response",
"description": "Timeout interval in seconds after which if no data has been received from the client, sshd will send a message to request a response. Default is 1 hour ('3600')"
},
"defaultValue": "3600"
},
"loginGraceTime": {
"type": "string",
"metadata": {
"displayName": "The time in seconds after which the server disconnects if the user has not successfully logged in",
"description": "The time in seconds after which the server disconnects if the user has not successfully logged in. Default is 1 minute ('60')"
},
"defaultValue": "60"
},
"messageAuthenticationCodeAlgorithms": {
"type": "string",
"metadata": {
"displayName": "The list of available message authentication code (MAC) algorithms",
"description": "The list of available message authentication code (MAC) algorithms. Default is 'hmac-sha2-256,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-512-etm@openssh.com'"
},
"defaultValue": "hmac-sha2-256,hmac-sha2-256-etm@openssh.com,hmac-sha2-512,hmac-sha2-512-etm@openssh.com"
},
"banner": {
"type": "string",
"metadata": {
"displayName": "The contents of the banner file that is sent to the remote user before authentication is allowed",
"description": "The contents of the banner file that is sent to the remote user before authentication is allowed. Default is '#######################################################################\n\nAuthorized access only!\n\nIf you are not authorized to access or use this system, disconnect now!\n\n#######################################################################\n'"
},
"defaultValue": "#######################################################################\n\nAuthorized access only!\n\nIf you are not authorized to access or use this system, disconnect now!\n\n#######################################################################\n"
},
"permitUserEnvironment": {
"type": "string",
"metadata": {
"displayName": "Whether ~/.ssh/environment and environment= options in ~/.ssh/authorized_keys are processed by sshd",
"description": "Whether ~/.ssh/environment and environment= options in ~/.ssh/authorized_keys are processed by sshd. Default is 'no'"
},
"defaultValue": "no"
},
"ciphers": {
"type": "string",
"metadata": {
"displayName": "The list of allowed ciphers",
"description": "The list of allowed ciphers. Default is 'aes128-ctr,aes192-ctr,aes256-ctr'"
},
"defaultValue": "aes128-ctr,aes192-ctr,aes256-ctr"
}
},
"policyRule": {
"if": {
"anyOf": [
{
"allOf": [
{
"anyOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachineScaleSets"
}
]
},
{
"field": "tags['aks-managed-orchestrator']",
"exists": "false"
},
{
"field": "tags['aks-managed-poolName']",
"exists": "false"
},
{
"anyOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"in": [
"microsoft-aks",
"qubole-inc",
"datastax",
"couchbase",
"scalegrid",
"checkpoint",
"paloaltonetworks",
"debian",
"credativ"
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "OpenLogic"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "6*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "Oracle"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "6*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "RedHat"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "6*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "center-for-internet-security-inc"
},
{
"field": "Microsoft.Compute/imageOffer",
"notLike": "cis-windows*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "Suse"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "11*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "Canonical"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "12*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "microsoft-dsvm"
},
{
"field": "Microsoft.Compute/imageOffer",
"notLike": "dsvm-win*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "cloudera"
},
{
"field": "Microsoft.Compute/imageSKU",
"notLike": "6*"
}
]
},
{
"allOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"equals": "microsoft-ads"
},
{
"field": "Microsoft.Compute/imageOffer",
"like": "linux*"
}
]
},
{
"allOf": [
{
"anyOf": [
{
"field": "Microsoft.Compute/virtualMachines/osProfile.linuxConfiguration",
"exists": true
},
{
"field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
"like": "Linux*"
},
{
"field": "Microsoft.Compute/virtualMachineScaleSets/osProfile.linuxConfiguration",
"exists": true
},
{
"field": "Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.osDisk.osType",
"like": "Linux*"
}
]
},
{
"anyOf": [
{
"field": "Microsoft.Compute/imagePublisher",
"exists": false
},
{
"field": "Microsoft.Compute/imagePublisher",
"notIn": [
"OpenLogic",
"RedHat",
"credativ",
"Suse",
"Canonical",
"microsoft-dsvm",
"cloudera",
"microsoft-ads",
"center-for-internet-security-inc",
"Oracle",
"AzureDatabricks",
"azureopenshift"
]
}
]
}
]
}
]
}
]
},
{
"allOf": [
{
"value": "[parameters('IncludeArcMachines')]",
"equals": true
},
{
"anyOf": [
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.HybridCompute/machines"
},
{
"field": "Microsoft.HybridCompute/imageOffer",
"like": "linux*"
}
]
},
{
"allOf": [
{
"field": "type",
"equals": "Microsoft.ConnectedVMwarevSphere/virtualMachines"
},
{
"field": "Microsoft.ConnectedVMwarevSphere/virtualMachines/osProfile.osType",
"like": "linux*"
}
]
}
]
}
]
}
]
},
"then": {
"effect": "[parameters('effect')]",
"details": {
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/088ab73d-1256-47ae-bea9-9de8e7131f31"
],
"type": "Microsoft.GuestConfiguration/guestConfigurationAssignments",
"name": "[concat('OsConfigPolicy$pid', uniqueString(policy().assignmentId, policy().definitionReferenceId))]",
"existenceCondition": {
"allOf": [
{
"field": "Microsoft.GuestConfiguration/guestConfigurationAssignments/complianceStatus",
"equals": "Compliant"
},
{
"field": "Microsoft.GuestConfiguration/guestConfigurationAssignments/parameterHash",
"equals": "[base64(concat('Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue', '=', parameters('accessPermissionsForSshdConfig'), ',', 'Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue', '=', parameters('ignoreHosts'), ',', 'Ensure that the SSH LogLevel is configured;DesiredObjectValue', '=', parameters('logLevel'), ',', 'Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue', '=', parameters('maxAuthTries'), ',', 'Ensure that the allowed users for SSH access are configured;DesiredObjectValue', '=', parameters('allowUsers'), ',', 'Ensure that the denied users for SSH are configured;DesiredObjectValue', '=', parameters('denyUsers'), ',', 'Ensure that the allowed groups for SSH are configured;DesiredObjectValue', '=', parameters('allowGroups'), ',', 'Ensure that the denied groups for SSH are configured;DesiredObjectValue', '=', parameters('denyGroups'), ',', 'Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue', '=', parameters('hostBasedAuthentication'), ',', 'Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue', '=', parameters('permitRootLogin'), ',', 'Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue', '=', parameters('permitEmptyPasswords'), ',', 'Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue', '=', parameters('clientAliveCountMax'), ',', 'Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue', '=', parameters('clientAliveInterval'), ',', 'Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue', '=', parameters('loginGraceTime'), ',', 'Ensure that only approved MAC algorithms are used;DesiredObjectValue', '=', parameters('messageAuthenticationCodeAlgorithms'), ',', 'Ensure that the SSH warning banner is configured;DesiredObjectValue', '=', parameters('banner'), ',', 'Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue', '=', parameters('permitUserEnvironment'), ',', 'Ensure that appropriate ciphers are used for SSH;DesiredObjectValue', '=', parameters('ciphers')))]"
}
]
},
"deployment": {
"properties": {
"mode": "incremental",
"parameters": {
"vmName": {
"value": "[field('name')]"
},
"location": {
"value": "[field('location')]"
},
"type": {
"value": "[field('type')]"
},
"assignmentName": {
"value": "[concat('OsConfigPolicy$pid', uniqueString(policy().assignmentId, policy().definitionReferenceId))]"
},
"accessPermissionsForSshdConfig": {
"value": "[parameters('accessPermissionsForSshdConfig')]"
},
"ignoreHosts": {
"value": "[parameters('ignoreHosts')]"
},
"logLevel": {
"value": "[parameters('logLevel')]"
},
"maxAuthTries": {
"value": "[parameters('maxAuthTries')]"
},
"allowUsers": {
"value": "[parameters('allowUsers')]"
},
"denyUsers": {
"value": "[parameters('denyUsers')]"
},
"allowGroups": {
"value": "[parameters('allowGroups')]"
},
"denyGroups": {
"value": "[parameters('denyGroups')]"
},
"hostBasedAuthentication": {
"value": "[parameters('hostBasedAuthentication')]"
},
"permitRootLogin": {
"value": "[parameters('permitRootLogin')]"
},
"permitEmptyPasswords": {
"value": "[parameters('permitEmptyPasswords')]"
},
"clientAliveCountMax": {
"value": "[parameters('clientAliveCountMax')]"
},
"clientAliveInterval": {
"value": "[parameters('clientAliveInterval')]"
},
"loginGraceTime": {
"value": "[parameters('loginGraceTime')]"
},
"messageAuthenticationCodeAlgorithms": {
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
},
"banner": {
"value": "[parameters('banner')]"
},
"permitUserEnvironment": {
"value": "[parameters('permitUserEnvironment')]"
},
"ciphers": {
"value": "[parameters('ciphers')]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vmName": {
"type": "string"
},
"location": {
"type": "string"
},
"type": {
"type": "string"
},
"assignmentName": {
"type": "string"
},
"accessPermissionsForSshdConfig": {
"type": "string"
},
"ignoreHosts": {
"type": "string"
},
"logLevel": {
"type": "string"
},
"maxAuthTries": {
"type": "string"
},
"allowUsers": {
"type": "string"
},
"denyUsers": {
"type": "string"
},
"allowGroups": {
"type": "string"
},
"denyGroups": {
"type": "string"
},
"hostBasedAuthentication": {
"type": "string"
},
"permitRootLogin": {
"type": "string"
},
"permitEmptyPasswords": {
"type": "string"
},
"clientAliveCountMax": {
"type": "string"
},
"clientAliveInterval": {
"type": "string"
},
"loginGraceTime": {
"type": "string"
},
"messageAuthenticationCodeAlgorithms": {
"type": "string"
},
"banner": {
"type": "string"
},
"permitUserEnvironment": {
"type": "string"
},
"ciphers": {
"type": "string"
}
},
"resources": [
{
"condition": "[equals(toLower(parameters('type')), toLower('Microsoft.Compute/virtualMachines'))]",
"apiVersion": "2018-11-20",
"type": "Microsoft.Compute/virtualMachines/providers/guestConfigurationAssignments",
"name": "[concat(parameters('vmName'), '/Microsoft.GuestConfiguration/', parameters('assignmentName'))]",
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSecurityBaselinePolicy.zip",
"contentHash": "DF222B0DB9E2A4A5AFC19475130BC2097E22158FC4B11028756775FFCDF1A62B",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
"name": "Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue",
"value": "[parameters('accessPermissionsForSshdConfig')]"
},
{
"name": "Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue",
"value": "[parameters('ignoreHosts')]"
},
{
"name": "Ensure that the SSH LogLevel is configured;DesiredObjectValue",
"value": "[parameters('logLevel')]"
},
{
"name": "Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue",
"value": "[parameters('maxAuthTries')]"
},
{
"name": "Ensure that the allowed users for SSH access are configured;DesiredObjectValue",
"value": "[parameters('allowUsers')]"
},
{
"name": "Ensure that the denied users for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyUsers')]"
},
{
"name": "Ensure that the allowed groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('allowGroups')]"
},
{
"name": "Ensure that the denied groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyGroups')]"
},
{
"name": "Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue",
"value": "[parameters('hostBasedAuthentication')]"
},
{
"name": "Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue",
"value": "[parameters('permitRootLogin')]"
},
{
"name": "Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue",
"value": "[parameters('permitEmptyPasswords')]"
},
{
"name": "Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue",
"value": "[parameters('clientAliveCountMax')]"
},
{
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
},
{
"name": "Ensure that the SSH warning banner is configured;DesiredObjectValue",
"value": "[parameters('banner')]"
},
{
"name": "Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue",
"value": "[parameters('permitUserEnvironment')]"
},
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
}
]
}
}
},
{
"condition": "[equals(toLower(parameters('type')), toLower('Microsoft.HybridCompute/machines'))]",
"apiVersion": "2018-11-20",
"type": "Microsoft.HybridCompute/machines/providers/guestConfigurationAssignments",
"name": "[concat(parameters('vmName'), '/Microsoft.GuestConfiguration/', parameters('assignmentName'))]",
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSecurityBaselinePolicy.zip",
"contentHash": "DF222B0DB9E2A4A5AFC19475130BC2097E22158FC4B11028756775FFCDF1A62B",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
"name": "Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue",
"value": "[parameters('accessPermissionsForSshdConfig')]"
},
{
"name": "Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue",
"value": "[parameters('ignoreHosts')]"
},
{
"name": "Ensure that the SSH LogLevel is configured;DesiredObjectValue",
"value": "[parameters('logLevel')]"
},
{
"name": "Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue",
"value": "[parameters('maxAuthTries')]"
},
{
"name": "Ensure that the allowed users for SSH access are configured;DesiredObjectValue",
"value": "[parameters('allowUsers')]"
},
{
"name": "Ensure that the denied users for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyUsers')]"
},
{
"name": "Ensure that the allowed groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('allowGroups')]"
},
{
"name": "Ensure that the denied groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyGroups')]"
},
{
"name": "Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue",
"value": "[parameters('hostBasedAuthentication')]"
},
{
"name": "Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue",
"value": "[parameters('permitRootLogin')]"
},
{
"name": "Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue",
"value": "[parameters('permitEmptyPasswords')]"
},
{
"name": "Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue",
"value": "[parameters('clientAliveCountMax')]"
},
{
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
},
{
"name": "Ensure that the SSH warning banner is configured;DesiredObjectValue",
"value": "[parameters('banner')]"
},
{
"name": "Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue",
"value": "[parameters('permitUserEnvironment')]"
},
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
}
]
}
}
},
{
"condition": "[equals(toLower(parameters('type')), toLower('Microsoft.Compute/virtualMachineScaleSets'))]",
"apiVersion": "2018-11-20",
"type": "Microsoft.Compute/virtualMachineScaleSets/providers/guestConfigurationAssignments",
"name": "[concat(parameters('vmName'), '/Microsoft.GuestConfiguration/', parameters('assignmentName'))]",
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSecurityBaselinePolicy.zip",
"contentHash": "DF222B0DB9E2A4A5AFC19475130BC2097E22158FC4B11028756775FFCDF1A62B",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
"name": "Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue",
"value": "[parameters('accessPermissionsForSshdConfig')]"
},
{
"name": "Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue",
"value": "[parameters('ignoreHosts')]"
},
{
"name": "Ensure that the SSH LogLevel is configured;DesiredObjectValue",
"value": "[parameters('logLevel')]"
},
{
"name": "Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue",
"value": "[parameters('maxAuthTries')]"
},
{
"name": "Ensure that the allowed users for SSH access are configured;DesiredObjectValue",
"value": "[parameters('allowUsers')]"
},
{
"name": "Ensure that the denied users for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyUsers')]"
},
{
"name": "Ensure that the allowed groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('allowGroups')]"
},
{
"name": "Ensure that the denied groups for SSH are configured;DesiredObjectValue",
"value": "[parameters('denyGroups')]"
},
{
"name": "Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue",
"value": "[parameters('hostBasedAuthentication')]"
},
{
"name": "Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue",
"value": "[parameters('permitRootLogin')]"
},
{
"name": "Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue",
"value": "[parameters('permitEmptyPasswords')]"
},
{
"name": "Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue",
"value": "[parameters('clientAliveCountMax')]"
},
{
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
},
{
"name": "Ensure that the SSH warning banner is configured;DesiredObjectValue",
"value": "[parameters('banner')]"
},
{
"name": "Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue",
"value": "[parameters('permitUserEnvironment')]"
},
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
}
]
}
}
}
]
}
}
}
}
}
}
},
"name": "6d76c6e7-0670-4931-8741-16cff3fbfa3e"
}

Просмотреть файл

@ -11,8 +11,11 @@
"Microsoft.GuestConfiguration"
],
"guestConfiguration": {
"name": "LinuxSshServerSecurityBaseline",
"version": "1.*",
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSshServerSecurityBaselinePolicy.zip",
"contentHash": "18F07A4F4F2E76DADEA169B41829A8DADBD2024BBAD374B3C5A20CEB4ECD15BE",
"configurationParameter": {
"accessPermissionsForSshdConfig": "Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue",
"ignoreHosts": "Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue",
@ -27,11 +30,11 @@
"permitEmptyPasswords": "Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue",
"clientAliveCountMax": "Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue",
"clientAliveInterval": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"loginGraceTime": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"messageAuthenticationCodeAlgorithms": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"banner": "Ensure that the SSH warning banner is configured;DesiredObjectValue",
"permitUserEnvironment": "Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue",
"ciphers": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"port": "Ensure that the SSH port is configured;DesiredObjectValue"
"ciphers": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue"
}
}
},
@ -53,7 +56,7 @@
"type": "string",
"metadata": {
"displayName": "Effect",
"description": "Configures between remediation (DeployIfNotExists), audit-only (AuditIfNotExists), or disabled (Disabled) policy execution mode. Default is AuditIfNotExists"
"description": "Configures between remediation (DeployIfNotExists) or audit-only (AuditIfNotExists) policy execution mode. Default is AuditIfNotExists"
},
"allowedValues": [
"DeployIfNotExists",
@ -165,6 +168,14 @@
},
"defaultValue": "3600"
},
"loginGraceTime": {
"type": "string",
"metadata": {
"displayName": "The time in seconds after which the server disconnects if the user has not successfully logged in",
"description": "The time in seconds after which the server disconnects if the user has not successfully logged in. Default is 1 minute ('60')"
},
"defaultValue": "60"
},
"messageAuthenticationCodeAlgorithms": {
"type": "string",
"metadata": {
@ -196,14 +207,6 @@
"description": "The list of allowed ciphers. Default is 'aes128-ctr,aes192-ctr,aes256-ctr'"
},
"defaultValue": "aes128-ctr,aes192-ctr,aes256-ctr"
},
"port": {
"type": "string",
"metadata": {
"displayName": "The SSH port",
"description": "The SSH port. Default is '22'"
},
"defaultValue": "22"
}
},
"policyRule": {
@ -462,7 +465,7 @@
},
{
"field": "Microsoft.GuestConfiguration/guestConfigurationAssignments/parameterHash",
"equals": "[base64(concat('Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue', '=', parameters('accessPermissionsForSshdConfig'), ',', 'Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue', '=', parameters('ignoreHosts'), ',', 'Ensure that the SSH LogLevel is configured;DesiredObjectValue', '=', parameters('logLevel'), ',', 'Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue', '=', parameters('maxAuthTries'), ',', 'Ensure that the allowed users for SSH access are configured;DesiredObjectValue', '=', parameters('allowUsers'), ',', 'Ensure that the denied users for SSH are configured;DesiredObjectValue', '=', parameters('denyUsers'), ',', 'Ensure that the allowed groups for SSH are configured;DesiredObjectValue', '=', parameters('allowGroups'), ',', 'Ensure that the denied groups for SSH are configured;DesiredObjectValue', '=', parameters('denyGroups'), ',', 'Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue', '=', parameters('hostBasedAuthentication'), ',', 'Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue', '=', parameters('permitRootLogin'), ',', 'Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue', '=', parameters('permitEmptyPasswords'), ',', 'Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue', '=', parameters('clientAliveCountMax'), ',', 'Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue', '=', parameters('clientAliveInterval'), ',', 'Ensure that only approved MAC algorithms are used;DesiredObjectValue', '=', parameters('messageAuthenticationCodeAlgorithms'), ',', 'Ensure that the SSH warning banner is configured;DesiredObjectValue', '=', parameters('banner'), ',', 'Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue', '=', parameters('permitUserEnvironment'), ',', 'Ensure that appropriate ciphers are used for SSH;DesiredObjectValue', '=', parameters('ciphers'), ',', 'Ensure that the SSH port is configured;DesiredObjectValue', '=', parameters('port')))]"
"equals": "[base64(concat('Ensure that permissions on /etc/ssh/sshd_config are configured;DesiredObjectValue', '=', parameters('accessPermissionsForSshdConfig'), ',', 'Ensure that the SSH IgnoreRhosts is configured;DesiredObjectValue', '=', parameters('ignoreHosts'), ',', 'Ensure that the SSH LogLevel is configured;DesiredObjectValue', '=', parameters('logLevel'), ',', 'Ensure that the SSH MaxAuthTries is configured;DesiredObjectValue', '=', parameters('maxAuthTries'), ',', 'Ensure that the allowed users for SSH access are configured;DesiredObjectValue', '=', parameters('allowUsers'), ',', 'Ensure that the denied users for SSH are configured;DesiredObjectValue', '=', parameters('denyUsers'), ',', 'Ensure that the allowed groups for SSH are configured;DesiredObjectValue', '=', parameters('allowGroups'), ',', 'Ensure that the denied groups for SSH are configured;DesiredObjectValue', '=', parameters('denyGroups'), ',', 'Ensure that the SSH HostBasedAuthentication is configured;DesiredObjectValue', '=', parameters('hostBasedAuthentication'), ',', 'Ensure that the SSH PermitRootLogin is configured;DesiredObjectValue', '=', parameters('permitRootLogin'), ',', 'Ensure that the SSH PermitEmptyPasswords is configured;DesiredObjectValue', '=', parameters('permitEmptyPasswords'), ',', 'Ensure that the SSH ClientAliveCountMax is configured;DesiredObjectValue', '=', parameters('clientAliveCountMax'), ',', 'Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue', '=', parameters('clientAliveInterval'), ',', 'Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue', '=', parameters('loginGraceTime'), ',', 'Ensure that only approved MAC algorithms are used;DesiredObjectValue', '=', parameters('messageAuthenticationCodeAlgorithms'), ',', 'Ensure that the SSH warning banner is configured;DesiredObjectValue', '=', parameters('banner'), ',', 'Ensure that the SSH PermitUserEnvironment is configured;DesiredObjectValue', '=', parameters('permitUserEnvironment'), ',', 'Ensure that appropriate ciphers are used for SSH;DesiredObjectValue', '=', parameters('ciphers')))]"
}
]
},
@ -521,6 +524,9 @@
"clientAliveInterval": {
"value": "[parameters('clientAliveInterval')]"
},
"loginGraceTime": {
"value": "[parameters('loginGraceTime')]"
},
"messageAuthenticationCodeAlgorithms": {
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
},
@ -532,9 +538,6 @@
},
"ciphers": {
"value": "[parameters('ciphers')]"
},
"port": {
"value": "[parameters('port')]"
}
},
"template": {
@ -592,6 +595,9 @@
"clientAliveInterval": {
"type": "string"
},
"loginGraceTime": {
"type": "string"
},
"messageAuthenticationCodeAlgorithms": {
"type": "string"
},
@ -603,9 +609,6 @@
},
"ciphers": {
"type": "string"
},
"port": {
"type": "string"
}
},
"resources": [
@ -617,8 +620,11 @@
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "LinuxSshServerSecurityBaseline",
"version": "1.*",
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSshServerSecurityBaselinePolicy.zip",
"contentHash": "18F07A4F4F2E76DADEA169B41829A8DADBD2024BBAD374B3C5A20CEB4ECD15BE",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
@ -673,6 +679,10 @@
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
@ -688,10 +698,6 @@
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
},
{
"name": "Ensure that the SSH port is configured;DesiredObjectValue",
"value": "[parameters('port')]"
}
]
}
@ -705,8 +711,11 @@
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "LinuxSshServerSecurityBaseline",
"version": "1.*",
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSshServerSecurityBaselinePolicy.zip",
"contentHash": "18F07A4F4F2E76DADEA169B41829A8DADBD2024BBAD374B3C5A20CEB4ECD15BE",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
@ -761,6 +770,10 @@
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
@ -776,10 +789,6 @@
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
},
{
"name": "Ensure that the SSH port is configured;DesiredObjectValue",
"value": "[parameters('port')]"
}
]
}
@ -793,8 +802,11 @@
"location": "[parameters('location')]",
"properties": {
"guestConfiguration": {
"name": "LinuxSshServerSecurityBaseline",
"version": "1.*",
"name": "OsConfigPolicy",
"version": "1.0.0",
"contentType": "Custom",
"contentUri": "https://github.com/Azure/azure-osconfig/releases/download/test_policy_package/LinuxSshServerSecurityBaselinePolicy.zip",
"contentHash": "18F07A4F4F2E76DADEA169B41829A8DADBD2024BBAD374B3C5A20CEB4ECD15BE",
"assignmentType": "ApplyAndAutoCorrect",
"configurationParameter": [
{
@ -849,6 +861,10 @@
"name": "Ensure that the SSH ClientAliveInterval is configured;DesiredObjectValue",
"value": "[parameters('clientAliveInterval')]"
},
{
"name": "Ensure that the SSH LoginGraceTime is configured;DesiredObjectValue",
"value": "[parameters('loginGraceTime')]"
},
{
"name": "Ensure that only approved MAC algorithms are used;DesiredObjectValue",
"value": "[parameters('messageAuthenticationCodeAlgorithms')]"
@ -864,10 +880,6 @@
{
"name": "Ensure that appropriate ciphers are used for SSH;DesiredObjectValue",
"value": "[parameters('ciphers')]"
},
{
"name": "Ensure that the SSH port is configured;DesiredObjectValue",
"value": "[parameters('port')]"
}
]
}
@ -882,4 +894,4 @@
}
},
"name": "6d76c6e7-0670-4931-8741-16cff3fbfa3e"
}
}

Просмотреть файл

@ -71,6 +71,7 @@ static ConnectionStringSource g_connectionStringSource = FromAis;
static int g_stopSignal = 0;
static int g_refreshSignal = 0;
static bool g_isIotHubEnabled = false;
static char* g_iotHubConnectionString = NULL;
const char* g_iotHubConnectionStringPrefix = "HostName=";
@ -172,13 +173,16 @@ static void SignalReloadConfiguration(int incomingSignal)
static IOTHUB_DEVICE_CLIENT_LL_HANDLE CallIotHubInitialize(void)
{
IOTHUB_DEVICE_CLIENT_LL_HANDLE moduleHandle = IotHubInitialize(g_modelId, g_productInfo, g_iotHubConnectionString, false, g_x509Certificate, g_x509PrivateKeyHandle,
&g_proxyOptions, (PROTOCOL_MQTT_WS == g_iotHubProtocol) ? MQTT_WebSocket_Protocol : MQTT_Protocol);
if (NULL == moduleHandle)
IOTHUB_DEVICE_CLIENT_LL_HANDLE moduleHandle = NULL;
if (g_isIotHubEnabled)
{
OsConfigLogError(GetLog(), "IotHubInitialize failed, failed to initialize connection to IoT Hub");
IotHubDeInitialize();
if (NULL == (moduleHandle = IotHubInitialize(g_modelId, g_productInfo, g_iotHubConnectionString, false, g_x509Certificate, g_x509PrivateKeyHandle,
&g_proxyOptions, (PROTOCOL_MQTT_WS == g_iotHubProtocol) ? MQTT_WebSocket_Protocol : MQTT_Protocol)))
{
OsConfigLogError(GetLog(), "IotHubInitialize failed, failed to initialize connection to IoT Hub");
IotHubDeInitialize();
}
}
return moduleHandle;
@ -191,40 +195,43 @@ static void RefreshConnection()
FREE_MEMORY(g_x509Certificate);
FREE_MEMORY(g_x509PrivateKeyHandle);
// If initialized with AIS, try to get a new connection string same way:
if ((FromAis == g_connectionStringSource) && (NULL != (connectionString = RequestConnectionStringFromAis(&g_x509Certificate, &g_x509PrivateKeyHandle))))
if (g_isIotHubEnabled)
{
FREE_MEMORY(g_iotHubConnectionString);
if (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString))
// If initialized with AIS, try to get a new connection string same way:
if ((FromAis == g_connectionStringSource) && (NULL != (connectionString = RequestConnectionStringFromAis(&g_x509Certificate, &g_x509PrivateKeyHandle))))
{
OsConfigLogError(GetLog(), "RefreshConnection: out of memory making copy of the connection string");
FREE_MEMORY(connectionString);
FREE_MEMORY(g_iotHubConnectionString);
if (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString))
{
OsConfigLogError(GetLog(), "RefreshConnection: out of memory making copy of the connection string");
FREE_MEMORY(connectionString);
}
}
}
else
{
if (FromAis == g_connectionStringSource)
{
// No new connection string from AIS, try to refresh using the existing connection string before bailing out:
OsConfigLogError(GetLog(), "RefreshConnection: failed to obtain a new connection string from AIS, trying refresh with existing connection string");
}
}
IotHubDeInitialize();
g_moduleHandle = NULL;
if ((NULL == g_moduleHandle) && (NULL != g_iotHubConnectionString))
{
if (NULL == (g_moduleHandle = CallIotHubInitialize()))
else
{
if (FromAis == g_connectionStringSource)
{
FREE_MEMORY(g_iotHubConnectionString);
// No new connection string from AIS, try to refresh using the existing connection string before bailing out:
OsConfigLogError(GetLog(), "RefreshConnection: failed to obtain a new connection string from AIS, trying refresh with existing connection string");
}
else if (!IsWatcherActive())
}
IotHubDeInitialize();
g_moduleHandle = NULL;
if ((NULL == g_moduleHandle) && (NULL != g_iotHubConnectionString))
{
if (NULL == (g_moduleHandle = CallIotHubInitialize()))
{
g_exitState = IotHubInitializationFailure;
SignalInterrupt(SIGQUIT);
if (FromAis == g_connectionStringSource)
{
FREE_MEMORY(g_iotHubConnectionString);
}
else if (!IsWatcherActive())
{
g_exitState = IotHubInitializationFailure;
SignalInterrupt(SIGQUIT);
}
}
}
}
@ -244,13 +251,15 @@ static void SignalChild(int signal)
static void SignalProcessDesired(int incomingSignal)
{
OsConfigLogInfo(GetLog(), "Processing desired twin updates");
ProcessDesiredTwinUpdates();
// Reset the signal handler for the next use otherwise the default handler will be invoked instead
signal(SIGUSR1, SignalProcessDesired);
UNUSED(incomingSignal);
if (g_isIotHubEnabled)
{
OsConfigLogInfo(GetLog(), "Processing desired twin updates");
ProcessDesiredTwinUpdates();
// Reset the signal handler for the next use otherwise the default handler will be invoked instead
signal(SIGUSR1, SignalProcessDesired);
}
}
static void ForkDaemon()
@ -321,7 +330,7 @@ bool RefreshMpiClientSession(bool* platformAlreadyRunning)
{
// Platform is already running
if (platformAlreadyRunning)
if (NULL != platformAlreadyRunning)
{
*platformAlreadyRunning = true;
}
@ -329,15 +338,15 @@ bool RefreshMpiClientSession(bool* platformAlreadyRunning)
return status;
}
if (platformAlreadyRunning)
if (NULL != platformAlreadyRunning)
{
*platformAlreadyRunning = false;
}
if (true == (status = EnableAndStartDaemon(OSCONFIG_PLATFORM, GetLog())))
{
sleep(1);
if (NULL == (g_mpiHandle = CallMpiOpen(g_productName, g_maxPayloadSizeBytes, GetLog())))
{
OsConfigLogError(GetLog(), "MpiOpen failed");
@ -347,7 +356,7 @@ bool RefreshMpiClientSession(bool* platformAlreadyRunning)
}
else
{
OsConfigLogError(GetLog(), "Platform could not be started");
OsConfigLogError(GetLog(), "The OSConfig Platform cannot be started");
g_exitState = PlatformInitializationFailure;
}
@ -356,30 +365,33 @@ bool RefreshMpiClientSession(bool* platformAlreadyRunning)
static bool InitializeAgent(void)
{
bool status = RefreshMpiClientSession(NULL);
bool status = true;
g_lastTime = (unsigned int)time(NULL);
if (status && g_iotHubConnectionString)
if (0 == (status = RefreshMpiClientSession(NULL)))
{
if (NULL == (g_moduleHandle = CallIotHubInitialize()))
if (g_isIotHubEnabled && g_iotHubConnectionString)
{
if (FromAis == g_connectionStringSource)
if (NULL == (g_moduleHandle = CallIotHubInitialize()))
{
// We will try to get a new connnection string from AIS and try to connect with that
FREE_MEMORY(g_iotHubConnectionString);
}
else if (!IsWatcherActive())
{
g_exitState = IotHubInitializationFailure;
status = false;
if (FromAis == g_connectionStringSource)
{
// We will try to get a new connnection string from AIS and try to connect with that
FREE_MEMORY(g_iotHubConnectionString);
}
else if (false == IsWatcherActive())
{
g_exitState = IotHubInitializationFailure;
status = false;
}
}
}
}
if (status)
{
OsConfigLogInfo(GetLog(), "OSConfig PnP Agent intialized");
OsConfigLogInfo(GetLog(), "The OSConfig Agent session is now intialized");
}
return status;
@ -387,7 +399,10 @@ static bool InitializeAgent(void)
void CloseAgent(void)
{
IotHubDeInitialize();
if (g_isIotHubEnabled)
{
IotHubDeInitialize();
}
if (NULL != g_mpiHandle)
{
@ -397,9 +412,7 @@ void CloseAgent(void)
FREE_MEMORY(g_reportedProperties);
WatcherCleanup(GetLog());
OsConfigLogInfo(GetLog(), "OSConfig PnP Agent terminated");
OsConfigLogInfo(GetLog(), "The OSConfig Agent session is closed");
}
static void ReportProperties()
@ -428,7 +441,7 @@ static void AgentDoWork(void)
if (timeInterval <= (currentTime - g_lastTime))
{
if ((NULL == g_iotHubConnectionString) && (FromAis == g_connectionStringSource))
if (g_isIotHubEnabled && (NULL == g_iotHubConnectionString) && (FromAis == g_connectionStringSource))
{
IotHubDeInitialize();
@ -458,14 +471,14 @@ static void AgentDoWork(void)
WatcherDoWork(GetLog());
// Process reported updates to the IoT Hub
if (g_moduleHandle)
if (g_isIotHubEnabled && g_moduleHandle)
{
ReportProperties();
}
g_lastTime = (unsigned int)time(NULL);
}
else
else if (g_isIotHubEnabled)
{
IotHubDoWork();
}
@ -521,7 +534,7 @@ int main(int argc, char *argv[])
CloseLog(&g_agentLog);
g_agentLog = OpenLog(LOG_FILE, ROLLED_LOG_FILE);
OsConfigLogInfo(GetLog(), "OSConfig PnP Agent starting (PID: %d, PPID: %d)", pid = getpid(), getppid());
OsConfigLogInfo(GetLog(), "OSConfig Agent starting (PID: %d, PPID: %d)", pid = getpid(), getppid());
OsConfigLogInfo(GetLog(), "OSConfig version: %s", OSCONFIG_VERSION);
if (IsCommandLoggingEnabled() || IsFullLoggingEnabled())
@ -536,22 +549,18 @@ int main(int argc, char *argv[])
g_modelVersion = GetModelVersionFromJsonConfig(jsonConfiguration, GetLog());
g_numReportedProperties = LoadReportedFromJsonConfig(jsonConfiguration, &g_reportedProperties, GetLog());
g_reportingInterval = GetReportingIntervalFromJsonConfig(jsonConfiguration, GetLog());
g_isIotHubEnabled = IsIotHubManagementEnabledInJsonConfig(jsonConfiguration);
g_iotHubProtocol = GetIotHubProtocolFromJsonConfig(jsonConfiguration, GetLog());
// Call the Watcher to initialize itself
InitializeWatcher(jsonConfiguration, GetLog());
FREE_MEMORY(jsonConfiguration);
}
RestrictFileAccessToCurrentAccountOnly(CONFIG_FILE);
snprintf(g_modelId, sizeof(g_modelId), g_modelIdTemplate, g_modelVersion);
OsConfigLogInfo(GetLog(), "Model id: %s", g_modelId);
snprintf(g_productName, sizeof(g_productName), g_productNameTemplate, g_modelVersion, OSCONFIG_VERSION);
OsConfigLogInfo(GetLog(), "Product name: %s", g_productName);
snprintf(g_modelId, sizeof(g_modelId), g_modelIdTemplate, g_modelVersion);
OsConfigLogInfo(GetLog(), "Model id: %s", g_modelId);
osName = GetOsName(GetLog());
osVersion = GetOsVersion(GetLog());
cpuType = GetCpuType(GetLog());
@ -593,77 +602,80 @@ int main(int argc, char *argv[])
FREE_MEMORY(productName);
FREE_MEMORY(productVendor);
FREE_MEMORY(encodedProductInfo);
OsConfigLogInfo(GetLog(), "Protocol: %s", (PROTOCOL_MQTT_WS == g_iotHubProtocol) ? "MQTT over Web Socket" : "MQTT");
if (PROTOCOL_MQTT_WS == g_iotHubProtocol)
if (g_isIotHubEnabled)
{
// Read the proxy options from environment variables, parse and fill the HTTP_PROXY_OPTIONS structure to pass to the SDK:
if (NULL != (proxyData = GetHttpProxyData(GetLog())))
{
if (ParseHttpProxyData((const char*)proxyData, &proxyHostAddress, &proxyPort, &proxyUsername, &proxyPassword, GetLog()))
{
// Assign the string pointers and trasfer ownership to the SDK
g_proxyOptions.host_address = proxyHostAddress;
g_proxyOptions.port = proxyPort;
g_proxyOptions.username = proxyUsername;
g_proxyOptions.password = proxyPassword;
OsConfigLogInfo(GetLog(), "Protocol: %s", (PROTOCOL_MQTT_WS == g_iotHubProtocol) ? "MQTT over Web Socket" : "MQTT");
FREE_MEMORY(proxyData);
}
}
}
if ((argc < 2) || ((2 == argc) && forkDaemon))
{
g_connectionStringSource = FromAis;
if (NULL != (connectionString = RequestConnectionStringFromAis(&g_x509Certificate, &g_x509PrivateKeyHandle)))
if (PROTOCOL_MQTT_WS == g_iotHubProtocol)
{
if (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString))
// Read the proxy options from environment variables, parse and fill the HTTP_PROXY_OPTIONS structure to pass to the SDK:
if (NULL != (proxyData = GetHttpProxyData(GetLog())))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string from AIS");
g_exitState = NoConnectionString;
goto done;
}
}
else
{
OsConfigLogError(GetLog(), "Failed to obtain a connection string from AIS, to retry");
}
}
else
{
if (0 == strncmp(argv[1], g_iotHubConnectionStringPrefix, strlen(g_iotHubConnectionStringPrefix)))
{
g_connectionStringSource = FromCommandline;
if (0 != mallocAndStrcpy_s(&connectionString, argv[1]))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string from the command line");
g_exitState = NoConnectionString;
goto done;
}
}
else
{
g_connectionStringSource = FromFile;
connectionString = LoadStringFromFile(argv[1], true, GetLog());
if (NULL == connectionString)
{
OsConfigLogError(GetLog(), "Failed to load a connection string from %s", argv[1]);
if (!IsWatcherActive())
if (ParseHttpProxyData((const char*)proxyData, &proxyHostAddress, &proxyPort, &proxyUsername, &proxyPassword, GetLog()))
{
// Assign the string pointers and trasfer ownership to the SDK
g_proxyOptions.host_address = proxyHostAddress;
g_proxyOptions.port = proxyPort;
g_proxyOptions.username = proxyUsername;
g_proxyOptions.password = proxyPassword;
FREE_MEMORY(proxyData);
}
}
}
if ((argc < 2) || ((2 == argc) && forkDaemon))
{
g_connectionStringSource = FromAis;
if (NULL != (connectionString = RequestConnectionStringFromAis(&g_x509Certificate, &g_x509PrivateKeyHandle)))
{
if (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string from AIS");
g_exitState = NoConnectionString;
goto done;
}
}
else
{
OsConfigLogError(GetLog(), "Failed to obtain a connection string from AIS, to retry");
}
}
}
else
{
if (0 == strncmp(argv[1], g_iotHubConnectionStringPrefix, strlen(g_iotHubConnectionStringPrefix)))
{
g_connectionStringSource = FromCommandline;
if (0 != mallocAndStrcpy_s(&connectionString, argv[1]))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string from the command line");
g_exitState = NoConnectionString;
goto done;
}
}
else
{
g_connectionStringSource = FromFile;
connectionString = LoadStringFromFile(argv[1], true, GetLog());
if (NULL == connectionString)
{
OsConfigLogError(GetLog(), "Failed to load a connection string from %s", argv[1]);
if (connectionString && (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString)))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string");
goto done;
if (!IsWatcherActive())
{
g_exitState = NoConnectionString;
goto done;
}
}
}
}
if (connectionString && (0 != mallocAndStrcpy_s(&g_iotHubConnectionString, connectionString)))
{
OsConfigLogError(GetLog(), "Out of memory making copy of the connection string");
goto done;
}
}
for (int i = 0; i < stopSignalsCount; i++)
@ -673,17 +685,15 @@ int main(int argc, char *argv[])
signal(SIGHUP, SignalReloadConfiguration);
signal(SIGUSR1, SignalProcessDesired);
if (!RefreshMpiClientSession(NULL))
if (false == InitializeAgent())
{
OsConfigLogError(GetLog(), "Failed to start the platform");
OsConfigLogError(GetLog(), "Failed to initialize the OSConfig Agent");
goto done;
}
if (!InitializeAgent())
{
OsConfigLogError(GetLog(), "Failed to initialize the OSConfig PnP Agent");
goto done;
}
// Call the Watcher to initialize itself
InitializeWatcher(jsonConfiguration, GetLog());
FREE_MEMORY(jsonConfiguration);
while (0 == g_stopSignal)
{
@ -699,13 +709,15 @@ int main(int argc, char *argv[])
}
done:
OsConfigLogInfo(GetLog(), "OSConfig PnP Agent (PID: %d) exiting with %d", pid, g_stopSignal);
OsConfigLogInfo(GetLog(), "OSConfig Agent (PID: %d) exiting with %d", pid, g_stopSignal);
FREE_MEMORY(g_x509Certificate);
FREE_MEMORY(g_x509PrivateKeyHandle);
FREE_MEMORY(connectionString);
FREE_MEMORY(g_iotHubConnectionString);
WatcherCleanup(GetLog());
CloseAgent();
StopAndDisableDaemon(OSCONFIG_PLATFORM, GetLog());

Просмотреть файл

@ -16,8 +16,6 @@
#define GIT_DC_CLONE "/etc/osconfig/gitops/"
#define GIT_DC_FILE GIT_DC_CLONE "osconfig_desired.json"
const char* g_mpiServer = "osconfig-platform";
static int g_localManagement = 0;
static size_t g_reportedHash = 0;
static size_t g_desiredHash = 0;
@ -29,8 +27,6 @@ static size_t g_gitDesiredHash = 0;
static bool g_gitCloneInitialized = false;
static MPI_HANDLE g_mpiHandle = NULL;
static void SaveReportedConfigurationToFile(const char* fileName, size_t* hash)
{
char* payload = NULL;
@ -39,7 +35,7 @@ static void SaveReportedConfigurationToFile(const char* fileName, size_t* hash)
bool platformAlreadyRunning = true;
int mpiResult = MPI_OK;
if (fileName && hash && g_mpiHandle)
if (fileName && hash)
{
mpiResult = CallMpiGetReported((MPI_JSON_STRING*)&payload, &payloadSizeBytes, GetLog());
if ((MPI_OK != mpiResult) && RefreshMpiClientSession(&platformAlreadyRunning) && (false == platformAlreadyRunning))
@ -73,7 +69,7 @@ static void ProcessDesiredConfigurationFromFile(const char* fileName, size_t* ha
bool platformAlreadyRunning = true;
int mpiResult = MPI_OK;
if (fileName && hash && g_mpiHandle)
if (fileName && hash)
{
RestrictFileAccessToCurrentAccountOnly(fileName);
@ -267,30 +263,6 @@ void InitializeWatcher(const char* jsonConfiguration, void* log)
RestrictFileAccessToCurrentAccountOnly(DC_FILE);
RestrictFileAccessToCurrentAccountOnly(RC_FILE);
RestrictFileAccessToCurrentAccountOnly(GIT_DC_FILE);
if (NULL != g_mpiHandle)
{
CallMpiClose(g_mpiHandle, GetLog());
g_mpiHandle = NULL;
}
if (true == EnableAndStartDaemon(g_mpiServer, GetLog()))
{
sleep(1);
if (NULL == (g_mpiHandle = CallMpiOpen(MPI_CLIENT_NAME, MAX_PAYLOAD_LENGTH, GetLog())))
{
OsConfigLogError(GetLog(), "Watcher: MpiOpen failed");
}
else
{
OsConfigLogInfo(GetLog(), "Watcher: initialized with MPI session '%p'", g_mpiHandle);
}
}
else
{
OsConfigLogError(GetLog(), "Watcher: the OSConfig Platform service '%s' is not active on this device", g_mpiServer);
}
}
void WatcherDoWork(void* log)
@ -321,11 +293,8 @@ void WatcherDoWork(void* log)
void WatcherCleanup(void* log)
{
OsConfigLogInfo(log, "Watcher: closing MPI session '%p' and shutting down", g_mpiHandle);
OsConfigLogInfo(log, "Watcher shutting down");
CallMpiClose(g_mpiHandle, GetLog());
g_mpiHandle = NULL;
DeleteGitClone(GIT_DC_CLONE, log);
FREE_MEMORY(g_gitRepositoryUrl);

Просмотреть файл

@ -5,7 +5,8 @@
"GitRepositoryUrl": " ",
"GitBranch": " ",
"LocalManagement": 1,
"ModelVersion": 18,
"ModelVersion": 20,
"IotHubManagement": 0,
"IotHubProtocol": 2,
"Reported": [
{
@ -20,6 +21,10 @@
"ComponentName": "Configuration",
"ObjectName": "fullLoggingEnabled"
},
{
"ComponentName": "Configuration",
"ObjectName": "iotHubManagement"
},
{
"ComponentName": "Configuration",
"ObjectName": "iotHubProtocol"
@ -781,5 +786,5 @@
"ObjectName": "auditEnsureUnnecessaryAccountsAreRemoved"
}
],
"ReportingIntervalSeconds": 300
"ReportingIntervalSeconds": 30
}

Просмотреть файл

@ -219,6 +219,7 @@ typedef struct REPORTED_PROPERTY
bool IsCommandLoggingEnabledInJsonConfig(const char* jsonString);
bool IsFullLoggingEnabledInJsonConfig(const char* jsonString);
bool IsIotHubManagementEnabledInJsonConfig(const char* jsonString);
int GetReportingIntervalFromJsonConfig(const char* jsonString, void* log);
int GetModelVersionFromJsonConfig(const char* jsonString, void* log);
int GetLocalManagementFromJsonConfig(const char* jsonString, void* log);

Просмотреть файл

@ -14,6 +14,8 @@
#define REPORTED_SETTING_NAME "ObjectName"
#define MODEL_VERSION_NAME "ModelVersion"
#define REPORTING_INTERVAL_SECONDS "ReportingIntervalSeconds"
#define IOT_HUB_MANAGEMENT "IotHubManagement"
#define LOCAL_MANAGEMENT "LocalManagement"
#define COMMAND_LOGGING "CommandLogging"
@ -28,7 +30,7 @@
#define MIN_DEVICE_MODEL_ID 7
#define MAX_DEVICE_MODEL_ID 999
static bool IsLoggingEnabledInJsonConfig(const char* jsonString, const char* loggingSetting)
static bool IsOptionEnabledInJsonConfig(const char* jsonString, const char* setting)
{
bool result = false;
JSON_Value* rootValue = NULL;
@ -40,7 +42,7 @@ static bool IsLoggingEnabledInJsonConfig(const char* jsonString, const char* log
{
if (NULL != (rootObject = json_value_get_object(rootValue)))
{
result = (0 == (int)json_object_get_number(rootObject, loggingSetting)) ? false : true;
result = (0 == (int)json_object_get_number(rootObject, setting)) ? false : true;
}
json_value_free(rootValue);
}
@ -51,12 +53,17 @@ static bool IsLoggingEnabledInJsonConfig(const char* jsonString, const char* log
bool IsCommandLoggingEnabledInJsonConfig(const char* jsonString)
{
return IsLoggingEnabledInJsonConfig(jsonString, COMMAND_LOGGING);
return IsOptionEnabledInJsonConfig(jsonString, COMMAND_LOGGING);
}
bool IsFullLoggingEnabledInJsonConfig(const char* jsonString)
{
return IsLoggingEnabledInJsonConfig(jsonString, FULL_LOGGING);
return IsOptionEnabledInJsonConfig(jsonString, FULL_LOGGING);
}
bool IsIotHubManagementEnabledInJsonConfig(const char* jsonString)
{
return IsOptionEnabledInJsonConfig(jsonString, IOT_HUB_MANAGEMENT);
}
static int GetIntegerFromJsonConfig(const char* valueName, const char* jsonString, int defaultValue, int minValue, int maxValue, void* log)

Просмотреть файл

@ -19,6 +19,7 @@ static const char* g_refreshIntervalObject = "refreshInterval";
static const char* g_localManagementEnabledObject = "localManagementEnabled";
static const char* g_fullLoggingEnabledObject = "fullLoggingEnabled";
static const char* g_commandLoggingEnabledObject = "commandLoggingEnabled";
static const char* g_iotHubManagementEnabledObject = "iotHubManagementEnabled";
static const char* g_iotHubProtocolObject = "iotHubProtocol";
static const char* g_gitManagementEnabledObject = "gitManagementEnabled";
static const char* g_gitBranchObject = "gitBranch";
@ -27,6 +28,7 @@ static const char* g_desiredRefreshIntervalObject = "desiredRefreshInterval";
static const char* g_desiredLocalManagementEnabledObject = "desiredLocalManagementEnabled";
static const char* g_desiredFullLoggingEnabledObject = "desiredFullLoggingEnabled";
static const char* g_desiredCommandLoggingEnabledObject = "desiredCommandLoggingEnabled";
static const char* g_desiredIotHubManagementEnabledObject = "desiredIotHubManagementEnabled";
static const char* g_desiredIotHubProtocolObject = "desiredIotHubProtocol";
static const char* g_desiredGitManagementEnabledObject = "desiredGitManagementEnabled";
static const char* g_desiredGitBranchObject = "desiredGitBranch";
@ -47,8 +49,8 @@ static const char* g_configurationModuleInfo = "{\"Name\": \"Configuration\","
"\"Description\": \"Provides functionality to manage OSConfig configuration on device\","
"\"Manufacturer\": \"Microsoft\","
"\"VersionMajor\": 1,"
"\"VersionMinor\": 3,"
"\"VersionInfo\": \"Zinc\","
"\"VersionMinor\": 4,"
"\"VersionInfo\": \"Dilithium\","
"\"Components\": [\"Configuration\"],"
"\"Lifetime\": 2,"
"\"UserAccount\": 0}";
@ -60,6 +62,7 @@ static int g_refreshInterval = DEFAULT_REPORTING_INTERVAL;
static bool g_localManagementEnabled = false;
static bool g_fullLoggingEnabled = false;
static bool g_commandLoggingEnabled = false;
static bool g_iotHubManagementEnabled = false;
static int g_iotHubProtocol = 0;
static bool g_gitManagementEnabled = false;
static char* g_gitBranch = NULL;
@ -84,6 +87,7 @@ static char* LoadConfigurationFromFile(const char* fileName)
g_localManagementEnabled = (GetLocalManagementFromJsonConfig(jsonConfiguration, ConfigurationGetLog())) ? true : false;
g_fullLoggingEnabled = IsFullLoggingEnabledInJsonConfig(jsonConfiguration);
g_commandLoggingEnabled = IsCommandLoggingEnabledInJsonConfig(jsonConfiguration);
g_iotHubManagementEnabled = IsIotHubManagementEnabledInJsonConfig(jsonConfiguration);
g_iotHubProtocol = GetIotHubProtocolFromJsonConfig(jsonConfiguration, ConfigurationGetLog());
g_gitManagementEnabled = GetGitManagementFromJsonConfig(jsonConfiguration, ConfigurationGetLog());
g_gitBranch = GetGitBranchFromJsonConfig(jsonConfiguration, ConfigurationGetLog());
@ -127,6 +131,7 @@ static int UpdateConfigurationFile(void)
const char* fullLoggingEnabledName = "FullLogging";
const char* localManagementEnabledName = "LocalManagement";
const char* modelVersionName = "ModelVersion";
const char* iotHubtManagementEnabledName = "IotHubManagement";
const char* iotHubProtocolName = "IotHubProtocol";
const char* refreshIntervalName = "ReportingIntervalSeconds";
const char* gitManagementEnabledName = "GitManagement";
@ -142,6 +147,7 @@ static int UpdateConfigurationFile(void)
bool localManagementEnabled = g_localManagementEnabled;
bool fullLoggingEnabled = g_fullLoggingEnabled;
bool commandLoggingEnabled = g_commandLoggingEnabled;
bool iotHubManagementEnabled = g_iotHubManagementEnabled;
int iotHubProtocol = g_iotHubProtocol;
bool gitManagementEnabled = g_gitManagementEnabled;
char* gitBranch = DuplicateString(g_gitBranch);
@ -156,7 +162,8 @@ static int UpdateConfigurationFile(void)
}
if ((modelVersion != g_modelVersion) || (refreshInterval != g_refreshInterval) || (localManagementEnabled != g_localManagementEnabled) ||
(fullLoggingEnabled != g_fullLoggingEnabled) || (commandLoggingEnabled != g_commandLoggingEnabled) || (iotHubProtocol != g_iotHubProtocol) ||
(fullLoggingEnabled != g_fullLoggingEnabled) || (commandLoggingEnabled != g_commandLoggingEnabled) ||
(iotHubManagementEnabled != g_iotHubManagementEnabled) || (iotHubProtocol != g_iotHubProtocol) ||
(gitManagementEnabled != g_gitManagementEnabled) || strcmp(gitBranch, g_gitBranch))
{
if (NULL == (jsonValue = json_parse_string(existingConfiguration)))
@ -216,7 +223,16 @@ static int UpdateConfigurationFile(void)
{
OsConfigLogError(ConfigurationGetLog(), "json_object_set_boolean(%s, %s) failed", g_commandLoggingEnabledObject, commandLoggingEnabled ? "true" : "false");
}
if (JSONSuccess == json_object_set_number(jsonObject, iotHubtManagementEnabledName, (double)(iotHubManagementEnabled ? 1 : 0)))
{
g_iotHubManagementEnabled = iotHubManagementEnabled;
}
else
{
OsConfigLogError(ConfigurationGetLog(), "json_object_set_boolean(%s, %s) failed", g_iotHubManagementEnabledObject, iotHubManagementEnabled ? "true" : "false");
}
if (JSONSuccess == json_object_set_number(jsonObject, iotHubProtocolName, (double)iotHubProtocol))
{
g_iotHubProtocol = iotHubProtocol;
@ -423,6 +439,10 @@ int ConfigurationMmiGet(MMI_HANDLE clientSession, const char* componentName, con
{
snprintf(buffer, maximumLength, "%s", g_commandLoggingEnabled ? "true" : "false");
}
else if (0 == strcmp(objectName, g_iotHubManagementEnabledObject))
{
snprintf(buffer, maximumLength, "%s", g_iotHubManagementEnabled ? "true" : "false");
}
else if (0 == strcmp(objectName, g_iotHubProtocolObject))
{
snprintf(buffer, maximumLength, "%u", g_iotHubProtocol);
@ -589,6 +609,22 @@ int ConfigurationMmiSet(MMI_HANDLE clientSession, const char* componentName, con
status = EINVAL;
}
}
else if (0 == strcmp(objectName, g_desiredIotHubManagementEnabledObject))
{
if (0 == strcmp(stringTrue, payloadString))
{
g_iotHubManagementEnabled = true;
}
else if (0 == strcmp(stringFalse, payloadString))
{
g_iotHubManagementEnabled = false;
}
else
{
OsConfigLogError(ConfigurationGetLog(), "Unsupported %s value: %s", g_desiredIotHubManagementEnabledObject, payloadString);
status = EINVAL;
}
}
else if (0 == strcmp(objectName, g_desiredIotHubProtocolObject))
{
if (0 == strcmp(g_auto, payloadString))

Просмотреть файл

@ -16,8 +16,8 @@ class ConfigurationTest : public ::testing::Test
"\"Description\": \"Provides functionality to manage OSConfig configuration on device\","
"\"Manufacturer\": \"Microsoft\","
"\"VersionMajor\": 1,"
"\"VersionMinor\": 3,"
"\"VersionInfo\": \"Zinc\","
"\"VersionMinor\": 4,"
"\"VersionInfo\": \"Dilithium\","
"\"Components\": [\"Configuration\"],"
"\"Lifetime\": 2,"
"\"UserAccount\": 0}";
@ -30,6 +30,7 @@ class ConfigurationTest : public ::testing::Test
const char* m_localManagementEnabledObject = "localManagementEnabled";
const char* m_fullLoggingEnabledObject = "fullLoggingEnabled";
const char* m_commandLoggingEnabledObject = "commandLoggingEnabled";
const char* m_iotHubManagementEnabledObject = "iotHubManagementEnabled";
const char* m_iotHubProtocolObject = "iotHubProtocol";
const char* m_gitManagementEnabledObject = "gitManagementEnabled";
const char* m_gitBranchObject = "gitBranch";
@ -38,6 +39,7 @@ class ConfigurationTest : public ::testing::Test
const char* m_desiredLocalManagementEnabledObject = "desiredLocalManagementEnabled";
const char* m_desiredFullLoggingEnabledObject = "desiredFullLoggingEnabled";
const char* m_desiredCommandLoggingEnabledObject = "desiredCommandLoggingEnabled";
const char* m_desiredIotHubManagementEnabledObject = "desiredIotHubManagementEnabled";
const char* m_desiredIotHubProtocolObject = "desiredIotHubProtocol";
const char* m_desiredGitManagementEnabledObject = "desiredGitManagementEnabled";
const char* m_desiredGitBranchObject = "desiredGitBranch";
@ -48,6 +50,7 @@ class ConfigurationTest : public ::testing::Test
"\"FullLogging\" : 0,"
"\"LocalManagement\" : 0,"
"\"ModelVersion\" : 15,"
"\"IotHubManagement\" : 0,"
"\"IotHubProtocol\" : 2,"
"\"ReportingIntervalSeconds\": 30,"
"\"GitManagement\" : 1,"
@ -129,6 +132,7 @@ TEST_F(ConfigurationTest, MmiGet)
m_localManagementEnabledObject,
m_fullLoggingEnabledObject,
m_commandLoggingEnabledObject,
m_iotHubManagementEnabledObject,
m_iotHubProtocolObject,
m_gitManagementEnabledObject,
m_gitBranchObject
@ -165,6 +169,7 @@ TEST_F(ConfigurationTest, MmiGetTruncatedPayload)
m_localManagementEnabledObject,
m_fullLoggingEnabledObject,
m_commandLoggingEnabledObject,
m_iotHubManagementEnabledObject,
m_iotHubProtocolObject,
m_gitManagementEnabledObject,
m_gitBranchObject
@ -263,6 +268,9 @@ TEST_F(ConfigurationTest, MmiSet)
{ m_desiredCommandLoggingEnabledObject, "true", 0, m_commandLoggingEnabledObject, "true" },
{ m_desiredCommandLoggingEnabledObject, "false", 0, m_commandLoggingEnabledObject, "false" },
{ m_desiredCommandLoggingEnabledObject, "notImplemented", 22, m_commandLoggingEnabledObject, "false" },
{ m_desiredIotHubManagementEnabledObject, "true", 0, m_iotHubManagementEnabledObject, "true" },
{ m_desiredIotHubManagementEnabledObject, "false", 0, m_iotHubManagementEnabledObject, "false" },
{ m_desiredIotHubManagementEnabledObject, "notImplemented", 22, m_iotHubManagementEnabledObject, "false" },
{ m_desiredIotHubProtocolObject, "\"auto\"", 0, m_iotHubProtocolObject, "\"auto\"" },
{ m_desiredIotHubProtocolObject, "\"mqtt\"", 0, m_iotHubProtocolObject, "\"mqtt\"" },
{ m_desiredIotHubProtocolObject, "\"mqttWebSocket\"", 0, m_iotHubProtocolObject, "\"mqttWebSocket\"" },

Просмотреть файл

@ -30,6 +30,12 @@
"desired": true,
"schema": "boolean"
},
{
"name": "desiredIotHubManagementEnabled",
"type": "mimObject",
"desired": true,
"schema": "boolean"
},
{
"name": "desiredIotHubProtocol",
"type": "mimObject",
@ -95,6 +101,12 @@
"desired": false,
"schema": "boolean"
},
{
"name": "iotHubManagamentEnabled",
"type": "mimObject",
"desired": false,
"schema": "boolean"
},
{
"name": "iotHubProtocol",
"type": "mimObject",