diff --git a/src/confcom/HISTORY.rst b/src/confcom/HISTORY.rst index 8b11a4655f1..c49117f5864 100644 --- a/src/confcom/HISTORY.rst +++ b/src/confcom/HISTORY.rst @@ -2,6 +2,11 @@ Release History =============== +0.2.16 +* adding stop signals as a field that is picked up from image manifest and placed into policy +* updating --print-existing-policy to print the whole policy +* refactoring tests to be more portable across releases + 0.2.15 * updating dmverity-vhd interface to be more flexible with output formats * bugfix for --print-existing-policy flag with parameter values diff --git a/src/confcom/azext_confcom/config.py b/src/confcom/azext_confcom/config.py index a9807cc236c..0dd9104ff9d 100644 --- a/src/confcom/azext_confcom/config.py +++ b/src/confcom/azext_confcom/config.py @@ -167,3 +167,39 @@ DEFAULT_UNPRIVILEGED_CAPABILITIES = _config["default_unprivileged_capabilities"] # default priviliged user capabilities to be added for security context DEFAULT_PRIVILEGED_CAPABILITIES = _config["default_privileged_capabilities"] +# these signals are hardcoded because the signals package in python is not portable across platforms +SIGNALS = { + "SIGHUP": 1, + "SIGINT": 2, + "SIGQUIT": 3, + "SIGILL": 4, + "SIGTRAP": 5, + "SIGABRT": 6, + "SIGIOT": 6, + "SIGBUS": 7, + "SIGFPE": 8, + "SIGKILL": 9, + "SIGUSR1": 10, + "SIGSEGV": 11, + "SIGUSR2": 12, + "SIGPIPE": 13, + "SIGALRM": 14, + "SIGTERM": 15, + "SIGSTKFLT": 16, + "SIGCHLD": 17, + "SIGCONT": 18, + "SIGSTOP": 19, + "SIGTSTP": 20, + "SIGTTIN": 21, + "SIGTTOU": 22, + "SIGURG": 23, + "SIGXCPU": 24, + "SIGXFSZ": 25, + "SIGVTALRM": 26, + "SIGPROF": 27, + "SIGWINCH": 28, + "SIGIO": 29, + "SIGPWR": 30, + "SIGSYS": 31, + "SIGUNUSED": 31 +} diff --git a/src/confcom/azext_confcom/container.py b/src/confcom/azext_confcom/container.py index ff6a5955223..33d506dbc67 100644 --- a/src/confcom/azext_confcom/container.py +++ b/src/confcom/azext_confcom/container.py @@ -11,7 +11,8 @@ case_insensitive_dict_get, replace_params_and_vars, str_to_sha256, - process_seccomp_policy + process_seccomp_policy, + translate_signals ) from azext_confcom import config from azext_confcom.errors import eprint @@ -575,6 +576,10 @@ def get_id(self) -> str: def get_working_dir(self) -> str: return self._workingDir + def set_signals(self, signals: List) -> None: + signals = translate_signals([signals] if not isinstance(signals, list) else signals) + self._signals = signals + def set_working_dir(self, workingDir: str) -> None: self._workingDir = workingDir @@ -688,6 +693,7 @@ def _get_mounts_json(self) -> Dict[str, Any]: return mounts def _populate_policy_json_elements(self) -> Dict[str, Any]: + elements = { config.POLICY_FIELD_CONTAINERS_ID: self._identifier, config.POLICY_FIELD_CONTAINERS_ELEMENTS_LAYERS: self._layers, diff --git a/src/confcom/azext_confcom/data/internal_config.json b/src/confcom/azext_confcom/data/internal_config.json index f82165beed9..39bb6c04940 100644 --- a/src/confcom/azext_confcom/data/internal_config.json +++ b/src/confcom/azext_confcom/data/internal_config.json @@ -1,5 +1,5 @@ { - "version": "0.2.15", + "version": "0.2.16", "hcsshim_config": { "maxVersion": "1.0.0", "minVersion": "0.0.1" diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index 97f1763073c..cf07ec19930 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -350,6 +350,7 @@ def _policy_serialization(self, pretty_print=False) -> str: return pretty_print_func(policy) return print_func(policy) + # pylint: disable=R0914, R0915 def populate_policy_content_for_all_images( self, individual_image=False, tar_mapping=None ) -> None: @@ -432,6 +433,11 @@ def populate_policy_content_for_all_images( } ) + # merge signals for user container image + signals = image_info.get("StopSignal") + if signals: + image.set_signals(signals) + 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" diff --git a/src/confcom/azext_confcom/template_util.py b/src/confcom/azext_confcom/template_util.py index 4bf6efe7289..fdc6e15b299 100644 --- a/src/confcom/azext_confcom/template_util.py +++ b/src/confcom/azext_confcom/template_util.py @@ -582,6 +582,13 @@ def is_sidecar(image_name: str) -> bool: return image_name.split(":")[0] in config.BASELINE_SIDECAR_CONTAINERS +def translate_signals(signals: List[str]) -> List[int]: + for i, signal_val in enumerate(signals): + if isinstance(signal_val, str) and signal_val.upper() in config.SIGNALS: + signals[i] = config.SIGNALS[signal_val.upper()] + return signals + + def compare_env_vars( id_val, env_list1: List[Dict[str, Any]], env_list2: List[Dict[str, Any]] ) -> Dict[str, List[str]]: 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 d2ec5ff127b..8f63479256d 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_arm.py @@ -5302,4 +5302,142 @@ def test_arm_template_security_context_seccomp_profile_missing_syscalls(self): ) ) - self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) \ No newline at end of file + self.assertEqual(regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256], expected_seccomp_profile_sha256) + + +# @unittest.skip("not in use") +@pytest.mark.run(order=18) +class PolicyStopSignal(unittest.TestCase): + custom_arm_json = """ + { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "variables": { + "image": "nginx:1.24" + }, + + + "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": "80" + }, + "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')]" + } + } + + } + } + + ], + + "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]" + } + } + } + """ + aci_policy = None + + @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() + + def test_stop_signal(self): + regular_image_json = json.loads( + self.aci_arm_policy.get_serialized_output( + output_type=OutputType.RAW, rego_boilerplate=False + ) + ) + # check for the signal for SIGQUIT. this is part of the nginx image + self.assertTrue( + 3 in + regular_image_json[0][config.POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES] + ) diff --git a/src/confcom/setup.py b/src/confcom/setup.py index be1c5a95f38..f551a70a383 100644 --- a/src/confcom/setup.py +++ b/src/confcom/setup.py @@ -17,7 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = "0.2.15" +VERSION = "0.2.16" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers