diff --git a/changelogs/fragments/Add_log_inspection_rules_resource_module.yaml b/changelogs/fragments/Add_log_inspection_rules_resource_module.yaml new file mode 100644 index 0000000..edaca21 --- /dev/null +++ b/changelogs/fragments/Add_log_inspection_rules_resource_module.yaml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Add Log Inspection rules resource module. diff --git a/meta/runtime.yml b/meta/runtime.yml index f7cb2ab..6d538b0 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -30,6 +30,15 @@ plugin_routing: redirect: trendmicro.deepsec.deepsec_hosts_info log_inspectionrules: redirect: trendmicro.deepsec.deepsec_log_inspectionrules + deprecation: + removal_date: "2023-12-08" + warning_text: See the plugin documentation for more details + deepsec_log_inspectionrules: + deprecation: + removal_date: "2023-12-08" + warning_text: See the plugin documentation for more details + log_inspection_rules: + redirect: trendmicro.deepsec.deepsec_log_inspection_rules syslog: redirect: trendmicro.deepsec.deepsec_syslog system_settings: diff --git a/plugins/action/deepsec_log_inspection_rules.py b/plugins/action/deepsec_log_inspection_rules.py new file mode 100644 index 0000000..b4d3be2 --- /dev/null +++ b/plugins/action/deepsec_log_inspection_rules.py @@ -0,0 +1,417 @@ +# -*- coding: utf-8 -*- +# Copyright 2021 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The module file for deepsec_log_inspection_rules +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from ansible.plugins.action import ActionBase +from ansible.errors import AnsibleActionFail +from ansible.module_utils.connection import Connection +from ansible.module_utils.six import iteritems +from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( + utils, +) +from ansible_collections.trendmicro.deepsec.plugins.module_utils.deepsec import ( + DeepSecurityRequest, + map_obj_to_params, + map_params_to_obj, + remove_get_keys_from_payload_dict, +) +from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import ( + AnsibleArgSpecValidator, +) +from ansible_collections.trendmicro.deepsec.plugins.modules.deepsec_log_inspection_rules import ( + DOCUMENTATION, +) + + +class ActionModule(ActionBase): + """ action module + """ + + def __init__(self, *args, **kwargs): + super(ActionModule, self).__init__(*args, **kwargs) + self._result = None + self.api_object = "/api/loginspectionrules" + self.api_object_search = "/api/loginspectionrules/search" + self.api_return = "logInspectionRules" + self.module_return = "log_inspection_rules" + self.key_transform = { + "id": "ID", + "minimum_agent_version": "minimumAgentVersion", + "minimum_manager_version": "minimumManagerVersion", + "original_issue": "originalIssue", + "last_updated": "lastUpdated", + "rule_id": "ruleID", + "rule_description": "ruleDescription", + "pattern_type": "patternType", + "dependency_rule_id": "dependencyRuleID", + "dependency_group": "dependencyGroup", + "time_frame": "timeFrame", + "rule_xml": "ruleXML", + "alert_enabled": "alertEnabled", + "alert_minimum_severity": "alertMinimumSeverity", + "recommendations_mode": "recommendationsMode", + "sort_order": "sortOrder", + "can_be_assigned_alone": "canBeAssignedAlone", + "depends_onrule_id": "dependsOnRuleIDs", + } + + def log_files_fn(self, module_params): + temp_obj = {} + if module_params.get("log_files"): + temp_obj = { + "logFiles": module_params.get("log_files")["log_files"] + } + elif module_params.get("logFiles"): + temp_obj["log_files"] = module_params["logFiles"]["logFiles"] + return temp_obj + + def convert_list_to_dict(self, params, key, keys): + if isinstance(params[key], list): + temp = {} + temp[key] = {} + for each in params[key]: + each_key = "" + for every in keys: + each_key += each[every] + temp[key][each_key] = each + params[key] = temp[key] + return params + + def convert_dict_to_list(self, params, key, sub_key): + if isinstance(params[key][sub_key], dict): + temp = [] + for k, v in iteritems(params[key][sub_key]): + temp.append(v) + params[key][sub_key] = temp + return params + + def _check_argspec(self): + aav = AnsibleArgSpecValidator( + data=self._task.args, + schema=DOCUMENTATION, + schema_format="doc", + name=self._task.action, + ) + valid, errors, self._task.args = aav.validate() + if not valid: + self._result["failed"] = True + self._result["msg"] = errors + + def _check_for_response_code(self, response_code, response): + if response_code >= 400: + if response.get("errors"): + raise AnsibleActionFail( + "Request failed with HTTPerror code: {0}, and with a response: {1}".format( + response_code, response["errors"] + ) + ) + elif response.get("message"): + raise AnsibleActionFail( + "Request failed with HTTPerror code: {0}, and with a response: {1}".format( + response_code, response["message"] + ) + ) + + def search_for_existing_rules(self, conn_request, search_payload=None): + code, resource_response = conn_request.post( + self.api_object_search, data=search_payload + ) + self._check_for_response_code(code, resource_response) + return resource_response + + def search_for_resource_name(self, conn_request, search_resource_by_names): + search_result = [] + if isinstance(search_resource_by_names, list): + for each in search_resource_by_names: + search_payload = { + "maxItems": 1, + "searchCriteria": [ + { + "fieldName": "name", + "stringTest": "equal", + "stringValue": each["name"], + } + ], + } + temp_search_response = self.search_for_existing_rules( + conn_request, search_payload + ) + if ( + temp_search_response.get(self.api_return) + and temp_search_response[self.api_return] + ): + api_response = map_obj_to_params( + temp_search_response[self.api_return][0], + self.key_transform, + self.api_return, + ) + if api_response.get("logFiles"): + api_response["log_files"] = self.log_files_fn( + api_response + ) + api_response.pop("logFiles") + search_result.append(api_response) + else: + search_payload = { + "maxItems": 1, + "searchCriteria": [ + { + "fieldName": "name", + "stringTest": "equal", + "stringValue": search_resource_by_names, + } + ], + } + search_result = self.search_for_existing_rules( + conn_request, search_payload + ) + + return search_result + + def delete_module_api_config(self, conn_request, module_config_params): + config = {} + before = [] + after = [] + changed = False + for each in module_config_params: + search_by_name = self.search_for_resource_name( + conn_request, each["name"] + ) + if search_by_name.get(self.api_return): + every = map_obj_to_params( + search_by_name[self.api_return][0], + self.key_transform, + self.api_return, + ) + response_code, api_response = conn_request.delete( + "{0}/{1}".format(self.api_object, every["id"]), data=each + ) + if every.get("logFiles"): + every["log_files"] = self.log_files_fn(every) + every.pop("logFiles") + before.append(every) + self._check_for_response_code(response_code, api_response) + + changed = True + if api_response: + api_response = map_obj_to_params( + api_response, self.key_transform, self.api_return + ) + if api_response.get("logFiles"): + api_response["log_files"] = self.log_files_fn( + api_response + ) + api_response.pop("logFiles") + after.append(api_response) + if changed: + config.update({"before": before, "after": after}) + else: + config.update({"before": before}) + return config, changed + + def configure_module_api(self, conn_request, module_config_params): + get_supported_keys = ["id", "identifier", "can_be_assigned_alone"] + config = {} + before = [] + after = [] + changed = False + # Add to the THIS list for the value which needs to be excluded + # from HAVE params when compared to WANT param like 'ID' can be + # part of HAVE param but may not be part of your WANT param + remove_from_diff_compare = ["id", "type"] + temp_name = [] + for each in module_config_params: + + search_by_name = self.search_for_resource_name( + conn_request, each["name"] + ) + if search_by_name and search_by_name.get(self.api_return): + each_result = search_by_name[self.api_return] + for every in each_result: + every = map_obj_to_params( + every, self.key_transform, self.api_return + ) + if every.get("logFiles"): + every["log_files"] = self.log_files_fn(every) + every["log_files"] = self.convert_list_to_dict( + every["log_files"], + "log_files", + ["format", "location"], + ) + every.pop("logFiles") + if each.get("log_files"): + each["log_files"] = self.convert_list_to_dict( + each["log_files"], + "log_files", + ["format", "location"], + ) + if every["name"] == each["name"]: + each = utils.remove_empties(each) + diff = utils.dict_diff(every, each) + if diff: + diff = remove_get_keys_from_payload_dict( + diff, remove_from_diff_compare + ) + if diff: + if self._task.args["state"] == "merged": + # Check for actual modification and if present fire + # the request over that integrity_monitoring_rules ID + each = utils.remove_empties( + utils.dict_merge(every, each) + ) + each = remove_get_keys_from_payload_dict( + each, remove_from_diff_compare + ) + changed = True + each = self.convert_dict_to_list( + each, "log_files", "log_files" + ) + payload = map_params_to_obj( + each, self.key_transform + ) + if payload.get("log_files"): + payload["logFiles"] = self.log_files_fn(each) + payload.pop("log_files") + response_code, api_response = conn_request.post( + "{0}/{1}".format(self.api_object, every["id"]), + data=payload, + ) + self._check_for_response_code( + response_code, api_response + ) + api_response = map_obj_to_params( + api_response, + self.key_transform, + self.api_return, + ) + if api_response.get("logFiles"): + api_response["log_files"] = self.log_files_fn( + api_response + ) + api_response.pop("logFiles") + after.append(api_response) + elif self._task.args["state"] == "replaced": + response_code, api_response = conn_request.delete( + "{0}/{1}".format(self.api_object, every["id"]), + data=every, + ) + self._check_for_response_code( + response_code, api_response + ) + changed = True + each = self.convert_dict_to_list( + each, "log_files", "log_files" + ) + payload = map_params_to_obj( + each, self.key_transform + ) + if payload.get("log_files"): + payload["logFiles"] = self.log_files_fn(each) + payload.pop("log_files") + response_code, api_response = conn_request.post( + "{0}".format(self.api_object), data=payload + ) + self._check_for_response_code( + response_code, api_response + ) + api_response = map_obj_to_params( + api_response, + self.key_transform, + self.api_return, + ) + if api_response.get("logFiles"): + api_response["log_files"] = self.log_files_fn( + api_response + ) + api_response.pop("logFiles") + after.append(api_response) + if every.get("log_files"): + every = self.convert_dict_to_list( + every, "log_files", "log_files" + ) + before.append(every) + else: + if every.get("logFiles"): + every["log_files"] = self.log_files_fn(every) + every.pop("logFiles") + before.append(every) + after.append(every) + temp_name.append(every["name"]) + else: + if every.get("logFiles"): + every["log_files"] = self.log_files_fn(every) + every.pop("logFiles") + before.append(every) + after.append(every) + else: + changed = True + each = utils.remove_empties(each) + each = remove_get_keys_from_payload_dict( + each, get_supported_keys + ) + if each.get("log_files"): + each["logFiles"] = self.log_files_fn(each) + each.pop("log_files") + payload = map_params_to_obj(each, self.key_transform) + code, api_response = conn_request.post( + "{0}".format(self.api_object), data=payload + ) + self._check_for_response_code(code, api_response) + after.extend(before) + api_response = map_obj_to_params( + api_response, self.key_transform, self.api_return + ) + if api_response.get("logFiles"): + api_response["log_files"] = self.log_files_fn(api_response) + api_response.pop("logFiles") + after.append(api_response) + if not changed: + after = [] + config.update({"before": before, "after": after}) + + return config, changed + + def run(self, tmp=None, task_vars=None): + self._supports_check_mode = True + self._result = super(ActionModule, self).run(tmp, task_vars) + self._check_argspec() + if self._result.get("failed"): + return self._result + conn = Connection(self._connection.socket_path) + conn_request = DeepSecurityRequest( + connection=conn, task_vars=task_vars + ) + if self._task.args["state"] == "gathered": + if self._task.args.get("config"): + self._result["gathered"] = self.search_for_resource_name( + conn_request, self._task.args["config"] + ) + else: + self._result["gathered"] = conn_request.get(self.api_object) + elif ( + self._task.args["state"] == "merged" + or self._task.args["state"] == "replaced" + ): + if self._task.args.get("config"): + self._result[self.module_return], self._result[ + "changed" + ] = self.configure_module_api( + conn_request, self._task.args["config"] + ) + elif self._task.args["state"] == "deleted": + if self._task.args.get("config"): + self._result[self.module_return], self._result[ + "changed" + ] = self.delete_module_api_config( + conn_request, self._task.args["config"] + ) + + return self._result diff --git a/plugins/modules/deepsec_log_inspection_rules.py b/plugins/modules/deepsec_log_inspection_rules.py new file mode 100644 index 0000000..6e20f63 --- /dev/null +++ b/plugins/modules/deepsec_log_inspection_rules.py @@ -0,0 +1,649 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +module: deepsec_log_inspection_rules +short_description: Manages Log Inspection Rule resource module +description: Contains string matching and threshold to trigger alerts as well as group + information for LogInspectionRules. +version_added: 2.0.0 +options: + config: + description: A dictionary of Log Inspection Rules options + type: list + elements: dict + suboptions: + name: + description: Name of the LogInspectionRule. Searchable as String. + type: str + description: + description: Description of the LogInspectionRule that appears in search results, + and on the General tab in the Deep Security Manager user interface. Searchable + as String. + type: str + minimum_agent_version: + description: Minimum Deep Security Agent version required by the LogInspectionRule. + Searchable as String. + type: str + minimum_manager_version: + description: Minimumn Deep Security Manager version required by the LogInspectionRule. + Searchable as String. + type: str + type: + description: Type of the LogInspectionRule. The value 'Defined' is used for + LogInspectionRules provided by Trend Micro. Searchable as String. + type: str + original_issue: + description: Creation timestamp of the LogInspectionRule, measured in milliseconds + since epoch. Searchable as Date. + type: int + last_updated: + description: Update timestamp of the LogInspectionRule, measured in milliseconds + since epoch. Searchable as Date. + type: int + identifier: + description: Indentifier of the LogInspectionRule used in the Deep Security + Manager user interface. Searchable as String. + type: str + template: + description: Template used to create this rule. + type: str + choices: + - basic-rule + - custom + rule_id: + description: ID of the LogInspectionRule sent to the Deep Security Agent. + The values 100000 - 109999 are reserved for user-definded rules. + type: int + level: + description: Log level of the LogInspectionRule indicates severity of attack. + Level 0 is the least severe and will not log an event. Level 15 is the most + severe. + type: int + groups: + description: Groups that the LogInspectionRule is assigned to, separated by + commas. Useful when dependency is used as it's possible to create a LogInspectionRule + that fires when another LogInspectionRule belonging to a specific group + fires. + type: list + elements: str + rule_description: + description: Description of the LogInspectionRule that appears on events and + the Content tab in the Deep Security Manager user interface. Alternatively, + you can configure this by inserting a description in 'ruleXML'. + type: str + pattern: + description: Regular expression pattern the LogInspectionRule will look for + in the logs. The rule will be triggered on a match. Open Source HIDS SEcurity + (OSSEC) regular expression syntax is supported, see http://www.ossec.net/docs/syntax/regex.html. + type: str + pattern_type: + description: Pattern the LogInspectionRule will look for in the logs. The + string matching pattern is faster than the regex pattern. + type: str + choices: + - string + - regex + dependency: + description: Indicates if a dependant rule or dependency group is set or not. + If set, the LogInspectionRule will only log an event if the dependency is + triggered. Available for user-defined rules. + type: str + choices: + - none + - rule + - group + dependency_rule_id: + description: If dependency is configured, the ID of the rule that this rule + is dependant on. Ignored if the rule is from Trend Micro, which uses 'dependsOnRuleIDs' + instead. + type: int + dependency_group: + description: If dependency is configured, the dependancy groups that this + rule is dependant on. + type: str + frequency: + description: Number of times the dependant rule has to match within a specific + time frame before the rule is triggered. + type: int + time_frame: + description: Time period for the frequency of LogInspectionRule triggers that + will generate an event, in seconds. + type: int + rule_xml: + description: LogInspectionRule in an XML format. For information on the XML + format, see http://ossec-docs.readthedocs.io/en/latest/syntax/head_rules.html + type: str + log_files: + description: Log file objects + type: dict + suboptions: + log_files: + description: Array of objects (logFile) + type: list + elements: dict + suboptions: + location: + description: File path of the log file. + type: str + format: + description: Structure of the data in the log file. The application that generates + the log file defines the structure of the data. + choices: ["syslog", "snort-full", "snort-fast", "apache", "iis", "squid", "nmapg", + "mysql-log", "postgresql-log", "dbj-multilog", "eventlog", "single-line-text-log"] + type: str + alert_enabled: + description: Controls whether to raise an alert when a LogInspectionRule logs + an event. Use true to raise an alert. Searchable as Boolean. + type: bool + alert_minimum_severity: + description: Severity level that will trigger an alert. Ignored unless 'ruleXML' + contains multiple rules with different severities, and so you must indicate + which severity level to use. Searchable as Numeric. + type: int + recommendations_mode: + description: Indicates whether recommendation scans consider the LogInspectionRule. + Can be set to enabled or ignored. Custom rules cannot be recommended. Searchable + as Choice. + type: str + choices: + - enabled + - ignored + - unknown + - disabled + sort_order: + description: Order in which LogInspectionRules are sent to the Deep Security + Agent. Log inspeciton rules are sent in ascending order. Valid values are + between 10000 and 20000. + type: int + can_be_assigned_alone: + description: Indicates whether this LogInspectionRule can be allocated without + allocating any additional LogInspectionRules. Ignored if the rule is user-defined, + which uses 'dependency' instead. + type: bool + depends_on_rule_ids: + description: IDs of LogInspectionRules, separated by commas, that are required + by this rule. Ignored if the rule is user-defined, which uses 'dependencyRuleID' + or 'dependencyGroup' instead. + type: list + elements: int + id: + description: ID of the LogInspectionRule. This number is set automatically. + Searchable as ID. + type: int + state: + description: + - The state the configuration should be left in + - The state I(gathered) will get the module API configuration from the device + and transform it into structured data in the format as per the module argspec + and the value is returned in the I(gathered) key within the result. + type: str + choices: + - merged + - replaced + - overridden + - gathered + - deleted + +author: Ansible Security Automation Team (@justjais) +""" + +EXAMPLES = """ + +# Using MERGED state +# ------------------- + +- name: Create Log Inspection Rules + trendmicro.deepsec.deepsec_log_inspection_rules: + state: merged + config: + - name: custom log_rule for mysqld event + description: some description + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100001 + rule_description: test rule description + groups: + - test + alert_minimum_severity: 4 + alert_enabled: true + log_files: + log_files: + - location: /var/log/mysqld.log + format: mysql-log + - name: custom log_rule for mysqld event + description: some description + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100001 + rule_description: test rule description + groups: + - test + alert_minimum_severity: 4 + alert_enabled: true + log_files: + log_files: + - location: /var/log/mysqld.log + format: mysql-log + +# Play Run: +# ========= +# +# "log_inspection_rules": { +# "after": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 4, +# "dependency": "none", +# "description": "log mysqld event", +# "groups": [ +# "test" +# ], +# "id": 93, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "mysql-log", +# "location": "/var/log/mysqld.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for mysqld event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "mysqld rule description", +# "rule_id": 100001, +# "sort_order": 15000, +# "template": "basic-rule" +# }, +# { +# "alert_enabled": true, +# "alert_minimum_severity": 5, +# "dependency": "none", +# "description": "log daemon event", +# "groups": [ +# "test" +# ], +# "id": 94, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "syslog", +# "location": "/var/log/daemon.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for daemon event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "daemon rule description", +# "rule_id": 100002, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ], +# "before": [] +# } + +- name: Modify the Pattern type of Log Inspection Rule by name + trendmicro.deepsec.deepsec_log_inspection_rules: + state: merged + config: + - name: custom log_rule for mysqld event + description: Modified pattern type for mysqld log event + pattern: name + pattern_type: regex + log_files: + log_files: + - location: /var/log/messages + format: syslog + +# Play Run: +# ========= +# +# "log_inspection_rules": { +# "after": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 4, +# "dependency": "none", +# "description": "Modified pattern type for mysqld log event", +# "groups": [ +# "test" +# ], +# "id": 134, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "mysql-log", +# "location": "/var/log/mysqld.log" +# }, +# { +# "format": "syslog", +# "location": "/var/log/messages" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for mysqld event", +# "pattern": "name", +# "pattern_type": "regex", +# "rule_description": "mysqld rule description", +# "rule_id": 100001, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ], +# "before": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 4, +# "dependency": "none", +# "description": "log mysqld event", +# "groups": [ +# "test" +# ], +# "id": 134, +# "level": 0, +# "log_files": { +# "log_files": { +# "mysql-log/var/log/mysqld.log": { +# "format": "mysql-log", +# "location": "/var/log/mysqld.log" +# } +# } +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for mysqld event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "mysqld rule description", +# "rule_id": 100001, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ] +# } + +# Using REPLACED state +# -------------------- + +- name: Replace existing Log Inspection Rules + trendmicro.deepsec.deepsec_log_inspection_rules: + state: replaced + config: + - name: custom log_rule for daemon event + description: Replaced log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100003 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/messages + format: syslog + +# Play Run: +# ========= +# +# "log_inspection_rules": { +# "after": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 5, +# "dependency": "none", +# "description": "Replaced log daemon event", +# "groups": [ +# "test" +# ], +# "id": 155, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "syslog", +# "location": "/var/log/messages" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for daemon event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "daemon rule description", +# "rule_id": 100003, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ], +# "before": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 5, +# "dependency": "none", +# "description": "log daemon event", +# "groups": [ +# "test" +# ], +# "id": 154, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "syslog", +# "location": "/var/log/daemon.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for daemon event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "daemon rule description", +# "rule_id": 100002, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ] +# } + +# Using GATHERED state +# -------------------- + +- name: Gather Log Inspection Rules by IPR names + trendmicro.deepsec.deepsec_log_inspection_rules: + state: gathered + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event + +# Play Run: +# ========= +# +# "gathered": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 4, +# "dependency": "none", +# "description": "log mysqld event", +# "groups": [ +# "test" +# ], +# "id": 153, +# "level": 0, +# "logFiles": { +# "logFiles": [ +# { +# "format": "mysql-log", +# "location": "/var/log/mysqld.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for mysqld event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "mysqld rule description", +# "rule_id": 100001, +# "sort_order": 15000, +# "template": "basic-rule" +# }, +# { +# "alert_enabled": true, +# "alert_minimum_severity": 5, +# "dependency": "none", +# "description": "log daemon event", +# "groups": [ +# "test" +# ], +# "id": 154, +# "level": 0, +# "logFiles": { +# "logFiles": [ +# { +# "format": "syslog", +# "location": "/var/log/daemon.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for daemon event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "daemon rule description", +# "rule_id": 100002, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ] + +- name: Gather ALL of the Log Inspection Rules + trendmicro.deepsec.deepsec_log_inspection_rules: + state: gathered + +# Using DELETED state +# ------------------ + +- name: Delete Log Inspection Rules + trendmicro.deepsec.deepsec_log_inspection_rules: + state: deleted + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event + +# Play Run: +# ========= +# +# "log_inspection_rules": { +# "after": [], +# "before": [ +# { +# "alert_enabled": true, +# "alert_minimum_severity": 4, +# "dependency": "none", +# "description": "Modified pattern type for mysqld log event", +# "groups": [ +# "test" +# ], +# "id": 151, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "mysql-log", +# "location": "/var/log/mysqld.log" +# }, +# { +# "format": "syslog", +# "location": "/var/log/messages" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for mysqld event", +# "pattern": "name", +# "pattern_type": "regex", +# "rule_description": "mysqld rule description", +# "rule_id": 100001, +# "sort_order": 15000, +# "template": "basic-rule" +# }, +# { +# "alert_enabled": true, +# "alert_minimum_severity": 5, +# "dependency": "none", +# "description": "log daemon event", +# "groups": [ +# "test" +# ], +# "id": 152, +# "level": 0, +# "log_files": { +# "log_files": [ +# { +# "format": "syslog", +# "location": "/var/log/daemon.log" +# } +# ] +# }, +# "minimum_agent_version": "6.0.0.0", +# "minimum_manager_version": "6.0.0", +# "name": "custom log_rule for daemon event", +# "pattern": "name", +# "pattern_type": "string", +# "rule_description": "daemon rule description", +# "rule_id": 100002, +# "sort_order": 15000, +# "template": "basic-rule" +# } +# ] +# } + +""" + + +RETURN = """ +before: + description: The configuration as structured data prior to module invocation. + returned: always + type: list + sample: The configuration returned will always be in the same format of the parameters above. +after: + description: The configuration as structured data after module completion. + returned: when changed + type: list + sample: The configuration returned will always be in the same format of the parameters above. + +""" diff --git a/plugins/modules/deepsec_log_inspectionrules.py b/plugins/modules/deepsec_log_inspectionrules.py index 873f699..fc4d070 100644 --- a/plugins/modules/deepsec_log_inspectionrules.py +++ b/plugins/modules/deepsec_log_inspectionrules.py @@ -34,6 +34,10 @@ description: - This module creates a new log inspection rule under TrendMicro Deep Security. version_added: 1.0.0 +deprecated: + alternative: deepsec_log_inspection_rules + why: Newer and updated modules released with more functionality + removed_at_date: '2023-12-08' author: "Ansible Security Automation Team (@justjais) " options: name: diff --git a/tests/integration/targets/deepsec_log_inspection_rules/defaults/main.yaml b/tests/integration/targets/deepsec_log_inspection_rules/defaults/main.yaml new file mode 100644 index 0000000..10c0fab --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: '*' diff --git a/tests/integration/targets/deepsec_log_inspection_rules/meta/main.yaml b/tests/integration/targets/deepsec_log_inspection_rules/meta/main.yaml new file mode 100644 index 0000000..23d65c7 --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/meta/main.yaml @@ -0,0 +1,2 @@ +--- +dependencies: [] diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tasks/cli.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tasks/cli.yaml new file mode 100644 index 0000000..dc4768b --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tasks/cli.yaml @@ -0,0 +1,18 @@ +--- +- name: collect all cli test cases + find: + paths: '{{ role_path }}/tests/cli' + patterns: '{{ testcase }}.yaml' + register: test_cases + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: Run test case (connection=ansible.netcommon.httpapi) + include: '{{ test_case_to_run }}' + vars: + ansible_connection: ansible.netcommon.httpapi + with_items: '{{ test_items }}' + loop_control: + loop_var: test_case_to_run + tags: connection_httpapi diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tasks/main.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tasks/main.yaml new file mode 100644 index 0000000..62cc1ae --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tasks/main.yaml @@ -0,0 +1,7 @@ +--- +- include: cli.yaml + tags: + - cli + +- include: redirection.yaml + when: ansible_version.full is version('2.10.0', '>=') diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tasks/redirection.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tasks/redirection.yaml new file mode 100644 index 0000000..a57054c --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tasks/redirection.yaml @@ -0,0 +1,6 @@ +--- +- name: collect all cli test cases + find: + paths: '{{ role_path }}/tests/redirection' + patterns: '{{ testcase }}.yaml' + register: test_cases diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_populate_lir_config.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_populate_lir_config.yaml new file mode 100644 index 0000000..dceb06f --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_populate_lir_config.yaml @@ -0,0 +1,40 @@ +--- +- name: Populate Log Inspection Rules for tests + tags: merged + trendmicro.deepsec.deepsec_log_inspection_rules: &id001 + state: merged + config: + - name: custom log_rule for mysqld event + description: log mysqld event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100001 + rule_description: mysqld rule description + groups: + - test + alert_minimum_severity: 4 + alert_enabled: true + log_files: + log_files: + - location: /var/log/mysqld.log + format: mysql-log + - name: custom log_rule for daemon event + description: log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100002 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/daemon.log + format: syslog diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_remove_lir_config.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_remove_lir_config.yaml new file mode 100644 index 0000000..846500e --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/_remove_lir_config.yaml @@ -0,0 +1,7 @@ +--- +- name: Remove already configured Log Inspection Rules Config by Name + trendmicro.deepsec.deepsec_log_inspection_rules: + state: deleted + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/deleted.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/deleted.yaml new file mode 100644 index 0000000..92fce2b --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/deleted.yaml @@ -0,0 +1,38 @@ +--- +- debug: + msg: Start Deleted integration state for deepsec_log_inspection_rules ansible_connection={{ ansible_connection + }} + +- include_tasks: _remove_lir_config.yaml + +- include_tasks: _populate_lir_config.yaml + +- block: + + - name: Delete attributes of provided configured Log Inspection Rules + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: &id001 + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event + state: deleted + + - assert: + that: + - result.changed == true + - "{{ merged['after'] | symmetric_difference(result['log_inspection_rules']['before']) |\ + \ length == 4 }}" + - merged['before'] == result['log_inspection_rules']['after'] + + - name: Delete attributes of all Log Inspection Rules (IDEMPOTENT) + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: *id001 + + - name: Assert that the previous delete task was idempotent + assert: + that: + - result.changed == false + + always: + + - include_tasks: _remove_lir_config.yaml diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/gathered.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/gathered.yaml new file mode 100644 index 0000000..17b02f8 --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/gathered.yaml @@ -0,0 +1,27 @@ +--- +- debug: + msg: START deepsec_log_inspection_rules gathered integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_lir_config.yaml + +- include_tasks: _populate_lir_config.yaml + +- block: + + - name: Gather the provided configuration with the exisiting running configuration + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event + state: gathered + + - assert: + that: + - "{{ merged['after'] | symmetric_difference(result['gathered']) |\ + \ length == 4 }}" + - result['changed'] == false + always: + + - include_tasks: _remove_lir_config.yaml diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/merged.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/merged.yaml new file mode 100644 index 0000000..1b1479a --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/merged.yaml @@ -0,0 +1,74 @@ +--- +- debug: + msg: START Merged deepsec_log_inspection_rules state for integration tests on connection={{ + ansible_connection }} + +- include_tasks: _remove_lir_config.yaml + +- block: + + - name: Merge and Create new Log Inspection Rules + tags: merged + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: &id001 + state: merged + config: + - name: custom log_rule for mysqld event + description: log mysqld event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100001 + rule_description: mysqld rule description + groups: + - test + alert_minimum_severity: 4 + alert_enabled: true + log_files: + log_files: + - location: /var/log/mysqld.log + format: mysql-log + - name: custom log_rule for daemon event + description: log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100002 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/daemon.log + format: syslog + + - name: Assert that task reports change and after dict is correctly generated + assert: + that: + - result['changed'] == true + - "{{ merged['after'] | symmetric_difference(result['log_inspection_rules']['after']) |\ + \ length == 4 }}" + + - name: Assert that before dicts are correctly generated + assert: + that: + - merged['before'] == result['log_inspection_rules']['before'] + + - name: Merge provided configuration with device configuration (IDEMPOTENT) + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: *id001 + + - name: Assert that the previous task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_lir_config.yaml \ No newline at end of file diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/replaced.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/replaced.yaml new file mode 100644 index 0000000..402d5f8 --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/replaced.yaml @@ -0,0 +1,54 @@ +--- +- debug: + msg: START Replaced deepsec_log_inspection_rules state for integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_lir_config.yaml +- include_tasks: _populate_lir_config.yaml + +- block: + + - name: Replace existing Log Inspection Rules + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: &id001 + state: replaced + config: + - name: custom log_rule for daemon event + description: Replaced log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100003 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/messages + format: syslog + + - assert: + that: + - result.changed == true + - "{{ replaced['before'] | symmetric_difference(result['log_inspection_rules']['before']) |\ + \ length == 2 }}" + - "{{ replaced['after'] | symmetric_difference(result['log_inspection_rules']['after']) |\ + \ length == 2 }}" + + - name: Replaces device configuration of Log Inspection Rule with provided configuration + (IDEMPOTENT) + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: *id001 + + - name: Assert that task was idempotent + assert: + that: + - result['changed'] == false + + always: + + - include_tasks: _remove_lir_config.yaml diff --git a/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/rtt.yaml b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/rtt.yaml new file mode 100644 index 0000000..d6a30a6 --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/tests/cli/rtt.yaml @@ -0,0 +1,110 @@ +--- +- debug: + msg: START deepsec_log_inspection_rules round trip integration tests on connection={{ ansible_connection + }} + +- include_tasks: _remove_lir_config.yaml +- include_tasks: _populate_lir_config.yaml + +- block: + + - name: Apply the provided configuration (base config) + register: base_config + trendmicro.deepsec.deepsec_log_inspection_rules: &id001 + state: merged + config: + - name: custom log_rule for mysqld event + description: log mysqld event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100001 + rule_description: mysqld rule description + groups: + - test + alert_minimum_severity: 4 + alert_enabled: true + log_files: + log_files: + - location: /var/log/mysqld.log + format: mysql-log + - name: custom log_rule for daemon event + description: log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100002 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/daemon.log + format: syslog + + - name: Gather TM Log Inspection Rules facts + register: gather_result + trendmicro.deepsec.deepsec_log_inspection_rules: + config: + - name: custom log_rule for mysqld event + - name: custom log_rule for daemon event + state: gathered + + - name: Apply the configuration which need to be reverted + register: result + trendmicro.deepsec.deepsec_log_inspection_rules: + config: + - name: custom log_rule for daemon event + description: Replaced log daemon event + minimum_agent_version: 6.0.0.0 + type: defined + template: basic-rule + pattern: name + pattern_type: string + rule_id: 100003 + rule_description: daemon rule description + groups: + - test + alert_minimum_severity: 5 + alert_enabled: true + log_files: + log_files: + - location: /var/log/messages + format: syslog + state: replaced + + - assert: + that: + - result.changed == true + - "{{ replaced['before'] | symmetric_difference(result['log_inspection_rules']['before']) |\ + \ length == 2 }}" + - "{{ replaced['after'] | symmetric_difference(result['log_inspection_rules']['after']) |\ + \ length == 2 }}" + + - name: Revert back to base config using facts round trip + register: revert + trendmicro.deepsec.deepsec_log_inspection_rules: + config: "{{ gather_result['gathered'] }}" + state: replaced + + - debug: + msg: + - "{{ merged['after'] | symmetric_difference(revert['log_inspection_rules']['after']) |\ + \ length == 4 }}" + - "{{ merged['after'] | symmetric_difference(revert['log_inspection_rules']['after']) }}" + + - assert: + that: + - revert['changed'] == true + - "{{ merged['after'] | symmetric_difference(revert['log_inspection_rules']['after']) |\ + \ length == 4 }}" + + always: + + - include_tasks: _remove_lir_config.yaml diff --git a/tests/integration/targets/deepsec_log_inspection_rules/vars/main.yaml b/tests/integration/targets/deepsec_log_inspection_rules/vars/main.yaml new file mode 100644 index 0000000..a2a70d7 --- /dev/null +++ b/tests/integration/targets/deepsec_log_inspection_rules/vars/main.yaml @@ -0,0 +1,92 @@ +--- +merged: + before: [] + + after: + - alert_enabled: true + alert_minimum_severity: 4 + dependency: none + description: "log mysqld event" + groups: + - "test" + level: 0 + log_files: + log_files: + - format: mysql-log + location: /var/log/mysqld.log + minimum_agent_version: 6.0.0.0 + minimum_manager_version: 6.0.0 + name: "custom log_rule for mysqld event" + pattern: name + pattern_type: string + rule_description: "mysqld rule description" + rule_id: 100001 + sort_order: 15000 + template: basic-rule + - alert_enabled: true + alert_minimum_severity: 5 + dependency: none + description: false + destination_iptype: "log daemon event" + groups: + - "test" + level: 0 + log_files: + log_files: + - format: syslog + location: /var/log/daemon.log + minimum_agent_version: 6.0.0.0 + minimum_manager_version: 6.0.0 + name: "custom log_rule for daemon event" + pattern: name + pattern_type: string + rule_description: "daemon rule description" + rule_id: 100002 + sort_order: 15000 + template: basic-rule + +replaced: + before: + - alert_enabled: true + alert_minimum_severity: 5 + dependency: none + description: false + destination_iptype: "log daemon event" + groups: + - "test" + level: 0 + log_files: + log_files: + - format: syslog + location: /var/log/daemon.log + minimum_agent_version: 6.0.0.0 + minimum_manager_version: 6.0.0 + name: "custom log_rule for daemon event" + pattern: name + pattern_type: string + rule_description: "daemon rule description" + rule_id: 100002 + sort_order: 15000 + template: basic-rule + after: + - alert_enabled: true + alert_minimum_severity: 5 + dependency: none + description: false + destination_iptype: "Replaced log daemon event" + groups: + - "test" + level: 0 + log_files: + log_files: + - format: syslog + location: /var/log/messages + minimum_agent_version: 6.0.0.0 + minimum_manager_version: 6.0.0 + name: "custom log_rule for daemon event" + pattern: name + pattern_type: string + rule_description: "daemon rule description" + rule_id: 100002 + sort_order: 15000 + template: basic-rule diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index bdb2cab..a12e07f 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -6,3 +6,5 @@ plugins/modules/deepsec_firewallrules.py validate-modules:deprecation-mismatch # plugins/modules/deepsec_firewallrules.py validate-modules:invalid-documentation # removed_at_date not supported in `deprecated` dict plugins/modules/deepsec_anti_malware.py validate-modules:deprecation-mismatch # 2.9 expects METADATA plugins/modules/deepsec_anti_malware.py validate-modules:invalid-documentation # removed_at_date not supported in `deprecated` dict +plugins/modules/deepsec_log_inspectionrules.py validate-modules:deprecation-mismatch # 2.9 expects METADATA +plugins/modules/deepsec_log_inspectionrules.py validate-modules:invalid-documentation # removed_at_date not supported in `deprecated` dict