diff --git a/azurelinuxagent/ga/policy/policy_engine.py b/azurelinuxagent/ga/policy/policy_engine.py index 5f008dc97..20350dac5 100644 --- a/azurelinuxagent/ga/policy/policy_engine.py +++ b/azurelinuxagent/ga/policy/policy_engine.py @@ -16,66 +16,21 @@ # from azurelinuxagent.common import logger -from azurelinuxagent.common.version import DISTRO_VERSION, DISTRO_NAME -from azurelinuxagent.common.utils.distro_version import DistroVersion from azurelinuxagent.common.event import WALAEventOperation, add_event from azurelinuxagent.common import conf -from azurelinuxagent.common.osutil import get_osutil -import azurelinuxagent.ga.policy.regorus as regorus -from azurelinuxagent.ga.policy.regorus import PolicyError +from azurelinuxagent.common.exception import AgentError -# Define support matrix for Regorus and policy engine feature. -# Dict in the format: { distro:min_supported_version } -POLICY_SUPPORTED_DISTROS_MIN_VERSIONS = { - 'ubuntu': DistroVersion('16.04'), - 'mariner': DistroVersion('2'), - 'azurelinux': DistroVersion('3') -} -# TODO: add 'arm64', 'aarch64' here once support is enabled for ARM64 -POLICY_SUPPORTED_ARCHITECTURE = ['x86_64'] + +class PolicyError(AgentError): + """ + Error raised during agent policy enforcement. + """ class PolicyEngine(object): """ Implements base policy engine API. - If any errors are thrown in regorus.py, they will be caught and re-raised here. - The caller will be responsible for handling errors. """ - def __init__(self, rule_file, policy_file): - """ - Constructor checks that policy enforcement should be enabled, and then sets up the - Regorus policy engine (add rule and policy file). - - rule_file: Path to a Rego file that specifies rules for policy behavior. - - policy_file: Path to a JSON file that specifies parameters for policy behavior - for example, - whether allowlist or extension signing should be enforced. - The expected file format is: - { - "azureGuestAgentPolicy": { - "policyVersion": "0.1.0", - "signingRules": { - "extensionSigned": - }, - "allowListOnly": - }, - "azureGuestExtensionsPolicy": { - "allowed_ext_1": { - "signingRules": { - "extensionSigned": - } - } - } - """ - self._engine = None - if not self.is_policy_enforcement_enabled(): - self._log_policy(msg="Policy enforcement is not enabled.") - return - - # If unsupported, this call will raise an error - self._check_policy_enforcement_supported() - self._engine = regorus.Engine(policy_file=policy_file, rule_file=rule_file) - @classmethod def _log_policy(cls, msg, is_success=True, op=WALAEventOperation.Policy, send_event=True): """ @@ -94,76 +49,7 @@ def is_policy_enforcement_enabled(): Check whether user has opted into policy enforcement feature. Caller function should check this before performing any operations. """ - # TODO: The conf flag will be removed post private preview. Before public preview, add checks - # according to the planned user experience (TBD). + # TODO: Add check for policy file present at /etc/waagent_policy.json. + # Policy should only be enabled if conf flag is true AND policy file is present. return conf.get_extension_policy_enabled() - @staticmethod - def _check_policy_enforcement_supported(): - """ - Check that both platform architecture and distro/version are supported. - If supported, do nothing. - If not supported, raise PolicyError with user-friendly error message. - """ - osutil = get_osutil() - arch = osutil.get_vm_arch() - # TODO: surface as a user error with clear instructions for fixing - msg = "Attempted to enable policy enforcement, but feature is not supported on " - if arch not in POLICY_SUPPORTED_ARCHITECTURE: - msg += " architecture " + str(arch) - elif DISTRO_NAME not in POLICY_SUPPORTED_DISTROS_MIN_VERSIONS: - msg += " distro " + str(DISTRO_NAME) - else: - min_version = POLICY_SUPPORTED_DISTROS_MIN_VERSIONS.get(DISTRO_NAME) - if DISTRO_VERSION < min_version: - msg += " distro " + DISTRO_NAME + " " + DISTRO_VERSION + ". Policy is only supported on version " + \ - str(min_version) + " and above." - else: - return # do nothing if platform is supported - raise PolicyError(msg) - - def evaluate_query(self, input_to_check, query): - """ - Input_to_check is the input we want to check against the policy engine (ex: extensions we want to install). - Input_to_check should be a dict. Expected format: - { - "extensions": { - "": { - "signingInfo": { - "extensionSigned": - } - }, ... - } - - The query parameter specifies the value we want to retrieve from the policy engine. - Example format for query: "data.agent_extension_policy.extensions_to_download" - """ - # This method should never be called if policy is not enabled, this would be a developer error. - if not self.is_policy_enforcement_enabled(): - raise PolicyError("Policy enforcement is disabled, cannot evaluate query.") - - try: - full_result = self._engine.eval_query(input_to_check, query) - debug_info = "Rule file is located at '{0}'. \nFull query output: {1}".format(self._engine.rule_file, full_result) - if full_result is None or full_result == {}: - raise PolicyError("query returned empty output. Please validate rule file. {0}".format(debug_info)) - result = full_result.get('result') - if result is None or not isinstance(result, list) or len(result) == 0: - raise PolicyError("query returned unexpected output with no 'result' list. Please validate rule file. {0}".format(debug_info)) - expressions = result[0].get('expressions') - if expressions is None or not isinstance(expressions, list) or len(expressions) == 0: - raise PolicyError("query returned unexpected output with no 'expressions' list. {0}".format(debug_info)) - value = expressions[0].get('value') - if value is None: - raise PolicyError("query returned unexpected output, 'value' not found in 'expressions' list. {0}".format(debug_info)) - if value == {}: - raise PolicyError("query returned expected output format, but value is empty. Please validate policy file '{0}'. '{1}" - .format(self._engine.policy_file, debug_info)) - # TODO: surface as a user error with clear instructions for fixing - return value - except Exception as ex: - msg = "Failed to evaluate query for Regorus policy engine: '{0}'".format(ex) - self._log_policy(msg=msg, is_success=False) - raise PolicyError(msg) - -# TODO: Implement class ExtensionPolicyEngine with API is_extension_download_allowed(ext_name) that calls evaluate_query. diff --git a/azurelinuxagent/ga/policy/regorus.py b/azurelinuxagent/ga/policy/regorus.py deleted file mode 100644 index f75c1499c..000000000 --- a/azurelinuxagent/ga/policy/regorus.py +++ /dev/null @@ -1,146 +0,0 @@ -# Copyright 2018 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -import json -import tempfile -import azurelinuxagent.common.utils.shellutil as shellutil -from azurelinuxagent.common.utils.shellutil import CommandError -from azurelinuxagent.common.exception import AgentError - - -def get_regorus_path(): - """ - Returns path to Regorus executable. The executable is not yet officially released as part of the agent package. - Currently, the executable is copied into the agent directory for unit testing. - """ - # TODO: update the logic to get regorus path once executable is released as part of agent package - # This method is currently mocked for unit tests. - regorus_exe = "" - return regorus_exe - - -class PolicyError(AgentError): - """ - Error raised during agent policy enforcement. - """ - # TODO: split into two error classes for internal/dev errors and user errors. - - -class Engine: - """ - This class implements the basic operations for the Regorus policy engine via subprocess. - Any errors thrown in this class should be caught and handled by PolicyEngine. - """ - - def __init__(self, policy_file, rule_file): - """ - Rule_file is expected to point to a valid Regorus file. - - Policy_file should be a path to a valid JSON policy (data) file. - The expected file format is: - { - "azureGuestAgentPolicy": { - "policyVersion": "0.1.0", - "signingRules": { - "extensionSigned": - }, - "allowListOnly": - }, - "azureGuestExtensionsPolicy": { - "allowed_ext_1": { - "signingRules": { - "extensionSigned": - } - } - } - """ - self._engine = None - self._rule_file = rule_file - self._policy_file = policy_file - - - @property - def policy_file(self): - return self._policy_file - - @property - def rule_file(self): - return self._rule_file - - def eval_query(self, input_to_check, query): - """ - Input_to_check should be type dict. - Example format for extension policy: - { - "extensions": { - "": { - "signingInfo": { - "extensionSigned": - } - }, - "": { - "signingInfo": { - "extensionSigned": - } - }, ... - } - - In this method, we call the Regorus executable via run_command to query the policy engine. - - Command: - regorus eval -d -d -i - - Parameters: - -d, --data : Rego file or JSON file. - -i, --input : Input file in JSON format. - Query. Rego query block in the format "data." - - Return Codes: - 0 - successful query. optional parameters may be missing - 1 - file error: unsupported file type, error parsing file, file not found - ex: "Error: Unsupported data file . Must be rego or json." - ex: "Error: Failed to read . No such file or directory." - 2 - usage error: missing query argument, unexpected or unlabeled parameter - ex: "Error: the following required arguments were not provided: " - ex: "Error: Unexpected argument found." - """ - # Write input_to_check to a temp file, because Regorus requires input to be a file path. - # Tempfile is automatically cleaned up at the end of with block - with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=True) as input_file: - json.dump(input_to_check, input_file, indent=4) - input_file.flush() - - regorus_exe = get_regorus_path() - command = [regorus_exe, "eval", "-d", self._rule_file, "-d", self._policy_file, - "-i", input_file.name, query] - - try: - stdout = shellutil.run_command(command) - except CommandError as ex: - code = ex.returncode - if code == 1: - msg = "file error when using policy engine. {0}".format(ex) - # TODO: surface as a user error with clear instructions for fixing - elif code == 2: - msg = "incorrect parameters passed to policy engine. {0}".format(ex) - # TODO: log this as an internal/developer error and include debug information - else: - msg = "error when using policy engine. {0}".format(ex) - raise PolicyError(msg=msg) - - json_output = json.loads(stdout) - return json_output diff --git a/tests/data/policy/agent-extension-data-invalid.json b/tests/data/policy/agent-extension-data-invalid.json deleted file mode 100644 index d1eca62b3..000000000 --- a/tests/data/policy/agent-extension-data-invalid.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "policy": { - "invalid_attribute": "0" - } diff --git a/tests/data/policy/agent-extension-default-data.json b/tests/data/policy/agent-extension-default-data.json deleted file mode 100644 index 4ddc83bd7..000000000 --- a/tests/data/policy/agent-extension-default-data.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "azureGuestAgentPolicy": { - "policyVersion": "0.1.0", - "signingRules": { - "extensionSigned": false - }, - "allowListOnly": false - } -} \ No newline at end of file diff --git a/tests/data/policy/agent-extension-input.json b/tests/data/policy/agent-extension-input.json deleted file mode 100644 index a40a63a8c..000000000 --- a/tests/data/policy/agent-extension-input.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extensions": { - "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux": { - "signingInfo": { - "extensionSigned": false - } - }, - "test2": { - "signingInfo": { - "extensionSigned": true - } - }, - "test3": { - "signingInfo": { - "extensionSigned": false - } - }, - "test1": { - "signingInfo": { - "extensionSigned": false - } - }, - "test4": {} - } -} \ No newline at end of file diff --git a/tests/data/policy/agent_extension_policy.rego b/tests/data/policy/agent_extension_policy.rego deleted file mode 100644 index 776d79399..000000000 --- a/tests/data/policy/agent_extension_policy.rego +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright 2018 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -package agent_extension_policy - -import rego.v1 - -policy_version := "0.1.0" - -default default_global_rules := { - "allowListOnly": false, - "signingRules": { - "extensionSigned": false, - "signingDetails": {}, - }, - "updateAllowed": true, - "uninstallAllowed": true, -} - -default global_rules := { - "allowListOnly": false, - "signingRules": { - "extensionSigned": false, - "signingDetails": {}, - }, - "updateAllowed": true, - "uninstallAllowed": true, -} - -global_rules := object.union(default_global_rules, data.azureGuestAgentPolicy) if { - data.azureGuestAgentPolicy -} - -default any_extension_allowed := true - -any_extension_allowed := false if { - global_rules.allowListOnly -} - -default default_signing_info := {"signingInfo": {}} - -# Download rule 1: if the extension is in the list and download rule satisfied: download allowed -extensions_to_download[name] := extension if { - some name, input_extension in input.extensions - data.azureGuestExtensionsPolicy[name] - download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name]) - extension := object.union(input_extension, {"downloadAllowed": true}) -} - -# Download rule 2: if the extension is in the list and download rule not satisfied: download denied -extensions_to_download[name] := extension if { - some name, input_extension in input.extensions - data.azureGuestExtensionsPolicy[name] - not download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name]) - extension := object.union(input_extension, {"downloadAllowed": false}) -} - -# Download rule 3: if the extension is not in the list: depending on allowListOnly on or off -extensions_to_download[name] := extension if { - some name, input_extension in input.extensions - not data.azureGuestExtensionsPolicy[name] - extension := object.union(input_extension, {"downloadAllowed": any_extension_allowed}) -} - -# Validate rule 1: if individual signing rule exists, signing rule validated according to the rules -extensions_validated[name] := extension if { - some name, input_extension in input.extensions - data.azureGuestExtensionsPolicy[name] - - extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name]) - extension_signing_info := object.union(extension_global_rules, default_signing_info) - output := object.union(input_extension, extension_signing_info) - signing_validated(output.signingInfo, output.signingRules) - extension := object.union(output, {"signingValidated": true}) -} - -# Validate rule 2: if indivual signing rule exists, signing rule not validated according to the rules -extensions_validated[name] := extension if { - some name, input_extension in input.extensions - data.azureGuestExtensionsPolicy[name] - - extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name]) - extension_signing_info := object.union(extension_global_rules, default_signing_info) - output := object.union(input_extension, extension_signing_info) - not signing_validated(output.signingInfo, output.signingRules) - extension := object.union(output, {"signingValidated": false}) -} - -# Validate rule 3: if individual signing rule doesn't exist, signing rule validated according to global signing rule -extensions_validated[name] := extension if { - some name, input_extension in input.extensions - not data.azureGuestExtensionsPolicy[name] - extension_global_rules := object.union(input_extension, global_rules) - output := object.union(extension_global_rules, default_signing_info) - signing_validated(output.signingInfo, output.signingRules) - extension := object.union(output, {"signingValidated": true}) -} - -# Validate rule 4: if individual signing rule doesn't exist, signing rule not validated according to the global rules -extensions_validated[name] := extension if { - some name, input_extension in input.extensions - not data.azureGuestExtensionsPolicy[name] - extension_global_rules := object.union(input_extension, global_rules) - output := object.union(extension_global_rules, default_signing_info) - not signing_validated(output.signingInfo, output.signingRules) - extension := object.union(output, {"signingValidated": false}) -} - -# Currently if download rules doesn't exist, allow the extension because its name is in the list. -# In the future additional rules can be checked with downloadRules present. -download_rule_validated(_, rules) if { - not rules.downloadRules -} - -# Signing is validated if input comes with extension signed, or the input of signing information is matching the -# rules in data. -signing_validated(signingInfo, signingRules) if { - signingInfo - signingRules - signingInfo.extensionSigned -} else if { - signingInfo - signingRules - signingInfo.extensionSigned == signingRules.extensionSigned -} \ No newline at end of file diff --git a/tests/data/policy/agent_extension_policy_invalid.rego b/tests/data/policy/agent_extension_policy_invalid.rego deleted file mode 100644 index 02ca5feb3..000000000 --- a/tests/data/policy/agent_extension_policy_invalid.rego +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2018 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -# nothing here! \ No newline at end of file diff --git a/tests/data/policy/regorus b/tests/data/policy/regorus deleted file mode 100755 index f99035403..000000000 Binary files a/tests/data/policy/regorus and /dev/null differ diff --git a/tests/ga/test_policy_engine.py b/tests/ga/test_policy_engine.py index d31fa6fdd..7f9a8bcc7 100644 --- a/tests/ga/test_policy_engine.py +++ b/tests/ga/test_policy_engine.py @@ -15,127 +15,20 @@ # Requires Python 2.4+ and Openssl 1.0+ # -import json -import os -import shutil - from tests.lib.tools import AgentTestCase -from azurelinuxagent.ga.policy.policy_engine import PolicyEngine, POLICY_SUPPORTED_DISTROS_MIN_VERSIONS, PolicyError -from tests.lib.tools import patch, data_dir, test_dir +from azurelinuxagent.ga.policy.policy_engine import PolicyEngine +from tests.lib.tools import patch class TestPolicyEngine(AgentTestCase): - patcher = None - regorus_dest_path = None # Location where real regorus executable should be. - default_policy_path = os.path.join(data_dir, 'policy', "agent-extension-default-data.json") - default_rule_path = os.path.join(data_dir, 'policy', "agent_extension_policy.rego") - input_json = None # Input is stored in a file, and extracted into this variable during class setup. - - @classmethod - def setUpClass(cls): - - # On a production VM, Regorus will be located in the agent package. Unit tests - # run within the agent directory, so we copy the executable to ga/policy/regorus and patch path. - # Note: Regorus has not been published officially, so for now, unofficial exe is stored in tests/data/policy. - regorus_source_path = os.path.abspath(os.path.join(data_dir, "policy/regorus")) - cls.regorus_dest_path = os.path.abspath(os.path.join(test_dir, "..", "azurelinuxagent/ga/policy/regorus")) - if not os.path.exists(cls.regorus_dest_path): - shutil.copy(regorus_source_path, cls.regorus_dest_path) - cls.patcher = patch('azurelinuxagent.ga.policy.regorus.get_regorus_path', return_value=cls.regorus_dest_path) - cls.patcher.start() - - # We store input in a centralized file, we want to extract the JSON contents into a dict for testing. - # TODO: remove this logic once we add tests for ExtensionPolicyEngine - with open(os.path.join(data_dir, 'policy', "agent-extension-input.json"), 'r') as input_file: - cls.input_json = json.load(input_file) - AgentTestCase.setUpClass() - - @classmethod - def tearDownClass(cls): - # Clean up the Regorus binary that was copied to ga/policy/regorus. - if os.path.exists(cls.regorus_dest_path): - os.remove(cls.regorus_dest_path) - cls.patcher.stop() - AgentTestCase.tearDownClass() - - def test_policy_should_be_enabled_on_supported_distro(self): - """Policy should be enabled on all supported distros.""" - for distro_name, version in POLICY_SUPPORTED_DISTROS_MIN_VERSIONS.items(): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_NAME', new=distro_name): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_VERSION', new=version): - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - engine = PolicyEngine(self.default_rule_path, self.default_policy_path) - self.assertTrue(engine.is_policy_enforcement_enabled(), "Policy should be enabled on supported distro {0} {1}".format(distro_name, version)) - - def test_should_raise_exception_on_unsupported_distro(self): - """Policy should NOT be enabled on unsupported distros.""" - test_matrix = { - "rhel": "9.0", - "mariner": "1" - } - for distro_name, version in test_matrix.items(): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_NAME', new=distro_name): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_VERSION', new=version): - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - with self.assertRaises(Exception, - msg="Policy should not be enabled on unsupported distro {0} {1}".format(distro_name, version)): - PolicyEngine(self.default_rule_path, self.default_policy_path) - - def test_should_raise_exception_on_unsupported_architecture(self): - """Policy should NOT be enabled on ARM64.""" - # TODO: remove this test when support for ARM64 is added. - with patch('azurelinuxagent.ga.policy.policy_engine.get_osutil') as mock_get_osutil: - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - with self.assertRaises(PolicyError, msg="Policy should not be enabled on unsupported architecture ARM64, should have raised exception."): - mock_get_osutil.get_vm_arch.return_value = "arm64" - PolicyEngine(self.default_rule_path, self.default_policy_path) - - def test_policy_engine_should_evaluate_query(self): - """ - Should be able to initialize policy engine and evaluate query without an error. - """ - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_NAME', new='ubuntu'): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_VERSION', new='20.04'): - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - engine = PolicyEngine(self.default_rule_path, self.default_policy_path) - query = "data.agent_extension_policy.extensions_to_download" - result = engine.evaluate_query(self.input_json, query) - test_ext_name = "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux" - self.assertIsNotNone(result.get(test_ext_name), msg="Query should not have returned empty dict.") - self.assertTrue(result.get(test_ext_name).get('downloadAllowed'), - msg="Query should have returned that extension is allowed.") - - def test_eval_query_should_throw_error_when_disabled(self): - """ - When policy enforcement is disabled, evaluate_query should throw an error. - """ - engine = PolicyEngine(self.default_rule_path, self.default_policy_path) - with self.assertRaises(PolicyError, msg="Should throw error when policy enforcement is disabled."): - engine.evaluate_query(self.input_json, "data") - - def test_should_throw_error_with_invalid_rule_file(self): - """ - Evaluate query with invalid rule file, should throw error. - """ - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_NAME', new='ubuntu'): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_VERSION', new='20.04'): - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - with self.assertRaises(PolicyError, msg="Should throw error when input is incorrectly formatted."): - # pass policy file instead of rule file in init - invalid_rule = os.path.join(data_dir, 'policy', "agent_extension_policy_invalid.rego") - engine = PolicyEngine(invalid_rule, self.default_policy_path) - engine.evaluate_query(self.input_json, "data") + def test_policy_enforcement_should_be_enabled(self): + with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): + engine = PolicyEngine() + self.assertTrue(engine.is_policy_enforcement_enabled(), + msg="Conf flag is set to true so policy enforcement should be enabled.") - def test_should_throw_error_with_invalid_policy_file(self): - """ - Evaluate query with invalid policy file, should throw error. - """ - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_NAME', new='ubuntu'): - with patch('azurelinuxagent.ga.policy.policy_engine.DISTRO_VERSION', new='20.04'): - with patch('azurelinuxagent.ga.policy.policy_engine.conf.get_extension_policy_enabled', return_value=True): - with self.assertRaises(PolicyError, msg="Should throw error when policy file is incorrectly formatted."): - invalid_policy = os.path.join(data_dir, 'policy', "agent-extension-data-invalid.json") - engine = PolicyEngine(self.default_rule_path, invalid_policy) - engine.evaluate_query(self.input_json, "data") + def test_policy_enforcement_should_be_disabled(self): + engine = PolicyEngine() + self.assertFalse(engine.is_policy_enforcement_enabled(), + msg="Conf flag is set to false so policy enforcement should be disabled.") -# TODO: add tests for all combinations of extensions and policy parameters when ExtensionPolicyEngine() class is added \ No newline at end of file diff --git a/tests/ga/test_regorus.py b/tests/ga/test_regorus.py deleted file mode 100644 index 169786f04..000000000 --- a/tests/ga/test_regorus.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2018 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Requires Python 2.4+ and Openssl 1.0+ -# - -import json -import os -import shutil - -from tests.lib.tools import AgentTestCase -from azurelinuxagent.ga.policy.regorus import Engine -from tests.lib.tools import patch, data_dir, test_dir - - -class TestRegorusEngine(AgentTestCase): - patcher = None - regorus_dest_path = None # Location where real regorus executable should be. - default_policy_path = os.path.join(data_dir, 'policy', "agent-extension-default-data.json") - default_rule_path = os.path.join(data_dir, 'policy', "agent_extension_policy.rego") - input_json = None # Input is stored in a file, and extracted into this variable during class setup. - - @classmethod - def setUpClass(cls): - # On a production VM, Regorus will be located in the agent package. Unit tests - # run within the agent directory, so we copy the executable to ga/policy/regorus and patch path. - # Note: Regorus has not been published officially, so for now, unofficial exe is stored in tests/data/policy.s - regorus_source_path = os.path.abspath(os.path.join(data_dir, "policy/regorus")) - cls.regorus_dest_path = os.path.abspath(os.path.join(test_dir, "..", "azurelinuxagent/ga/policy/regorus")) - if not os.path.exists(cls.regorus_dest_path): - shutil.copy(regorus_source_path, cls.regorus_dest_path) - # Patch the path to regorus for all unit tests. - cls.patcher = patch('azurelinuxagent.ga.policy.regorus.get_regorus_path', return_value=cls.regorus_dest_path) - cls.patcher.start() - - # We store input in a centralized file, we want to extract the JSON contents into a dict for testing. - with open(os.path.join(data_dir, 'policy', "agent-extension-input.json"), 'r') as input_file: - cls.input_json = json.load(input_file) - - AgentTestCase.setUpClass() - - @classmethod - def tearDownClass(cls): - # Clean up the Regorus binary that was copied to ga/policy/regorus. - if os.path.exists(cls.regorus_dest_path): - os.remove(cls.regorus_dest_path) - cls.patcher.stop() - AgentTestCase.tearDownClass() - - def test_should_evaluate_query_with_valid_params(self): - """ - Eval_query should return the expected output with a valid policy, data, and input file. - """ - engine = Engine(self.default_policy_path, self.default_rule_path) - output = engine.eval_query(self.input_json, "data.agent_extension_policy.extensions_to_download") - result = output['result'][0]['expressions'][0]['value'] - test_ext_name = "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux" - ext_info = result.get(test_ext_name) - self.assertIsNotNone(ext_info, msg="Query failed, should have returned result for extension.") - self.assertTrue(ext_info.get('downloadAllowed')) - - def test_missing_rule_file_should_raise_exception(self): - """Exception should be raised when we eval_query with invalid rule file path.""" - engine = Engine("fake_file_path", self.default_policy_path) - with self.assertRaises(Exception, msg="Adding a bad path to rule file should have raised an exception."): - engine.eval_query(self.input_json, "data") - - def test_invalid_rule_file_should_raise_exception(self): - """Exception should be raised when we eval_query with invalid rule file contents.""" - invalid_rule = os.path.join(data_dir, 'policy', "agent_extension_policy_invalid.rego") - with self.assertRaises(Exception, msg="Adding a rule file with invalid contents should have raised an exception."): - engine = Engine(self.default_policy_path, invalid_rule) - engine.eval_query(self.input_json, "data") - - def test_missing_policy_file_should_raise_exception(self): - """Exception should be raised when we eval_query with invalid policy file path.""" - invalid_policy = os.path.join("agent-extension-data-invalid.json") - with self.assertRaises(Exception, msg="Adding a bad path to policy file should have raised an exception."): - engine = Engine(invalid_policy, self.default_rule_path) - engine.eval_query(self.input_json, "data") - - def test_invalid_policy_file_should_raise_exception(self): - """Exception should be raised when we eval_query with bad data file contents.""" - invalid_policy = os.path.join(data_dir, 'policy', "agent-extension-data-invalid.json") - with self.assertRaises(Exception, msg="Adding an invalid data file should have raised an exception."): - engine = Engine(invalid_policy, self.default_rule_path) - engine.eval_query(self.input_json, "data") -