Industrial Edge App Security¶
The Industrial Edge Ecosystem provides certain features for secure Industrial Edge App operations.
Exposing an Industrial Edge App on an Industrial Edge Device¶
The Industrial Edge Device itself has an NGINX-based reverse proxy which can be used by Industrial Edge Apps. The reverse proxy is in this case responsible for TLS termination and secure HTTP header injection. Its certificate can be managed by the local Industrial Edge Admin web interface running on each Industrial Edge Device. Certain services provided by Industrial Edge Apps can be exposed by specifying metadata which is used to dynamically configure the NGINX web server. This can be done by the Industrial Edge App publisher or by the Industrial Edge Management during the installation process.
Basically, it maps a unique redirect path on the NGINX instance to the running workload container.
Alternatively, apps can - if needed - directly expose their services via network ports on the device or local networks, e.g., to support protocols other than HTTP. The App Developer is in this case responsible for implementing secure communication and authentication/authorization measures as part of the app.
The exposure of a container within an Industrial Edge App can be further reduced using local Docker networks which are
- not exposed and/or routed publicly, or
- not shared between different Industrial Edge Apps.
Industrial Edge App Signing Process¶
An Industrial Edge Device app is signed when it is released on the Industrial Edge Hub. The entire Industrial Edge app-signing methodology provides proof of integrity and authenticity of an Industrial Edge Device app package from the app creator via Industrial Edge Management to the Industrial Edge Device. Accordingly, the Industrial Edge Device can detect whether a container belongs to a specific Industrial Edge Device app and version.
To verify app integrity, the signing process consists of three stages:
- Signing the container images of an app by using cosign.
- Retagging the image tag provided in the
docker-compose.ymlwith the app's version and prefixing it withappver-. - Providing the proof of integrity and authenticity of the entire
.appfile by signing thedigests.jsonfile and adding adigests.p7sfile into the app file.
The first two tasks modify content in the .app file and therefore require updates to the checksums stored in the app's digests.json file.
How the tasks mentioned above are implemented is described in the next sections.
"cosigning the Industrial Edge App's container images¶
The container images belonging to an Industrial Edge app are signed by using cosign container image signing. While performing this task, two additional layers are added to the image data:
- A signing layer containing the actual cosign signature of the image.
- An intermediate certificate chain as an additional blob which is used by the Industrial Edge Device during the verification process.
After the container image signing process is finished, the layout is transformed to the OCI layout having gzipped blob layers (independently if it was uploaded just as tar blob layers or tar.gz blob layers).
IEMs (starting from IEM Pro V2) capable of processing signed images use the index.json file, according to the OCI specification, to import images. Older IEMs use the additionally provided manifest.json file to import the Industrial Edge App's container images.
Retagging the image tags in the docker-compose.yml¶
To avoid reusing identical image tags across different app versions, the container image tags defined by the app creator in the docker-compose.yml are replaced by the tag appver-<appversion>. The appversion is the version being specified for the entire Industrial Edge App but - if required - converted to a string which is compliant for OCI registries.
This implies that the name of the tarred image file inside the .app file is changed. The name of the image being stored in the <repository>/images/ directory is determined by
echo -n <imageStringBeingUsedInDockerCompose> | sha256sum
For example
echo -n 'testapp-repository/myimage:appver-0.0.1' | sha256sum
The corresponding file name would be in this case for instance b76d97c48498f2026efb05a54bd62f9a2f551b026ff39634faf9253f142bbadc.tar or b76d97c48498f2026efb05a54bd62f9a2f551b026ff39634faf9253f142bbadc.tar.gz.
Retagging the docker-compose.yml also requires an update of the digests.json file of the app package.
How can I compare the originally uploaded images with the signed ones?¶
To compare the originally uploaded images with the signed images, the entire .app file must be extracted by using tar xfv <nameOfTheAppFile>.app.
Detecting the hashed name for an image reference¶
To detect which image references in the docker-compose.yml belong to which image file, the following command can be used:
for img in $(grep image docker-compose.yml | awk '{print $2}' | tr -d "'\"\r");
do
echo -n "$img --> "
echo -n "$img" | sha256sum | awk '{print $1}'
done
# Example:
testapp-repository/myimage:appver-0.0.1 -->
b76d97c48498f2026efb05a54bd62f9a2f551b026ff39634faf9253f142bbadc
The corresponding images having the prefix are located in the images folder; for instance:
images/b76d97c48498f2026efb05a54bd62f9a2f551b026ff39634faf9253f142bbadc.tar.gz
.
This step must be performed for both the signed and unsigned app versions to determine which images should be compared.
Comparing the images by using the OCI Config layer¶
After determining which image files should be compared, both images, or at least their manifest.json or index.json and Config layer files, must be extracted.
Detecting the Config layer by using the manifest.json¶
The corresponding Config layer of the image can be detected by running, for example,
jq -r '.[0].Config' unsignedImage/manifest.json
# Example output:
blobs/sha256/a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6
jq -r '.[0].Config' signedImage/manifest.json
# Example output:
blobs/sha256/a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6
It can happen that the paths are prefixed differently, but both filenames must be identical.
In the next step, you can verify whether both files have the expected checksum by running, for example, sha256sum
blobs/sha256/a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6.
This check is usually sufficient, as different Config layers sharing the same sha256 hash value is highly unlikely.
Using the index.json of an unsigned image for Config layer detection¶
Since manifest.json can only be used as an entry point for unsigned images, the Config layer is also referenced (indirectly) by index.json, which is typically used for OCI images.
In a first step, the manifest file must be detected, e.g., by using:
jq -r '.manifests[0].digest' unsigned/index.json
sha256:372d1a057ed435f5280bacb7522d5c2a482341d3136db0634b94dacb16eb061e
In the next step, the file being stored in blobs/sha256/ must be analyzed. In this example
blobs/sha256/372d1a057ed435f5280bacb7522d5c2a482341d3136db0634b94dacb16eb061e
by using for instance the following command:
jq -r .config.digest
blobs/sha256/372d1a057ed435f5280bacb7522d5c2a482341d3136db0634b94dacb16eb061e
sha256:a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6
The hash returned by this query must be identical to the hash mentioned above.
Using the index.json of a signed image for Config layer detection¶
Since signed images contain the signature and the additional trust anchor, the OCI structure and the queries to be performed are unfortunately slightly different as described above.
In this case, the index.json must be queried by using the following command to detect the manifest directing to the config layer:
jq -r '.manifests[] | select(.mediaType ==
"application/vnd.docker.distribution.manifest.v2+json") | .digest' index.json
### Example hash
sha256:dba4cebabfcf85deecd3f5b7f4fd799df7a30ee27d0b3c24ae7a24a63d5cb0a1
In the next step, the hash being detected by the jq command must be used to determine the Config layer being specified in the file being stored in the directory blobs/sha256, for instance
blobs/sha256/dba4cebabfcf85deecd3f5b7f4fd799df7a30ee27d0b3c24ae7a24a63d5cb0a1.
In this example, the reference to the Config layer can be determined by using the command
jq -r .config.digest
blobs/sha256/dba4cebabfcf85deecd3f5b7f4fd799df7a30ee27d0b3c24ae7a24a63d5cb0a1
sha256:a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6
which is actually the same Config layer as determined by using the manifest.json files or the index.json being used in the examples above.
Further checksum verification can be done as described above.
Optional additional verification steps by using the blob layer diff_ids¶
If a more detailed check is desired, in the next step, the diff_ids of the layers can be extracted, e.g., by running
jq -r '.rootfs.diff_ids[]'
blobs/sha256/a2956aeaef1989a33d001d9dc992c53d139ea87a4267c2ed91918682858883d6
sha256:5535fda0356bb3eff348b2f618314a47946d7ccfbeb880b3fc910dd7375ae140
sha256:db53251364b401f2374a20b7fe2de4ef26849195d1835add3cc5b0800bf93b41
sha256:e5477f2b3b2b4559725293e51e16e675bb14c36eafa2c96043fd629ad972de3f
sha256:063dee13d8449bd44771c27f3e7ae96cd2872205dbe2545e92235f2d80d212e0
sha256:79fd1e9b7a4b6480f103999d5a912c99b03f9cec008d011619bf3794525b112c
These hashes represent the sha256sum of the un(g)zipped tar layer of the blob layer being used for assembling the image by the container runtime (e.g. Docker).
Typically the container runtime verifies these hashes during the image load process and interrupts the startup process of a container in case the verification of a layer fails during the load process of the container image. Container signature methods like cosign rely on this verification as well and just sign the container image index containing the hashes being used but not the blobs themselves.
Evaluating Elevated Privileges of Industrial Edge Apps¶
Industrial Edge Apps are operated on a container runtime environment (Docker) on a local Industrial Edge Device. An Industrial Edge App can consist of multiple containers. The entire network design of an Industrial Edge App and its assigned privileges is in the responsibility of the App Developer. By choosing a suitable network design, the App Developer can decide which services of an app are exposed and which are not.
In case an Industrial Edge App is requested for installation on an Industrial Edge Device, the Industrial Edge Operator is informed about the requested privileges and security-relevant resources by the Industrial Edge App. Such resources could be, e.g., critical host paths or device files. If a warning message is created, the Industrial Edge Operator can either accept or deny these privileges. Block messages result in not being able to install the Industrial Edge App on the target Industrial Edge Device at all. This mechanism is also provided for specific features required by an Industrial Edge App. For such use cases, the App Developer must specify the requested feature (e.g., the requirement of having a specific GPU type on the target Industrial Edge Device). In case the feature is available, an allow message is created, otherwise β depending on the feature β a warning or block message is created by the Industrial Edge Management.
Siemens Edge Apps are digitally signed by Siemens and are presented to the Industrial Edge Operator as trusted Industrial Edge Apps.
Apps, if they are unprivileged, have certain isolation boundaries. Standard apps are intended to have CPU limits assigned and not to share resources of the underlying host.
It is strongly recommended applying the principle of least privilege to Industrial Edge Apps, especially if Linux host namespaces are used which break the isolation of a container.
App Developers should also avoid using privileged users (like the root user).
Additionally, minimizing the software footprint of an Industrial Edge App and applying a secure configuration helps to reduce the attack surface of an exposed app.
App Developers are responsible for applying these measures and for mitigating vulnerabilities by updating app versions containing vulnerable components in the Industrial Edge Hub.
The Industrial Edge Operator is responsible for installing updated versions on the corresponding Industrial Edge Devices.
Some Industrial Edge Apps have elevated requirements for fulfilling their intended tasks and require special permissions that break the isolation boundaries. Use cases for such apps are, e.g., diagnosing tasks of other apps or tasks on the underlying device, or they could require layer-2 access for communication via industrial protocols. Elevated privileges are managed by the Industrial Edge Management and its integrated policy engine.
| Component | Purpose | IE offering & app partner responsibility |
|---|---|---|
| Confidentiality | Encrypted communication client / server side |
|
Encrypted communication for data in transit:
|
To be done by App Developers. Apps in the IE Hub are virus-checked and signed. Elevated privileges are only allowed for Industrial Edge Apps provided by the customer or signed by a privileged organization. The policy enforcement point for app isolation is represented by the customer's Industrial Edge Management. See also [here](ie_management_security.md). | |
| Secure storage of data and configuration (e.g., cloud access credentials) | IED disk encryption, access credentials shall be stored in the app-specific private directory `.cfg-data`, which is not exposed to other non-privileged apps. Storing app-specific secrets (access credentials, private keys, ...) securely needs to be managed by the provider of the Industrial Edge App. | |
| Rotation of Secrets / Key Material | Rotating secrets used by an Industrial Edge App is the responsibility of the App Developer. | |
| Integrity | App File Integrity | Digital signing of apps in case they are consumed from the IE Hub. To ensure the integrity of the app itself, only dedicated apps signed by a privileged identity (Siemens) can request privileges which break the app-isolation boundaries. The privilege management is done by using the Industrial Edge Management and its [policy engine](./ie_management_security.md). |
| Availability | Backup of App | Provided by IE State Service |
| Backup of configuration | Provided by IE State Service | |
| Offline Operation | IE Devices and apps can operate completely offline, except for apps that require an Internet connection, such as the IE Cloud Connector (this is described in the respective manual). Even when the administrative connection is lost, data is still collected and forwarded. Connection is only required for configuring the app and for maintenance purposes, for example for updates or new app deployments, and is fully controlled by the operator. Caching of application-related data is the responsibility of the App Developer. Changing the set of installed apps on the device requires connectivity to Industrial Edge Management. | |
| Auditability/Logging | Creation of App-specific Log messages | Generation of app-specific logs is the responsibility of the provider of the Industrial Edge App. The Industrial Edge ecosystem itself only logs the installation and removal of Industrial Edge apps. |
| Auditing changes in the app configuration on a device. | Audit logs describing change requests on an Industrial Edge device are created by the Industrial Edge Management. |