diff --git a/src/confcom/.gitignore b/src/confcom/.gitignore index d60f4d0f18a..eb7212ecef5 100644 --- a/src/confcom/.gitignore +++ b/src/confcom/.gitignore @@ -27,6 +27,8 @@ azext_confcom/bin/ azext_confcom/bin/* **/dmverity-vhd.exe **/dmverity-vhd + # metadata file for coverage reports **/.coverage -**/htmlcov \ No newline at end of file + +**/htmlcov diff --git a/src/confcom/HISTORY.rst b/src/confcom/HISTORY.rst index 770f05f1382..f9a73ca86a7 100644 --- a/src/confcom/HISTORY.rst +++ b/src/confcom/HISTORY.rst @@ -2,6 +2,11 @@ Release History =============== +0.2.14 +* changing the name of api_svn and framework_svn to api_version and framework_version +* changing fragment versions to an integer instead of semver +* bugfix for allowing 32bit python on a 64bit OS + 0.2.13 * fixing bug where you could not pull by sha value if a tag was not specified * fixing error message when attempting to use sha value with tar files diff --git a/src/confcom/README.md b/src/confcom/README.md index ec9fca2a49a..fa5e8d19f87 100644 --- a/src/confcom/README.md +++ b/src/confcom/README.md @@ -55,6 +55,7 @@ The `confcom` extension does not currently support: - [ARM Template functions](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-functions) other than `variables` and `parameters`. - Variables and Parameters with non-primitive data types e.g. objects and arrays +- Nested and Linked ARM Templates ## Trademarks diff --git a/src/confcom/azext_confcom/README.md b/src/confcom/azext_confcom/README.md index c0399d99173..aa4e2d56951 100644 --- a/src/confcom/azext_confcom/README.md +++ b/src/confcom/azext_confcom/README.md @@ -2,6 +2,8 @@ - [Microsoft Azure CLI 'confcom' Extension Examples and Security Policy Rules Documentation](#microsoft-azure-cli-confcom-extension-examples-and-security-policy-rules-documentation) - [Microsoft Azure CLI 'confcom' Extension Examples](#microsoft-azure-cli-confcom-extension-examples) + - [dmverity Layer Hashing](#dmverity-layer-hashing) + - [Security Policy Information Sources](#security-policy-information-sources) - [Security Policy Rules Documentation](#security-policy-rules-documentation) - [mount_device](#mount_device) - [unmount_device](#unmount_device) @@ -17,7 +19,7 @@ - [scratch_mount](#scratch_mount) - [scratch_unmount](#scratch_unmount) - [load_fragment](#load_fragment) - - [fragments](#fragments) + - [policy fragments](#policy-fragments) - [reason](#reason) - [A Sample Policy that Uses Framework](#a-sample-policy-that-uses-framework) - [allow_properties_access](#a-sample-policy-that-uses-framework) @@ -25,6 +27,7 @@ - [allow_runtime_logging](#a-sample-policy-that-uses-framework) - [allow_environment_variable_dropping](#allow_environment_variable_dropping) - [allow_unencrypted_scratch](#allow_unencrypted_scratch) + - [allow_capabilities_dropping](#allow_capabilities_dropping) ## Microsoft Azure CLI 'confcom' Extension Examples @@ -37,66 +40,94 @@ Install the Azure CLI and Confidential Computing extension. See the most recently released version of `confcom` extension. - az extension list-available -o table | grep confcom +```bash +az extension list-available -o table | grep confcom +``` To add the most recent confcom extension, run: - az extension add --name confcom +```bash +az extension add --name confcom +``` Use the `--version` argument to specify a version to add. +Run this to update to the latest version if an older version is already installed: + +```bash +az extension update --name confcom +``` + The `acipolicygen` command generates confidential computing security policies using an image, an input JSON file, or an ARM template. You can control the format of the generated policies using arguments. Note: It is recommended to use images with specific tags instead of the `latest` tag, as the `latest` tag can change at any time and images with different configurations may also have the latest tag. **Examples:** -Example 1: The following command creates a CCE policy and outputs it to the command line:
+Example 1: The following command creates a CCE policy and outputs it to the command line: - az confcom acipolicygen -a .\template.json --print-policy +```bash +az confcom acipolicygen -a .\template.json --print-policy +``` This command combines the information of images from the ARM template with other information such as mount, environment variables and commands from the ARM template to create a CCE policy. The `--print-policy` argument is included to display the policy on the command line rather than injecting it into the input ARM template. -Example 2: This command injects a CCE policy into [ARM-template](arm.template.md) based on input from [parameters-file](template.parameters.md) so that there is no need to change the ARM template to pass variables into the CCE policy:
+Example 2: This command injects a CCE policy into [ARM-template](arm.template.md) based on input from [parameters-file](template.parameters.md) so that there is no need to change the ARM template to pass variables into the CCE policy: - az confcom acipolicygen -a .\arm-template.json -p .\template.parameters.json +```bash +az confcom acipolicygen -a .\arm-template.json -p .\template.parameters.json +``` -This is mainly for decoupling purposes so that an ARM template can remain the same and evolving variables can go into a different file. +This is mainly for decoupling purposes so that an ARM template can remain the same and evolving variables can go into a different file. When a security policy gets injected into the ARM Template, the corresponding sha256 hash of the decoded security policy gets printed to the command line. This sha256 hash can be used for verifying the hostdata field of the SEV-SNP Attestation Report and/or used for key release policies using MAA (Microsoft Azure Attestation) or mHSM (managed Hardware Security Module) Example 3: This command takes the input of an ARM template to create a human-readable CCE policy in pretty print JSON format and output the result to the console. -NOTE: Generating JSON policy is for use by the customer only, and is not used by ACI In most cases. The default REGO format security policy is required.
+NOTE: Generating JSON policy is for use by the customer only, and is not used by ACI In most cases. The default REGO format security policy is required. - az confcom acipolicygen -a ".\arm_template" --outraw-pretty-print --json +```bash +az confcom acipolicygen -a ".\arm_template" --outraw-pretty-print +``` The default output of `acipolicygen` command is base64 encoded REGO format. -This example uses the `--json` argument to generate output in JSON format, use `--outraw-pretty-print` to indicate decoding policy in clear text and in pretty print format and print result to console. +This example uses `--outraw-pretty-print` to indicate decoding policy in clear text and in pretty print format and print result to console. -Example 4: The following command takes the input of an ARM template to create a human-readable CCE policy in clear text and print to console:
+Example 4: The following command takes the input of an ARM template to create a human-readable CCE policy in clear text and print to console: - az confcom acipolicygen -a ".\arm-template.json" --outraw +```bash +az confcom acipolicygen -a ".\arm-template.json" --outraw +``` Use `--outraw` argument to output policy in clear text compact REGO format. -Example 5: Input an ARM template to create a human-readable CCE policy in pretty print REGO format and save the result to a file named ".\output-file.rego":
+Example 5: Input an ARM template to create a human-readable CCE policy in pretty print REGO format and save the result to a file named ".\output-file.rego": - az confcom acipolicygen -a ".\arm-template" --outraw-pretty-print --save-to-file ".\output-file.rego" +```bash +az confcom acipolicygen -a ".\arm-template" --outraw-pretty-print --save-to-file ".\output-file.rego" +``` -Example 6: Validate the policy present in the ARM template under "ccepolicy" and the containers within the ARM template are compatible. If they are incompatible, a list of reasons is given and the exit status code will be 2:
+Example 6: Validate the policy present in the ARM template under "ccepolicy" and the containers within the ARM template are compatible. If they are incompatible, a list of reasons is given and the exit status code will be 2: - az confcom acipolicygen -a ".\arm-template.json" --diff +```bash +az confcom acipolicygen -a ".\arm-template.json" --diff +``` Example 7: Decode the existing CCE policy in ARM template and print to console in clear text. - az confcom acipolicygen -a ".\arm-template.json" --print-existing-policy +```bash +az confcom acipolicygen -a ".\arm-template.json" --print-existing-policy +``` Example 8: Generate a CCE policy using `--disable-stdio` argument. `--disable-stdio` argument disables container standard I/O access by setting `allow_stdio_access` to false. - az confcom acipolicygen -a ".\arm-template.json" --disable-stdio +```bash +az confcom acipolicygen -a ".\arm-template.json" --disable-stdio +``` Example 9: Inject a CCE policy into ARM template. -This command adds the `--debug-mode` argument to enable executing /bin/sh and /bin/bash in the container group:
+This command adds the `--debug-mode` argument to enable executing /bin/sh and /bin/bash in the container group: - az confcom acipolicygen -a .\sample-arm-input.json --debug-mode +```bash +az confcom acipolicygen -a .\sample-arm-input.json --debug-mode +``` In the above example, The `--debug-mode` modifies the following to allow users to shell into the container via portal or the command line: @@ -127,14 +158,18 @@ See [A Sample Policy that Uses Framework](#a-sample-policy-that-uses-framework) - allow_dump_stacks - allow_runtime_logging -Example 10: The confidential computing extension CLI is designed in such a way that generating policy does not necessarily have to depend on network calls as long as users have the layers of the images they want to generate policies for saved in a tar file locally. See the following example:
+Example 10: The confidential computing extension CLI is designed in such a way that generating policy does not necessarily have to depend on network calls as long as users have the layers of the images they want to generate policies for saved in a tar file locally. See the following example: - docker save ImageTag -o file.tar +```bash +docker save ImageTag -o file.tar +``` Disconnect from network and delete the local image from the docker daemon. Use the following command to generate CCE policy for the image. - az confcom acipolicygen -a .\sample-template-input.json --tar .\file.tar +```bash +az confcom acipolicygen -a .\sample-template-input.json --tar .\file.tar +``` Some users have unique scenarios such as cleanroom requirement. In this case, users can still generate security policies witout relying on network calls. @@ -143,123 +178,159 @@ Users just need to make a tar file by using the `docker save` command above, inc When generating security policy without using `--tar` argument, the confcom extension CLI tool attemps to fetch the image remotely if it is not locally available. However, the CLI tool does not attempt to fetch remotely if `--tar` argument is used. -Example 11: The process used in example 10 can also be used to save multiple images into the same tar file. See the following example:
+Example 11: The process used in example 10 can also be used to save multiple images into the same tar file. See the following example: - docker save ImageTag1 ImageTag2 ImageTag3 -o file.tar +```bash +docker save ImageTag1 ImageTag2 ImageTag3 -o file.tar +``` Disconnect from network and delete the local image from the docker daemon. Use the following command to generate CCE policy for the image. - az confcom acipolicygen -a .\sample-template-input.json --tar .\file.tar +```bash +az confcom acipolicygen -a .\sample-template-input.json --tar .\file.tar +``` -Example 12: If it is necessary to put images in their own tarballs, an external file can be used that maps images to their respective tarball paths. See the following example:
+Example 12: If it is necessary to put images in their own tarballs, an external file can be used that maps images to their respective tarball paths. See the following example: - docker save image:tag1 -o file1.tar - docker save image:tag2 -o file2.tar - docker save image:tag3 -o file3.tar +```bash +docker save image:tag1 -o file1.tar +docker save image:tag2 -o file2.tar +docker save image:tag3 -o file3.tar +``` Create the following file (as an example named "tar_mappings.json")on the local filesystem: - ```json - { - "image:tag1": "./file1.tar", - "image:tag2": "./file2.tar", - "image:tag3": "./file3.tar", - } - ``` +```json +{ + "image:tag1": "./file1.tar", + "image:tag2": "./file2.tar", + "image:tag3": "./file3.tar", +} +``` Disconnect from network and delete the local image from the docker daemon. Use the following command to generate CCE policy for the image. - az confcom acipolicygen -a .\sample-template-input.json --tar .\tar_mappings.json +```bash +az confcom acipolicygen -a .\sample-template-input.json --tar .\tar_mappings.json +``` -Example 13: Some use cases necessitate the use of regular expressions to allow for environment variables where either their values are secret, or unknown at policy-generation time. For these cases, the workflow below can be used:
+Example 13: Some use cases necessitate the use of regular expressions to allow for environment variables where either their values are secret, or unknown at policy-generation time. For these cases, the workflow below can be used: Create parameters in the ARM Template for each environment variable that has an unknown or secret value such as: - ```json - { - "parameters": { - "placeholderValue": { - "type": "string", - "metadata": { - "description": "This value will not be placed in the template.json" - } +```json +{ + "parameters": { + "placeholderValue": { + "type": "string", + "metadata": { + "description": "This value will not be placed in the template.json" } - }, - } - ``` + } + }, +} +``` Note that this parameter declaration does not have a "defaultValue". Once these parameters are defined, they may be used later in the ARM Template such as: - ```json - { - "environmentVariables": [ - { - "name": "PATH", - "value": "/customized/path/value" - }, - { - "name": "MY_SECRET", - "value": "[parameters('placeholderValue')]" - } - ] - } - ``` +```json +{ + "environmentVariables": [ + { + "name": "PATH", + "value": "/customized/path/value" + }, + { + "name": "MY_SECRET", + "value": "[parameters('placeholderValue')]" + } + ] +} +``` The policy can then be generated with: - az confcom acipolicygen -a template.json +```bash +az confcom acipolicygen -a template.json +``` Because the ARM Template does not have a value defined for the "placeholderValue", the regular expression ".*" is used in the Rego policy. This allows for any value to be used. If the value is contained in a parameters file, that can be used when deploying such as: - az deployment group create --template-file "template.json" --parameters "parameters.json" +```bash +az deployment group create --template-file "template.json" --parameters "parameters.json" +``` + +## dmverity Layer Hashing + +To ensure the container that is being deployed is the intended container, the `confcom` tooling uses [dmverity hashing](https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html). This is done by downloading the container locally with the Docker Daemon (or using a pre-downloaded tar file of the OCI image) and performing the dmverity hashing using the [dmverity-vhd tool](https://github.com/microsoft/hcsshim/tree/main/cmd/dmverity-vhd). These layer hashes are placed into the Rego security policy in the "layers" field of their respective container. Note that these dmverity layer hashes are different than the layer hashes reported by `docker image inspect`. + +### Mixed-mode Policy Generation + +An OCI image can be made available for policy generation in three ways: + +1. The image is in the local Docker Daemon and can be found either with its image and tag names or its sha256 hash. +2. The image is in an accessible remote repository. Usually this is either Docker Hub or Azure Container Registry. Note that if the registry is private, you must log in prior to policy generation. +3. The image is locally saved as a tar file in the form specified by `docker save`. + +Mixed-mode policy generation is available in the `confcom` tooling, meaning images within the same security policy can be in any of these three locations with no issues. + +## Security Policy Information Sources + +Each container in a security policy can get its information from two different sources: + +1. The image manifest. This can be explored using `docker image inspect` +2. The ARM Template used to generate the security policy. This can be used for startup command, environment variables, etc. + +The `confcom` tooling uses the image manifest for default values and then adds or overwrites those values using what is found in the ARM Template. The API Reference for defining values in the [ARM Template can be found here](https://learn.microsoft.com/en-us/azure/templates/microsoft.containerinstance/containergroups?pivots=deployment-language-arm-template) ## Security Policy Rules Documentation Below is an example rego policy: - ```rego - package policy +```rego +package policy - import future.keywords.every - import future.keywords.in +import future.keywords.every +import future.keywords.in - api_svn := "0.10.0" - framework_svn := "0.1.0" +api_version := "0.10.0" +framework_version := "0.1.0" - fragments := [...] +fragments := [...] - containers := [...] +containers := [...] - allow_properties_access := false - allow_dump_stacks := false - allow_runtime_logging := false - allow_environment_variable_dropping := true - allow_unencrypted_scratch := false +allow_properties_access := false +allow_dump_stacks := false +allow_runtime_logging := false +allow_environment_variable_dropping := true +allow_unencrypted_scratch := false +allow_capabilities_dropping := true - mount_device := data.framework.mount_device - unmount_device := data.framework.unmount_device - mount_overlay := data.framework.mount_overlay - unmount_overlay := data.framework.unmount_overlay - create_container := data.framework.create_container - exec_in_container := data.framework.exec_in_container - exec_external := data.framework.exec_external - shutdown_container := data.framework.shutdown_container - signal_container_process := data.framework.signal_container_process - plan9_mount := data.framework.plan9_mount - plan9_unmount := data.framework.plan9_unmount - get_properties := data.framework.get_properties - dump_stacks := data.framework.dump_stacks - runtime_logging := data.framework.runtime_logging - load_fragment := data.framework.load_fragment - scratch_mount := data.framework.scratch_mount - scratch_unmount := data.framework.scratch_unmount +mount_device := data.framework.mount_device +unmount_device := data.framework.unmount_device +mount_overlay := data.framework.mount_overlay +unmount_overlay := data.framework.unmount_overlay +create_container := data.framework.create_container +exec_in_container := data.framework.exec_in_container +exec_external := data.framework.exec_external +shutdown_container := data.framework.shutdown_container +signal_container_process := data.framework.signal_container_process +plan9_mount := data.framework.plan9_mount +plan9_unmount := data.framework.plan9_unmount +get_properties := data.framework.get_properties +dump_stacks := data.framework.dump_stacks +runtime_logging := data.framework.runtime_logging +load_fragment := data.framework.load_fragment +scratch_mount := data.framework.scratch_mount +scratch_unmount := data.framework.scratch_unmount - reason := {"errors": data.framework.errors} - ``` +reason := {"errors": data.framework.errors} +``` Every valid policy contain rules with the following names in the policy namespace. Each rule must return a Rego object with a member named allowed, which indicates whether the action is allowed by policy. @@ -269,165 +340,165 @@ We document each rule as follow: Receives an input object with the following members: - ```json - { - "name": "mount_device", - "target": "", - "deviceHash": "" - } - ``` +```json +{ + "name": "mount_device", + "target": "", + "deviceHash": "" +} +``` ## unmount_device Receives an input object with the following members: - ```json - { - "name": "unmount_device", - "unmountTarget": "" - } - ``` +```json +{ + "name": "unmount_device", + "unmountTarget": "" +} +``` ## mount_overlay Describe the layers to mount: - ```json - { - "name": "mount_overlay", - "containerID": "", - "layerPaths": [ - "", - "", - "", - /*...*/ - ], - "target": "" - } - ``` +```json +{ + "name": "mount_overlay", + "containerID": "", + "layerPaths": [ + "", + "", + "", + /*...*/ + ], + "target": "" +} +``` ## unmount_overlay Receives an input object with the following members: - ```json - { - "name": "unmount_overlay", - "unmountTarget": "" - } - ``` +```json +{ + "name": "unmount_overlay", + "unmountTarget": "" +} +``` ## create_container Indicates whether the UVM is allowed to create a specific container with the exact parameters provided to the method. Provided in the following input object, the framework rule checks the exact parameters such as (command, environment variables, mounts etc.) - ```json - { - "name": "create_container", - "containerID": "", - "argList": [ - "", - "", - "", - /*...*/ - ], - "envList": [ - "=", - /*...*/ - ], - "workingDir": "", - "sandboxDir": "", - "hugePagesDir": "", - "mounts": [ - { - "destination": "", - "options": [ - "", - "", - /*...*/ - ], - "source": "", - "type": ""}, - ], - "privileged": "" - } - ``` +```json +{ + "name": "create_container", + "containerID": "", + "argList": [ + "", + "", + "", + /*...*/ + ], + "envList": [ + "=", + /*...*/ + ], + "workingDir": "", + "sandboxDir": "", + "hugePagesDir": "", + "mounts": [ + { + "destination": "", + "options": [ + "", + "", + /*...*/ + ], + "source": "", + "type": ""}, + ], + "privileged": "" +} +``` ## exec_in_container Determines if a process should be executed in a container. Receives an input object with the following elements: - ```json - { - "containerID": "", - "argList": [ - "", - "", - "", - /*...*/ - ], - "envList": [ - "=", - /*...*/ - ], - "workingDir": "" - } - ``` +```json +{ + "containerID": "", + "argList": [ + "", + "", + "", + /*...*/ + ], + "envList": [ + "=", + /*...*/ + ], + "workingDir": "" +} +``` ## exec_external Determines if a process should be executed in the UVM. Receives an input object with the following elements: - ```json - { - "name": "exec_external", - "argList": [ - "", - "", - "", - /*...*/ - ], - "envList": [ - "=", - /*...*/ - ], - "workingDir": "" - } - ``` +```json +{ + "name": "exec_external", + "argList": [ + "", + "", + "", + /*...*/ + ], + "envList": [ + "=", + /*...*/ + ], + "workingDir": "" +} +``` ## shutdown_container Receives an input object with the following elements: - ```json - { - "name": "shutdown_container", - "containerID": "" - } - ``` +```json +{ + "name": "shutdown_container", + "containerID": "" +} +``` ## signal_container_process Describe the signal sent to the container. Receives an input object with the following elements: - ```json - { - "name": "signal_container_process", - "containerID": "", - "signal": "", - "isInitProcess": "", - "argList": [ - "", - "", - "", - /*...*/ - ] - } - ``` +```json +{ + "name": "signal_container_process", + "containerID": "", + "signal": "", + "isInitProcess": "", + "argList": [ + "", + "", + "", + /*...*/ + ] +} +``` ## plan9_mount @@ -437,52 +508,52 @@ A serious attack consists of overwriting attested directories on the UVM and the This rule contains a target that designates destination mount so that the mentioned attack does not happen. It receives an input with the following elements: - ```json - { - "name": "plan9_mount", - "target": "" - } - ``` +```json +{ + "name": "plan9_mount", + "target": "" +} +``` ## plan9_unmount Receives an input with the following elements: - ```json - { - "name": "plan9_unmount", - "unmountTarget": "" - } - ``` +```json +{ + "name": "plan9_unmount", + "unmountTarget": "" +} +``` ## scratch_mount Scratch is writable storage from the UVM to the container. It receives an input with the following elements: - ```json - { - "name": "scratch_mount", - "target": "", - "encrypted": "true|false" - } - ``` +```json +{ + "name": "scratch_mount", + "target": "", + "encrypted": "true|false" +} +``` ## scratch_unmount Receives an input with the following elements: - ```json - { - "name": "scratch_unmount", - "unmountTarget": "", - } - ``` +```json +{ + "name": "scratch_unmount", + "unmountTarget": "", +} +``` ## load_fragment This rule is used to determine whether a policy fragment can be loaded. -See [policy fragments](#policy_fragments) for detailed explanation. +See [policy fragments](#policy-fragments) for detailed explanation. ## policy fragments @@ -499,23 +570,23 @@ For now, we will focus on the ACI sidecar use case. See the following example and how it defines a policy fragment. The following policy fragment states that my confidential computing environment should trust containers published by the DID `did:web:accdemo.github.io` on the feed named `accdemo/utilities`. - ```rego - fragments := [ - { - "feed": "accdemo/utilities", - "iss": "did:web:accdemo.github.io", - "includes": [<"containers"|"fragments"|"external_processes"|"namespace">] - } - ] - - default load_fragment := [] - load_fragment := includes { - some fragment in fragments - input.iss == fragment.iss - input.feed == fragment.feed - includes := fragment.includes +```rego +fragments := [ + { + "feed": "accdemo/utilities", + "iss": "did:web:accdemo.github.io", + "includes": [<"containers"|"fragments"|"external_processes"|"namespace">] } - ``` +] + +default load_fragment := [] +load_fragment := includes { + some fragment in fragments + input.iss == fragment.iss + input.feed == fragment.feed + includes := fragment.includes +} +``` Every time a policy fragment is presented to the enclosing system (e.g. GCS), the enclosing system is provided with a COSE_Sign1 signed envelope. The header in the envelope contains the feed and the issuer and these information are included in the `input` context. @@ -568,3 +639,7 @@ It then tests to see if that set satisfies any containers. ## allow_unencrypted_scratch This rule determines whether unencrypted writable storage from the UVM to the container is allowed. + +## allow_capabilities_dropping + +Whether to allow capabilities to be dropped in the same manner as allow_environment_variable_dropping. diff --git a/src/confcom/azext_confcom/_help.py b/src/confcom/azext_confcom/_help.py index c195a985727..f1f455134e3 100644 --- a/src/confcom/azext_confcom/_help.py +++ b/src/confcom/azext_confcom/_help.py @@ -65,10 +65,6 @@ type: boolean short-summary: 'When combined with an input ARM Template, verifies the policy present in the ARM Template under "ccePolicy" and the containers within the ARM Template are compatible. If they are incompatible, a list of reasons is given and the exit status code will be 2.' - - name: --json -j - type: string - short-summary: 'Outputs in JSON format instead of Rego' - - name: --outraw type: boolean short-summary: 'Output policy in clear text compact JSON instead of default base64 format' @@ -90,8 +86,8 @@ text: az confcom acipolicygen --template-file "./template.json" - name: Input an ARM Template file to create a human-readable Confidential Container Security Policy text: az confcom acipolicygen --template-file "./template.json" --outraw-pretty-print - - name: Input an ARM Template file to save a Confidential Container Security Policy to a file - text: az confcom acipolicygen --template-file "./template.json" -s "./output-file.txt" + - name: Input an ARM Template file to save a Confidential Container Security Policy to a file as base64 encoded text + text: az confcom acipolicygen --template-file "./template.json" -s "./output-file.txt" --print-policy - name: Input an ARM Template file and use a tar file as the image source instead of the Docker daemon text: az confcom acipolicygen --template-file "./template.json" --tar "./image.tar" """ diff --git a/src/confcom/azext_confcom/_params.py b/src/confcom/azext_confcom/_params.py index db08cf5e228..41e8babdd61 100644 --- a/src/confcom/azext_confcom/_params.py +++ b/src/confcom/azext_confcom/_params.py @@ -76,12 +76,6 @@ def load_arguments(self, _): required=False, help="Disabling container stdio will disable the ability to see the output of the container in the terminal for Confidential ACI", ) - c.argument( - "use_json", - options_list=("--json", "-j"), - required=False, - help="Output in JSON format", - ) c.argument( "diff", options_list=("--diff", "-d"), @@ -95,7 +89,7 @@ def load_arguments(self, _): help="Validate that the image used to generate the CCE Policy for a sidecar container will be allowed by its generated policy", ) c.argument( - "print-existing-policy", + "print_existing_policy", options_list=("--print-existing-policy"), required=False, action="store_true", diff --git a/src/confcom/azext_confcom/azext_metadata.json b/src/confcom/azext_confcom/azext_metadata.json index b44aea150b4..906e368a65c 100644 --- a/src/confcom/azext_confcom/azext_metadata.json +++ b/src/confcom/azext_confcom/azext_metadata.json @@ -1,4 +1,3 @@ { - "azext.isPreview": true, "azext.minCliCoreVersion": "2.26.2" } \ No newline at end of file diff --git a/src/confcom/azext_confcom/config.py b/src/confcom/azext_confcom/config.py index c6a1b8be4d1..a9807cc236c 100644 --- a/src/confcom/azext_confcom/config.py +++ b/src/confcom/azext_confcom/config.py @@ -25,6 +25,11 @@ ACI_FIELD_CONTAINERS_MOUNTS_READONLY = "readonly" ACI_FIELD_CONTAINERS_WAIT_MOUNT_POINTS = "wait_mount_points" ACI_FIELD_CONTAINERS_ALLOW_ELEVATED = "allow_elevated" +ACI_FIELD_CONTAINERS_SECURITY_CONTEXT = "securityContext" +ACI_FIELD_CONTAINERS_ALLOW_PRIVILEGE_ESCALATION = "allowPrivilegeEscalation" +ACI_FIELD_CONTAINERS_RUN_AS_USER = "runAsUser" +ACI_FIELD_CONTAINERS_RUN_AS_GROUP = "runAsGroup" +ACI_FIELD_CONTAINERS_SECCOMP_PROFILE = "seccompProfile" ACI_FIELD_CONTAINERS_REGO_FRAGMENTS = "fragments" ACI_FIELD_CONTAINERS_REGO_FRAGMENTS_FEED = "feed" ACI_FIELD_CONTAINERS_REGO_FRAGMENTS_ISS = "iss" @@ -51,6 +56,7 @@ ACI_FIELD_TEMPLATE_VARIABLES = "variables" ACI_FIELD_TEMPLATE_VOLUMES = "volumes" ACI_FIELD_TEMPLATE_IMAGE = "image" +ACI_FIELD_TEMPLATE_SECURITY_CONTEXT = "securityContext" ACI_FIELD_TEMPLATE_RESOURCE_LABEL = "Microsoft.ContainerInstance/containerGroups" ACI_FIELD_TEMPLATE_COMMAND = "command" ACI_FIELD_TEMPLATE_ENVS = "environmentVariables" @@ -60,6 +66,10 @@ ACI_FIELD_TEMPLATE_MOUNTS_READONLY = "readOnly" ACI_FIELD_TEMPLATE_CONFCOM_PROPERTIES = "confidentialComputeProperties" ACI_FIELD_TEMPLATE_CCE_POLICY = "ccePolicy" +ACI_FIELD_CONTAINERS_PRIVILEGED = "privileged" +ACI_FIELD_CONTAINERS_CAPABILITIES = "capabilities" +ACI_FIELD_CONTAINERS_CAPABILITIES_ADD = "add" +ACI_FIELD_CONTAINERS_CAPABILITIES_DROP = "drop" # output json values @@ -84,9 +94,23 @@ POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS_OPTIONS = "options" POLICY_FIELD_CONTAINERS_ELEMENTS_WAIT_MOUNT_POINTS = "wait_mount_points" POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED = "allow_elevated" -POLICY_FIELD_CONTAINER_EXEC_PROCESSES = "exec_processes" -POLICY_FIELD_CONTAINER_SIGNAL_CONTAINER_PROCESSES = "signals" -POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS = "allow_stdio_access" +POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES = "no_new_privileges" +POLICY_FIELD_CONTAINERS_ELEMENTS_EXEC_PROCESSES = "exec_processes" +POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES = "signals" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER = "user" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER_USER_IDNAME = "user_idname" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER_GROUP_IDNAMES = "group_idnames" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER_UMASK = "umask" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER_PATTERN = "pattern" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES = "capabilities" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING = "bounding" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE = "effective" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE = "inheritable" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED = "permitted" +POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT = "ambient" +POLICY_FIELD_CONTAINERS_ELEMENTS_USER_STRATEGY = "strategy" +POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256 = "seccomp_profile_sha256" +POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS = "allow_stdio_access" POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS = "fragments" POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS_FEED = "feed" POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS_ISS = "iss" @@ -132,5 +156,14 @@ SIDECAR_REGO_FILE = "./data/sidecar_rego_policy.txt" SIDECAR_REGO_FILE_PATH = f"{script_directory}/{SIDECAR_REGO_FILE}" SIDECAR_REGO_POLICY = os_util.load_str_from_file(SIDECAR_REGO_FILE_PATH) + +# api version +API_VERSION = _config["version_api"] # default containers to be added to all container groups DEFAULT_CONTAINERS = _config["default_containers"] +# default container user config to be added for security context +DEFAULT_USER = _config["default_user"] +# default unpriviliged user capabilities to be added for security context +DEFAULT_UNPRIVILEGED_CAPABILITIES = _config["default_unprivileged_capabilities"] +# default priviliged user capabilities to be added for security context +DEFAULT_PRIVILEGED_CAPABILITIES = _config["default_privileged_capabilities"] diff --git a/src/confcom/azext_confcom/container.py b/src/confcom/azext_confcom/container.py index b52be58638e..ff6a5955223 100644 --- a/src/confcom/azext_confcom/container.py +++ b/src/confcom/azext_confcom/container.py @@ -7,13 +7,21 @@ import json import os from typing import Any, List, Dict -from azext_confcom.template_util import case_insensitive_dict_get, replace_params_and_vars +from azext_confcom.template_util import ( + case_insensitive_dict_get, + replace_params_and_vars, + str_to_sha256, + process_seccomp_policy +) from azext_confcom import config from azext_confcom.errors import eprint +from azext_confcom.os_util import base64_to_str _DEFAULT_MOUNTS = config.DEFAULT_MOUNTS_USER +_DEFAULT_USER = config.DEFAULT_USER + _INJECTED_CUSTOMER_ENV_RULES = ( config.OPENGCS_ENV_RULES + config.FABRIC_ENV_RULES @@ -21,6 +29,14 @@ + config.ENABLE_RESTART_ENV_RULE ) +_CAPABILITIES = { + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING: [], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE: [], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE: [], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED: [], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT: [], +} + def extract_container_image(container_json: Any) -> str: containerImage = case_insensitive_dict_get( @@ -207,28 +223,12 @@ def extract_exec_process(container_json: Any) -> List: exec_processes_output.append( { config.POLICY_FIELD_CONTAINERS_ELEMENTS_COMMANDS: exec_command, - config.POLICY_FIELD_CONTAINER_SIGNAL_CONTAINER_PROCESSES: exec_signals, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES: exec_signals, } ) return exec_processes_output -def extract_allow_elevated(container_json: Any) -> bool: - _allow_elevated = case_insensitive_dict_get( - container_json, config.ACI_FIELD_CONTAINERS_ALLOW_ELEVATED - ) - if _allow_elevated: - if not isinstance(_allow_elevated, bool): - eprint( - f'Field ["{config.ACI_FIELD_CONTAINERS}"]' - + f'["{config.ACI_FIELD_CONTAINERS_ALLOW_ELEVATED}"] can only be boolean value.' - ) - else: - # default is allow_elevated should be true - _allow_elevated = True - return _allow_elevated - - def extract_allow_stdio_access(container_json: Any) -> bool: # get the field for Standard IO access, default to true allow_stdio_value = case_insensitive_dict_get( @@ -238,6 +238,219 @@ def extract_allow_stdio_access(container_json: Any) -> bool: return allow_stdio_access +def extract_user(container_json: Any) -> Dict: + security_context = case_insensitive_dict_get( + container_json, config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT + ) + + user = copy.deepcopy(_DEFAULT_USER) + # assumes that securityContext field is optional + if security_context: + # To-Do: figure out how to determine if regex patterns + # get the field for run as user + run_as_user_value = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_RUN_AS_USER + ) + + if isinstance(run_as_user_value, int) and run_as_user_value >= 0: + user[config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_USER_IDNAME] = { + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_PATTERN: str(run_as_user_value), + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_STRATEGY: "id" + } + elif run_as_user_value is not None: + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_RUN_AS_USER}"] can only be an integer value.' + ) + + # get the field for run as group + run_as_group_value = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_RUN_AS_GROUP + ) + + if isinstance(run_as_group_value, int) and run_as_group_value >= 0: + user[config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_GROUP_IDNAMES][0] = { + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_PATTERN: str(run_as_group_value), + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_STRATEGY: "id" + } + elif run_as_group_value is not None: + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_RUN_AS_GROUP}"] can only be an integer value.' + ) + + return user + + +def extract_capabilities(container_json: Any, privileged_value: bool): + security_context = case_insensitive_dict_get( + container_json, config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT + ) + + output_capabilities = copy.deepcopy(_CAPABILITIES) + non_added_fields = [ + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED, + ] + + # add privileged default capabilities if true, otherwise add unprivileged default capabilities + if privileged_value: + # only ambient should be empty + non_added_fields.append(config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE) + for key in non_added_fields: + output_capabilities[key] = copy.deepcopy(config.DEFAULT_PRIVILEGED_CAPABILITIES) + else: + # add the default capabilities to the output + for key in non_added_fields: + output_capabilities[key] = copy.deepcopy(config.DEFAULT_UNPRIVILEGED_CAPABILITIES) + + # add and drop capabilities if they are explicitly set in the ARM template + capabilities = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_CAPABILITIES + ) + + # user can ADD and DROP capabilities in the ARM template + if capabilities: + # error check if capabilities is not a dict + if not isinstance(capabilities, dict): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_CAPABILITIES}"] can only be a dictionary.' + ) + + # get the add field + add = case_insensitive_dict_get( + capabilities, config.ACI_FIELD_CONTAINERS_CAPABILITIES_ADD + ) + if add: + # error check if add is not a list + if not isinstance(add, list): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_CAPABILITIES_ADD}"] can only be a list.' + ) + # error check if add contains non-string values + for capability in add: + # error check that all the items in "add" are strings + if not isinstance(capability, str): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]' + + f'["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_CAPABILITIES_ADD}"] can only contain strings.' + ) + for key in non_added_fields: + # add the capabilities to the output, except ambient list + # we still want the ambient set to be empty + output_capabilities[key] += add + + # get the drop field + drop = case_insensitive_dict_get( + capabilities, config.ACI_FIELD_CONTAINERS_CAPABILITIES_DROP + ) + if drop: + # error check if drop is not a list + if not isinstance(drop, list): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_CAPABILITIES_DROP}"] can only be a list.' + ) + # error check that all the items in "drop" are strings + for capability in drop: + if not isinstance(capability, str): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]' + + f'["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_CAPABILITIES_DROP}"] can only contain strings.' + ) + # drop the capabilities from the output + for keys in output_capabilities: + output_capabilities[keys] = [x for x in output_capabilities[keys] if x not in drop] + # de-duplicate the capabilities + for key, value in output_capabilities.items(): + output_capabilities[key] = sorted(list(set(value))) + + return output_capabilities + + +def extract_allow_elevated(container_json: Any) -> bool: + security_context = case_insensitive_dict_get( + container_json, config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT + ) + + # get the field for privileged, default to false + privileged_value = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_PRIVILEGED + ) + if privileged_value and not isinstance(privileged_value, bool) and not isinstance(privileged_value, str): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_PRIVILEGED}"] can only be a boolean or string value.' + ) + + # force the field into a bool + if isinstance(privileged_value, str): + privileged_value = privileged_value.lower() == "true" + # default to false + return privileged_value or False + + +def extract_seccomp_profile_sha256(container_json: Any) -> Dict: + security_context = case_insensitive_dict_get( + container_json, config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT + ) + + seccomp_profile_sha256 = "" + # assumes that securityContext field is optional + if security_context: + # get the field for seccomp_profile + seccomp_profile_base64 = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_SECCOMP_PROFILE + ) + + if seccomp_profile_base64 is not None and not isinstance(seccomp_profile_base64, str): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_SECCOMP_PROFILE}"] can only be a string.' + ) + elif seccomp_profile_base64 is not None: + # clean up and jsonify the seccomp profile + seccomp_profile = process_seccomp_policy(base64_to_str(seccomp_profile_base64)) + seccomp_profile_str = json.dumps(seccomp_profile, separators=(',', ':')) + # hash the seccomp profile + seccomp_profile_sha256 = str_to_sha256(seccomp_profile_str) + return seccomp_profile_sha256 + + +def extract_allow_privilege_escalation(container_json: Any) -> bool: + security_context = case_insensitive_dict_get( + container_json, config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT + ) + + # default to false so that no_new_privileges defaults to false + allow_privilege_escalation = True + # assumes that securityContext field is optional + if security_context: + + # get the field for allow privilege escalation, default to true + temp_privilege_escalation = case_insensitive_dict_get( + security_context, + config.ACI_FIELD_CONTAINERS_ALLOW_PRIVILEGE_ESCALATION + ) + if temp_privilege_escalation is not None: + if not isinstance(temp_privilege_escalation, bool) and not isinstance(temp_privilege_escalation, str): + eprint( + f'Field ["{config.ACI_FIELD_CONTAINERS}"]["{config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT}"]' + + f'["{config.ACI_FIELD_CONTAINERS_PRIVILEGED}"] can only be a boolean or string value.' + ) + + # force the field into a bool + if isinstance(temp_privilege_escalation, str): + temp_privilege_escalation = temp_privilege_escalation.lower() == "true" + allow_privilege_escalation = temp_privilege_escalation + return allow_privilege_escalation + + def extract_get_signals(container_json: Any) -> List: # get the signals info used as a liveness probe signals = ( @@ -278,12 +491,19 @@ def from_json( command = extract_command(container_json) working_dir = extract_working_dir(container_json) mounts = extract_mounts(container_json) - allow_elevated = extract_allow_elevated(container_json) + # the first half of the conditional is for backwards compatibility with input.json-formatted files + allow_elevated = case_insensitive_dict_get( + container_json, + config.ACI_FIELD_CONTAINERS_ALLOW_ELEVATED) or extract_allow_elevated(container_json) exec_processes = extract_exec_process( container_json ) signals = extract_get_signals(container_json) + user = extract_user(container_json) + capabilities = extract_capabilities(container_json, allow_elevated) + seccomp_profile_sha256 = extract_seccomp_profile_sha256(container_json) allow_stdio_access = extract_allow_stdio_access(container_json) + allow_privilege_escalation = extract_allow_privilege_escalation(container_json) return ContainerImage( containerImage=container_image, environmentRules=environment_rules, @@ -294,7 +514,11 @@ def from_json( extraEnvironmentRules=[], execProcesses=exec_processes, signals=signals, + user=user, + capabilities=capabilities, + seccomp_profile_sha256=seccomp_profile_sha256, allowStdioAccess=allow_stdio_access, + allowPrivilegeEscalation=allow_privilege_escalation, id_val=id_val, ) @@ -308,7 +532,11 @@ def __init__( allow_elevated: bool, id_val: str, extraEnvironmentRules: Dict, + capabilities: Dict = copy.deepcopy(_CAPABILITIES), + user: Dict = copy.deepcopy(_DEFAULT_USER), + seccomp_profile_sha256: str = "", allowStdioAccess: bool = True, + allowPrivilegeEscalation: bool = True, execProcesses: List = None, signals: List = None, ) -> None: @@ -324,6 +552,10 @@ def __init__( self._mounts = mounts self._allow_elevated = allow_elevated self._allow_stdio_access = allowStdioAccess + self._seccomp_profile_sha256 = seccomp_profile_sha256 + self._user = user or {} + self._capabilities = capabilities + self._allow_privilege_escalation = allowPrivilegeEscalation self._policy_json = None self._policy_json_str = None self._policy_json_str_pp = None @@ -335,7 +567,6 @@ def __init__( def get_policy_json(self) -> str: if not self._policy_json: self._policy_json_serialization() - return self._policy_json def get_id(self) -> str: @@ -362,9 +593,18 @@ def get_layers(self) -> List[str]: def set_layers(self, layers: List[str]) -> None: self._layers = layers + def get_user(self) -> Dict: + return self._user + + def set_user(self, user: Dict) -> None: + self._user = user + def get_mounts(self) -> List: return self._mounts + def get_seccomp_profile_sha256(self) -> str: + return self._seccomp_profile_sha256 + def set_extra_environment_rules(self, rules: Dict) -> None: self._extraEnvironmentRules = rules @@ -456,11 +696,14 @@ def _populate_policy_json_elements(self) -> Dict[str, Any]: config.POLICY_FIELD_CONTAINERS_ELEMENTS_WORKINGDIR: self._workingDir, config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS: self._get_mounts_json(), config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED: self._allow_elevated, - config.POLICY_FIELD_CONTAINER_EXEC_PROCESSES: self._exec_processes, - config.POLICY_FIELD_CONTAINER_SIGNAL_CONTAINER_PROCESSES: self._signals, - config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS: self._allow_stdio_access, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_EXEC_PROCESSES: self._exec_processes, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES: self._signals, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user(), + config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES: self._capabilities, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256: self._seccomp_profile_sha256, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS: self._allow_stdio_access, + config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES: not self._allow_privilege_escalation } - self._policy_json = elements return self._policy_json @@ -487,32 +730,6 @@ def from_json( image.set_extra_environment_rules(_INJECTED_CUSTOMER_ENV_RULES) return image - def __init__( - self, - containerImage: str, - environmentRules: List[Dict], - command: List[str], - mounts: List[Dict], - workingDir: str, - allowElevated: bool, - id_val: str, - execProcesses: List = None, - signals: List = None, - extraEnvironmentRules: Dict = _INJECTED_CUSTOMER_ENV_RULES, - ) -> None: - super().__init__( - containerImage=containerImage, - environmentRules=environmentRules, - command=command, - mounts=mounts, - workingDir=workingDir, - allow_elevated=allowElevated, - id_val=id_val, - signals=signals or [], - extraEnvironmentRules=extraEnvironmentRules, - execProcesses=execProcesses or [], - ) - def _populate_policy_json_elements(self) -> Dict[str, Any]: elements = super()._populate_policy_json_elements() self._policy_json = elements diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index b7016452baa..62f8461b599 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -10,10 +10,11 @@ from knack.log import get_logger from azext_confcom.config import DEFAULT_REGO_FRAGMENTS from azext_confcom import os_util -from azext_confcom.template_util import pretty_print_func, print_func +from azext_confcom.template_util import pretty_print_func, print_func, str_to_sha256 from azext_confcom.init_checks import run_initial_docker_checks from azext_confcom.template_util import inject_policy_into_template, print_existing_policy_from_arm_template from azext_confcom import security_policy +from azext_confcom.security_policy import OutputType logger = get_logger(__name__) @@ -27,7 +28,6 @@ def acipolicygen_confcom( infrastructure_svn: str, tar_mapping_location: str, approve_wildcards: str = False, - use_json: bool = False, outraw: bool = False, outraw_pretty_print: bool = False, diff: bool = False, @@ -40,27 +40,26 @@ def acipolicygen_confcom( ): if sum(map(bool, [input_path, arm_template, image_name])) != 1: - logger.error("Can only generate CCE policy from one source at a time") - sys.exit(1) + error_out("Can only generate CCE policy from one source at a time") if sum(map(bool, [print_policy_to_terminal, outraw, outraw_pretty_print])) > 1: - logger.error("Can only print in one format at a time") - sys.exit(1) + error_out("Can only print in one format at a time") elif (diff and input_path) or (diff and image_name): - logger.error("Can only diff CCE policy from ARM Template") - sys.exit(1) + error_out("Can only diff CCE policy from ARM Template") elif arm_template_parameters and not arm_template: - logger.error( + error_out( "Can only use ARM Template Parameters if ARM Template is also present" ) - sys.exit(1) + elif save_to_file and arm_template and not (print_policy_to_terminal or outraw or outraw_pretty_print): + error_out("Must print policy to terminal when saving to file") if print_existing_policy: - if not arm_template: - logger.error("Can only print existing policy from ARM Template") - sys.exit(1) - else: - print_existing_policy_from_arm_template(arm_template, arm_template_parameters) - sys.exit(0) + print_existing_policy_from_arm_template(arm_template, arm_template_parameters) + sys.exit(0) + + if debug_mode: + logger.warning("WARNING: %s %s", + "Debug mode must only be used for debugging purposes. ", + "It should not be used for production systems.\n") tar_mapping = tar_mapping_validation(tar_mapping_location) @@ -69,13 +68,7 @@ def acipolicygen_confcom( container_group_policies = None # warn user that input infrastructure_svn is less than the configured default value - if infrastructure_svn and parse_version(infrastructure_svn) < parse_version( - DEFAULT_REGO_FRAGMENTS[0]["minimum_svn"] - ): - logger.warning( - "Input Infrastructure Fragment Software Version Number is less than the default Infrastructure SVN: %s", - DEFAULT_REGO_FRAGMENTS[0]["minimum_svn"], - ) + check_infrastructure_svn(infrastructure_svn) # telling the user what operation we're doing logger.warning( @@ -122,17 +115,27 @@ def acipolicygen_confcom( exit_code = validate_sidecar_in_policy(policy, output_type == security_policy.OutputType.PRETTY_PRINT) elif diff: exit_code = get_diff_outputs(policy, output_type == security_policy.OutputType.PRETTY_PRINT) - elif arm_template and (not print_policy_to_terminal and not outraw and not outraw_pretty_print): + elif arm_template and not (print_policy_to_terminal or outraw or outraw_pretty_print): + seccomp_profile_hashes = {x.get_id(): x.get_seccomp_profile_sha256() for x in policy.get_images()} result = inject_policy_into_template(arm_template, arm_template_parameters, - policy.get_serialized_output(output_type, use_json), count) + policy.get_serialized_output(), count, + seccomp_profile_hashes) if result: - print("CCE Policy successfully injected into ARM Template") + # this is always going to be the unencoded policy + print(str_to_sha256(policy.get_serialized_output(OutputType.RAW))) + logger.info("CCE Policy successfully injected into ARM Template") else: # output to terminal - print(f"{policy.get_serialized_output(output_type, use_json)}\n\n") + print(f"{policy.get_serialized_output(output_type)}\n\n") # output to file if save_to_file: - policy.save_to_file(save_to_file, output_type, use_json) + logger.warning( + "%s %s %s", + "(Deprecation Warning) the --save-to-file (-s) flag is deprecated ", + "and will be removed in a future release. ", + "Please print to the console and redirect to a file instead." + ) + policy.save_to_file(save_to_file, output_type) sys.exit(exit_code) @@ -143,6 +146,16 @@ def update_confcom(cmd, instance, tags=None): return instance +def check_infrastructure_svn(infrastructure_svn): + if infrastructure_svn and parse_version(infrastructure_svn) < parse_version( + DEFAULT_REGO_FRAGMENTS[0]["minimum_svn"] + ): + logger.warning( + "Input Infrastructure Fragment Software Version Number is less than the default Infrastructure SVN: %s", + DEFAULT_REGO_FRAGMENTS[0]["minimum_svn"], + ) + + def validate_sidecar_in_policy(policy: security_policy.AciPolicy, outraw_pretty_print: bool): is_valid, output = policy.validate_sidecars() @@ -220,3 +233,8 @@ def get_output_type(outraw, outraw_pretty_print): elif outraw_pretty_print: output_type = security_policy.OutputType.PRETTY_PRINT return output_type + + +def error_out(error_string): + logger.error(error_string) + sys.exit(1) diff --git a/src/confcom/azext_confcom/data/customer_rego_policy.txt b/src/confcom/azext_confcom/data/customer_rego_policy.txt index 0268a8ea999..d3b891c04b0 100644 --- a/src/confcom/azext_confcom/data/customer_rego_policy.txt +++ b/src/confcom/azext_confcom/data/customer_rego_policy.txt @@ -3,8 +3,8 @@ package policy import future.keywords.every import future.keywords.in -api_svn := "0.10.0" -framework_svn := "0.1.0" +api_version := %s +framework_version := "0.2.3" fragments := %s @@ -15,8 +15,7 @@ allow_dump_stacks := %s allow_runtime_logging := %s allow_environment_variable_dropping := %s allow_unencrypted_scratch := %s - - +allow_capability_dropping := %s mount_device := data.framework.mount_device unmount_device := data.framework.unmount_device diff --git a/src/confcom/azext_confcom/data/internal_config.json b/src/confcom/azext_confcom/data/internal_config.json index 5c29558af2f..36d40f2e067 100644 --- a/src/confcom/azext_confcom/data/internal_config.json +++ b/src/confcom/azext_confcom/data/internal_config.json @@ -1,9 +1,10 @@ { - "version": "0.2.13", + "version": "0.2.14", "hcsshim_config": { "maxVersion": "1.0.0", "minVersion": "0.0.1" }, + "version_api": "0.10.0", "openGCS": { "environmentVariables": [ { @@ -109,6 +110,7 @@ "allowDumpStacks": true, "allowRuntimeLogging": true, "allowEnvironmentVariableDropping": true, + "allowCapabilityDropping": true, "allowUnencryptedScratch": false }, "containerd": { @@ -173,9 +175,10 @@ { "issuer": "did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6.1.4.1.311.76.59.1.3", "feed": "mcr.microsoft.com/aci/aci-cc-infra-fragment", - "minimum_svn": "1.0.0", + "minimum_svn": "1", "includes": [ - "containers" + "containers", + "fragments" ] } ], @@ -204,7 +207,146 @@ "signals": [], "allow_elevated": false, "allow_stdio_access": true, - "working_dir": "/" + "working_dir": "/", + "no_new_privileges": false, + "seccomp_profile_sha256": "", + "user": { + "user_idname": { + "pattern": "", + "strategy": "any" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }, + "capabilities": { + "bounding": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "effective": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "inheritable": [], + "permitted": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "ambient": [] + } } + ], + "default_user": { + "user_idname": { + "pattern": "", + "strategy": "any" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }, + "default_unprivileged_capabilities": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "default_privileged_capabilities": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETPCAP", + "CAP_LINUX_IMMUTABLE", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_ADMIN", + "CAP_NET_RAW", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_SYS_MODULE", + "CAP_SYS_RAWIO", + "CAP_SYS_CHROOT", + "CAP_SYS_PTRACE", + "CAP_SYS_PACCT", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_NICE", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_MKNOD", + "CAP_LEASE", + "CAP_AUDIT_WRITE", + "CAP_AUDIT_CONTROL", + "CAP_SETFCAP", + "CAP_MAC_OVERRIDE", + "CAP_MAC_ADMIN", + "CAP_SYSLOG", + "CAP_WAKE_ALARM", + "CAP_BLOCK_SUSPEND", + "CAP_AUDIT_READ", + "CAP_PERFMON", + "CAP_BPF", + "CAP_CHECKPOINT_RESTORE" ] } \ No newline at end of file diff --git a/src/confcom/azext_confcom/data/sidecar_rego_policy.txt b/src/confcom/azext_confcom/data/sidecar_rego_policy.txt index fe79034b5da..be39ded50b2 100644 --- a/src/confcom/azext_confcom/data/sidecar_rego_policy.txt +++ b/src/confcom/azext_confcom/data/sidecar_rego_policy.txt @@ -1,7 +1,6 @@ package microsoftcontainerinstance -svn := "1.0.0" -api_svn := "0.10.0" -framework_svn := "0.1.0" +api_version := %s +framework_version := "0.2.3" containers := %s \ No newline at end of file diff --git a/src/confcom/azext_confcom/rootfs_proxy.py b/src/confcom/azext_confcom/rootfs_proxy.py index 88ab6025303..f6831c38e2f 100644 --- a/src/confcom/azext_confcom/rootfs_proxy.py +++ b/src/confcom/azext_confcom/rootfs_proxy.py @@ -9,17 +9,50 @@ import stat from pathlib import Path import platform +import requests from azext_confcom.errors import eprint host_os = platform.system() -arch = platform.architecture()[0] +machine = platform.machine() class SecurityPolicyProxy: # pylint: disable=too-few-public-methods # static variable to cache layer hashes between container groups layer_cache = {} + @staticmethod + def download_binaries(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + + bin_folder = os.path.join(dir_path, "bin") + if not os.path.exists(bin_folder): + os.makedirs(bin_folder) + + # get the most recent release artifacts from github + r = requests.get("https://api.github.com/repos/microsoft/hcsshim/releases") + bin_flag = False + exe_flag = False + # search for dmverity-vhd in the assets from hcsshim releases + for release in r.json(): + # these should be newest to oldest + for asset in release["assets"]: + # download the file if it contains dmverity-vhd + if "dmverity-vhd" in asset["name"]: + if "exe" in asset["name"]: + exe_flag = True + else: + bin_flag = True + # get the download url for the dmverity-vhd file + exe_url = asset["browser_download_url"] + # download the file + r = requests.get(exe_url) + # save the file to the bin folder + with open(os.path.join(bin_folder, asset["name"]), "wb") as f: + f.write(r.content) + if bin_flag and exe_flag: + break + def __init__(self): script_directory = os.path.dirname(os.path.realpath(__file__)) DEFAULT_LIB = "./bin/dmverity-vhd" @@ -27,11 +60,11 @@ def __init__(self): if host_os == "Linux": pass elif host_os == "Windows": - if arch == "64bit": + if machine.endswith("64"): DEFAULT_LIB += ".exe" else: - raise NotImplementedError( - f"The current architecture {arch} for windows is not supported." + eprint( + "32-bit Windows is not supported." ) elif host_os == "Darwin": eprint("The extension for MacOS has not been implemented.") @@ -42,8 +75,9 @@ def __init__(self): self.policy_bin = Path(os.path.join(f"{script_directory}", f"{DEFAULT_LIB}")) + # check if the extension binary exists if not os.path.exists(self.policy_bin): - raise RuntimeError("The extension binary file cannot be located.") + eprint("The extension binary file cannot be located.") if not os.access(self.policy_bin, os.X_OK): # add executable permissions for the current user if they don't exist st = os.stat(self.policy_bin) @@ -91,14 +125,12 @@ def get_policy_image_layers( output = [output[j * 2 + 1] for j in range(len(output) // 2)] output = [i.rstrip("\n").split(": ", 1)[1] for i in output] else: - output = [] - # eprint( - # "Cannot get layer hashes. Please check whether the image exists in local repository/daemon." - # ) + eprint( + "Cannot get layer hashes" + ) if err.decode("utf8") != "": - output = [] - # eprint(err.decode("utf8")) + eprint(err.decode("utf8")) # cache output layers self.layer_cache[image_name] = output return output diff --git a/src/confcom/azext_confcom/sample_policy.md b/src/confcom/azext_confcom/sample_policy.md index 9f8ab74f655..eed109c2aa7 100644 --- a/src/confcom/azext_confcom/sample_policy.md +++ b/src/confcom/azext_confcom/sample_policy.md @@ -23,8 +23,6 @@ fragments := [ containers := [ { "command": ["", "", "", /*...*/], - "allow_stdio_access": true, - "signals": [/*...*/], "env_rules": [ { "pattern": "", @@ -46,15 +44,37 @@ containers := [ }, /*...*/ ], - "allow_elevated": , - "working_dir": "", "exec_processes": [ { - "command": ["", "", "", /*...*/], - "signals": [/*...*/] + "command": ["", "", "", /*...*/], + "signals": [/*...*/] }, /*...*/ ], + "signals": [/*...*/], + "user": { + "user_idname": { + "pattern": "", + "strategy": "", + }, + "group_idnames": [{ + "pattern": "", + "strategy": "", + }], + "umask": "" + }, + "capabilities": { + "ambient": ["", /*...*/], + "bounding": ["", /*...*/], + "effective": ["", /*...*/], + "inheritable": ["", /*...*/], + "permitted": ["", /*...*/] + }, + "seccomp_profile_sha256": "", + "allow_elevated": , + "working_dir": "", + "allow_stdio_access": , + "no_new_privileges": } ] @@ -63,7 +83,7 @@ allow_dump_stacks := false allow_runtime_logging := false allow_environment_variable_dropping := true allow_unencrypted_scratch := false - +allow_capabilities_dropping := true mount_device := data.framework.mount_device diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index f928382ac71..bb5d14ea5b2 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -51,7 +51,7 @@ class AciPolicy: # pylint: disable=too-many-instance-attributes def __init__( self, deserialized_config: Any, - rego_fragments: Any = config.DEFAULT_REGO_FRAGMENTS, + rego_fragments: Any = copy.deepcopy(config.DEFAULT_REGO_FRAGMENTS), existing_rego_fragments: Any = None, debug_mode: bool = False, disable_stdio: bool = False, @@ -63,6 +63,8 @@ def __init__( self._disable_stdio = disable_stdio self._fragments = rego_fragments self._existing_fragments = existing_rego_fragments + self._api_version = config.API_VERSION + if debug_mode: self._allow_properties_access = config.DEBUG_MODE_SETTINGS.get( "allowPropertiesAccess" @@ -79,12 +81,16 @@ def __init__( self._allow_unencrypted_scratch = config.DEBUG_MODE_SETTINGS.get( "allowUnencryptedScratch" ) + self._allow_capability_dropping = config.DEBUG_MODE_SETTINGS.get( + "allowCapabilityDropping" + ) else: self._allow_properties_access = False self._allow_dump_stacks = False self._allow_runtime_logging = False self._allow_environment_variable_dropping = True self._allow_unencrypted_scratch = False + self._allow_capability_dropping = True self.version = case_insensitive_dict_get( deserialized_config, config.ACI_FIELD_VERSION @@ -149,7 +155,6 @@ def close(self) -> None: def get_serialized_output( self, output_type: OutputType = OutputType.DEFAULT, - use_json=False, rego_boilerplate=True, ) -> str: # error check the output type @@ -157,13 +162,11 @@ def get_serialized_output( eprint("Unknown output type for serialization.") policy_str = self._policy_serialization( - use_json, output_type == OutputType.PRETTY_PRINT + output_type == OutputType.PRETTY_PRINT ) - if not use_json and rego_boilerplate: + if rego_boilerplate: policy_str = self._add_rego_boilerplate(policy_str) - elif use_json and output_type == OutputType.PRETTY_PRINT: - policy_str = json.dumps(json.loads(policy_str), indent=2, sort_keys=True) # if we're not outputting base64 if output_type in (OutputType.RAW, OutputType.PRETTY_PRINT): @@ -175,8 +178,12 @@ def _add_rego_boilerplate(self, output: str) -> str: # determine if we're outputting for a sidecar or not if self._images[0].get_id() and is_sidecar(self._images[0].get_id()): - return config.SIDECAR_REGO_POLICY % (output) + return config.SIDECAR_REGO_POLICY % ( + pretty_print_func(self._api_version), + output + ) return config.CUSTOMER_REGO_POLICY % ( + pretty_print_func(self._api_version), pretty_print_func(self._fragments), output, pretty_print_func(self._allow_properties_access), @@ -184,50 +191,9 @@ def _add_rego_boilerplate(self, output: str) -> str: pretty_print_func(self._allow_runtime_logging), pretty_print_func(self._allow_environment_variable_dropping), pretty_print_func(self._allow_unencrypted_scratch), + pretty_print_func(self._allow_capability_dropping), ) - def _add_elements(self, dictionary) -> Dict: - """Recursive function to convert CCE policy rego into a json policy - - adds 'length' keys to dicts that were arrays - - expands 'elements' dicts from an array - """ - - if isinstance(dictionary, (str, int)): - return None - if isinstance(dictionary, list): - for item in dictionary: - self._add_elements(item) - if isinstance(dictionary, dict): - for key in dictionary.keys(): - if isinstance(dictionary[key], list): - elements_list = {} - for i, item in enumerate(dictionary[key]): - elements_list[str(i)] = item - dictionary[key] = { - "elements": elements_list, - "length": len(dictionary[key]), - } - - for i in range(len(dictionary[key]["elements"].keys())): - self._add_elements(dictionary[key]["elements"][str(i)]) - else: - self._add_elements(dictionary[key]) - - return dictionary - - def _convert_to_json(self, dictionary) -> Dict: - # need to make a deep copy so we can change the underlying config data - # dicts - editable = copy.deepcopy(dictionary) - out = {"length": len(editable), "elements": {}} - - for i, container in enumerate(editable): - out["elements"][str(i)] = container - - self._add_elements(out) - - return {config.POLICY_FIELD_CONTAINERS: out} - def validate_cce_policy(self) -> Tuple[bool, Dict]: """Utility method: check to see if the existing policy that instantiates this function would allow the policy created by the input ARM Template""" @@ -359,12 +325,11 @@ def save_to_file( self, file_path: str, output_type: OutputType = OutputType.DEFAULT, - use_json=False, ) -> None: - output = self.get_serialized_output(output_type, use_json=use_json) + output = self.get_serialized_output(output_type) os_util.write_str_to_file(file_path, output) - def _policy_serialization(self, use_json, pretty_print=False) -> str: + def _policy_serialization(self, pretty_print=False) -> str: policy = [] regular_container_images = self.get_images() @@ -379,11 +344,8 @@ def _policy_serialization(self, use_json, pretty_print=False) -> str: policy += copy.deepcopy(config.DEFAULT_CONTAINERS) if self._disable_stdio: for container in policy: - container[config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS] = False + container[config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS] = False - # default output is rego policy - if use_json: - policy = self._convert_to_json(policy) if pretty_print: return pretty_print_func(policy) return print_func(policy) @@ -470,6 +432,32 @@ def populate_policy_content_for_all_images( } ) + if (deepdiff.DeepDiff(image.get_user(), config.DEFAULT_USER, ignore_order=True) == {} + and image_info.get("User") != ""): + # valid values are in the form "user", "user:group", "uid", "uid:gid", "user:gid", "uid:group" + # where each entry is either a string or an unsigned integer + # "" means any user (use default) + # TO-DO figure out why groups is a list + user = copy.deepcopy(config.DEFAULT_USER) + parts = image_info.get("User").split(":", 1) + + strategy = ["name", "name"] + if parts[0].isdigit(): + strategy[0] = "id" + user[config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_USER_IDNAME] = { + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_PATTERN: parts[0], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_STRATEGY: strategy[0] + } + if len(parts) == 2: + # group also specified + if parts[1].isdigit(): + strategy[1] = "id" + user[config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_GROUP_IDNAMES][0] = { + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_PATTERN: parts[1], + config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER_STRATEGY: strategy[1] + } + image.set_user(user) + # populate tar location if isinstance(tar_mapping, dict): tar_location = get_tar_location_from_mapping(tar_mapping, image_name) @@ -566,7 +554,7 @@ def load_policy_from_arm_template_str( # add init containers to the list of other containers since they aren't treated differently # in the security policy if init_container_list: - container_list = container_list + init_container_list + container_list.extend(init_container_list) existing_containers, fragments = extract_confidential_properties( container_group_properties @@ -618,13 +606,15 @@ def load_policy_from_arm_template_str( ) or [], config.ACI_FIELD_CONTAINERS_MOUNTS: process_mounts(image_properties, volumes), - config.ACI_FIELD_CONTAINERS_ALLOW_ELEVATED: False, config.ACI_FIELD_CONTAINERS_EXEC_PROCESSES: exec_processes + config.DEBUG_MODE_SETTINGS.get("execProcesses") if debug_mode else exec_processes, config.ACI_FIELD_CONTAINERS_SIGNAL_CONTAINER_PROCESSES: [], config.ACI_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS: not disable_stdio, + config.ACI_FIELD_CONTAINERS_SECURITY_CONTEXT: case_insensitive_dict_get( + image_properties, config.ACI_FIELD_TEMPLATE_SECURITY_CONTEXT + ), } ) diff --git a/src/confcom/azext_confcom/template_util.py b/src/confcom/azext_confcom/template_util.py index 275c0d982d0..4c4a83b98cd 100644 --- a/src/confcom/azext_confcom/template_util.py +++ b/src/confcom/azext_confcom/template_util.py @@ -8,9 +8,11 @@ import copy import tarfile from typing import Any, Tuple, Dict, List +from hashlib import sha256 import deepdiff import yaml import docker +import pydash from azext_confcom.errors import ( eprint, ) @@ -326,7 +328,7 @@ def readable_diff(diff_dict) -> Dict[str, Any]: name_translation = { "values_changed": "values_changed", "iterable_item_removed": "values_removed", - "iterable_item_added": "values_added", + "iterable_item_added": "values_added" } human_readable_diff = {} @@ -355,6 +357,7 @@ def compare_containers(container1, container2) -> Dict[str, Any]: diff = deepdiff.DeepDiff( container1, container2, + ignore_order=True, ) # cast to json using built-in function in deepdiff so there's safe translation # e.g. a type will successfully cast to string @@ -397,7 +400,10 @@ def replace_params_and_vars(params: dict, vars_dict: dict, attribute): full_param_name = next(param_name, None) if full_param_name: full_param_name = full_param_name.group(0) - out = attribute.replace(full_param_name, find_value_in_params_and_vars(params, vars_dict, attribute)) + # cast to string + out = f"{out}" + out = attribute.replace(full_param_name, out) + elif isinstance(attribute, list): out = [] for item in attribute: @@ -569,6 +575,10 @@ def pretty_print_func(x: dict) -> str: return json.dumps(x, indent=2, sort_keys=True) +def str_to_sha256(x: str) -> str: + return sha256(x.encode('utf-8')).hexdigest() + + def is_sidecar(image_name: str) -> bool: return image_name.split(":")[0] in config.BASELINE_SIDECAR_CONTAINERS @@ -657,7 +667,7 @@ def compare_env_vars( def inject_policy_into_template( - arm_template_path: str, parameter_data_path: str, policy: str, count: int + arm_template_path: str, parameter_data_path: str, policy: str, count: int, hashes: dict ) -> bool: write_flag = False parameter_data = None @@ -720,6 +730,25 @@ def inject_policy_into_template( config.ACI_FIELD_TEMPLATE_CCE_POLICY ] = policy write_flag = True + # get containers to inject the base64 encoding of seccom profile hash into template if exists + containers = case_insensitive_dict_get( + container_group_properties, config.ACI_FIELD_CONTAINERS + ) + for c in containers: + container_image = case_insensitive_dict_get(c, config.ACI_FIELD_TEMPLATE_IMAGE) + container_properties = case_insensitive_dict_get(c, config.ACI_FIELD_TEMPLATE_PROPERTIES) + security_context = case_insensitive_dict_get( + container_properties, config.ACI_FIELD_TEMPLATE_SECURITY_CONTEXT + ) + if security_context: + seccomp_profile = case_insensitive_dict_get( + security_context, config.ACI_FIELD_CONTAINERS_SECCOMP_PROFILE + ) + if seccomp_profile: + hash_base64 = os_util.str_to_base64(hashes.get(container_image, "")) + security_context[config.ACI_FIELD_CONTAINERS_SECCOMP_PROFILE] = hash_base64 + write_flag = True + # write base64 encoding of seccomp profile hash to the template if write_flag: os_util.write_json_to_file(arm_template_path, input_arm_json) return True @@ -785,6 +814,8 @@ def get_container_group_name( def print_existing_policy_from_arm_template(arm_template_path, parameter_data_path): + if not arm_template_path: + eprint("Can only print existing policy from ARM Template") input_arm_json = os_util.load_json_from_file(arm_template_path) parameter_data = None if parameter_data_path: @@ -818,3 +849,32 @@ def print_existing_policy_from_arm_template(arm_template_path, parameter_data_pa eprint("CCE Policy is either in an supported format or not present") print(f"CCE Policy for Container Group: {container_group_name}\n") print(pretty_print_func(containers)) + + +def process_seccomp_policy(policy2): + policy = json.loads(policy2) + policy = pydash.defaults(policy, {'defaultAction': ""}) + policy = pydash.pick(policy, 'defaultAction', 'defaultErrnoRet', 'architectures', + 'flags', 'listenerPath', 'listenerMetadata', 'syscalls') + if 'syscalls' in policy: + syscalls = policy['syscalls'] + temp_syscalls = [] + for s in syscalls: + syscall = s + syscall = pydash.defaults(syscall, {'names': [], 'action': ""}) + syscall = pydash.pick(syscall, 'names', 'action', 'errnoRet', 'args') + + if 'args' in syscall: + temp_args = [] + args = syscall['args'] + + for j in args: + arg = j + arg = pydash.defaults(arg, {'value': 0, 'op': "", 'index': 0}) + arg = pydash.pick(arg, 'index', 'value', 'valueTwo', 'op') + temp_args.append(arg) + syscall['args'] = temp_args + temp_syscalls.append(syscall) + # put temp_syscalls back into policy + policy['syscalls'] = temp_syscalls + return policy diff --git a/src/confcom/azext_confcom/tests/latest/README.md b/src/confcom/azext_confcom/tests/latest/README.md index 41ea57a2208..19d8e07f453 100644 --- a/src/confcom/azext_confcom/tests/latest/README.md +++ b/src/confcom/azext_confcom/tests/latest/README.md @@ -21,6 +21,7 @@ Test Name | Image Used | Purpose ---|---|--- test_arm_template_policy | python:3.6.14-slim-buster | Generate an ARM Template policy and policy.json policy and see if their outputs match test_default_infrastructure_svn | python:3.6.14-slim-buster | See the default value of the minimum SVN for the infrastructure fragment +test_default_pause_container | python:3.6.14-slim-buster | See if the default pause containers match the config test_arm_template_missing_image_name | N/A | Error condition if an image isn't specified test_arm_template_missing_resources | N/A | Error condition where no resources are specified to deploy test_arm_template_missing_aci | N/A | Error condition where ACI is not specified in resources @@ -36,10 +37,25 @@ test_update_infrastructure_svn | python:3.6.14-slim-buster | Change the minimum test_multiple_policies | python:3.6.14-slim-buster & rust:1.52.1 | See if two unique policies are generated from a single ARM Template container multiple container groups. Also have an extra resource that is untouched. Also has a secureValue for an environment variable. test_arm_template_with_init_container | python:3.6.14-slim-buster & rust:1.52.1 | See if having an initContainer is picked up and added to the list of valid containers test_arm_template_without_stdio_access | rust:1.52.1 | See if disabling container stdio access gets passed down to individual containers +test_arm_template_allow_elevated_false | rust:1.52.1 | Disabling allow_elevated via securityContext test_arm_template_policy_regex | python:3.6.14-slim-buster | Make sure the regex generated from the ARM Template workflow matches that of the policy.json workflow test_wildcard_env_var | python:3.6.14-slim-buster | Check that an "allow all" regex is created when a value for env var is not provided via a parameter value test_wildcard_env_var_invalid | N/A | Make sure the process errors out if a value is not given for an env var or an undefined parameter is used for the name of an env var test_arm_template_with_env_var | rust:1.52.1 | Make sure that a value that looks similar to but is not an ARM parameter is treated as a string +test_arm_template_security_context_defaults | N/A | Make sure default values for securityContext are correct +test_arm_template_security_context_allow_privilege_escalation | N/A | See if changing the allowPrivilegeEscalation flag is working +test_arm_template_security_context_user | N/A | Set the user field manually to make sure it is reflected in the policy +test_arm_template_security_context_seccomp_profile | N/A | Make sure we have the correct seccomp profile hash +test_arm_template_capabilities_unprivileged | N/A | See if unprivileged capabilities are in the correct sets and have the right values. Using add and drop fields +test_arm_template_capabilities_privileged | N/A | See if privilileged capabilities are correct +test_arm_template_security_context_no_run_as_group | N/A | See if user is set correctly if run_as_group is not set in ARM template +test_arm_template_security_context_no_run_as_user | N/A | See if user is set correctly if run_as_user is not set in ARM template +test_arm_template_security_context_uid_gid | N/A | See if user is set correctly by getting the user field from the Docker image in the format uid:gid +test_arm_template_security_context_user_gid | N/A | See if user is set correctly by getting the user field from the Docker image in the format user:gid +test_arm_template_security_context_user_group | N/A | See if user is set correctly by getting the user field from the Docker image in the format user:group +test_arm_template_security_context_uid_group | N/A | See if user is set correctly by getting the user field from the Docker image in the format uid:group +test_arm_template_security_context_uid | N/A | See if user is set correctly by getting the user field from the Docker image in the format uid +test_arm_template_security_context_user_dockerfile | N/A | See if user is set correctly by getting the user field from the Docker image in the format user ## policy.json [test file](test_confcom_scenario.py) @@ -51,7 +67,7 @@ Test Name | Image Used | Purpose test_user_container_customized_mounts | rust:1.52.1 | See if mounts are translated correctly to the appropriate source and destination locations test_user_container_mount_injected_dns | python:3.6.14-slim-buster | See if the resolvconf mount works properly test_injected_sidecar_container_msi | mcr.microsoft.com/aci/msi-atlas-adapter:master_20201203.1 | Make sure User mounts and env vars aren't added to sidecar containers, using JSON output format -test_logging_enabled | python:3.6.14-slim-buster | Enable logging via debug_mode +test_debug_flags | python:3.6.14-slim-buster | Enable flags set via debug_mode test_sidecar | mcr.microsoft.com/aci/msi-atlas-adapter:master_20201210.1 | See if sidecar validation would pass policy created by given policy.json test_sidecar_stdio_access_default | Check that sidecar containers have std I/O access by default test_incorrect_sidecar | mcr.microsoft.com/aci/msi-atlas-adapter:master_20201210.1 | See what output format for failing sidecar validation would be @@ -60,6 +76,7 @@ test_allow_elevated | python:3.6.14-slim-buster | Using allow_elevated in contai test_image_layers_python | python:3.6.14-slim-buster | Make sure image layers are as expected test_image_layers_rust | rust:1.52.1 | Make sure image layers are as expected with different image test_docker_pull | rust:1.52.1 | Test pulling an image from docker client +test_infrastructure_svn | rust:1.52.1 | make sure the correct infrastructure_svn is present in the policy test_stdio_access_default | python:3.6.14-slim-buster | Checking the default value for std I/O access test_stdio_access_updated | python:3.6.14-slim-buster | Checking the value for std I/O when it's set test_environment_variables_parsing | mcr.microsoft.com/azuredocs/aci-dataprocessing-cc:v1 | Make sure env vars are output in the right format diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py b/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py index bc54a58f5cd..eb56bd49541 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py @@ -244,21 +244,17 @@ def setUpClass(cls): def test_arm_template_policy(self): # deep diff the output policies from the regular policy.json and the ARM template normalized_aci_policy = json.loads( - self.aci_policy.get_serialized_output(output_type=OutputType.RAW, use_json=True) + self.aci_policy.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) normalized_aci_arm_policy = json.loads( self.aci_arm_policy.get_serialized_output( - output_type=OutputType.RAW, use_json=True + output_type=OutputType.RAW, rego_boilerplate=False ) ) - normalized_aci_policy[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - normalized_aci_arm_policy[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) + normalized_aci_policy[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + normalized_aci_arm_policy[0].pop(config.POLICY_FIELD_CONTAINERS_ID) self.assertEqual( deepdiff.DeepDiff( @@ -277,6 +273,14 @@ def test_default_infrastructure_svn(self): ], ) + def test_default_pause_container(self): + regular_image_json = json.loads( + self.aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + # check default pause container + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_CONTAINERS[0], regular_image_json[1], ignore_order=True), {}) # @unittest.skip("not in use") @pytest.mark.run(order=2) @@ -611,7 +615,6 @@ def test_arm_template_missing_containers(self): load_policy_from_arm_template_str(custom_arm_json_missing_containers, "") self.assertEqual(exc_info.exception.code, 1) - # @unittest.skip("not in use") @pytest.mark.run(order=3) class PolicyGeneratingArmParametersIncorrect(unittest.TestCase): @@ -740,7 +743,6 @@ def test_arm_template_missing_definition(self): ) self.assertEqual(exc_info.exception.code, 1) - # @unittest.skip("not in use") @pytest.mark.run(order=4) class PolicyGeneratingArmParameters(unittest.TestCase): @@ -889,7 +891,6 @@ def test_arm_template_with_parameter_file(self): python_flag = True self.assertTrue(python_flag) - # @unittest.skip("not in use") @pytest.mark.run(order=5) class PolicyGeneratingArmParameters2(unittest.TestCase): @@ -1050,21 +1051,16 @@ def test_arm_template_with_parameter_file_injected_env_vars(self): ) output[0].populate_policy_content_for_all_images() output_json = json.loads( - output[0].get_serialized_output(output_type=OutputType.RAW, use_json=True) + output[0].get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) # see if we have environment variables specific to the python image in the parameter file python_flag = False - for _, value in output_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ].items(): + for value in output_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS]: if "PYTHON" in value[config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS_RULE]: python_flag = True self.assertTrue(python_flag) - # @unittest.skip("not in use") @pytest.mark.run(order=6) class PolicyGeneratingArmContainerConfig(unittest.TestCase): @@ -1237,15 +1233,9 @@ def test_arm_template_with_parameter_file_arm_config(self): ) output[0].populate_policy_content_for_all_images() # see if we have environment variables that are in the template - output_json = json.loads( - output[0].get_serialized_output(output_type=OutputType.RAW, use_json=True) - ) + output_json = json.loads(output[0].get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False)) - for _, value in output_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ].items(): + for value in output_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS]: if case_insensitive_dict_get( value, config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS_RULE ).startswith("PORT"): @@ -1261,14 +1251,9 @@ def test_arm_template_with_parameter_file_arm_config(self): "-c", "while sleep 5; do cat /mnt/input/access.log; done", ] - for _, value in output_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"][config.POLICY_FIELD_CONTAINERS_ELEMENTS_COMMANDS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ].items(): + for value in output_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_COMMANDS]: self.assertTrue(value in expected) - # @unittest.skip("not in use") @pytest.mark.run(order=7) class PolicyGeneratingArmParametersCleanRoom(unittest.TestCase): @@ -1440,19 +1425,15 @@ def test_arm_template_with_parameter_file_clean_room(self): clean_room[0].populate_policy_content_for_all_images() regular_image_json = json.loads( - regular_image[0].get_serialized_output(output_type=OutputType.RAW, use_json=True) + regular_image[0].get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) clean_room_json = json.loads( - clean_room[0].get_serialized_output(output_type=OutputType.RAW, use_json=True) + clean_room[0].get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) - regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - clean_room_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) + regular_image_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + clean_room_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) # see if the remote image and the local one produce the same output self.assertEqual( @@ -1460,7 +1441,6 @@ def test_arm_template_with_parameter_file_clean_room(self): {}, ) - # @unittest.skip("not in use") @pytest.mark.run(order=8) class PolicyDiff(unittest.TestCase): @@ -1481,7 +1461,7 @@ class PolicyDiff(unittest.TestCase): "properties": { "confidentialComputeProperties": { "isolationType": "SevSnp", - "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbImJhc2giXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vY3VzdG9taXplZC9wYXRoL3ZhbHVlIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFU1RfUkVHRVhQX0VOVj10ZXN0X3JlZ2V4cF9lbnYiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUlVTVFVQX0hPTUU9L3Vzci9sb2NhbC9ydXN0dXAiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiQ0FSR09fSE9NRT0vdXNyL2xvY2FsL2NhcmdvIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RfVkVSU0lPTj0xLjUyLjEiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InJ1c3Q6MS41Mi4xIiwibGF5ZXJzIjpbImZlODRjOWQ1YmZkZGQwN2EyNjI0ZDAwMzMzY2YxM2MxYTljOTQxZjNhMjYxZjEzZWFkNDRmYzZhOTNiYzBlN2EiLCI0ZGVkYWU0Mjg0N2M3MDRkYTg5MWEyOGMyNWQzMjIwMWExYWU0NDBiY2UyYWVjY2NmYThlNmYwM2I5N2E2YTZjIiwiNDFkNjRjZGViMzQ3YmYyMzZiNGMxM2I3NDAzYjYzM2ZmMTFmMWNmOTRkYmM3Y2Y4ODFhNDRkNmRhODhjNTE1NiIsImViMzY5MjFlMWY4MmFmNDZkZmUyNDhlZjhmMWIzYWZiNmE1MjMwYTY0MTgxZDk2MGQxMDIzN2EwOGNkNzNjNzkiLCJlNzY5ZDc0ODdjYzMxNGQzZWU3NDhhNDQ0MDgwNTMxN2MxOTI2MmM3YWNkMmZkYmRiMGQ0N2QyZTQ2MTNhMTVjIiwiMWI4MGYxMjBkYmQ4OGU0MzU1ZDYyNDFiNTE5YzNlMjUyOTAyMTVjNDY5NTE2YjQ5ZGVjZTljZjA3MTc1YTc2NiJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL21vdW50L2F6dXJlZmlsZSIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" + "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJiYXNoIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L2N1c3RvbWl6ZWQvcGF0aC92YWx1ZSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVNUX1JFR0VYUF9FTlY9dGVzdF9yZWdleHBfZW52IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RVUF9IT01FPS91c3IvbG9jYWwvcnVzdHVwIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkNBUkdPX0hPTUU9L3Vzci9sb2NhbC9jYXJnbyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJSVVNUX1ZFUlNJT049MS41Mi4xIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IlQoRSk/TVA9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljUGFja2FnZUZpbGVOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6Ikhvc3RlZFNlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0FQSV9WRVJTSU9OPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0hFQURFUj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9TRVJWRVJfVEhVTUJQUklOVD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlX3Jlc3RhcnRlZF9ieT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwiaWQiOiJydXN0OjEuNTIuMSIsImxheWVycyI6WyJmZTg0YzlkNWJmZGRkMDdhMjYyNGQwMDMzM2NmMTNjMWE5Yzk0MWYzYTI2MWYxM2VhZDQ0ZmM2YTkzYmMwZTdhIiwiNGRlZGFlNDI4NDdjNzA0ZGE4OTFhMjhjMjVkMzIyMDFhMWFlNDQwYmNlMmFlY2NjZmE4ZTZmMDNiOTdhNmE2YyIsIjQxZDY0Y2RlYjM0N2JmMjM2YjRjMTNiNzQwM2I2MzNmZjExZjFjZjk0ZGJjN2NmODgxYTQ0ZDZkYTg4YzUxNTYiLCJlYjM2OTIxZTFmODJhZjQ2ZGZlMjQ4ZWY4ZjFiM2FmYjZhNTIzMGE2NDE4MWQ5NjBkMTAyMzdhMDhjZDczYzc5IiwiZTc2OWQ3NDg3Y2MzMTRkM2VlNzQ4YTQ0NDA4MDUzMTdjMTkyNjJjN2FjZDJmZGJkYjBkNDdkMmU0NjEzYTE1YyIsIjFiODBmMTIwZGJkODhlNDM1NWQ2MjQxYjUxOWMzZTI1MjkwMjE1YzQ2OTUxNmI0OWRlY2U5Y2YwNzE3NWE3NjYiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9tb3VudC9henVyZWZpbGUiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL2F6dXJlRmlsZVZvbHVtZS8uKyIsInR5cGUiOiJiaW5kIn0seyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNhcGFiaWxpdGllcyI6eyJhbWJpZW50IjpbXSwiYm91bmRpbmciOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiZWZmZWN0aXZlIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImluaGVyaXRhYmxlIjpbXSwicGVybWl0dGVkIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl19LCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKYWxsb3dfY2FwYWJpbGl0eV9kcm9wcGluZyA6PSB0cnVlCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" }, "containers": [ { @@ -1560,13 +1540,16 @@ class PolicyDiff(unittest.TestCase): "properties": { "confidentialComputeProperties": { "isolationType": "SevSnp", - "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbImJhc2giXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vY3VzdG9taXplZC9wYXRoL3ZhbHVlIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFU1RfUkVHRVhQX0VOVj10ZXN0X3JlZ2V4cF9lbnYiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUlVTVFVQX0hPTUU9L3Vzci9sb2NhbC9ydXN0dXAiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiQ0FSR09fSE9NRT0vdXNyL2xvY2FsL2NhcmdvIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RfVkVSU0lPTj0xLjUyLjEiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InJ1c3Q6MS41Mi4xIiwibGF5ZXJzIjpbImZlODRjOWQ1YmZkZGQwN2EyNjI0ZDAwMzMzY2YxM2MxYTljOTQxZjNhMjYxZjEzZWFkNDRmYzZhOTNiYzBlN2EiLCI0ZGVkYWU0Mjg0N2M3MDRkYTg5MWEyOGMyNWQzMjIwMWExYWU0NDBiY2UyYWVjY2NmYThlNmYwM2I5N2E2YTZjIiwiNDFkNjRjZGViMzQ3YmYyMzZiNGMxM2I3NDAzYjYzM2ZmMTFmMWNmOTRkYmM3Y2Y4ODFhNDRkNmRhODhjNTE1NiIsImViMzY5MjFlMWY4MmFmNDZkZmUyNDhlZjhmMWIzYWZiNmE1MjMwYTY0MTgxZDk2MGQxMDIzN2EwOGNkNzNjNzkiLCJlNzY5ZDc0ODdjYzMxNGQzZWU3NDhhNDQ0MDgwNTMxN2MxOTI2MmM3YWNkMmZkYmRiMGQ0N2QyZTQ2MTNhMTVjIiwiMWI4MGYxMjBkYmQ4OGU0MzU1ZDYyNDFiNTE5YzNlMjUyOTAyMTVjNDY5NTE2YjQ5ZGVjZTljZjA3MTc1YTc2NiJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL21vdW50L2F6dXJlZmlsZSIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" + "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJiYXNoIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L2N1c3RvbWl6ZWQvcGF0aC92YWx1ZSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVNUX1JFR0VYUF9FTlY9dGVzdF9yZWdleHBfZW52IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RVUF9IT01FPS91c3IvbG9jYWwvcnVzdHVwIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkNBUkdPX0hPTUU9L3Vzci9sb2NhbC9jYXJnbyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJSVVNUX1ZFUlNJT049MS41Mi4xIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IlQoRSk/TVA9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljUGFja2FnZUZpbGVOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6Ikhvc3RlZFNlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0FQSV9WRVJTSU9OPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0hFQURFUj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9TRVJWRVJfVEhVTUJQUklOVD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlX3Jlc3RhcnRlZF9ieT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwiaWQiOiJydXN0OjEuNTIuMSIsImxheWVycyI6WyJmZTg0YzlkNWJmZGRkMDdhMjYyNGQwMDMzM2NmMTNjMWE5Yzk0MWYzYTI2MWYxM2VhZDQ0ZmM2YTkzYmMwZTdhIiwiNGRlZGFlNDI4NDdjNzA0ZGE4OTFhMjhjMjVkMzIyMDFhMWFlNDQwYmNlMmFlY2NjZmE4ZTZmMDNiOTdhNmE2YyIsIjQxZDY0Y2RlYjM0N2JmMjM2YjRjMTNiNzQwM2I2MzNmZjExZjFjZjk0ZGJjN2NmODgxYTQ0ZDZkYTg4YzUxNTYiLCJlYjM2OTIxZTFmODJhZjQ2ZGZlMjQ4ZWY4ZjFiM2FmYjZhNTIzMGE2NDE4MWQ5NjBkMTAyMzdhMDhjZDczYzc5IiwiZTc2OWQ3NDg3Y2MzMTRkM2VlNzQ4YTQ0NDA4MDUzMTdjMTkyNjJjN2FjZDJmZGJkYjBkNDdkMmU0NjEzYTE1YyIsIjFiODBmMTIwZGJkODhlNDM1NWQ2MjQxYjUxOWMzZTI1MjkwMjE1YzQ2OTUxNmI0OWRlY2U5Y2YwNzE3NWE3NjYiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9tb3VudC9henVyZWZpbGUiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL2F6dXJlRmlsZVZvbHVtZS8uKyIsInR5cGUiOiJiaW5kIn0seyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNhcGFiaWxpdGllcyI6eyJhbWJpZW50IjpbXSwiYm91bmRpbmciOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiZWZmZWN0aXZlIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImluaGVyaXRhYmxlIjpbXSwicGVybWl0dGVkIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl19LCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKYWxsb3dfY2FwYWJpbGl0eV9kcm9wcGluZyA6PSB0cnVlCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" }, "containers": [ { "name": "[variables('container1name')]", "properties": { "image": "[variables('container1image')]", + "securityContext": { + "allowPrivilegeEscalation": false + }, "resources": { "requests": { "cpu": 1, @@ -1584,6 +1567,7 @@ class PolicyDiff(unittest.TestCase): "mountPath": "/mount/azure" } ], + "allow_elevated": true, "environmentVariables": [ { "name": "PATH", @@ -1639,13 +1623,11 @@ def setUpClass(cls): cls.aci_policy2.populate_policy_content_for_all_images() def test_policy_diff(self): - is_valid, diff = self.aci_policy.validate_cce_policy() self.assertTrue(is_valid) self.assertTrue(not diff) def test_incorrect_policy_diff(self): - is_valid, diff = self.aci_policy2.validate_cce_policy() self.assertFalse(is_valid) expected_diff = { @@ -1656,7 +1638,8 @@ def test_incorrect_policy_diff(self): "tested_value": "/mount/azure", "policy_value": "/mount/azurefile", } - ] + ], + "no_new_privileges": [{"tested_value": True, "policy_value": False}] }, "env_rules": [ "environment variable with rule 'TEST_REGEXP_ENV=test_regexp_en' does not match strings or regex in policy rules", @@ -1667,7 +1650,6 @@ def test_incorrect_policy_diff(self): self.assertEqual(diff, expected_diff) - # @unittest.skip("not in use") @pytest.mark.run(order=9) class PolicyGeneratingArmInfrastructureSvn(unittest.TestCase): @@ -1816,23 +1798,21 @@ class PolicyGeneratingArmInfrastructureSvn(unittest.TestCase): @classmethod def setUpClass(cls): cls.aci_arm_policy = load_policy_from_arm_template_str( - cls.custom_arm_json, "", infrastructure_svn="2.0.0" + cls.custom_arm_json, "", infrastructure_svn="2" )[0] cls.aci_arm_policy.populate_policy_content_for_all_images() def test_update_infrastructure_svn(self): - expected_policy = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjIuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbInB5dGhvbjMiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL2JpbjovdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiTEFORz1DLlVURi04IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkdQR19LRVk9MEQ5NkRGNEQ0MTEwRTVDNDNGQkZCMTdGMkQzNDdFQTZBQTY1NDIxRCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fVkVSU0lPTj0zLjYuMTQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX1BJUF9WRVJTSU9OPTIxLjIuNCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9VUkw9aHR0cHM6Ly9naXRodWIuY29tL3B5cGEvZ2V0LXBpcC9yYXcvYzIwYjBjZmQ2NDNjZDRhMTkyNDZjY2YyMDRlMjk5N2FmNzBmNmIyMS9wdWJsaWMvZ2V0LXBpcC5weSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9TSEEyNTY9ZmE2ZjNmYjkzY2NlMjM0Y2Q0ZThkZDJiZWI1NGE1MWFiOWMyNDc2NTNiNTI4NTVhNDhkZDQ0ZTZiMjFmZjI4YiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IigoP2kpRkFCUklDKV8uKz0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIT1NUTkFNRT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJUKEUpP01QPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY1BhY2thZ2VGaWxlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIb3N0ZWRTZXJ2aWNlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9IRUFERVI9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiYXp1cmVjb250YWluZXJpbnN0YW5jZV9yZXN0YXJ0ZWRfYnk9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImlkIjoicHl0aG9uOjMuNi4xNC1zbGltLWJ1c3RlciIsImxheWVycyI6WyIyNTRjYzg1M2RhNjA4MTkwNWM5MTA5YzhiOWQ5OWM5ZmIwOTg3YmExZDg4ZjcyOTA4ODkwM2NmZmI4MGY1NWYxIiwiYTU2OGYxOTAwYmVkNjBhMDY0MWI3NmI5OTFhZDQzMTQ0NmQ5YzNhMzQ0ZDdiMjYxZjEwZGU4ZDhlNzM3NjNhYyIsImM3MGM1MzBlODQyZjY2MjE1YjBiZDk1NTg3NzE1N2JhMjRjMzc5OTMwMzU2N2MzZjU2NzNjNDU2NjNlYTRkMTUiLCIzZTg2YzNjY2YxNjQyYmY1ODRkZTMzYjQ5YzcyNDhmODdlZWNkMGY2ZDhjMDgzNTNkYWEzNmNjN2FkMGE3YjZhIiwiMWU0Njg0ZDhjN2NhYTc0YzY1MjQxNzJiNGQ1YTE1OWExMDg4NzYxM2VkNzBmMThkMGE1NWQwNWIyYWY2MWFjZCJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL2FjaS9sb2dzIiwib3B0aW9ucyI6WyJyYmluZCIsInJzaGFyZWQiLCJydyJdLCJzb3VyY2UiOiJzYW5kYm94Oi8vL3RtcC9hdGxhcy9henVyZUZpbGVWb2x1bWUvLisiLCJ0eXBlIjoiYmluZCJ9LHsiZGVzdGluYXRpb24iOiIvYWNpL3NlY3JldCIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicm8iXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvc2VjcmV0c1ZvbHVtZS8uKyIsInR5cGUiOiJiaW5kIn0seyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwic2lnbmFscyI6W10sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNvbW1hbmQiOlsiL3BhdXNlIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwicmVxdWlyZWQiOnRydWUsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwibGF5ZXJzIjpbIjE2YjUxNDA1N2EwNmFkNjY1ZjkyYzAyODYzYWNhMDc0ZmQ1OTc2Yzc1NWQyNmJmZjE2MzY1Mjk5MTY5ZTg0MTUiXSwibW91bnRzIjpbXSwic2lnbmFscyI6W10sIndvcmtpbmdfZGlyIjoiLyJ9XQoKYWxsb3dfcHJvcGVydGllc19hY2Nlc3MgOj0gZmFsc2UKYWxsb3dfZHVtcF9zdGFja3MgOj0gZmFsc2UKYWxsb3dfcnVudGltZV9sb2dnaW5nIDo9IGZhbHNlCmFsbG93X2Vudmlyb25tZW50X3ZhcmlhYmxlX2Ryb3BwaW5nIDo9IHRydWUKYWxsb3dfdW5lbmNyeXB0ZWRfc2NyYXRjaCA6PSBmYWxzZQoKCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" - + expected_policy = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjIiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJweXRob24zIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L3Vzci9sb2NhbC9iaW46L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkxBTkc9Qy5VVEYtOCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJHUEdfS0VZPTBEOTZERjRENDExMEU1QzQzRkJGQjE3RjJEMzQ3RUE2QUE2NTQyMUQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX1ZFUlNJT049My42LjE0IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlBZVEhPTl9QSVBfVkVSU0lPTj0yMS4yLjQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX0dFVF9QSVBfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9weXBhL2dldC1waXAvcmF3L2MyMGIwY2ZkNjQzY2Q0YTE5MjQ2Y2NmMjA0ZTI5OTdhZjcwZjZiMjEvcHVibGljL2dldC1waXAucHkiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX0dFVF9QSVBfU0hBMjU2PWZhNmYzZmI5M2NjZTIzNGNkNGU4ZGQyYmViNTRhNTFhYjljMjQ3NjUzYjUyODU1YTQ4ZGQ0NGU2YjIxZmYyOGIiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InB5dGhvbjozLjYuMTQtc2xpbS1idXN0ZXIiLCJsYXllcnMiOlsiMjU0Y2M4NTNkYTYwODE5MDVjOTEwOWM4YjlkOTljOWZiMDk4N2JhMWQ4OGY3MjkwODg5MDNjZmZiODBmNTVmMSIsImE1NjhmMTkwMGJlZDYwYTA2NDFiNzZiOTkxYWQ0MzE0NDZkOWMzYTM0NGQ3YjI2MWYxMGRlOGQ4ZTczNzYzYWMiLCJjNzBjNTMwZTg0MmY2NjIxNWIwYmQ5NTU4NzcxNTdiYTI0YzM3OTkzMDM1NjdjM2Y1NjczYzQ1NjYzZWE0ZDE1IiwiM2U4NmMzY2NmMTY0MmJmNTg0ZGUzM2I0OWM3MjQ4Zjg3ZWVjZDBmNmQ4YzA4MzUzZGFhMzZjYzdhZDBhN2I2YSIsIjFlNDY4NGQ4YzdjYWE3NGM2NTI0MTcyYjRkNWExNTlhMTA4ODc2MTNlZDcwZjE4ZDBhNTVkMDViMmFmNjFhY2QiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9hY2kvbG9ncyIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2FjaS9zZWNyZXQiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJvIl0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3NlY3JldHNWb2x1bWUvLisiLCJ0eXBlIjoiYmluZCJ9LHsiZGVzdGluYXRpb24iOiIvZXRjL3Jlc29sdi5jb25mIiwib3B0aW9ucyI6WyJyYmluZCIsInJzaGFyZWQiLCJydyJdLCJzb3VyY2UiOiJzYW5kYm94Oi8vL3RtcC9hdGxhcy9yZXNvbHZjb25mLy4rIiwidHlwZSI6ImJpbmQifV0sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifSx7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImVmZmVjdGl2ZSI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZTRVRJRCIsIkNBUF9GT1dORVIiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUVUlEIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUUENBUCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX1NZU19DSFJPT1QiLCJDQVBfS0lMTCIsIkNBUF9BVURJVF9XUklURSJdfSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJub19uZXdfcHJpdmlsZWdlcyI6ZmFsc2UsInNlY2NvbXBfcHJvZmlsZV9zaGEyNTYiOiIiLCJzaWduYWxzIjpbXSwidXNlciI6eyJncm91cF9pZG5hbWVzIjpbeyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifV0sInVtYXNrIjoiMDAyMiIsInVzZXJfaWRuYW1lIjp7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9fSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCmFsbG93X2NhcGFiaWxpdHlfZHJvcHBpbmcgOj0gdHJ1ZQoKbW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLm1vdW50X2RldmljZQp1bm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X2RldmljZQptb3VudF9vdmVybGF5IDo9IGRhdGEuZnJhbWV3b3JrLm1vdW50X292ZXJsYXkKdW5tb3VudF9vdmVybGF5IDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfb3ZlcmxheQpjcmVhdGVfY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmNyZWF0ZV9jb250YWluZXIKZXhlY19pbl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19pbl9jb250YWluZXIKZXhlY19leHRlcm5hbCA6PSBkYXRhLmZyYW1ld29yay5leGVjX2V4dGVybmFsCnNodXRkb3duX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5zaHV0ZG93bl9jb250YWluZXIKc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzIDo9IGRhdGEuZnJhbWV3b3JrLnNpZ25hbF9jb250YWluZXJfcHJvY2VzcwpwbGFuOV9tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV9tb3VudApwbGFuOV91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X3VubW91bnQKZ2V0X3Byb3BlcnRpZXMgOj0gZGF0YS5mcmFtZXdvcmsuZ2V0X3Byb3BlcnRpZXMKZHVtcF9zdGFja3MgOj0gZGF0YS5mcmFtZXdvcmsuZHVtcF9zdGFja3MKcnVudGltZV9sb2dnaW5nIDo9IGRhdGEuZnJhbWV3b3JrLnJ1bnRpbWVfbG9nZ2luZwpsb2FkX2ZyYWdtZW50IDo9IGRhdGEuZnJhbWV3b3JrLmxvYWRfZnJhZ21lbnQKc2NyYXRjaF9tb3VudCA6PSBkYXRhLmZyYW1ld29yay5zY3JhdGNoX21vdW50CnNjcmF0Y2hfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5zY3JhdGNoX3VubW91bnQKCnJlYXNvbiA6PSB7ImVycm9ycyI6IGRhdGEuZnJhbWV3b3JrLmVycm9yc30=" self.assertEqual(expected_policy, self.aci_arm_policy.get_serialized_output()) self.assertEqual( - "2.0.0", + "2", self.aci_arm_policy._fragments[0][ config.POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS_MINIMUM_SVN ], ) - # @unittest.skip("not in use") @pytest.mark.run(order=10) class MultiplePolicyTemplate(unittest.TestCase): @@ -2006,13 +1986,12 @@ def test_multiple_policies(self): output2 = self.aci_policy2.get_serialized_output() self.assertTrue(output1 != output2) - expected_output1 = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbImJhc2giXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vY3VzdG9taXplZC9wYXRoL3ZhbHVlIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFU1RfUkVHRVhQX0VOVj10ZXN0X3JlZ2V4cF9lbnYiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUlVTVFVQX0hPTUU9L3Vzci9sb2NhbC9ydXN0dXAiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiQ0FSR09fSE9NRT0vdXNyL2xvY2FsL2NhcmdvIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RfVkVSU0lPTj0xLjUyLjEiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InJ1c3Q6MS41Mi4xIiwibGF5ZXJzIjpbImZlODRjOWQ1YmZkZGQwN2EyNjI0ZDAwMzMzY2YxM2MxYTljOTQxZjNhMjYxZjEzZWFkNDRmYzZhOTNiYzBlN2EiLCI0ZGVkYWU0Mjg0N2M3MDRkYTg5MWEyOGMyNWQzMjIwMWExYWU0NDBiY2UyYWVjY2NmYThlNmYwM2I5N2E2YTZjIiwiNDFkNjRjZGViMzQ3YmYyMzZiNGMxM2I3NDAzYjYzM2ZmMTFmMWNmOTRkYmM3Y2Y4ODFhNDRkNmRhODhjNTE1NiIsImViMzY5MjFlMWY4MmFmNDZkZmUyNDhlZjhmMWIzYWZiNmE1MjMwYTY0MTgxZDk2MGQxMDIzN2EwOGNkNzNjNzkiLCJlNzY5ZDc0ODdjYzMxNGQzZWU3NDhhNDQ0MDgwNTMxN2MxOTI2MmM3YWNkMmZkYmRiMGQ0N2QyZTQ2MTNhMTVjIiwiMWI4MGYxMjBkYmQ4OGU0MzU1ZDYyNDFiNTE5YzNlMjUyOTAyMTVjNDY5NTE2YjQ5ZGVjZTljZjA3MTc1YTc2NiJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL21vdW50L2F6dXJlZmlsZSIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" - expected_output2 = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbInB5dGhvbjMiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vY3VzdG9taXplZC9kaWZmZXJlbnQvcGF0aC92YWx1ZSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJMQU5HPUMuVVRGLTgiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiR1BHX0tFWT0wRDk2REY0RDQxMTBFNUM0M0ZCRkIxN0YyRDM0N0VBNkFBNjU0MjFEIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlBZVEhPTl9WRVJTSU9OPTMuNi4xNCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fUElQX1ZFUlNJT049MjEuMi40IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlBZVEhPTl9HRVRfUElQX1VSTD1odHRwczovL2dpdGh1Yi5jb20vcHlwYS9nZXQtcGlwL3Jhdy9jMjBiMGNmZDY0M2NkNGExOTI0NmNjZjIwNGUyOTk3YWY3MGY2YjIxL3B1YmxpYy9nZXQtcGlwLnB5IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlBZVEhPTl9HRVRfUElQX1NIQTI1Nj1mYTZmM2ZiOTNjY2UyMzRjZDRlOGRkMmJlYjU0YTUxYWI5YzI0NzY1M2I1Mjg1NWE0OGRkNDRlNmIyMWZmMjhiIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IlQoRSk/TVA9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljUGFja2FnZUZpbGVOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6Ikhvc3RlZFNlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0FQSV9WRVJTSU9OPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0hFQURFUj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9TRVJWRVJfVEhVTUJQUklOVD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlX3Jlc3RhcnRlZF9ieT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwiaWQiOiJweXRob246My42LjE0LXNsaW0tYnVzdGVyIiwibGF5ZXJzIjpbIjI1NGNjODUzZGE2MDgxOTA1YzkxMDljOGI5ZDk5YzlmYjA5ODdiYTFkODhmNzI5MDg4OTAzY2ZmYjgwZjU1ZjEiLCJhNTY4ZjE5MDBiZWQ2MGEwNjQxYjc2Yjk5MWFkNDMxNDQ2ZDljM2EzNDRkN2IyNjFmMTBkZThkOGU3Mzc2M2FjIiwiYzcwYzUzMGU4NDJmNjYyMTViMGJkOTU1ODc3MTU3YmEyNGMzNzk5MzAzNTY3YzNmNTY3M2M0NTY2M2VhNGQxNSIsIjNlODZjM2NjZjE2NDJiZjU4NGRlMzNiNDljNzI0OGY4N2VlY2QwZjZkOGMwODM1M2RhYTM2Y2M3YWQwYTdiNmEiLCIxZTQ2ODRkOGM3Y2FhNzRjNjUyNDE3MmI0ZDVhMTU5YTEwODg3NjEzZWQ3MGYxOGQwYTU1ZDA1YjJhZjYxYWNkIl0sIm1vdW50cyI6W3siZGVzdGluYXRpb24iOiIvbW91bnQvZmlsZSIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" + expected_output1 = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJiYXNoIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L2N1c3RvbWl6ZWQvcGF0aC92YWx1ZSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVNUX1JFR0VYUF9FTlY9dGVzdF9yZWdleHBfZW52IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RVUF9IT01FPS91c3IvbG9jYWwvcnVzdHVwIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkNBUkdPX0hPTUU9L3Vzci9sb2NhbC9jYXJnbyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJSVVNUX1ZFUlNJT049MS41Mi4xIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IlQoRSk/TVA9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljUGFja2FnZUZpbGVOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6Ikhvc3RlZFNlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0FQSV9WRVJTSU9OPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX0hFQURFUj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9TRVJWRVJfVEhVTUJQUklOVD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlX3Jlc3RhcnRlZF9ieT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwiaWQiOiJydXN0OjEuNTIuMSIsImxheWVycyI6WyJmZTg0YzlkNWJmZGRkMDdhMjYyNGQwMDMzM2NmMTNjMWE5Yzk0MWYzYTI2MWYxM2VhZDQ0ZmM2YTkzYmMwZTdhIiwiNGRlZGFlNDI4NDdjNzA0ZGE4OTFhMjhjMjVkMzIyMDFhMWFlNDQwYmNlMmFlY2NjZmE4ZTZmMDNiOTdhNmE2YyIsIjQxZDY0Y2RlYjM0N2JmMjM2YjRjMTNiNzQwM2I2MzNmZjExZjFjZjk0ZGJjN2NmODgxYTQ0ZDZkYTg4YzUxNTYiLCJlYjM2OTIxZTFmODJhZjQ2ZGZlMjQ4ZWY4ZjFiM2FmYjZhNTIzMGE2NDE4MWQ5NjBkMTAyMzdhMDhjZDczYzc5IiwiZTc2OWQ3NDg3Y2MzMTRkM2VlNzQ4YTQ0NDA4MDUzMTdjMTkyNjJjN2FjZDJmZGJkYjBkNDdkMmU0NjEzYTE1YyIsIjFiODBmMTIwZGJkODhlNDM1NWQ2MjQxYjUxOWMzZTI1MjkwMjE1YzQ2OTUxNmI0OWRlY2U5Y2YwNzE3NWE3NjYiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9tb3VudC9henVyZWZpbGUiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL2F6dXJlRmlsZVZvbHVtZS8uKyIsInR5cGUiOiJiaW5kIn0seyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNhcGFiaWxpdGllcyI6eyJhbWJpZW50IjpbXSwiYm91bmRpbmciOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiZWZmZWN0aXZlIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImluaGVyaXRhYmxlIjpbXSwicGVybWl0dGVkIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl19LCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKYWxsb3dfY2FwYWJpbGl0eV9kcm9wcGluZyA6PSB0cnVlCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" + expected_output2 = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJweXRob24zIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L2N1c3RvbWl6ZWQvZGlmZmVyZW50L3BhdGgvdmFsdWUiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiTEFORz1DLlVURi04IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkdQR19LRVk9MEQ5NkRGNEQ0MTEwRTVDNDNGQkZCMTdGMkQzNDdFQTZBQTY1NDIxRCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fVkVSU0lPTj0zLjYuMTQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX1BJUF9WRVJTSU9OPTIxLjIuNCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9VUkw9aHR0cHM6Ly9naXRodWIuY29tL3B5cGEvZ2V0LXBpcC9yYXcvYzIwYjBjZmQ2NDNjZDRhMTkyNDZjY2YyMDRlMjk5N2FmNzBmNmIyMS9wdWJsaWMvZ2V0LXBpcC5weSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9TSEEyNTY9ZmE2ZjNmYjkzY2NlMjM0Y2Q0ZThkZDJiZWI1NGE1MWFiOWMyNDc2NTNiNTI4NTVhNDhkZDQ0ZTZiMjFmZjI4YiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IigoP2kpRkFCUklDKV8uKz0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIT1NUTkFNRT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJUKEUpP01QPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY1BhY2thZ2VGaWxlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIb3N0ZWRTZXJ2aWNlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9IRUFERVI9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiYXp1cmVjb250YWluZXJpbnN0YW5jZV9yZXN0YXJ0ZWRfYnk9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImlkIjoicHl0aG9uOjMuNi4xNC1zbGltLWJ1c3RlciIsImxheWVycyI6WyIyNTRjYzg1M2RhNjA4MTkwNWM5MTA5YzhiOWQ5OWM5ZmIwOTg3YmExZDg4ZjcyOTA4ODkwM2NmZmI4MGY1NWYxIiwiYTU2OGYxOTAwYmVkNjBhMDY0MWI3NmI5OTFhZDQzMTQ0NmQ5YzNhMzQ0ZDdiMjYxZjEwZGU4ZDhlNzM3NjNhYyIsImM3MGM1MzBlODQyZjY2MjE1YjBiZDk1NTg3NzE1N2JhMjRjMzc5OTMwMzU2N2MzZjU2NzNjNDU2NjNlYTRkMTUiLCIzZTg2YzNjY2YxNjQyYmY1ODRkZTMzYjQ5YzcyNDhmODdlZWNkMGY2ZDhjMDgzNTNkYWEzNmNjN2FkMGE3YjZhIiwiMWU0Njg0ZDhjN2NhYTc0YzY1MjQxNzJiNGQ1YTE1OWExMDg4NzYxM2VkNzBmMThkMGE1NWQwNWIyYWY2MWFjZCJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL21vdW50L2ZpbGUiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL2F6dXJlRmlsZVZvbHVtZS8uKyIsInR5cGUiOiJiaW5kIn0seyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNhcGFiaWxpdGllcyI6eyJhbWJpZW50IjpbXSwiYm91bmRpbmciOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiZWZmZWN0aXZlIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImluaGVyaXRhYmxlIjpbXSwicGVybWl0dGVkIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl19LCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKYWxsb3dfY2FwYWJpbGl0eV9kcm9wcGluZyA6PSB0cnVlCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" self.assertEqual(output1, expected_output1) self.assertEqual(output2, expected_output2) - # @unittest.skip("not in use") @pytest.mark.run(order=11) class PolicyGeneratingArmInitContainer(unittest.TestCase): @@ -2190,18 +2169,15 @@ def setUpClass(cls): def test_arm_template_with_init_container(self): regular_image_json = json.loads( self.aci_arm_policy.get_serialized_output( - output_type=OutputType.RAW, use_json=True + output_type=OutputType.RAW, rego_boilerplate=False ) ) - python_image_name = regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["1"].pop(config.POLICY_FIELD_CONTAINERS_ID) + python_image_name = regular_image_json[1].pop(config.POLICY_FIELD_CONTAINERS_ID) # see if the remote image and the local one produce the same output self.assertTrue("python" in python_image_name) - # @unittest.skip("not in use") @pytest.mark.run(order=12) class PolicyGeneratingDisableStdioAccess(unittest.TestCase): @@ -2344,14 +2320,164 @@ def test_arm_template_without_stdio_access(self): ) ) - stdio_access = regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS] + stdio_access = regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS] # see if the remote image and the local one produce the same output self.assertFalse(stdio_access) - # @unittest.skip("not in use") @pytest.mark.run(order=13) +class PolicyGeneratingAllowElevated(unittest.TestCase): + + custom_arm_json_default_value = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + "image": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"rust:1.52.1" + }, + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + "properties": { + "securityContext":{ + "privileged":"false" + }, + "image": "[parameters('image')]", + "environmentVariables": [ + { + "name": "PORT", + "value": "80" + } + ], + + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "command": [ + "/bin/bash", + "-c", + "while sleep 5; do cat /mnt/input/access.log; done" + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + } + } + } + ], + + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + @classmethod + def setUpClass(cls): + cls.aci_arm_policy = load_policy_from_arm_template_str( + cls.custom_arm_json_default_value, "", disable_stdio=True + )[0] + cls.aci_arm_policy.populate_policy_content_for_all_images() + + def test_arm_template_allow_elevated_false(self): + regular_image_json = json.loads( + self.aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + allow_elevated = regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED] + + # see if the remote image and the local one produce the same output + self.assertFalse(allow_elevated) + + +# @unittest.skip("not in use") +@pytest.mark.run(order=14) class PrintExistingPolicy(unittest.TestCase): def test_printing_existing_policy(self): @@ -2652,7 +2778,7 @@ def test_printing_existing_policy(self): os.remove("test_template2.json") # @unittest.skip("not in use") -@pytest.mark.run(order=14) +@pytest.mark.run(order=15) class PolicyGeneratingArmWildcardEnvs(unittest.TestCase): custom_json = """ { @@ -3314,9 +3440,8 @@ def test_wildcard_env_var_invalid(self): self.assertEqual(wrapped_exit.exception.code, 1) - # @unittest.skip("not in use") -@pytest.mark.run(order=15) +@pytest.mark.run(order=16) class PolicyGeneratingEdgeCases(unittest.TestCase): custom_arm_json_default_value = """ @@ -3461,4 +3586,1711 @@ def test_arm_template_with_env_var(self): # see if the remote image and the local one produce the same output self.assertEquals(env_var, "PORT=parameters('abc')") - self.assertEquals(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ID], "rust:1.52.1") \ No newline at end of file + self.assertEquals(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ID], "rust:1.52.1") + +# @unittest.skip("not in use") +@pytest.mark.run(order=16) +class PolicyGeneratingSecurityContext(unittest.TestCase): + custom_arm_json = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + custom_arm_json2 = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext": { + "privileged": "true", + "capabilities":{ + "add": ["CAP_SYS_TIME","CAP_DAC_READ_SEARCH"], + "drop": ["CAP_CHOWN","CAP_KILL"] + }, + "runAsGroup": 123, + "runAsUser": 456, + "seccompProfile": "seccompStr" + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + + custom_arm_json3 = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext":{ + "privileged": true, + "allowPrivilegeEscalation": true, + "runAsGroup":123, + "runAsUser":456 + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + custom_arm_json4 = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext": { + "privileged": "false", + "capabilities":{ + "add": ["CAP_SYS_TIME","CAP_DAC_READ_SEARCH"], + "drop": ["CAP_CHOWN","CAP_KILL"] + }, + "runAsGroup": 123, + "runAsUser": 456 + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + @classmethod + def setUpClass(cls): + seccomp_profile_contents = "ewoJImNvbW1lbnQiOiAiRGVmYXVsdCBtb2J5IHNlY2NvbXAgcG9saWN5IGNvbmZpZyBmaWxlIGF0OiBodHRwczovL2dpdGh1Yi5jb20vbW9ieS9tb2J5L2Jsb2IvbWFzdGVyL3Byb2ZpbGVzL3NlY2NvbXAvZGVmYXVsdC5qc29uIiwKCSJkZWZhdWx0QWN0aW9uIjogIlNDTVBfQUNUX0VSUk5PIiwKCSJkZWZhdWx0RXJybm9SZXQiOiAxLAoJImFyY2hNYXAiOiBbCgkJewoJCQkiYXJjaGl0ZWN0dXJlIjogIlNDTVBfQVJDSF9YODZfNjQiLAoJCQkic3ViQXJjaGl0ZWN0dXJlcyI6IFsKCQkJCSJTQ01QX0FSQ0hfWDg2IiwKCQkJCSJTQ01QX0FSQ0hfWDMyIgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX0FBUkNINjQiLAoJCQkic3ViQXJjaGl0ZWN0dXJlcyI6IFsKCQkJCSJTQ01QX0FSQ0hfQVJNIgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX01JUFM2NCIsCgkJCSJzdWJBcmNoaXRlY3R1cmVzIjogWwoJCQkJIlNDTVBfQVJDSF9NSVBTIiwKCQkJCSJTQ01QX0FSQ0hfTUlQUzY0TjMyIgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX01JUFM2NE4zMiIsCgkJCSJzdWJBcmNoaXRlY3R1cmVzIjogWwoJCQkJIlNDTVBfQVJDSF9NSVBTIiwKCQkJCSJTQ01QX0FSQ0hfTUlQUzY0IgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX01JUFNFTDY0IiwKCQkJInN1YkFyY2hpdGVjdHVyZXMiOiBbCgkJCQkiU0NNUF9BUkNIX01JUFNFTCIsCgkJCQkiU0NNUF9BUkNIX01JUFNFTDY0TjMyIgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX01JUFNFTDY0TjMyIiwKCQkJInN1YkFyY2hpdGVjdHVyZXMiOiBbCgkJCQkiU0NNUF9BUkNIX01JUFNFTCIsCgkJCQkiU0NNUF9BUkNIX01JUFNFTDY0IgoJCQldCgkJfSwKCQl7CgkJCSJhcmNoaXRlY3R1cmUiOiAiU0NNUF9BUkNIX1MzOTBYIiwKCQkJInN1YkFyY2hpdGVjdHVyZXMiOiBbCgkJCQkiU0NNUF9BUkNIX1MzOTAiCgkJCV0KCQl9CgldLAoJInN5c2NhbGxzIjogWwoJCXsKCQkJIm5hbWVzIjogWwoJCQkJImFjY2VwdCIsCgkJCQkiYWNjZXB0NCIsCgkJCQkiYWNjZXNzIiwKCQkJCSJhZGp0aW1leCIsCgkJCQkiYWxhcm0iLAoJCQkJImJpbmQiLAoJCQkJImJyayIsCgkJCQkiY2FwZ2V0IiwKCQkJCSJjaGRpciIsCgkJCQkiY2htb2QiLAoJCQkJImNsb2NrX2FkanRpbWUiLAoJCQkJImNsb2NrX2FkanRpbWU2NCIsCgkJCQkiY2xvY2tfZ2V0cmVzIiwKCQkJCSJjbG9ja19nZXRyZXNfdGltZTY0IiwKCQkJCSJjbG9ja19nZXR0aW1lIiwKCQkJCSJjbG9ja19nZXR0aW1lNjQiLAoJCQkJImNsb2NrX25hbm9zbGVlcCIsCgkJCQkiY2xvY2tfbmFub3NsZWVwX3RpbWU2NCIsCgkJCQkiY2xvc2UiLAoJCQkJImNsb3NlX3JhbmdlIiwKCQkJCSJjb25uZWN0IiwKCQkJCSJjb3B5X2ZpbGVfcmFuZ2UiLAoJCQkJImNyZWF0IiwKCQkJCSJkdXAiLAoJCQkJImR1cDIiLAoJCQkJImR1cDMiLAoJCQkJImVwb2xsX2NyZWF0ZSIsCgkJCQkiZXBvbGxfY3JlYXRlMSIsCgkJCQkiZXBvbGxfY3RsIiwKCQkJCSJlcG9sbF9jdGxfb2xkIiwKCQkJCSJlcG9sbF9wd2FpdCIsCgkJCQkiZXBvbGxfcHdhaXQyIiwKCQkJCSJlcG9sbF93YWl0IiwKCQkJCSJlcG9sbF93YWl0X29sZCIsCgkJCQkiZXZlbnRmZCIsCgkJCQkiZXZlbnRmZDIiLAoJCQkJImV4ZWN2ZSIsCgkJCQkiZXhlY3ZlYXQiLAoJCQkJImV4aXQiLAoJCQkJImV4aXRfZ3JvdXAiLAoJCQkJImZhY2Nlc3NhdCIsCgkJCQkiZmFjY2Vzc2F0MiIsCgkJCQkiZmFkdmlzZTY0IiwKCQkJCSJmYWR2aXNlNjRfNjQiLAoJCQkJImZhbGxvY2F0ZSIsCgkJCQkiZmFub3RpZnlfbWFyayIsCgkJCQkiZmNoZGlyIiwKCQkJCSJmY2htb2QiLAoJCQkJImZjaG1vZGF0IiwKCQkJCSJmY2hvd24iLAoJCQkJImZjaG93bjMyIiwKCQkJCSJmY2hvd25hdCIsCgkJCQkiZmNudGwiLAoJCQkJImZjbnRsNjQiLAoJCQkJImZkYXRhc3luYyIsCgkJCQkiZmdldHhhdHRyIiwKCQkJCSJmbGlzdHhhdHRyIiwKCQkJCSJmbG9jayIsCgkJCQkiZm9yayIsCgkJCQkiZnJlbW92ZXhhdHRyIiwKCQkJCSJmc2V0eGF0dHIiLAoJCQkJImZzdGF0IiwKCQkJCSJmc3RhdDY0IiwKCQkJCSJmc3RhdGF0NjQiLAoJCQkJImZzdGF0ZnMiLAoJCQkJImZzdGF0ZnM2NCIsCgkJCQkiZnN5bmMiLAoJCQkJImZ0cnVuY2F0ZSIsCgkJCQkiZnRydW5jYXRlNjQiLAoJCQkJImZ1dGV4IiwKCQkJCSJmdXRleF90aW1lNjQiLAoJCQkJImZ1dGltZXNhdCIsCgkJCQkiZ2V0Y3B1IiwKCQkJCSJnZXRjd2QiLAoJCQkJImdldGRlbnRzIiwKCQkJCSJnZXRkZW50czY0IiwKCQkJCSJnZXRlZ2lkIiwKCQkJCSJnZXRlZ2lkMzIiLAoJCQkJImdldGV1aWQiLAoJCQkJImdldGV1aWQzMiIsCgkJCQkiZ2V0Z2lkIiwKCQkJCSJnZXRnaWQzMiIsCgkJCQkiZ2V0Z3JvdXBzIiwKCQkJCSJnZXRncm91cHMzMiIsCgkJCQkiZ2V0aXRpbWVyIiwKCQkJCSJnZXRwZWVybmFtZSIsCgkJCQkiZ2V0cGdpZCIsCgkJCQkiZ2V0cGdycCIsCgkJCQkiZ2V0cGlkIiwKCQkJCSJnZXRwcGlkIiwKCQkJCSJnZXRwcmlvcml0eSIsCgkJCQkiZ2V0cmFuZG9tIiwKCQkJCSJnZXRyZXNnaWQiLAoJCQkJImdldHJlc2dpZDMyIiwKCQkJCSJnZXRyZXN1aWQiLAoJCQkJImdldHJlc3VpZDMyIiwKCQkJCSJnZXRybGltaXQiLAoJCQkJImdldF9yb2J1c3RfbGlzdCIsCgkJCQkiZ2V0cnVzYWdlIiwKCQkJCSJnZXRzaWQiLAoJCQkJImdldHNvY2tuYW1lIiwKCQkJCSJnZXRzb2Nrb3B0IiwKCQkJCSJnZXRfdGhyZWFkX2FyZWEiLAoJCQkJImdldHRpZCIsCgkJCQkiZ2V0dGltZW9mZGF5IiwKCQkJCSJnZXR1aWQiLAoJCQkJImdldHVpZDMyIiwKCQkJCSJnZXR4YXR0ciIsCgkJCQkiaW5vdGlmeV9hZGRfd2F0Y2giLAoJCQkJImlub3RpZnlfaW5pdCIsCgkJCQkiaW5vdGlmeV9pbml0MSIsCgkJCQkiaW5vdGlmeV9ybV93YXRjaCIsCgkJCQkiaW9fY2FuY2VsIiwKCQkJCSJpb2N0bCIsCgkJCQkiaW9fZGVzdHJveSIsCgkJCQkiaW9fZ2V0ZXZlbnRzIiwKCQkJCSJpb19wZ2V0ZXZlbnRzIiwKCQkJCSJpb19wZ2V0ZXZlbnRzX3RpbWU2NCIsCgkJCQkiaW9wcmlvX2dldCIsCgkJCQkiaW9wcmlvX3NldCIsCgkJCQkiaW9fc2V0dXAiLAoJCQkJImlvX3N1Ym1pdCIsCgkJCQkiaW9fdXJpbmdfZW50ZXIiLAoJCQkJImlvX3VyaW5nX3JlZ2lzdGVyIiwKCQkJCSJpb191cmluZ19zZXR1cCIsCgkJCQkiaXBjIiwKCQkJCSJraWxsIiwKCQkJCSJsY2hvd24iLAoJCQkJImxjaG93bjMyIiwKCQkJCSJsZ2V0eGF0dHIiLAoJCQkJImxpbmsiLAoJCQkJImxpbmthdCIsCgkJCQkibGlzdGVuIiwKCQkJCSJsaXN0eGF0dHIiLAoJCQkJImxsaXN0eGF0dHIiLAoJCQkJIl9sbHNlZWsiLAoJCQkJImxyZW1vdmV4YXR0ciIsCgkJCQkibHNlZWsiLAoJCQkJImxzZXR4YXR0ciIsCgkJCQkibHN0YXQiLAoJCQkJImxzdGF0NjQiLAoJCQkJIm1hZHZpc2UiLAoJCQkJIm1lbWJhcnJpZXIiLAoJCQkJIm1lbWZkX2NyZWF0ZSIsCgkJCQkibWluY29yZSIsCgkJCQkibWtkaXIiLAoJCQkJIm1rZGlyYXQiLAoJCQkJIm1rbm9kIiwKCQkJCSJta25vZGF0IiwKCQkJCSJtbG9jayIsCgkJCQkibWxvY2syIiwKCQkJCSJtbG9ja2FsbCIsCgkJCQkibW1hcCIsCgkJCQkibW1hcDIiLAoJCQkJIm1wcm90ZWN0IiwKCQkJCSJtcV9nZXRzZXRhdHRyIiwKCQkJCSJtcV9ub3RpZnkiLAoJCQkJIm1xX29wZW4iLAoJCQkJIm1xX3RpbWVkcmVjZWl2ZSIsCgkJCQkibXFfdGltZWRyZWNlaXZlX3RpbWU2NCIsCgkJCQkibXFfdGltZWRzZW5kIiwKCQkJCSJtcV90aW1lZHNlbmRfdGltZTY0IiwKCQkJCSJtcV91bmxpbmsiLAoJCQkJIm1yZW1hcCIsCgkJCQkibXNnY3RsIiwKCQkJCSJtc2dnZXQiLAoJCQkJIm1zZ3JjdiIsCgkJCQkibXNnc25kIiwKCQkJCSJtc3luYyIsCgkJCQkibXVubG9jayIsCgkJCQkibXVubG9ja2FsbCIsCgkJCQkibXVubWFwIiwKCQkJCSJuYW5vc2xlZXAiLAoJCQkJIm5ld2ZzdGF0YXQiLAoJCQkJIl9uZXdzZWxlY3QiLAoJCQkJIm9wZW4iLAoJCQkJIm9wZW5hdCIsCgkJCQkib3BlbmF0MiIsCgkJCQkicGF1c2UiLAoJCQkJInBpZGZkX29wZW4iLAoJCQkJInBpZGZkX3NlbmRfc2lnbmFsIiwKCQkJCSJwaXBlIiwKCQkJCSJwaXBlMiIsCgkJCQkicG9sbCIsCgkJCQkicHBvbGwiLAoJCQkJInBwb2xsX3RpbWU2NCIsCgkJCQkicHJjdGwiLAoJCQkJInByZWFkNjQiLAoJCQkJInByZWFkdiIsCgkJCQkicHJlYWR2MiIsCgkJCQkicHJsaW1pdDY0IiwKCQkJCSJwc2VsZWN0NiIsCgkJCQkicHNlbGVjdDZfdGltZTY0IiwKCQkJCSJwd3JpdGU2NCIsCgkJCQkicHdyaXRldiIsCgkJCQkicHdyaXRldjIiLAoJCQkJInJlYWQiLAoJCQkJInJlYWRhaGVhZCIsCgkJCQkicmVhZGxpbmsiLAoJCQkJInJlYWRsaW5rYXQiLAoJCQkJInJlYWR2IiwKCQkJCSJyZWN2IiwKCQkJCSJyZWN2ZnJvbSIsCgkJCQkicmVjdm1tc2ciLAoJCQkJInJlY3ZtbXNnX3RpbWU2NCIsCgkJCQkicmVjdm1zZyIsCgkJCQkicmVtYXBfZmlsZV9wYWdlcyIsCgkJCQkicmVtb3ZleGF0dHIiLAoJCQkJInJlbmFtZSIsCgkJCQkicmVuYW1lYXQiLAoJCQkJInJlbmFtZWF0MiIsCgkJCQkicmVzdGFydF9zeXNjYWxsIiwKCQkJCSJybWRpciIsCgkJCQkicnNlcSIsCgkJCQkicnRfc2lnYWN0aW9uIiwKCQkJCSJydF9zaWdwZW5kaW5nIiwKCQkJCSJydF9zaWdwcm9jbWFzayIsCgkJCQkicnRfc2lncXVldWVpbmZvIiwKCQkJCSJydF9zaWdyZXR1cm4iLAoJCQkJInJ0X3NpZ3N1c3BlbmQiLAoJCQkJInJ0X3NpZ3RpbWVkd2FpdCIsCgkJCQkicnRfc2lndGltZWR3YWl0X3RpbWU2NCIsCgkJCQkicnRfdGdzaWdxdWV1ZWluZm8iLAoJCQkJInNjaGVkX2dldGFmZmluaXR5IiwKCQkJCSJzY2hlZF9nZXRhdHRyIiwKCQkJCSJzY2hlZF9nZXRwYXJhbSIsCgkJCQkic2NoZWRfZ2V0X3ByaW9yaXR5X21heCIsCgkJCQkic2NoZWRfZ2V0X3ByaW9yaXR5X21pbiIsCgkJCQkic2NoZWRfZ2V0c2NoZWR1bGVyIiwKCQkJCSJzY2hlZF9ycl9nZXRfaW50ZXJ2YWwiLAoJCQkJInNjaGVkX3JyX2dldF9pbnRlcnZhbF90aW1lNjQiLAoJCQkJInNjaGVkX3NldGFmZmluaXR5IiwKCQkJCSJzY2hlZF9zZXRhdHRyIiwKCQkJCSJzY2hlZF9zZXRwYXJhbSIsCgkJCQkic2NoZWRfc2V0c2NoZWR1bGVyIiwKCQkJCSJzY2hlZF95aWVsZCIsCgkJCQkic2VsZWN0IiwKCQkJCSJzZW1jdGwiLAoJCQkJInNlbWdldCIsCgkJCQkic2Vtb3AiLAoJCQkJInNlbXRpbWVkb3AiLAoJCQkJInNlbXRpbWVkb3BfdGltZTY0IiwKCQkJCSJzZW5kIiwKCQkJCSJzZW5kZmlsZSIsCgkJCQkic2VuZGZpbGU2NCIsCgkJCQkic2VuZG1tc2ciLAoJCQkJInNlbmRtc2ciLAoJCQkJInNlbmR0byIsCgkJCQkic2V0aXRpbWVyIiwKCQkJCSJzZXRwcmlvcml0eSIsCgkJCQkic2V0X3JvYnVzdF9saXN0IiwKCQkJCSJzZXRzaWQiLAoJCQkJInNldHNvY2tvcHQiLAoJCQkJInNldF90aHJlYWRfYXJlYSIsCgkJCQkic2V0X3RpZF9hZGRyZXNzIiwKCQkJCSJzZXR4YXR0ciIsCgkJCQkic2htYXQiLAoJCQkJInNobWN0bCIsCgkJCQkic2htZHQiLAoJCQkJInNobWdldCIsCgkJCQkic2h1dGRvd24iLAoJCQkJInNpZ2FsdHN0YWNrIiwKCQkJCSJzaWduYWxmZCIsCgkJCQkic2lnbmFsZmQ0IiwKCQkJCSJzaWdwcm9jbWFzayIsCgkJCQkic2lncmV0dXJuIiwKCQkJCSJzcGxpY2UiLAoJCQkJInN0YXQiLAoJCQkJInN0YXQ2NCIsCgkJCQkic3RhdGZzIiwKCQkJCSJzdGF0ZnM2NCIsCgkJCQkic3RhdHgiLAoJCQkJInN5bWxpbmsiLAoJCQkJInN5bWxpbmthdCIsCgkJCQkic3luYyIsCgkJCQkic3luY19maWxlX3JhbmdlIiwKCQkJCSJzeW5jZnMiLAoJCQkJInN5c2luZm8iLAoJCQkJInRlZSIsCgkJCQkidGdraWxsIiwKCQkJCSJ0aW1lIiwKCQkJCSJ0aW1lcl9jcmVhdGUiLAoJCQkJInRpbWVyX2RlbGV0ZSIsCgkJCQkidGltZXJfZ2V0b3ZlcnJ1biIsCgkJCQkidGltZXJfZ2V0dGltZSIsCgkJCQkidGltZXJfZ2V0dGltZTY0IiwKCQkJCSJ0aW1lcl9zZXR0aW1lIiwKCQkJCSJ0aW1lcl9zZXR0aW1lNjQiLAoJCQkJInRpbWVyZmRfY3JlYXRlIiwKCQkJCSJ0aW1lcmZkX2dldHRpbWUiLAoJCQkJInRpbWVyZmRfZ2V0dGltZTY0IiwKCQkJCSJ0aW1lcmZkX3NldHRpbWUiLAoJCQkJInRpbWVyZmRfc2V0dGltZTY0IiwKCQkJCSJ0aW1lcyIsCgkJCQkidGtpbGwiLAoJCQkJInRydW5jYXRlIiwKCQkJCSJ0cnVuY2F0ZTY0IiwKCQkJCSJ1Z2V0cmxpbWl0IiwKCQkJCSJ1bWFzayIsCgkJCQkidW5hbWUiLAoJCQkJInVubGluayIsCgkJCQkidW5saW5rYXQiLAoJCQkJInV0aW1lIiwKCQkJCSJ1dGltZW5zYXQiLAoJCQkJInV0aW1lbnNhdF90aW1lNjQiLAoJCQkJInV0aW1lcyIsCgkJCQkidmZvcmsiLAoJCQkJInZtc3BsaWNlIiwKCQkJCSJ3YWl0NCIsCgkJCQkid2FpdGlkIiwKCQkJCSJ3YWl0cGlkIiwKCQkJCSJ3cml0ZSIsCgkJCQkid3JpdGV2IgoJCQldLAoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIgoJCX0sCgkJewoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSwgMCIsCgkJCSJhcmdzIjogWwoJCQkJewoJCQkJCSJpbmRleCI6IDAsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDIKCQkJCX0sCgkJCQl7CgkJCQkJImluZGV4IjogMSwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMQoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAwCgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19OT05CTE9DSywgMCIsCgkJCSJhcmdzIjogWwoJCQkJewoJCQkJCSJpbmRleCI6IDAsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDIKCQkJCX0sCgkJCQl7CgkJCQkJImluZGV4IjogMSwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMjA0OQoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAwCgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19DTE9FWEVDLCAwIiwKCQkJImFyZ3MiOiBbCgkJCQl7CgkJCQkJImluZGV4IjogMCwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMgoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAxLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiA1MjQyODkKCQkJCX0sCgkJCQl7CgkJCQkJImluZGV4IjogMiwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMAoJCQkJfQoKCQkJXQoJCX0sCgkJewoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0sgfCBTT0NLX0NMT0VYRUMsIDAiLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAyCgkJCQl9LAoJCQkJewoJCQkJCSJpbmRleCI6IDEsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDUyNjMzNwoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAwCgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNLCBJUFBST1RPX1RDUCIsCgkJCSJhcmdzIjogWwoJCQkJewoJCQkJCSJpbmRleCI6IDAsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDIKCQkJCX0sCgkJCQl7CgkJCQkJImluZGV4IjogMSwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMQoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiA2CgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19OT05CTE9DSywgSVBQUk9UT19UQ1AiLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAyCgkJCQl9LAoJCQkJewoJCQkJCSJpbmRleCI6IDEsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDIwNDkKCQkJCX0sCgkJCQl7CgkJCQkJImluZGV4IjogMiwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogNgoJCQkJfQoKCQkJXQoJCX0sCgkJewoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfQ0xPRVhFQywgSVBQUk9UT19UQ1AiLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAyCgkJCQl9LAoJCQkJewoJCQkJCSJpbmRleCI6IDEsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDUyNDI4OQoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiA2CgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19OT05CTE9DSyB8IFNPQ0tfQ0xPRVhFQywgSVBQUk9UT19UQ1AiLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAyCgkJCQl9LAoJCQkJewoJCQkJCSJpbmRleCI6IDEsCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsCgkJCQkJInZhbHVlIiA6IDUyNjMzNwoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiA2CgkJCQl9CgoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsgInNvY2tldHBhaXIiIF0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiY29tbWVudCI6ICJBRl9VTklYLCAqLCAwIiwKCQkJImFyZ3MiOiBbCgkJCQl7CgkJCQkJImluZGV4IjogMCwKCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwKCQkJCQkidmFsdWUiIDogMQoJCQkJfSwKCQkJCXsKCQkJCQkiaW5kZXgiOiAyLAoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLAoJCQkJCSJ2YWx1ZSIgOiAwCgkJCQl9CgkJCV0KCQl9LAoJCXsKCQkJIm5hbWVzIjogWwoJCQkJInByb2Nlc3Nfdm1fcmVhZHYiLAoJCQkJInByb2Nlc3Nfdm1fd3JpdGV2IiwKCQkJCSJwdHJhY2UiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiaW5jbHVkZXMiOiB7CgkJCQkibWluS2VybmVsIjogIjQuOCIKCQkJfQoJCX0sCgkJewoJCQkibmFtZXMiOiBbCgkJCQkicGVyc29uYWxpdHkiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJ2YWx1ZSI6IDAsCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIgoJCQkJfQoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsKCQkJCSJwZXJzb25hbGl0eSIKCQkJXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJhcmdzIjogWwoJCQkJewoJCQkJCSJpbmRleCI6IDAsCgkJCQkJInZhbHVlIjogOCwKCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiCgkJCQl9CgkJCV0KCQl9LAoJCXsKCQkJIm5hbWVzIjogWwoJCQkJInBlcnNvbmFsaXR5IgoJCQldLAoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwKCQkJImFyZ3MiOiBbCgkJCQl7CgkJCQkJImluZGV4IjogMCwKCQkJCQkidmFsdWUiOiAxMzEwNzIsCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIgoJCQkJfQoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsKCQkJCSJwZXJzb25hbGl0eSIKCQkJXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJhcmdzIjogWwoJCQkJewoJCQkJCSJpbmRleCI6IDAsCgkJCQkJInZhbHVlIjogMTMxMDgwLAoJCQkJCSJvcCI6ICJTQ01QX0NNUF9FUSIKCQkJCX0KCQkJXQoJCX0sCgkJewoJCQkibmFtZXMiOiBbCgkJCQkicGVyc29uYWxpdHkiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAwLAoJCQkJCSJ2YWx1ZSI6IDQyOTQ5NjcyOTUsCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIgoJCQkJfQoJCQldCgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsKCQkJCSJzeW5jX2ZpbGVfcmFuZ2UyIgoJCQldLAoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwKCQkJImluY2x1ZGVzIjogewoJCQkJImFyY2hlcyI6IFsKCQkJCQkicHBjNjRsZSIKCQkJCV0KCQkJfQoJCX0sCgkJewoJCQkibmFtZXMiOiBbCgkJCQkiYXJtX2ZhZHZpc2U2NF82NCIsCgkJCQkiYXJtX3N5bmNfZmlsZV9yYW5nZSIsCgkJCQkic3luY19maWxlX3JhbmdlMiIsCgkJCQkiYnJlYWtwb2ludCIsCgkJCQkiY2FjaGVmbHVzaCIsCgkJCQkic2V0X3RscyIKCQkJXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsCgkJCSJpbmNsdWRlcyI6IHsKCQkJCSJhcmNoZXMiOiBbCgkJCQkJImFybSIsCgkJCQkJImFybTY0IgoJCQkJXQoJCQl9CgkJfSwKCQl7CgkJCSJuYW1lcyI6IFsKCQkJCSJhcmNoX3ByY3RsIgoJCQldLAoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwKCQkJImluY2x1ZGVzIjogewoJCQkJImFyY2hlcyI6IFsKCQkJCQkiYW1kNjQiLAoJCQkJCSJ4MzIiCgkJCQldCgkJCX0KCQl9LAoJCXsKCQkJIm5hbWVzIjogWwoJCQkJIm1vZGlmeV9sZHQiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiaW5jbHVkZXMiOiB7CgkJCQkiYXJjaGVzIjogWwoJCQkJCSJhbWQ2NCIsCgkJCQkJIngzMiIsCgkJCQkJIng4NiIKCQkJCV0KCQkJfQoJCX0sCgkJewoJCQkibmFtZXMiOiBbCgkJCQkiczM5MF9wY2lfbW1pb19yZWFkIiwKCQkJCSJzMzkwX3BjaV9tbWlvX3dyaXRlIiwKCQkJCSJzMzkwX3J1bnRpbWVfaW5zdHIiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiaW5jbHVkZXMiOiB7CgkJCQkiYXJjaGVzIjogWwoJCQkJCSJzMzkwIiwKCQkJCQkiczM5MHgiCgkJCQldCgkJCX0KCQl9LAoJCXsKCQkJIm5hbWVzIjogWwoJCQkJImNsb25lIgoJCQldLAoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwKCQkJImFyZ3MiOiBbCgkJCQl7CgkJCQkJImluZGV4IjogMCwKCQkJCQkidmFsdWUiOiAyMTE0MDYwMjg4LAoJCQkJCSJvcCI6ICJTQ01QX0NNUF9NQVNLRURfRVEiCgkJCQl9CgkJCV0sCgkJCSJleGNsdWRlcyI6IHsKCQkJCSJjYXBzIjogWwoJCQkJCSJDQVBfU1lTX0FETUlOIgoJCQkJXSwKCQkJCSJhcmNoZXMiOiBbCgkJCQkJInMzOTAiLAoJCQkJCSJzMzkweCIKCQkJCV0KCQkJfQoJCX0sCgkJewoJCQkibmFtZXMiOiBbCgkJCQkiY2xvbmUiCgkJCV0sCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLAoJCQkiYXJncyI6IFsKCQkJCXsKCQkJCQkiaW5kZXgiOiAxLAoJCQkJCSJ2YWx1ZSI6IDIxMTQwNjAyODgsCgkJCQkJIm9wIjogIlNDTVBfQ01QX01BU0tFRF9FUSIKCQkJCX0KCQkJXSwKCQkJImNvbW1lbnQiOiAiczM5MCBwYXJhbWV0ZXIgb3JkZXJpbmcgZm9yIGNsb25lIGlzIGRpZmZlcmVudCIsCgkJCSJpbmNsdWRlcyI6IHsKCQkJCSJhcmNoZXMiOiBbCgkJCQkJInMzOTAiLAoJCQkJCSJzMzkweCIKCQkJCV0KCQkJfSwKCQkJImV4Y2x1ZGVzIjogewoJCQkJImNhcHMiOiBbCgkJCQkJIkNBUF9TWVNfQURNSU4iCgkJCQldCgkJCX0KCQl9LAoJCXsKCQkJIm5hbWVzIjogWwoJCQkJImNsb25lMyIKCQkJXSwKCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9FUlJOTyIsCgkJCSJlcnJub1JldCI6IDM4LAoJCQkiZXhjbHVkZXMiOiB7CgkJCQkiY2FwcyI6IFsKCQkJCQkiQ0FQX1NZU19BRE1JTiIKCQkJCV0KCQkJfQoJCX0KCV0KfQo=" + cls.aci_arm_policy = load_policy_from_arm_template_str(cls.custom_arm_json, "")[ + 0 + ] + cls.aci_arm_policy.populate_policy_content_for_all_images() + + cls.aci_arm_policy2 = load_policy_from_arm_template_str(cls.custom_arm_json2.replace("seccompStr", seccomp_profile_contents), "")[ + 0 + ] + cls.aci_arm_policy2.populate_policy_content_for_all_images() + + cls.aci_arm_policy3 = load_policy_from_arm_template_str(cls.custom_arm_json3, "")[ + 0 + ] + cls.aci_arm_policy3.populate_policy_content_for_all_images() + + cls.aci_arm_policy4 = load_policy_from_arm_template_str(cls.custom_arm_json4, "")[ + 0 + ] + cls.aci_arm_policy4.populate_policy_content_for_all_images() + + + def test_arm_template_security_context_defaults(self): + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "", + "strategy": "any" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }""") + + regular_image_json = json.loads( + self.aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertFalse(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES]) + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], "") + # check all the default unprivileged capabilities are present + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_UNPRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING], ignore_order=True), {}) + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_UNPRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE], ignore_order=True), {}) + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_UNPRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED], ignore_order=True), {}) + self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT]) + self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE]) + # check default pause container + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_CONTAINERS[0], regular_image_json[1], ignore_order=True), {}) + + def test_arm_template_security_context_allow_privilege_escalation(self): + regular_image_json = json.loads( + self.aci_arm_policy3.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + # value of NO_NEW_PRIVILEGES should be the opposite of allowPrivilegeEscalation + self.assertFalse(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES]) + + def test_arm_template_security_context_user(self): + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "456", + "strategy": "id" + }, + "group_idnames": [ + { + "pattern": "123", + "strategy": "id" + } + ], + "umask": "0022" + }""") + + regular_image_json = json.loads( + self.aci_arm_policy2.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + def test_arm_template_security_context_seccomp_profile(self): + expected_seccomp_profile_sha256 = "aeb9bbbd14679be3aab28c35960e2a398e4ce838a066ce2dd5645c4b8da8de21" + + regular_image_json = json.loads( + self.aci_arm_policy2.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) + + def test_arm_template_capabilities_unprivileged(self): + attempted_new_capabilities = ["CAP_SYS_TIME", "CAP_DAC_READ_SEARCH"] + attempted_removed_capabilities = ["CAP_CHOWN", "CAP_KILL"] + regular_image_json = json.loads( + self.aci_arm_policy4.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + # ambient & inheritable should still be empty + self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT]) + self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE]) + for cap in attempted_new_capabilities: + self.assertIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING]) + self.assertIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE]) + self.assertNotIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE]) + self.assertIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED]) + for cap in attempted_removed_capabilities: + self.assertNotIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING]) + self.assertNotIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE]) + self.assertNotIn(cap, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED]) + + def test_arm_template_capabilities_privileged(self): + regular_image_json = json.loads( + self.aci_arm_policy3.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + # check all the default unprivileged capabilities are present + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_PRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_BOUNDING], ignore_order=True), {}) + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_PRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_EFFECTIVE], ignore_order=True), {}) + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_PRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_PERMITTED], ignore_order=True), {}) + self.assertEquals([], regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_AMBIENT]) + self.assertEquals(deepdiff.DeepDiff(config.DEFAULT_PRIVILEGED_CAPABILITIES, regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES][config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES_INHERITABLE], ignore_order=True), {}) + +# @unittest.skip("not in use") +@pytest.mark.run(order=17) +class PolicyGeneratingSecurityContextUserEdgeCases(unittest.TestCase): + custom_arm_json = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext":{ + "privileged":"true", + "allowPrivilegeEscalation":"true", + "capabilities":{ + "add":["ADDCAP1","ADDCAP2"], + "drop":["DROPCAP1","DROPCAP2"] + }, + "runAsUser":456 + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + custom_arm_json2 = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext":{ + "privileged":"true", + "allowPrivilegeEscalation":"true", + "capabilities":{ + "add":["ADDCAP1","ADDCAP2"], + "drop":["DROPCAP1","DROPCAP2"] + }, + "runAsGroup":123 + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + custom_arm_json3 = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "temp_image" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext":{ + "privileged":"true", + "allowPrivilegeEscalation":"true", + "capabilities":{ + "add":["ADDCAP1","ADDCAP2"], + "drop":["DROPCAP1","DROPCAP2"] + } + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + + @classmethod + def setUpClass(cls): + cls.aci_arm_policy = load_policy_from_arm_template_str(cls.custom_arm_json, "")[ + 0 + ] + cls.aci_arm_policy.populate_policy_content_for_all_images() + + cls.aci_arm_policy2 = load_policy_from_arm_template_str(cls.custom_arm_json2, "")[ + 0 + ] + cls.aci_arm_policy2.populate_policy_content_for_all_images() + + # create docker file to build and test on + cls.path = os.path.dirname(__file__) + cls.dockerfile_path = os.path.join(cls.path, "./Dockerfile") + cls.dockerfile_path2 = os.path.join(cls.path, "./Dockerfile2.dockerfile") + cls.dockerfile_path3 = os.path.join(cls.path, "./Dockerfile3.dockerfile") + cls.dockerfile_path4 = os.path.join(cls.path, "./Dockerfile4.dockerfile") + cls.dockerfile_path5 = os.path.join(cls.path, "./Dockerfile5.dockerfile") + cls.dockerfile_path6 = os.path.join(cls.path, "./Dockerfile6.dockerfile") + + cls.client = docker.from_env() + + @classmethod + def tearDownClass(cls): + cls.client.close() + + def test_arm_template_security_context_no_run_as_group(self): + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "456", + "strategy": "id" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }""") + + regular_image_json = json.loads( + self.aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + def test_arm_template_security_context_no_run_as_user(self): + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "", + "strategy": "any" + }, + "group_idnames": [ + { + "pattern": "123", + "strategy": "id" + } + ], + "umask": "0022" + }""") + + regular_image_json = json.loads( + self.aci_arm_policy2.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + def test_arm_template_security_context_uid_gid(self): + dockerfile_contents = ["FROM ubuntu\n", "USER 456:123\n"] + + try: + with open(self.dockerfile_path, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image", fileobj=open(self.dockerfile_path, "rb")) + finally: + if os.path.exists(self.dockerfile_path): + os.remove(self.dockerfile_path) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3, "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "456", + "strategy": "id" + }, + "group_idnames": [ + { + "pattern": "123", + "strategy": "id" + } + ], + "umask": "0022" + }""") + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + + def test_arm_template_security_context_user_gid(self): + dockerfile_contents = ["FROM ubuntu\n", "USER test_user:123\n"] + + try: + with open(self.dockerfile_path2, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image2", fileobj=open(self.dockerfile_path2, "rb")) + finally: + if os.path.exists(self.dockerfile_path2): + os.remove(self.dockerfile_path2) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3.replace("temp_image", "temp_image2"), "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "test_user", + "strategy": "name" + }, + "group_idnames": [ + { + "pattern": "123", + "strategy": "id" + } + ], + "umask": "0022" + }""") + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + + def test_arm_template_security_context_user_group(self): + dockerfile_contents = ["FROM ubuntu\n", "USER test_user:test_group\n"] + try: + with open(self.dockerfile_path3, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image3", fileobj=open(self.dockerfile_path3, "rb")) + finally: + if os.path.exists(self.dockerfile_path3): + os.remove(self.dockerfile_path3) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3.replace("temp_image", "temp_image3"), "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "test_user", + "strategy": "name" + }, + "group_idnames": [ + { + "pattern": "test_group", + "strategy": "name" + } + ], + "umask": "0022" + }""") + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + + def test_arm_template_security_context_uid_group(self): + # valid values are "user", "uid", + dockerfile_contents = ["FROM ubuntu\n", "USER 456:test_group\n"] + try: + with open(self.dockerfile_path4, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image4", fileobj=open(self.dockerfile_path4, "rb")) + finally: + if os.path.exists(self.dockerfile_path4): + os.remove(self.dockerfile_path4) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3.replace("temp_image", "temp_image4"), "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "456", + "strategy": "id" + }, + "group_idnames": [ + { + "pattern": "test_group", + "strategy": "name" + } + ], + "umask": "0022" + }""") + + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + + def test_arm_template_security_context_uid(self): + dockerfile_contents = ["FROM ubuntu\n", "USER 456\n"] + try: + with open(self.dockerfile_path5, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image5", fileobj=open(self.dockerfile_path5, "rb")) + finally: + if os.path.exists(self.dockerfile_path5): + os.remove(self.dockerfile_path5) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3.replace("temp_image", "temp_image5"), "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "456", + "strategy": "id" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }""") + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + + def test_arm_template_security_context_user_dockerfile(self): + dockerfile_contents = ["FROM ubuntu\n", "USER test_user\n"] + try: + with open(self.dockerfile_path6, "w") as dockerfile: + dockerfile.writelines(dockerfile_contents) + + # build docker image + image = self.client.images.build(nocache=True, tag="temp_image6", fileobj=open(self.dockerfile_path6, "rb")) + finally: + if os.path.exists(self.dockerfile_path6): + os.remove(self.dockerfile_path6) + + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json3.replace("temp_image", "temp_image6"), "")[0] + aci_arm_policy.populate_policy_content_for_all_images() + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + expected_user_json = json.loads("""{ + "user_idname": + { + "pattern": "test_user", + "strategy": "name" + }, + "group_idnames": [ + { + "pattern": "", + "strategy": "any" + } + ], + "umask": "0022" + }""") + self.assertEqual(deepdiff.DeepDiff(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER], expected_user_json, ignore_order=True), {}) + + self.client.images.remove(image[0].attrs.get("Id")) + +# @unittest.skip("not in use") +@pytest.mark.run(order=18) +class PolicyGeneratingSecurityContextSeccompProfileEdgeCases(unittest.TestCase): + custom_arm_json = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "python:3.6.14-slim-buster" + }, + + + "parameters": { + "containergroupname": { + "type": "string", + "metadata": { + "description": "Name for the container group" + }, + "defaultValue":"simple-container-group" + }, + + "containername": { + "type": "string", + "metadata": { + "description": "Name for the container" + }, + "defaultValue":"simple-container" + }, + "port": { + "type": "string", + "metadata": { + "description": "Port to open on the container and the public IP address." + }, + "defaultValue": "8080" + }, + "cpuCores": { + "type": "string", + "metadata": { + "description": "The number of CPU cores to allocate to the container." + }, + "defaultValue": "1.0" + }, + "memoryInGb": { + "type": "string", + "metadata": { + "description": "The amount of memory to allocate to the container in gigabytes." + }, + "defaultValue": "1.5" + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location for all resources." + } + } + }, + "resources": [ + { + "name": "[parameters('containergroupname')]", + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2022-04-01-preview", + "location": "[parameters('location')]", + "properties": { + "containers": [ + { + "name": "[parameters('containername')]", + + "properties": { + "image": "[variables('image')]", + "securityContext": { + "privileged": "false", + "capabilities":{ + "add": ["CAP_SYS_TIME","CAP_DAC_READ_SEARCH"], + "drop": ["CAP_CHOWN","CAP_KILL"] + }, + "runAsGroup": 123, + "runAsUser": 456, + "seccompProfile": "seccompStr" + }, + "command": [ + "python3" + ], + "ports": [ + { + "port": "[parameters('port')]" + } + ], + "resources": { + "requests": { + "cpu": "[parameters('cpuCores')]", + "memoryInGb": "[parameters('memoryInGb')]" + } + }, + "volumeMounts": [ + { + "name": "filesharevolume", + "mountPath": "/aci/logs", + "readOnly": false + }, + { + "name": "secretvolume", + "mountPath": "/aci/secret", + "readOnly": true + } + ] + } + } + ], + "volumes": [ + { + "name": "filesharevolume", + "azureFile": { + "shareName": "shareName1", + "storageAccountName": "storage-account-name", + "storageAccountKey": "storage-account-key" + } + }, + { + + "name": "secretvolume", + "secret": { + "mysecret1": "secret1", + "mysecret2": "secret2" + } + } + + ], + "osType": "Linux", + "restartPolicy": "OnFailure", + "confidentialComputeProperties": { + "IsolationType": "SevSnp" + }, + "ipAddress": { + "type": "Public", + "ports": [ + { + "protocol": "Tcp", + "port": "[parameters( 'port' )]" + } + ] + } + } + } + ], + "outputs": { + "containerIPv4Address": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ContainerInstance/containerGroups/', parameters('containergroupname'))).ipAddress.ip]" + } + } + } + """ + + def test_arm_template_security_context_seccomp_profile_all_fields(self): + seccomp_profile_contents = "ew0KCSJjb21tZW50IjogIkRlZmF1bHQgbW9ieSBzZWNjb21wIHBvbGljeSBjb25maWcgZmlsZSBhdDogaHR0cHM6Ly9naXRodWIuY29tL21vYnkvbW9ieS9ibG9iL21hc3Rlci9wcm9maWxlcy9zZWNjb21wL2RlZmF1bHQuanNvbiIsDQoJImRlZmF1bHRBY3Rpb24iOiAiU0NNUF9BQ1RfRVJSTk8iLA0KCSJkZWZhdWx0RXJybm9SZXQiOiAxLA0KCSJhcmNoaXRlY3R1cmVzIjogWyAiU0NNUF9BUkNIX1g4NiIsICJTQ01QX0FSQ0hfUFBDIl0sDQoJImZsYWdzIjogWyAiZmxhZzEiLCAiZmxhZzIiLCAiZmxhZzMiIF0sDQoJImxpc3RlbmVyUGF0aCI6ICIvbGlzdGVuZXIvUGF0aCIsDQoJImxpc3RlbmVyTWV0YWRhdGEiOiAibWV0YWRhdGEiLA0KCSJzeXNjYWxscyI6IFsNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSwgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDENCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAwDQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0gfCBTT0NLX05PTkJMT0NLLCAwIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMjA0OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfQ0xPRVhFQywgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNDI4OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0sgfCBTT0NLX0NMT0VYRUMsIDAiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA1MjYzMzcNCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAwDQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0sIElQUFJPVE9fVENQIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0ssIElQUFJPVE9fVENQIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMjA0OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfQ0xPRVhFQywgSVBQUk9UT19UQ1AiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA1MjQyODkNCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA2DQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0gfCBTT0NLX05PTkJMT0NLIHwgU09DS19DTE9FWEVDLCBJUFBST1RPX1RDUCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNjMzNw0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0cGFpciIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfVU5JWCwgKiwgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicHJvY2Vzc192bV9yZWFkdiIsDQoJCQkJInByb2Nlc3Nfdm1fd3JpdGV2IiwNCgkJCQkicHRyYWNlIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJtaW5LZXJuZWwiOiAiNC44Ig0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiAwLA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiA4LA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiAxMzEwNzIsDQoJCQkJCSJvcCI6ICJTQ01QX0NNUF9FUSINCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicGVyc29uYWxpdHkiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJ2YWx1ZSI6IDEzMTA4MCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIg0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJwZXJzb25hbGl0eSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJInZhbHVlIjogNDI5NDk2NzI5NSwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIg0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJzeW5jX2ZpbGVfcmFuZ2UyIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJwcGM2NGxlIg0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJhcm1fZmFkdmlzZTY0XzY0IiwNCgkJCQkiYXJtX3N5bmNfZmlsZV9yYW5nZSIsDQoJCQkJInN5bmNfZmlsZV9yYW5nZTIiLA0KCQkJCSJicmVha3BvaW50IiwNCgkJCQkiY2FjaGVmbHVzaCIsDQoJCQkJInNldF90bHMiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJImFybSIsDQoJCQkJCSJhcm02NCINCgkJCQldDQoJCQl9DQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkiYXJjaF9wcmN0bCINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJpbmNsdWRlcyI6IHsNCgkJCQkiYXJjaGVzIjogWw0KCQkJCQkiYW1kNjQiLA0KCQkJCQkieDMyIg0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJtb2RpZnlfbGR0Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJhbWQ2NCIsDQoJCQkJCSJ4MzIiLA0KCQkJCQkieDg2Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJzMzkwX3BjaV9tbWlvX3JlYWQiLA0KCQkJCSJzMzkwX3BjaV9tbWlvX3dyaXRlIiwNCgkJCQkiczM5MF9ydW50aW1lX2luc3RyIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJzMzkwIiwNCgkJCQkJInMzOTB4Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJjbG9uZSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJInZhbHVlIjogMjExNDA2MDI4OCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX01BU0tFRF9FUSINCgkJCQl9DQoJCQldLA0KCQkJImV4Y2x1ZGVzIjogew0KCQkJCSJjYXBzIjogWw0KCQkJCQkiQ0FQX1NZU19BRE1JTiINCgkJCQldLA0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJzMzkwIiwNCgkJCQkJInMzOTB4Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJjbG9uZSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJInZhbHVlIjogMjExNDA2MDI4OCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX01BU0tFRF9FUSINCgkJCQl9DQoJCQldLA0KCQkJImNvbW1lbnQiOiAiczM5MCBwYXJhbWV0ZXIgb3JkZXJpbmcgZm9yIGNsb25lIGlzIGRpZmZlcmVudCIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJInMzOTAiLA0KCQkJCQkiczM5MHgiDQoJCQkJXQ0KCQkJfSwNCgkJCSJleGNsdWRlcyI6IHsNCgkJCQkiY2FwcyI6IFsNCgkJCQkJIkNBUF9TWVNfQURNSU4iDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJImNsb25lMyINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0VSUk5PIiwNCgkJCSJlcnJub1JldCI6IDM4LA0KCQkJImV4Y2x1ZGVzIjogew0KCQkJCSJjYXBzIjogWw0KCQkJCQkiQ0FQX1NZU19BRE1JTiINCgkJCQldDQoJCQl9DQoJCX0NCgldDQp9" + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json.replace("seccompStr", seccomp_profile_contents), "")[ + 0 + ] + aci_arm_policy.populate_policy_content_for_all_images() + expected_seccomp_profile_sha256 = "fb38009a098475bf3423b00f4f7c30f52a66d455f1ef1dcbe1708ad00f26a8cc" + + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) + + def test_arm_template_security_context_seccomp_profile_missing_default_action(self): + seccomp_profile_contents = "ew0KCSJjb21tZW50IjogIkRlZmF1bHQgbW9ieSBzZWNjb21wIHBvbGljeSBjb25maWcgZmlsZSBhdDogaHR0cHM6Ly9naXRodWIuY29tL21vYnkvbW9ieS9ibG9iL21hc3Rlci9wcm9maWxlcy9zZWNjb21wL2RlZmF1bHQuanNvbiIsDQoJImRlZmF1bHRFcnJub1JldCI6IDEsDQoJInN5c2NhbGxzIjogWw0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNLCAwIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0ssIDAiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyMDQ5DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMA0KCQkJCX0NCg0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19DTE9FWEVDLCAwIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogNTI0Mjg5DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMA0KCQkJCX0NCg0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19OT05CTE9DSyB8IFNPQ0tfQ0xPRVhFQywgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNjMzNw0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSwgSVBQUk9UT19UQ1AiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAxDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogNg0KCQkJCX0NCg0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19OT05CTE9DSywgSVBQUk9UT19UQ1AiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyMDQ5DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogNg0KCQkJCX0NCg0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXQiIF0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJjb21tZW50IjogIkFGX0lORVQsIFNPQ0tfU1RSRUFNIHwgU09DS19DTE9FWEVDLCBJUFBST1RPX1RDUCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNDI4OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0sgfCBTT0NLX0NMT0VYRUMsIElQUFJPVE9fVENQIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogNTI2MzM3DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogNg0KCQkJCX0NCg0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbICJzb2NrZXRwYWlyIiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9VTklYLCAqLCAwIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAxDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDIsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMA0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJwcm9jZXNzX3ZtX3JlYWR2IiwNCgkJCQkicHJvY2Vzc192bV93cml0ZXYiLA0KCQkJCSJwdHJhY2UiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJIm1pbktlcm5lbCI6ICI0LjgiDQoJCQl9DQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicGVyc29uYWxpdHkiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJ2YWx1ZSI6IDAsDQoJCQkJCSJvcCI6ICJTQ01QX0NNUF9FUSINCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicGVyc29uYWxpdHkiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJ2YWx1ZSI6IDgsDQoJCQkJCSJvcCI6ICJTQ01QX0NNUF9FUSINCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicGVyc29uYWxpdHkiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJ2YWx1ZSI6IDEzMTA3MiwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIg0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJwZXJzb25hbGl0eSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJInZhbHVlIjogMTMxMDgwLA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiA0Mjk0OTY3Mjk1LA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInN5bmNfZmlsZV9yYW5nZTIiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJInBwYzY0bGUiDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJImFybV9mYWR2aXNlNjRfNjQiLA0KCQkJCSJhcm1fc3luY19maWxlX3JhbmdlIiwNCgkJCQkic3luY19maWxlX3JhbmdlMiIsDQoJCQkJImJyZWFrcG9pbnQiLA0KCQkJCSJjYWNoZWZsdXNoIiwNCgkJCQkic2V0X3RscyINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJpbmNsdWRlcyI6IHsNCgkJCQkiYXJjaGVzIjogWw0KCQkJCQkiYXJtIiwNCgkJCQkJImFybTY0Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJhcmNoX3ByY3RsIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJhbWQ2NCIsDQoJCQkJCSJ4MzIiDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJIm1vZGlmeV9sZHQiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJImFtZDY0IiwNCgkJCQkJIngzMiIsDQoJCQkJCSJ4ODYiDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInMzOTBfcGNpX21taW9fcmVhZCIsDQoJCQkJInMzOTBfcGNpX21taW9fd3JpdGUiLA0KCQkJCSJzMzkwX3J1bnRpbWVfaW5zdHIiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJInMzOTAiLA0KCQkJCQkiczM5MHgiDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJImNsb25lIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiAyMTE0MDYwMjg4LA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfTUFTS0VEX0VRIg0KCQkJCX0NCgkJCV0sDQoJCQkiZXhjbHVkZXMiOiB7DQoJCQkJImNhcHMiOiBbDQoJCQkJCSJDQVBfU1lTX0FETUlOIg0KCQkJCV0sDQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJInMzOTAiLA0KCQkJCQkiczM5MHgiDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJImNsb25lIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkidmFsdWUiOiAyMTE0MDYwMjg4LA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfTUFTS0VEX0VRIg0KCQkJCX0NCgkJCV0sDQoJCQkiY29tbWVudCI6ICJzMzkwIHBhcmFtZXRlciBvcmRlcmluZyBmb3IgY2xvbmUgaXMgZGlmZmVyZW50IiwNCgkJCSJpbmNsdWRlcyI6IHsNCgkJCQkiYXJjaGVzIjogWw0KCQkJCQkiczM5MCIsDQoJCQkJCSJzMzkweCINCgkJCQldDQoJCQl9LA0KCQkJImV4Y2x1ZGVzIjogew0KCQkJCSJjYXBzIjogWw0KCQkJCQkiQ0FQX1NZU19BRE1JTiINCgkJCQldDQoJCQl9DQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkiY2xvbmUzIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfRVJSTk8iLA0KCQkJImVycm5vUmV0IjogMzgsDQoJCQkiZXhjbHVkZXMiOiB7DQoJCQkJImNhcHMiOiBbDQoJCQkJCSJDQVBfU1lTX0FETUlOIg0KCQkJCV0NCgkJCX0NCgkJfQ0KCV0NCn0=" + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json.replace("seccompStr", seccomp_profile_contents), "")[ + 0 + ] + aci_arm_policy.populate_policy_content_for_all_images() + expected_seccomp_profile_sha256 = "fa881ac8600f3b835f7f3b5cb8fb49a5eeab2a3eb335134587dd0e30eb69d353" + + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) + + def test_arm_template_security_context_seccomp_profile_missing_default_errno(self): + seccomp_profile_contents = "ew0KCSJjb21tZW50IjogIkRlZmF1bHQgbW9ieSBzZWNjb21wIHBvbGljeSBjb25maWcgZmlsZSBhdDogaHR0cHM6Ly9naXRodWIuY29tL21vYnkvbW9ieS9ibG9iL21hc3Rlci9wcm9maWxlcy9zZWNjb21wL2RlZmF1bHQuanNvbiIsDQoJImRlZmF1bHRBY3Rpb24iOiAiU0NNUF9BQ1RfRVJSTk8iLA0KCSJzeXNjYWxscyI6IFsNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSwgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDENCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAwDQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0gfCBTT0NLX05PTkJMT0NLLCAwIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMjA0OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfQ0xPRVhFQywgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNDI4OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0sgfCBTT0NLX0NMT0VYRUMsIDAiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA1MjYzMzcNCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAwDQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0sIElQUFJPVE9fVENQIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfTk9OQkxPQ0ssIElQUFJPVE9fVENQIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiAyDQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDEsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMjA0OQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0IiBdLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiY29tbWVudCI6ICJBRl9JTkVULCBTT0NLX1NUUkVBTSB8IFNPQ0tfQ0xPRVhFQywgSVBQUk9UT19UQ1AiLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDINCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA1MjQyODkNCgkJCQl9LA0KCQkJCXsNCgkJCQkJImluZGV4IjogMiwNCgkJCQkJIm9wIiA6ICJTQ01QX0NNUF9FUSIsDQoJCQkJCSJ2YWx1ZSIgOiA2DQoJCQkJfQ0KDQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsgInNvY2tldCIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfSU5FVCwgU09DS19TVFJFQU0gfCBTT0NLX05PTkJMT0NLIHwgU09DS19DTE9FWEVDLCBJUFBST1RPX1RDUCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMg0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAxLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDUyNjMzNw0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDYNCgkJCQl9DQoNCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWyAic29ja2V0cGFpciIgXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImNvbW1lbnQiOiAiQUZfVU5JWCwgKiwgMCIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJvcCIgOiAiU0NNUF9DTVBfRVEiLA0KCQkJCQkidmFsdWUiIDogMQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAyLA0KCQkJCQkib3AiIDogIlNDTVBfQ01QX0VRIiwNCgkJCQkJInZhbHVlIiA6IDANCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicHJvY2Vzc192bV9yZWFkdiIsDQoJCQkJInByb2Nlc3Nfdm1fd3JpdGV2IiwNCgkJCQkicHRyYWNlIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJtaW5LZXJuZWwiOiAiNC44Ig0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiAwLA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiA4LA0KCQkJCQkib3AiOiAiU0NNUF9DTVBfRVEiDQoJCQkJfQ0KCQkJXQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJInBlcnNvbmFsaXR5Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImFyZ3MiOiBbDQoJCQkJew0KCQkJCQkiaW5kZXgiOiAwLA0KCQkJCQkidmFsdWUiOiAxMzEwNzIsDQoJCQkJCSJvcCI6ICJTQ01QX0NNUF9FUSINCgkJCQl9DQoJCQldDQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkicGVyc29uYWxpdHkiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiYXJncyI6IFsNCgkJCQl7DQoJCQkJCSJpbmRleCI6IDAsDQoJCQkJCSJ2YWx1ZSI6IDEzMTA4MCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIg0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJwZXJzb25hbGl0eSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJInZhbHVlIjogNDI5NDk2NzI5NSwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX0VRIg0KCQkJCX0NCgkJCV0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJzeW5jX2ZpbGVfcmFuZ2UyIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJwcGM2NGxlIg0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJhcm1fZmFkdmlzZTY0XzY0IiwNCgkJCQkiYXJtX3N5bmNfZmlsZV9yYW5nZSIsDQoJCQkJInN5bmNfZmlsZV9yYW5nZTIiLA0KCQkJCSJicmVha3BvaW50IiwNCgkJCQkiY2FjaGVmbHVzaCIsDQoJCQkJInNldF90bHMiDQoJCQldLA0KCQkJImFjdGlvbiI6ICJTQ01QX0FDVF9BTExPVyIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJImFybSIsDQoJCQkJCSJhcm02NCINCgkJCQldDQoJCQl9DQoJCX0sDQoJCXsNCgkJCSJuYW1lcyI6IFsNCgkJCQkiYXJjaF9wcmN0bCINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJpbmNsdWRlcyI6IHsNCgkJCQkiYXJjaGVzIjogWw0KCQkJCQkiYW1kNjQiLA0KCQkJCQkieDMyIg0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJtb2RpZnlfbGR0Ig0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJhbWQ2NCIsDQoJCQkJCSJ4MzIiLA0KCQkJCQkieDg2Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJzMzkwX3BjaV9tbWlvX3JlYWQiLA0KCQkJCSJzMzkwX3BjaV9tbWlvX3dyaXRlIiwNCgkJCQkiczM5MF9ydW50aW1lX2luc3RyIg0KCQkJXSwNCgkJCSJhY3Rpb24iOiAiU0NNUF9BQ1RfQUxMT1ciLA0KCQkJImluY2x1ZGVzIjogew0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJzMzkwIiwNCgkJCQkJInMzOTB4Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJjbG9uZSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMCwNCgkJCQkJInZhbHVlIjogMjExNDA2MDI4OCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX01BU0tFRF9FUSINCgkJCQl9DQoJCQldLA0KCQkJImV4Y2x1ZGVzIjogew0KCQkJCSJjYXBzIjogWw0KCQkJCQkiQ0FQX1NZU19BRE1JTiINCgkJCQldLA0KCQkJCSJhcmNoZXMiOiBbDQoJCQkJCSJzMzkwIiwNCgkJCQkJInMzOTB4Ig0KCQkJCV0NCgkJCX0NCgkJfSwNCgkJew0KCQkJIm5hbWVzIjogWw0KCQkJCSJjbG9uZSINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwNCgkJCSJhcmdzIjogWw0KCQkJCXsNCgkJCQkJImluZGV4IjogMSwNCgkJCQkJInZhbHVlIjogMjExNDA2MDI4OCwNCgkJCQkJIm9wIjogIlNDTVBfQ01QX01BU0tFRF9FUSINCgkJCQl9DQoJCQldLA0KCQkJImNvbW1lbnQiOiAiczM5MCBwYXJhbWV0ZXIgb3JkZXJpbmcgZm9yIGNsb25lIGlzIGRpZmZlcmVudCIsDQoJCQkiaW5jbHVkZXMiOiB7DQoJCQkJImFyY2hlcyI6IFsNCgkJCQkJInMzOTAiLA0KCQkJCQkiczM5MHgiDQoJCQkJXQ0KCQkJfSwNCgkJCSJleGNsdWRlcyI6IHsNCgkJCQkiY2FwcyI6IFsNCgkJCQkJIkNBUF9TWVNfQURNSU4iDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQoJCQkibmFtZXMiOiBbDQoJCQkJImNsb25lMyINCgkJCV0sDQoJCQkiYWN0aW9uIjogIlNDTVBfQUNUX0VSUk5PIiwNCgkJCSJlcnJub1JldCI6IDM4LA0KCQkJImV4Y2x1ZGVzIjogew0KCQkJCSJjYXBzIjogWw0KCQkJCQkiQ0FQX1NZU19BRE1JTiINCgkJCQldDQoJCQl9DQoJCX0NCgldDQp9" + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json.replace("seccompStr", seccomp_profile_contents), "")[ + 0 + ] + aci_arm_policy.populate_policy_content_for_all_images() + expected_seccomp_profile_sha256 = "7bf01bd03f545de915a4ef29d8a60febfe2ee2ef557240d181460fb9a24aea88" + + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) + + def test_arm_template_security_context_seccomp_profile_missing_syscalls(self): + seccomp_profile_contents = "ew0KCSJjb21tZW50IjogIkRlZmF1bHQgbW9ieSBzZWNjb21wIHBvbGljeSBjb25maWcgZmlsZSBhdDogaHR0cHM6Ly9naXRodWIuY29tL21vYnkvbW9ieS9ibG9iL21hc3Rlci9wcm9maWxlcy9zZWNjb21wL2RlZmF1bHQuanNvbiIsDQoJImRlZmF1bHRBY3Rpb24iOiAiU0NNUF9BQ1RfRVJSTk8iLA0KCSJkZWZhdWx0RXJybm9SZXQiOiAxLA0KCSJhcmNoaXRlY3R1cmVzIjogWyAiU0NNUF9BUkNIX1g4NiIsICJTQ01QX0FSQ0hfUFBDIl0sDQoJImZsYWdzIjogWyAiZmxhZzEiLCAiZmxhZzIiLCAiZmxhZzMiIF0sDQoJImxpc3RlbmVyUGF0aCI6ICIvbGlzdGVuZXIvUGF0aCIsDQoJImxpc3RlbmVyTWV0YWRhdGEiOiAibWV0YWRhdGEiDQp9" + aci_arm_policy = load_policy_from_arm_template_str(self.custom_arm_json.replace("seccompStr", seccomp_profile_contents), "")[ + 0 + ] + aci_arm_policy.populate_policy_content_for_all_images() + expected_seccomp_profile_sha256 = "4fef6e87b27dfb72359d960b62948bb2072226b497d8f8164c57d6eeaf108479" + + regular_image_json = json.loads( + aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) \ No newline at end of file diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_image.py b/src/confcom/azext_confcom/tests/latest/test_confcom_image.py index b1ae7646402..45bc7fac973 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_image.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_image.py @@ -29,11 +29,10 @@ def setUpClass(cls): cls.aci_policy = aci_policy def test_image_policy(self): - expected_policy = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbInB5dGhvbjMiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL2JpbjovdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiTEFORz1DLlVURi04IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkdQR19LRVk9MEQ5NkRGNEQ0MTEwRTVDNDNGQkZCMTdGMkQzNDdFQTZBQTY1NDIxRCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fVkVSU0lPTj0zLjYuMTQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX1BJUF9WRVJTSU9OPTIxLjIuNCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9VUkw9aHR0cHM6Ly9naXRodWIuY29tL3B5cGEvZ2V0LXBpcC9yYXcvYzIwYjBjZmQ2NDNjZDRhMTkyNDZjY2YyMDRlMjk5N2FmNzBmNmIyMS9wdWJsaWMvZ2V0LXBpcC5weSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJQWVRIT05fR0VUX1BJUF9TSEEyNTY9ZmE2ZjNmYjkzY2NlMjM0Y2Q0ZThkZDJiZWI1NGE1MWFiOWMyNDc2NTNiNTI4NTVhNDhkZDQ0ZTZiMjFmZjI4YiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IigoP2kpRkFCUklDKV8uKz0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIT1NUTkFNRT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJUKEUpP01QPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY1BhY2thZ2VGaWxlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIb3N0ZWRTZXJ2aWNlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9IRUFERVI9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiYXp1cmVjb250YWluZXJpbnN0YW5jZV9yZXN0YXJ0ZWRfYnk9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImlkIjoicHl0aG9uOjMuNi4xNC1zbGltLWJ1c3RlciIsImxheWVycyI6WyIyNTRjYzg1M2RhNjA4MTkwNWM5MTA5YzhiOWQ5OWM5ZmIwOTg3YmExZDg4ZjcyOTA4ODkwM2NmZmI4MGY1NWYxIiwiYTU2OGYxOTAwYmVkNjBhMDY0MWI3NmI5OTFhZDQzMTQ0NmQ5YzNhMzQ0ZDdiMjYxZjEwZGU4ZDhlNzM3NjNhYyIsImM3MGM1MzBlODQyZjY2MjE1YjBiZDk1NTg3NzE1N2JhMjRjMzc5OTMwMzU2N2MzZjU2NzNjNDU2NjNlYTRkMTUiLCIzZTg2YzNjY2YxNjQyYmY1ODRkZTMzYjQ5YzcyNDhmODdlZWNkMGY2ZDhjMDgzNTNkYWEzNmNjN2FkMGE3YjZhIiwiMWU0Njg0ZDhjN2NhYTc0YzY1MjQxNzJiNGQ1YTE1OWExMDg4NzYxM2VkNzBmMThkMGE1NWQwNWIyYWY2MWFjZCJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" + expected_policy = "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiLAogICAgICAiZnJhZ21lbnRzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEiCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjYXBhYmlsaXRpZXMiOnsiYW1iaWVudCI6W10sImJvdW5kaW5nIjpbIkNBUF9BVURJVF9XUklURSIsIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRk9XTkVSIiwiQ0FQX0ZTRVRJRCIsIkNBUF9LSUxMIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfTkVUX1JBVyIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX1NFVFVJRCIsIkNBUF9TWVNfQ0hST09UIl0sImVmZmVjdGl2ZSI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJpbmhlcml0YWJsZSI6W10sInBlcm1pdHRlZCI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdfSwiY29tbWFuZCI6WyJweXRob24zIl0sImVudl9ydWxlcyI6W3sicGF0dGVybiI6IlBBVEg9L3Vzci9sb2NhbC9iaW46L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IkxBTkc9Qy5VVEYtOCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJHUEdfS0VZPTBEOTZERjRENDExMEU1QzQzRkJGQjE3RjJEMzQ3RUE2QUE2NTQyMUQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX1ZFUlNJT049My42LjE0IiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlBZVEhPTl9QSVBfVkVSU0lPTj0yMS4yLjQiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX0dFVF9QSVBfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9weXBhL2dldC1waXAvcmF3L2MyMGIwY2ZkNjQzY2Q0YTE5MjQ2Y2NmMjA0ZTI5OTdhZjcwZjZiMjEvcHVibGljL2dldC1waXAucHkiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUFlUSE9OX0dFVF9QSVBfU0hBMjU2PWZhNmYzZmI5M2NjZTIzNGNkNGU4ZGQyYmViNTRhNTFhYjljMjQ3NjUzYjUyODU1YTQ4ZGQ0NGU2YjIxZmYyOGIiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InB5dGhvbjozLjYuMTQtc2xpbS1idXN0ZXIiLCJsYXllcnMiOlsiMjU0Y2M4NTNkYTYwODE5MDVjOTEwOWM4YjlkOTljOWZiMDk4N2JhMWQ4OGY3MjkwODg5MDNjZmZiODBmNTVmMSIsImE1NjhmMTkwMGJlZDYwYTA2NDFiNzZiOTkxYWQ0MzE0NDZkOWMzYTM0NGQ3YjI2MWYxMGRlOGQ4ZTczNzYzYWMiLCJjNzBjNTMwZTg0MmY2NjIxNWIwYmQ5NTU4NzcxNTdiYTI0YzM3OTkzMDM1NjdjM2Y1NjczYzQ1NjYzZWE0ZDE1IiwiM2U4NmMzY2NmMTY0MmJmNTg0ZGUzM2I0OWM3MjQ4Zjg3ZWVjZDBmNmQ4YzA4MzUzZGFhMzZjYzdhZDBhN2I2YSIsIjFlNDY4NGQ4YzdjYWE3NGM2NTI0MTcyYjRkNWExNTlhMTA4ODc2MTNlZDcwZjE4ZDBhNTVkMDViMmFmNjFhY2QiXSwibW91bnRzIjpbeyJkZXN0aW5hdGlvbiI6Ii9ldGMvcmVzb2x2LmNvbmYiLCJvcHRpb25zIjpbInJiaW5kIiwicnNoYXJlZCIsInJ3Il0sInNvdXJjZSI6InNhbmRib3g6Ly8vdG1wL2F0bGFzL3Jlc29sdmNvbmYvLisiLCJ0eXBlIjoiYmluZCJ9XSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiLyJ9LHsiYWxsb3dfZWxldmF0ZWQiOmZhbHNlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNhcGFiaWxpdGllcyI6eyJhbWJpZW50IjpbXSwiYm91bmRpbmciOlsiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GU0VUSUQiLCJDQVBfRk9XTkVSIiwiQ0FQX01LTk9EIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFVJRCIsIkNBUF9TRVRGQ0FQIiwiQ0FQX1NFVFBDQVAiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9TWVNfQ0hST09UIiwiQ0FQX0tJTEwiLCJDQVBfQVVESVRfV1JJVEUiXSwiZWZmZWN0aXZlIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl0sImluaGVyaXRhYmxlIjpbXSwicGVybWl0dGVkIjpbIkNBUF9DSE9XTiIsIkNBUF9EQUNfT1ZFUlJJREUiLCJDQVBfRlNFVElEIiwiQ0FQX0ZPV05FUiIsIkNBUF9NS05PRCIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEdJRCIsIkNBUF9TRVRVSUQiLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRQQ0FQIiwiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLCJDQVBfU1lTX0NIUk9PVCIsIkNBUF9LSUxMIiwiQ0FQX0FVRElUX1dSSVRFIl19LCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sIm5vX25ld19wcml2aWxlZ2VzIjpmYWxzZSwic2VjY29tcF9wcm9maWxlX3NoYTI1NiI6IiIsInNpZ25hbHMiOltdLCJ1c2VyIjp7Imdyb3VwX2lkbmFtZXMiOlt7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9XSwidW1hc2siOiIwMDIyIiwidXNlcl9pZG5hbWUiOnsicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In19LCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKYWxsb3dfY2FwYWJpbGl0eV9kcm9wcGluZyA6PSB0cnVlCgptb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfZGV2aWNlCnVubW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfZGV2aWNlCm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsubW91bnRfb3ZlcmxheQp1bm1vdW50X292ZXJsYXkgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9vdmVybGF5CmNyZWF0ZV9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuY3JlYXRlX2NvbnRhaW5lcgpleGVjX2luX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5leGVjX2luX2NvbnRhaW5lcgpleGVjX2V4dGVybmFsIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfZXh0ZXJuYWwKc2h1dGRvd25fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLnNodXRkb3duX2NvbnRhaW5lcgpzaWduYWxfY29udGFpbmVyX3Byb2Nlc3MgOj0gZGF0YS5mcmFtZXdvcmsuc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzCnBsYW45X21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X21vdW50CnBsYW45X3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfdW5tb3VudApnZXRfcHJvcGVydGllcyA6PSBkYXRhLmZyYW1ld29yay5nZXRfcHJvcGVydGllcwpkdW1wX3N0YWNrcyA6PSBkYXRhLmZyYW1ld29yay5kdW1wX3N0YWNrcwpydW50aW1lX2xvZ2dpbmcgOj0gZGF0YS5mcmFtZXdvcmsucnVudGltZV9sb2dnaW5nCmxvYWRfZnJhZ21lbnQgOj0gZGF0YS5mcmFtZXdvcmsubG9hZF9mcmFnbWVudApzY3JhdGNoX21vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfbW91bnQKc2NyYXRjaF91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnNjcmF0Y2hfdW5tb3VudAoKcmVhc29uIDo9IHsiZXJyb3JzIjogZGF0YS5mcmFtZXdvcmsuZXJyb3JzfQ==" # deep diff the output policies from the regular policy.json and the ARM template aci_policy_str = self.aci_policy.get_serialized_output() - self.assertEqual(aci_policy_str, expected_policy) @@ -49,7 +48,7 @@ def setUpClass(cls): cls.aci_policy = aci_policy def test_sidecar_image_policy(self): - expected_policy = "cGFja2FnZSBtaWNyb3NvZnRjb250YWluZXJpbnN0YW5jZQoKc3ZuIDo9ICIxLjAuMCIKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmNvbnRhaW5lcnMgOj0gW3siYWxsb3dfZWxldmF0ZWQiOnRydWUsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvbW91bnRfYXp1cmVfZmlsZS5zaCJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifV0sImV4ZWNfcHJvY2Vzc2VzIjpbXSwiaWQiOiJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYXRsYXMtbW91bnQtYXp1cmUtZmlsZS12b2x1bWU6bWFzdGVyXzIwMjAxMjEwLjIiLCJsYXllcnMiOlsiNjA2ZmQ2YmFmNWViMWE3MWZkMjg2YWVhMjk2NzJhMDZiZmU1NWYwMDA3ZGVkOTJlZTczMTQyYTM3NTkwZWQxOSIsIjNhZDFhMmZmNGE0NGJjODYwYjNjZDAyN2NjODZjZTQ1YTM5OWM0Yzk5NWMzNmU5ODAwYzUzNjhjYjcyN2E3ZTEiLCJiMWNmYzMwZjM3ZjA4ZTYwNjY4ZGIzZjcxNjA2OTdiMTlkMmFkNDViMTJmMDc1MTg4NTI5OTM3MzYxNmE2ZTBhIiwiZWYzNjQ4NDZjOGYxZjQzZDE0ZDJlM2U3OTE5YTA2NGIwYzgyNTUzYzA4YjM1NDIyZjVkMWYwN2MzNDM1YjQ2MiIsIjU4MmZlMzliZDM1OTA5YmFmNmM0MDM2NzM0ZTIwZjc2NjM5MWJhODM3MjdmYjFkNjgzYmUwNDVmZTQ1M2I1YWYiLCJhYWM5ZmI0MDQyNThjMDY5YWU4NTM4MjM2NGY1ZDJiYTFkNDA1MThjNmIxZjU2YWRlNmJjMjJmMzAyOGVhZmYwIl0sIm1vdW50cyI6W10sInNpZ25hbHMiOltdLCJ3b3JraW5nX2RpciI6Ii8ifV0=" + expected_policy = "cGFja2FnZSBtaWNyb3NvZnRjb250YWluZXJpbnN0YW5jZQoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY2FwYWJpbGl0aWVzIjp7ImFtYmllbnQiOltdLCJib3VuZGluZyI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJlZmZlY3RpdmUiOlsiQ0FQX0FVRElUX1dSSVRFIiwiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GT1dORVIiLCJDQVBfRlNFVElEIiwiQ0FQX0tJTEwiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFBDQVAiLCJDQVBfU0VUVUlEIiwiQ0FQX1NZU19DSFJPT1QiXSwiaW5oZXJpdGFibGUiOltdLCJwZXJtaXR0ZWQiOlsiQ0FQX0FVRElUX1dSSVRFIiwiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GT1dORVIiLCJDQVBfRlNFVElEIiwiQ0FQX0tJTEwiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFBDQVAiLCJDQVBfU0VUVUlEIiwiQ0FQX1NZU19DSFJPT1QiXX0sImNvbW1hbmQiOlsiL21vdW50X2F6dXJlX2ZpbGUuc2giXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImlkIjoibWNyLm1pY3Jvc29mdC5jb20vYWNpL2F0bGFzLW1vdW50LWF6dXJlLWZpbGUtdm9sdW1lOm1hc3Rlcl8yMDIwMTIxMC4yIiwibGF5ZXJzIjpbIjYwNmZkNmJhZjVlYjFhNzFmZDI4NmFlYTI5NjcyYTA2YmZlNTVmMDAwN2RlZDkyZWU3MzE0MmEzNzU5MGVkMTkiLCIzYWQxYTJmZjRhNDRiYzg2MGIzY2QwMjdjYzg2Y2U0NWEzOTljNGM5OTVjMzZlOTgwMGM1MzY4Y2I3MjdhN2UxIiwiYjFjZmMzMGYzN2YwOGU2MDY2OGRiM2Y3MTYwNjk3YjE5ZDJhZDQ1YjEyZjA3NTE4ODUyOTkzNzM2MTZhNmUwYSIsImVmMzY0ODQ2YzhmMWY0M2QxNGQyZTNlNzkxOWEwNjRiMGM4MjU1M2MwOGIzNTQyMmY1ZDFmMDdjMzQzNWI0NjIiLCI1ODJmZTM5YmQzNTkwOWJhZjZjNDAzNjczNGUyMGY3NjYzOTFiYTgzNzI3ZmIxZDY4M2JlMDQ1ZmU0NTNiNWFmIiwiYWFjOWZiNDA0MjU4YzA2OWFlODUzODIzNjRmNWQyYmExZDQwNTE4YzZiMWY1NmFkZTZiYzIyZjMwMjhlYWZmMCJdLCJtb3VudHMiOltdLCJub19uZXdfcHJpdmlsZWdlcyI6ZmFsc2UsInNlY2NvbXBfcHJvZmlsZV9zaGEyNTYiOiIiLCJzaWduYWxzIjpbXSwidXNlciI6eyJncm91cF9pZG5hbWVzIjpbeyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifV0sInVtYXNrIjoiMDAyMiIsInVzZXJfaWRuYW1lIjp7InBhdHRlcm4iOiIiLCJzdHJhdGVneSI6ImFueSJ9fSwid29ya2luZ19kaXIiOiIvIn1d" aci_policy_str = self.aci_policy.get_serialized_output() self.assertEqual(aci_policy_str, expected_policy) @@ -108,19 +107,15 @@ def test_clean_room_policy(self): policy.populate_policy_content_for_all_images(individual_image=True) regular_image_json = json.loads( - regular_image.get_serialized_output(output_type=OutputType.RAW, use_json=True) + regular_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) clean_room_json = json.loads( - policy.get_serialized_output(output_type=OutputType.RAW, use_json=True) + policy.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) - regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - clean_room_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) + regular_image_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + clean_room_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) # see if the remote image and the local one produce the same output self.assertEqual( diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py b/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py index d2fd2c292f7..1741d9afe77 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_scenario.py @@ -272,16 +272,17 @@ def setUpClass(cls): cls.aci_policy = aci_policy def test_injected_sidecar_container_msi(self): - expected_sidecar_container_ser = "eyJjb250YWluZXJzIjp7ImVsZW1lbnRzIjp7IjAiOnsiYWxsb3dfZWxldmF0ZWQiOnRydWUsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6eyJlbGVtZW50cyI6eyIwIjoiL2Jpbi9zaCIsIjEiOiItYyIsIjIiOiJ1bnRpbCAuL21zaUF0bGFzQWRhcHRlcjsgZG8gZWNobyAkPyByZXN0YXJ0aW5nOyBkb25lIn0sImxlbmd0aCI6M30sImVudl9ydWxlcyI6eyJlbGVtZW50cyI6eyIwIjp7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSwiMSI6eyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LCIxMCI6eyJwYXR0ZXJuIjoiRmFicmljX1NlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LCIxMSI6eyJwYXR0ZXJuIjoiRmFicmljX0FwcGxpY2F0aW9uTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSwiMTIiOnsicGF0dGVybiI6IkZhYnJpY19Db2RlUGFja2FnZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjEzIjp7InBhdHRlcm4iOiJGYWJyaWNfU2VydmljZURuc05hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjE0Ijp7InBhdHRlcm4iOiJBQ0lfTUlfREVGQVVMVD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSwiMTUiOnsicGF0dGVybiI6IlRva2VuUHJveHlJcEFkZHJlc3NFbnZLZXlOYW1lPVtDb250YWluZXJUb0hvc3RBZGRyZXNzfEZhYnJpY19Ob2RlbFBPckZRRE5dIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LCIxNiI6eyJwYXR0ZXJuIjoiQ29udGFpbmVyVG9Ib3N0QWRkcmVzcz0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0sIjE3Ijp7InBhdHRlcm4iOiJGYWJyaWNfTmV0d29ya2luZ01vZGU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjE4Ijp7InBhdHRlcm4iOiJhenVyZWNvbnRhaW5lcmluc3RhbmNlX3Jlc3RhcnRlZF9ieT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSwiMiI6eyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjMiOnsicGF0dGVybiI6IkFDSV9NSV9DTElFTlRfSURfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjQiOnsicGF0dGVybiI6IkFDSV9NSV9SRVNfSURfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0sIjUiOnsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LCI2Ijp7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LCI3Ijp7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSwiOCI6eyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LCI5Ijp7InBhdHRlcm4iOiJGYWJyaWNfSWQrPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9fSwibGVuZ3RoIjoxOX0sImV4ZWNfcHJvY2Vzc2VzIjp7ImVsZW1lbnRzIjp7fSwibGVuZ3RoIjowfSwiaWQiOiJtY3IubWljcm9zb2Z0LmNvbS9hY2kvbXNpLWF0bGFzLWFkYXB0ZXI6bWFzdGVyXzIwMjAxMjAzLjEiLCJsYXllcnMiOnsiZWxlbWVudHMiOnsiMCI6IjYwNmZkNmJhZjVlYjFhNzFmZDI4NmFlYTI5NjcyYTA2YmZlNTVmMDAwN2RlZDkyZWU3MzE0MmEzNzU5MGVkMTkiLCIxIjoiOTBhZDJmNWIyYzQyNWE3YzQ1OGY5ZjVkMjFjZjA2NGMyMTVmMTRlNDA2ODAwOTY4ZjY0NGQyYWIwYjRkMDRkZiIsIjIiOiIxYzRiNjM2NWE3YjkzODM4N2RmZDgyMjg2MmNhNDFhZTU0OTBiNTQ5MGU0YzI2ZWI0YjVkYTk2YzY0MDk2MGNmIn0sImxlbmd0aCI6M30sIm1vdW50cyI6eyJlbGVtZW50cyI6e30sImxlbmd0aCI6MH0sInNpZ25hbHMiOnsiZWxlbWVudHMiOnt9LCJsZW5ndGgiOjB9LCJ3b3JraW5nX2RpciI6Ii9yb290LyJ9fSwibGVuZ3RoIjoxfX0=" + expected_sidecar_container_ser = "cGFja2FnZSBtaWNyb3NvZnRjb250YWluZXJpbnN0YW5jZQoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY2FwYWJpbGl0aWVzIjp7ImFtYmllbnQiOltdLCJib3VuZGluZyI6WyJDQVBfQVVESVRfV1JJVEUiLCJDQVBfQ0hPV04iLCJDQVBfREFDX09WRVJSSURFIiwiQ0FQX0ZPV05FUiIsIkNBUF9GU0VUSUQiLCJDQVBfS0lMTCIsIkNBUF9NS05PRCIsIkNBUF9ORVRfQklORF9TRVJWSUNFIiwiQ0FQX05FVF9SQVciLCJDQVBfU0VURkNBUCIsIkNBUF9TRVRHSUQiLCJDQVBfU0VUUENBUCIsIkNBUF9TRVRVSUQiLCJDQVBfU1lTX0NIUk9PVCJdLCJlZmZlY3RpdmUiOlsiQ0FQX0FVRElUX1dSSVRFIiwiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GT1dORVIiLCJDQVBfRlNFVElEIiwiQ0FQX0tJTEwiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFBDQVAiLCJDQVBfU0VUVUlEIiwiQ0FQX1NZU19DSFJPT1QiXSwiaW5oZXJpdGFibGUiOltdLCJwZXJtaXR0ZWQiOlsiQ0FQX0FVRElUX1dSSVRFIiwiQ0FQX0NIT1dOIiwiQ0FQX0RBQ19PVkVSUklERSIsIkNBUF9GT1dORVIiLCJDQVBfRlNFVElEIiwiQ0FQX0tJTEwiLCJDQVBfTUtOT0QiLCJDQVBfTkVUX0JJTkRfU0VSVklDRSIsIkNBUF9ORVRfUkFXIiwiQ0FQX1NFVEZDQVAiLCJDQVBfU0VUR0lEIiwiQ0FQX1NFVFBDQVAiLCJDQVBfU0VUVUlEIiwiQ0FQX1NZU19DSFJPT1QiXX0sImNvbW1hbmQiOlsiL2Jpbi9zaCIsIi1jIiwidW50aWwgLi9tc2lBdGxhc0FkYXB0ZXI7IGRvIGVjaG8gJD8gcmVzdGFydGluZzsgZG9uZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9IRUFERVI9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiQUNJX01JX0NMSUVOVF9JRF8uKz0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJBQ0lfTUlfUkVTX0lEXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkhPU1ROQU1FPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiKCg/aSlGQUJSSUMpXy4rPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY19JZCs9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljX1NlcnZpY2VOYW1lPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY19BcHBsaWNhdGlvbk5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiRmFicmljX0NvZGVQYWNrYWdlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNfU2VydmljZURuc05hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiQUNJX01JX0RFRkFVTFQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVG9rZW5Qcm94eUlwQWRkcmVzc0VudktleU5hbWU9W0NvbnRhaW5lclRvSG9zdEFkZHJlc3N8RmFicmljX05vZGVsUE9yRlFETl0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiQ29udGFpbmVyVG9Ib3N0QWRkcmVzcz0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiRmFicmljX05ldHdvcmtpbmdNb2RlPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6Im1jci5taWNyb3NvZnQuY29tL2FjaS9tc2ktYXRsYXMtYWRhcHRlcjptYXN0ZXJfMjAyMDEyMDMuMSIsImxheWVycyI6WyI2MDZmZDZiYWY1ZWIxYTcxZmQyODZhZWEyOTY3MmEwNmJmZTU1ZjAwMDdkZWQ5MmVlNzMxNDJhMzc1OTBlZDE5IiwiOTBhZDJmNWIyYzQyNWE3YzQ1OGY5ZjVkMjFjZjA2NGMyMTVmMTRlNDA2ODAwOTY4ZjY0NGQyYWIwYjRkMDRkZiIsIjFjNGI2MzY1YTdiOTM4Mzg3ZGZkODIyODYyY2E0MWFlNTQ5MGI1NDkwZTRjMjZlYjRiNWRhOTZjNjQwOTYwY2YiXSwibW91bnRzIjpbXSwibm9fbmV3X3ByaXZpbGVnZXMiOmZhbHNlLCJzZWNjb21wX3Byb2ZpbGVfc2hhMjU2IjoiIiwic2lnbmFscyI6W10sInVzZXIiOnsiZ3JvdXBfaWRuYW1lcyI6W3sicGF0dGVybiI6IiIsInN0cmF0ZWd5IjoiYW55In1dLCJ1bWFzayI6IjAwMjIiLCJ1c2VyX2lkbmFtZSI6eyJwYXR0ZXJuIjoiIiwic3RyYXRlZ3kiOiJhbnkifX0sIndvcmtpbmdfZGlyIjoiL3Jvb3QvIn1d" + image = self.aci_policy.get_images()[0] self.assertEqual(image.base, "mcr.microsoft.com/aci/msi-atlas-adapter") self.assertIsNotNone(image) - + self.maxDiff = None expected_workingdir = "/root/" self.assertEqual(image._workingDir, expected_workingdir) self.assertEqual( - self.aci_policy.get_serialized_output(use_json=True), + self.aci_policy.get_serialized_output(), expected_sidecar_container_ser, ) @@ -312,7 +313,7 @@ def setUpClass(cls): aci_policy.populate_policy_content_for_all_images() cls.aci_policy = aci_policy - def test_logging_enabled(self): + def test_debug_flags(self): policy = self.aci_policy.get_serialized_output( output_type=OutputType.RAW, rego_boilerplate=True @@ -322,11 +323,17 @@ def test_logging_enabled(self): expected_logging_string = "allow_runtime_logging := true" expected_properties_access = "allow_properties_access := true" expected_dump_stacks = "allow_dump_stacks := true" + expected_env_var_dropping = "allow_environment_variable_dropping := true" + expected_capability_dropping = "allow_capability_dropping := true" + expected_unencrypted_scratch = "allow_unencrypted_scratch := false" # make sure all these are included in the policy self.assertTrue(expected_logging_string in policy) self.assertTrue(expected_properties_access in policy) self.assertTrue(expected_dump_stacks in policy) + self.assertTrue(expected_env_var_dropping in policy) + self.assertTrue(expected_capability_dropping in policy) + self.assertTrue(expected_unencrypted_scratch in policy) # @unittest.skip("not in use") @@ -400,12 +407,10 @@ def test_sidecar_stdio_access_default(self): self.assertTrue( json.loads( self.aci_policy.get_serialized_output( - use_json=True, output_type=OutputType.RAW + output_type=OutputType.RAW, rego_boilerplate=False ) - )[config.POLICY_FIELD_CONTAINERS][config.POLICY_FIELD_CONTAINERS_ELEMENTS][ - "0" - ][ - config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS + )[0][ + config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS ] ) @@ -568,6 +573,25 @@ def test_docker_pull(self): "sha256:83ac22b6cf50c51a1d11b3220316be73271e59d30a65f33f4391dc4cfabdc856", ) + def test_infrastructure_svn(self): + custom_json = """ + { + "version": "1.0", + "containers": [ + { + "containerImage": "rust:1.52.1", + "environmentVariables": [], + "command": ["echo", "hello"] + } + ] + } + """ + with load_policy_from_str(custom_json) as aci_policy: + aci_policy.populate_policy_content_for_all_images() + output = aci_policy.get_serialized_output(OutputType.PRETTY_PRINT) + + self.assertTrue('"0.2.3"' in output) + def test_environment_variables_parsing(self): custom_json = """ { @@ -641,16 +665,15 @@ def test_stdio_access_default(self): with load_policy_from_str(custom_json) as aci_policy: aci_policy.populate_policy_content_for_all_images() self.assertTrue( - json.loads( - aci_policy.get_serialized_output(use_json=True, output_type=OutputType.RAW) - )[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ][ - "0" - ][ - config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS - ] - ) + json.loads( + aci_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + )[0][ + config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS + ] + ) + def test_stdio_access_updated(self): custom_json = """ @@ -670,16 +693,15 @@ def test_stdio_access_updated(self): aci_policy.populate_policy_content_for_all_images() self.assertFalse( - json.loads( - aci_policy.get_serialized_output(use_json=True, output_type=OutputType.RAW) - )[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ][ - "0" - ][ - config.POLICY_FIELD_CONTAINERS_ALLOW_STDIO_ACCESS - ] - ) + json.loads( + aci_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + )[0][ + config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS + ] + ) + # @unittest.skip("not in use") @@ -872,3 +894,5 @@ def test_json_missing_command(self): with self.assertRaises(SystemExit) as exc_info: load_policy_from_str(custom_json) self.assertEqual(exc_info.exception.code, 1) + + diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py b/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py index af75d819790..69bac2e8ad2 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_tar.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- import os +import tempfile import unittest import pytest import deepdiff @@ -29,10 +30,8 @@ def setUpClass(cls) -> None: path = os.path.dirname(__file__) image_path = os.path.join(path, "./nginx.tar") - tar_mapping_file = {"nginx:1.22": image_path} - cls.path = path - cls.tar_mapping_file = tar_mapping_file + cls.image_path = image_path def test_arm_template_with_parameter_file_clean_room_tar(self): @@ -173,17 +172,17 @@ def test_arm_template_with_parameter_file_clean_room_tar(self): # save the tar file for the image in the testing directory client = docker.from_env() image = client.images.get("nginx:1.22") - + tar_mapping_file = {"nginx:1.22": self.image_path} # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. f = open(self.image_path, "wb") for chunk in image.save(named=True): f.write(chunk) f.close() client.close() - + tar_mapping_file = {"nginx:1.22": self.image_path} try: clean_room_image.populate_policy_content_for_all_images( - tar_mapping=self.tar_mapping_file + tar_mapping=tar_mapping_file ) except: raise AccContainerError("Could not get image from tar file") @@ -193,19 +192,15 @@ def test_arm_template_with_parameter_file_clean_room_tar(self): os.remove(self.image_path) regular_image_json = json.loads( - regular_image.get_serialized_output(output_type=OutputType.RAW, use_json=True) + regular_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) clean_room_json = json.loads( - clean_room_image.get_serialized_output(output_type=OutputType.RAW, use_json=True) + clean_room_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) - regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - clean_room_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) + regular_image_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + clean_room_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) # see if the remote image and the local one produce the same output self.assertEqual( @@ -392,43 +387,42 @@ def test_arm_template_mixed_mode_tar(self): # save the tar file for the image in the testing directory client = docker.from_env() image = client.images.get("nginx:1.22") - + image_path = self.image_path + "2" # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. - f = open(self.image_path, "wb") + # make a temp directory for the tar file + temp_dir = tempfile.TemporaryDirectory() + + image_path = os.path.join( + temp_dir.name, "nginx.tar" + ) + f = open(image_path, "wb") for chunk in image.save(named=True): f.write(chunk) f.close() client.close() - + tar_mapping_file = {"nginx:1.22": image_path} try: clean_room_image.populate_policy_content_for_all_images( - tar_mapping=self.tar_mapping_file + tar_mapping=image_path ) finally: + temp_dir.cleanup() # delete the tar file - if os.path.isfile(self.image_path): - os.remove(self.image_path) + if os.path.isfile(image_path): + os.remove(image_path) regular_image_json = json.loads( - regular_image.get_serialized_output(output_type=OutputType.RAW, use_json=True) + regular_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) clean_room_json = json.loads( - clean_room_image.get_serialized_output(output_type=OutputType.RAW, use_json=True) + clean_room_image.get_serialized_output(output_type=OutputType.RAW, rego_boilerplate=False) ) - regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - clean_room_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["0"].pop(config.POLICY_FIELD_CONTAINERS_ID) - regular_image_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["1"].pop(config.POLICY_FIELD_CONTAINERS_ID) - clean_room_json[config.POLICY_FIELD_CONTAINERS][ - config.POLICY_FIELD_CONTAINERS_ELEMENTS - ]["1"].pop(config.POLICY_FIELD_CONTAINERS_ID) + regular_image_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + clean_room_json[0].pop(config.POLICY_FIELD_CONTAINERS_ID) + regular_image_json[1].pop(config.POLICY_FIELD_CONTAINERS_ID) + clean_room_json[1].pop(config.POLICY_FIELD_CONTAINERS_ID) # see if the remote image and the local one produce the same output self.assertEqual( @@ -571,7 +565,12 @@ def test_arm_template_with_parameter_file_clean_room_tar_invalid(self): image = client.images.get("nginx:1.23") # Note: Class setup and teardown shouldn't have side effects, and reading from the tar file fails when all the tests are running in parallel, so we want to save and delete this tar file as a part of the test. Not as a part of the testing class. - f = open(self.image_path, "wb") + temp_dir = tempfile.TemporaryDirectory() + + image_path = os.path.join( + temp_dir.name, "nginx.tar" + ) + f = open(image_path, "wb") for chunk in image.save(named=True): f.write(chunk) f.close() @@ -579,13 +578,14 @@ def test_arm_template_with_parameter_file_clean_room_tar_invalid(self): try: clean_room_image.populate_policy_content_for_all_images( - tar_mapping=self.tar_mapping_file + tar_mapping=image_path ) raise AccContainerError("getting image should fail") except: pass finally: # delete the tar file + temp_dir.cleanup() if os.path.isfile(self.image_path): os.remove(self.image_path) diff --git a/src/confcom/requirements.txt b/src/confcom/requirements.txt index b7c00f45c15..35b762b9cfc 100644 --- a/src/confcom/requirements.txt +++ b/src/confcom/requirements.txt @@ -1,4 +1,5 @@ docker tqdm azure-devtools -deepdiff \ No newline at end of file +deepdiff +pydash \ No newline at end of file diff --git a/src/confcom/samples/sample-policy-output.rego b/src/confcom/samples/sample-policy-output.rego index a4ab429abef..11ac55c9d30 100644 --- a/src/confcom/samples/sample-policy-output.rego +++ b/src/confcom/samples/sample-policy-output.rego @@ -3,190 +3,200 @@ package policy import future.keywords.every import future.keywords.in -api_svn := "0.10.0" -framework_svn := "0.1.0" - -fragments := [ - { - "feed": "mcr.microsoft.com/aci/aci-cc-infra-fragment", - "includes": [ - "containers" - ], - "issuer": "did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6.1.4.1.311.76.59.1.3", - "minimum_svn": "1.0.0" - } -] +api_version := "0.10.0" + +framework_version := "0.2.3" + +fragments := [{ + "feed": "mcr.microsoft.com/aci/aci-cc-infra-fragment", + "includes": ["containers"], + "issuer": "did:x509:0:sha256:I__iuL25oXEVFdTP_aBLx_eT1RPHbCQ_ECBQfYZpt9s::eku:1.3.6.1.4.1.311.76.59.1.3", + "minimum_svn": "1", +}] containers := [ { - "allow_elevated":true, - "allow_stdio_access":true, - "command":[ - "bash" - ], - "env_rules":[ + "allow_elevated": true, + "allow_stdio_access": true, + "command": ["bash"], + "env_rules": [ { - "pattern":"PATH=/customized/path/value", - "required":false, - "strategy":"string" + "pattern": "PATH=/customized/path/value", + "required": false, + "strategy": "string", }, { - "pattern":"TEST_REGEXP_ENV=test_regexp_env", - "required":false, - "strategy":"string" + "pattern": "TEST_REGEXP_ENV=test_regexp_env", + "required": false, + "strategy": "string", }, { - "pattern":"RUSTUP_HOME=/usr/local/rustup", - "required":false, - "strategy":"string" + "pattern": "RUSTUP_HOME=/usr/local/rustup", + "required": false, + "strategy": "string", }, { - "pattern":"CARGO_HOME=/usr/local/cargo", - "required":false, - "strategy":"string" + "pattern": "CARGO_HOME=/usr/local/cargo", + "required": false, + "strategy": "string", }, { - "pattern":"RUST_VERSION=1.52.1", - "required":false, - "strategy":"string" + "pattern": "RUST_VERSION=1.52.1", + "required": false, + "strategy": "string", }, { - "pattern":"TERM=xterm", - "required":false, - "strategy":"string" + "pattern": "TERM=xterm", + "required": false, + "strategy": "string", }, { - "pattern":"((?i)FABRIC)_.+=.+", - "required":false, - "strategy":"re2" + "pattern": "((?i)FABRIC)_.+=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"HOSTNAME=.+", - "required":false, - "strategy":"re2" + "pattern": "HOSTNAME=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"T(E)?MP=.+", - "required":false, - "strategy":"re2" + "pattern": "T(E)?MP=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"FabricPackageFileName=.+", - "required":false, - "strategy":"re2" + "pattern": "FabricPackageFileName=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"HostedServiceName=.+", - "required":false, - "strategy":"re2" + "pattern": "HostedServiceName=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"IDENTITY_API_VERSION=.+", - "required":false, - "strategy":"re2" + "pattern": "IDENTITY_API_VERSION=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"IDENTITY_HEADER=.+", - "required":false, - "strategy":"re2" + "pattern": "IDENTITY_HEADER=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"IDENTITY_SERVER_THUMBPRINT=.+", - "required":false, - "strategy":"re2" + "pattern": "IDENTITY_SERVER_THUMBPRINT=.+", + "required": false, + "strategy": "re2", }, { - "pattern":"azurecontainerinstance_restarted_by=.+", - "required":false, - "strategy":"re2" - } + "pattern": "azurecontainerinstance_restarted_by=.+", + "required": false, + "strategy": "re2", + }, ], - "exec_processes":[], - "id":"rust:1.52.1", - "layers":[ + "exec_processes": [], + "id": "rust:1.52.1", + "layers": [ "fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a", "4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c", "41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156", "eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79", "e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c", - "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766" + "1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766", ], - "mounts":[ + "mounts": [ { - "destination":"/mount/azurefile", - "options":[ + "destination": "/mount/azurefile", + "options": [ "rbind", "rshared", - "rw" + "rw", ], - "source":"sandbox:///tmp/atlas/azureFileVolume/.+", - "type":"bind" + "source": "sandbox:///tmp/atlas/azureFileVolume/.+", + "type": "bind", }, { - "destination":"/etc/resolv.conf", - "options":[ + "destination": "/etc/resolv.conf", + "options": [ "rbind", "rshared", - "rw" + "rw", ], - "source":"sandbox:///tmp/atlas/resolvconf/.+", - "type":"bind" - } + "source": "sandbox:///tmp/atlas/resolvconf/.+", + "type": "bind", + }, ], - "signals":[], - "working_dir":"/" + "signals": [], + "working_dir": "/", }, { - "allow_elevated":false, - "allow_stdio_access":true, - "command":[ - "/pause" - ], - "env_rules":[ + "allow_elevated": false, + "allow_stdio_access": true, + "command": ["/pause"], + "env_rules": [ { - "pattern":"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "required":true, - "strategy":"string" + "pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "required": true, + "strategy": "string", }, { - "pattern":"TERM=xterm", - "required":false, - "strategy":"string" - } - ], - "exec_processes":[], - "layers":[ - "16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415" + "pattern": "TERM=xterm", + "required": false, + "strategy": "string", + }, ], - "mounts":[], - "signals":[], - "working_dir":"/" - } + "exec_processes": [], + "layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"], + "mounts": [], + "signals": [], + "working_dir": "/", + }, ] allow_properties_access := false + allow_dump_stacks := false + allow_runtime_logging := false -allow_environment_variable_dropping := true -allow_unencrypted_scratch := false +allow_environment_variable_dropping := true +allow_unencrypted_scratch := false mount_device := data.framework.mount_device + unmount_device := data.framework.unmount_device + mount_overlay := data.framework.mount_overlay + unmount_overlay := data.framework.unmount_overlay + create_container := data.framework.create_container + exec_in_container := data.framework.exec_in_container + exec_external := data.framework.exec_external + shutdown_container := data.framework.shutdown_container + signal_container_process := data.framework.signal_container_process + plan9_mount := data.framework.plan9_mount + plan9_unmount := data.framework.plan9_unmount + get_properties := data.framework.get_properties + dump_stacks := data.framework.dump_stacks + runtime_logging := data.framework.runtime_logging + load_fragment := data.framework.load_fragment + scratch_mount := data.framework.scratch_mount + scratch_unmount := data.framework.scratch_unmount + reason := {"errors": data.framework.errors} diff --git a/src/confcom/samples/sample-template-output.json b/src/confcom/samples/sample-template-output.json index 4473d563fef..0200a99875f 100644 --- a/src/confcom/samples/sample-template-output.json +++ b/src/confcom/samples/sample-template-output.json @@ -13,7 +13,7 @@ "location": "[resourceGroup().location]", "properties": { "confidentialComputeProperties": { - "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3N2biA6PSAiMC4xMC4wIgpmcmFtZXdvcmtfc3ZuIDo9ICIwLjEuMCIKCmZyYWdtZW50cyA6PSBbCiAgewogICAgImZlZWQiOiAibWNyLm1pY3Jvc29mdC5jb20vYWNpL2FjaS1jYy1pbmZyYS1mcmFnbWVudCIsCiAgICAiaW5jbHVkZXMiOiBbCiAgICAgICJjb250YWluZXJzIgogICAgXSwKICAgICJpc3N1ZXIiOiAiZGlkOng1MDk6MDpzaGEyNTY6SV9faXVMMjVvWEVWRmRUUF9hQkx4X2VUMVJQSGJDUV9FQ0JRZllacHQ5czo6ZWt1OjEuMy42LjEuNC4xLjMxMS43Ni41OS4xLjMiLAogICAgIm1pbmltdW1fc3ZuIjogIjEuMC4wIgogIH0KXQoKY29udGFpbmVycyA6PSBbeyJhbGxvd19lbGV2YXRlZCI6dHJ1ZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbImJhc2giXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vY3VzdG9taXplZC9wYXRoL3ZhbHVlIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFU1RfUkVHRVhQX0VOVj10ZXN0X3JlZ2V4cF9lbnYiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUlVTVFVQX0hPTUU9L3Vzci9sb2NhbC9ydXN0dXAiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiQ0FSR09fSE9NRT0vdXNyL2xvY2FsL2NhcmdvIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlJVU1RfVkVSU0lPTj0xLjUyLjEiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVSTT14dGVybSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiIoKD9pKUZBQlJJQylfLis9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSE9TVE5BTUU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiVChFKT9NUD0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJGYWJyaWNQYWNrYWdlRmlsZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSG9zdGVkU2VydmljZU5hbWU9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfQVBJX1ZFUlNJT049LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfSEVBREVSPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IklERU5USVRZX1NFUlZFUl9USFVNQlBSSU5UPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6ImF6dXJlY29udGFpbmVyaW5zdGFuY2VfcmVzdGFydGVkX2J5PS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJpZCI6InJ1c3Q6MS41Mi4xIiwibGF5ZXJzIjpbImZlODRjOWQ1YmZkZGQwN2EyNjI0ZDAwMzMzY2YxM2MxYTljOTQxZjNhMjYxZjEzZWFkNDRmYzZhOTNiYzBlN2EiLCI0ZGVkYWU0Mjg0N2M3MDRkYTg5MWEyOGMyNWQzMjIwMWExYWU0NDBiY2UyYWVjY2NmYThlNmYwM2I5N2E2YTZjIiwiNDFkNjRjZGViMzQ3YmYyMzZiNGMxM2I3NDAzYjYzM2ZmMTFmMWNmOTRkYmM3Y2Y4ODFhNDRkNmRhODhjNTE1NiIsImViMzY5MjFlMWY4MmFmNDZkZmUyNDhlZjhmMWIzYWZiNmE1MjMwYTY0MTgxZDk2MGQxMDIzN2EwOGNkNzNjNzkiLCJlNzY5ZDc0ODdjYzMxNGQzZWU3NDhhNDQ0MDgwNTMxN2MxOTI2MmM3YWNkMmZkYmRiMGQ0N2QyZTQ2MTNhMTVjIiwiMWI4MGYxMjBkYmQ4OGU0MzU1ZDYyNDFiNTE5YzNlMjUyOTAyMTVjNDY5NTE2YjQ5ZGVjZTljZjA3MTc1YTc2NiJdLCJtb3VudHMiOlt7ImRlc3RpbmF0aW9uIjoiL21vdW50L2F6dXJlZmlsZSIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvYXp1cmVGaWxlVm9sdW1lLy4rIiwidHlwZSI6ImJpbmQifSx7ImRlc3RpbmF0aW9uIjoiL2V0Yy9yZXNvbHYuY29uZiIsIm9wdGlvbnMiOlsicmJpbmQiLCJyc2hhcmVkIiwicnciXSwic291cmNlIjoic2FuZGJveDovLy90bXAvYXRsYXMvcmVzb2x2Y29uZi8uKyIsInR5cGUiOiJiaW5kIn1dLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn0seyJhbGxvd19lbGV2YXRlZCI6ZmFsc2UsImFsbG93X3N0ZGlvX2FjY2VzcyI6dHJ1ZSwiY29tbWFuZCI6WyIvcGF1c2UiXSwiZW52X3J1bGVzIjpbeyJwYXR0ZXJuIjoiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iLCJyZXF1aXJlZCI6dHJ1ZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9XSwiZXhlY19wcm9jZXNzZXMiOltdLCJsYXllcnMiOlsiMTZiNTE0MDU3YTA2YWQ2NjVmOTJjMDI4NjNhY2EwNzRmZDU5NzZjNzU1ZDI2YmZmMTYzNjUyOTkxNjllODQxNSJdLCJtb3VudHMiOltdLCJzaWduYWxzIjpbXSwid29ya2luZ19kaXIiOiIvIn1dCgphbGxvd19wcm9wZXJ0aWVzX2FjY2VzcyA6PSBmYWxzZQphbGxvd19kdW1wX3N0YWNrcyA6PSBmYWxzZQphbGxvd19ydW50aW1lX2xvZ2dpbmcgOj0gZmFsc2UKYWxsb3dfZW52aXJvbm1lbnRfdmFyaWFibGVfZHJvcHBpbmcgOj0gdHJ1ZQphbGxvd191bmVuY3J5cHRlZF9zY3JhdGNoIDo9IGZhbHNlCgoKCm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9kZXZpY2UKdW5tb3VudF9kZXZpY2UgOj0gZGF0YS5mcmFtZXdvcmsudW5tb3VudF9kZXZpY2UKbW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay5tb3VudF9vdmVybGF5CnVubW91bnRfb3ZlcmxheSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X292ZXJsYXkKY3JlYXRlX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5jcmVhdGVfY29udGFpbmVyCmV4ZWNfaW5fY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmV4ZWNfaW5fY29udGFpbmVyCmV4ZWNfZXh0ZXJuYWwgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19leHRlcm5hbApzaHV0ZG93bl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuc2h1dGRvd25fY29udGFpbmVyCnNpZ25hbF9jb250YWluZXJfcHJvY2VzcyA6PSBkYXRhLmZyYW1ld29yay5zaWduYWxfY29udGFpbmVyX3Byb2Nlc3MKcGxhbjlfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsucGxhbjlfbW91bnQKcGxhbjlfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV91bm1vdW50CmdldF9wcm9wZXJ0aWVzIDo9IGRhdGEuZnJhbWV3b3JrLmdldF9wcm9wZXJ0aWVzCmR1bXBfc3RhY2tzIDo9IGRhdGEuZnJhbWV3b3JrLmR1bXBfc3RhY2tzCnJ1bnRpbWVfbG9nZ2luZyA6PSBkYXRhLmZyYW1ld29yay5ydW50aW1lX2xvZ2dpbmcKbG9hZF9mcmFnbWVudCA6PSBkYXRhLmZyYW1ld29yay5sb2FkX2ZyYWdtZW50CnNjcmF0Y2hfbW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF9tb3VudApzY3JhdGNoX3VubW91bnQgOj0gZGF0YS5mcmFtZXdvcmsuc2NyYXRjaF91bm1vdW50CgpyZWFzb24gOj0geyJlcnJvcnMiOiBkYXRhLmZyYW1ld29yay5lcnJvcnN9" + "ccePolicy": "cGFja2FnZSBwb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuZXZlcnkKaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5pbgoKYXBpX3ZlcnNpb24gOj0gIjAuMTAuMCIKZnJhbWV3b3JrX3ZlcnNpb24gOj0gIjAuMi4zIgoKZnJhZ21lbnRzIDo9IFsKICB7CiAgICAiZmVlZCI6ICJtY3IubWljcm9zb2Z0LmNvbS9hY2kvYWNpLWNjLWluZnJhLWZyYWdtZW50IiwKICAgICJpbmNsdWRlcyI6IFsKICAgICAgImNvbnRhaW5lcnMiCiAgICBdLAogICAgImlzc3VlciI6ICJkaWQ6eDUwOTowOnNoYTI1NjpJX19pdUwyNW9YRVZGZFRQX2FCTHhfZVQxUlBIYkNRX0VDQlFmWVpwdDlzOjpla3U6MS4zLjYuMS40LjEuMzExLjc2LjU5LjEuMyIsCiAgICAibWluaW11bV9zdm4iOiAxCiAgfQpdCgpjb250YWluZXJzIDo9IFt7ImFsbG93X2VsZXZhdGVkIjp0cnVlLCJhbGxvd19zdGRpb19hY2Nlc3MiOnRydWUsImNvbW1hbmQiOlsiYmFzaCJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS9jdXN0b21pemVkL3BhdGgvdmFsdWUiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiVEVTVF9SRUdFWFBfRU5WPXRlc3RfcmVnZXhwX2VudiIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJSVVNUVVBfSE9NRT0vdXNyL2xvY2FsL3J1c3R1cCIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJDQVJHT19IT01FPS91c3IvbG9jYWwvY2FyZ28iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn0seyJwYXR0ZXJuIjoiUlVTVF9WRVJTSU9OPTEuNTIuMSIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJzdHJpbmcifSx7InBhdHRlcm4iOiJURVJNPXh0ZXJtIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IigoP2kpRkFCUklDKV8uKz0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIT1NUTkFNRT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJUKEUpP01QPS4rIiwicmVxdWlyZWQiOmZhbHNlLCJzdHJhdGVneSI6InJlMiJ9LHsicGF0dGVybiI6IkZhYnJpY1BhY2thZ2VGaWxlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJIb3N0ZWRTZXJ2aWNlTmFtZT0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9BUElfVkVSU0lPTj0uKyIsInJlcXVpcmVkIjpmYWxzZSwic3RyYXRlZ3kiOiJyZTIifSx7InBhdHRlcm4iOiJJREVOVElUWV9IRUFERVI9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiSURFTlRJVFlfU0VSVkVSX1RIVU1CUFJJTlQ9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn0seyJwYXR0ZXJuIjoiYXp1cmVjb250YWluZXJpbnN0YW5jZV9yZXN0YXJ0ZWRfYnk9LisiLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5IjoicmUyIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImlkIjoicnVzdDoxLjUyLjEiLCJsYXllcnMiOlsiZmU4NGM5ZDViZmRkZDA3YTI2MjRkMDAzMzNjZjEzYzFhOWM5NDFmM2EyNjFmMTNlYWQ0NGZjNmE5M2JjMGU3YSIsIjRkZWRhZTQyODQ3YzcwNGRhODkxYTI4YzI1ZDMyMjAxYTFhZTQ0MGJjZTJhZWNjY2ZhOGU2ZjAzYjk3YTZhNmMiLCI0MWQ2NGNkZWIzNDdiZjIzNmI0YzEzYjc0MDNiNjMzZmYxMWYxY2Y5NGRiYzdjZjg4MWE0NGQ2ZGE4OGM1MTU2IiwiZWIzNjkyMWUxZjgyYWY0NmRmZTI0OGVmOGYxYjNhZmI2YTUyMzBhNjQxODFkOTYwZDEwMjM3YTA4Y2Q3M2M3OSIsImU3NjlkNzQ4N2NjMzE0ZDNlZTc0OGE0NDQwODA1MzE3YzE5MjYyYzdhY2QyZmRiZGIwZDQ3ZDJlNDYxM2ExNWMiLCIxYjgwZjEyMGRiZDg4ZTQzNTVkNjI0MWI1MTljM2UyNTI5MDIxNWM0Njk1MTZiNDlkZWNlOWNmMDcxNzVhNzY2Il0sIm1vdW50cyI6W3siZGVzdGluYXRpb24iOiIvbW91bnQvYXp1cmVmaWxlIiwib3B0aW9ucyI6WyJyYmluZCIsInJzaGFyZWQiLCJydyJdLCJzb3VyY2UiOiJzYW5kYm94Oi8vL3RtcC9hdGxhcy9henVyZUZpbGVWb2x1bWUvLisiLCJ0eXBlIjoiYmluZCJ9LHsiZGVzdGluYXRpb24iOiIvZXRjL3Jlc29sdi5jb25mIiwib3B0aW9ucyI6WyJyYmluZCIsInJzaGFyZWQiLCJydyJdLCJzb3VyY2UiOiJzYW5kYm94Oi8vL3RtcC9hdGxhcy9yZXNvbHZjb25mLy4rIiwidHlwZSI6ImJpbmQifV0sInNpZ25hbHMiOltdLCJ3b3JraW5nX2RpciI6Ii8ifSx7ImFsbG93X2VsZXZhdGVkIjpmYWxzZSwiYWxsb3dfc3RkaW9fYWNjZXNzIjp0cnVlLCJjb21tYW5kIjpbIi9wYXVzZSJdLCJlbnZfcnVsZXMiOlt7InBhdHRlcm4iOiJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIsInJlcXVpcmVkIjp0cnVlLCJzdHJhdGVneSI6InN0cmluZyJ9LHsicGF0dGVybiI6IlRFUk09eHRlcm0iLCJyZXF1aXJlZCI6ZmFsc2UsInN0cmF0ZWd5Ijoic3RyaW5nIn1dLCJleGVjX3Byb2Nlc3NlcyI6W10sImxheWVycyI6WyIxNmI1MTQwNTdhMDZhZDY2NWY5MmMwMjg2M2FjYTA3NGZkNTk3NmM3NTVkMjZiZmYxNjM2NTI5OTE2OWU4NDE1Il0sIm1vdW50cyI6W10sInNpZ25hbHMiOltdLCJ3b3JraW5nX2RpciI6Ii8ifV0KCmFsbG93X3Byb3BlcnRpZXNfYWNjZXNzIDo9IGZhbHNlCmFsbG93X2R1bXBfc3RhY2tzIDo9IGZhbHNlCmFsbG93X3J1bnRpbWVfbG9nZ2luZyA6PSBmYWxzZQphbGxvd19lbnZpcm9ubWVudF92YXJpYWJsZV9kcm9wcGluZyA6PSB0cnVlCmFsbG93X3VuZW5jcnlwdGVkX3NjcmF0Y2ggOj0gZmFsc2UKCgoKbW91bnRfZGV2aWNlIDo9IGRhdGEuZnJhbWV3b3JrLm1vdW50X2RldmljZQp1bm1vdW50X2RldmljZSA6PSBkYXRhLmZyYW1ld29yay51bm1vdW50X2RldmljZQptb3VudF9vdmVybGF5IDo9IGRhdGEuZnJhbWV3b3JrLm1vdW50X292ZXJsYXkKdW5tb3VudF9vdmVybGF5IDo9IGRhdGEuZnJhbWV3b3JrLnVubW91bnRfb3ZlcmxheQpjcmVhdGVfY29udGFpbmVyIDo9IGRhdGEuZnJhbWV3b3JrLmNyZWF0ZV9jb250YWluZXIKZXhlY19pbl9jb250YWluZXIgOj0gZGF0YS5mcmFtZXdvcmsuZXhlY19pbl9jb250YWluZXIKZXhlY19leHRlcm5hbCA6PSBkYXRhLmZyYW1ld29yay5leGVjX2V4dGVybmFsCnNodXRkb3duX2NvbnRhaW5lciA6PSBkYXRhLmZyYW1ld29yay5zaHV0ZG93bl9jb250YWluZXIKc2lnbmFsX2NvbnRhaW5lcl9wcm9jZXNzIDo9IGRhdGEuZnJhbWV3b3JrLnNpZ25hbF9jb250YWluZXJfcHJvY2VzcwpwbGFuOV9tb3VudCA6PSBkYXRhLmZyYW1ld29yay5wbGFuOV9tb3VudApwbGFuOV91bm1vdW50IDo9IGRhdGEuZnJhbWV3b3JrLnBsYW45X3VubW91bnQKZ2V0X3Byb3BlcnRpZXMgOj0gZGF0YS5mcmFtZXdvcmsuZ2V0X3Byb3BlcnRpZXMKZHVtcF9zdGFja3MgOj0gZGF0YS5mcmFtZXdvcmsuZHVtcF9zdGFja3MKcnVudGltZV9sb2dnaW5nIDo9IGRhdGEuZnJhbWV3b3JrLnJ1bnRpbWVfbG9nZ2luZwpsb2FkX2ZyYWdtZW50IDo9IGRhdGEuZnJhbWV3b3JrLmxvYWRfZnJhZ21lbnQKc2NyYXRjaF9tb3VudCA6PSBkYXRhLmZyYW1ld29yay5zY3JhdGNoX21vdW50CnNjcmF0Y2hfdW5tb3VudCA6PSBkYXRhLmZyYW1ld29yay5zY3JhdGNoX3VubW91bnQKCnJlYXNvbiA6PSB7ImVycm9ycyI6IGRhdGEuZnJhbWV3b3JrLmVycm9yc30=" }, "containers": [ { diff --git a/src/confcom/setup.py b/src/confcom/setup.py index 364ebd90b65..dff0ec65448 100644 --- a/src/confcom/setup.py +++ b/src/confcom/setup.py @@ -8,9 +8,7 @@ from codecs import open from setuptools import setup, find_packages -import stat -import requests -import os +from azext_confcom.rootfs_proxy import SecurityPolicyProxy try: from azure_bdist_wheel import cmdclass @@ -19,9 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") -# TODO: Confirm this is the right version number you want and it matches your -# HISTORY.rst entry. -VERSION = "0.2.13" +VERSION = "0.2.14" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers @@ -39,25 +35,9 @@ "License :: OSI Approved :: MIT License", ] -DEPENDENCIES = ["docker", "tqdm", "deepdiff"] +DEPENDENCIES = ["docker", "tqdm", "deepdiff", "pydash"] -dir_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "azext_confcom") - -bin_folder = dir_path + "/bin" -if not os.path.exists(bin_folder): - os.makedirs(bin_folder) - -exe_path = dir_path + "/bin/dmverity-vhd.exe" -if not os.path.exists(exe_path): - r = requests.get("https://github.com/microsoft/hcsshim/releases/download/v0.10.0-rc.6/dmverity-vhd.exe") - with open(exe_path, "wb") as f: - f.write(r.content) - -bin_path = dir_path + "/bin/dmverity-vhd" -if not os.path.exists(bin_path): - r = requests.get("https://github.com/microsoft/hcsshim/releases/download/v0.10.0-rc.6/dmverity-vhd") - with open(bin_path, "wb") as f: - f.write(r.content) +SecurityPolicyProxy.download_binaries() with open("README.md", "r", encoding="utf-8") as f: README = f.read()