Integrator is a tool which easily connects Wazuh with external software. This is achieved by integrating the alert system with the APIs of the software products through scripts. Examples of this are the current integrations with Virustotal, Slack or PagerDuty.
In this article, we will learn how to configure Wazuh to communicate with external APIs with Integrator. In addition, we will show how a script should be prepared to process alerts as required.
To illustrate this process, we will develop a basic integration with the Jira planning tool, creating an issue in its system with each file integrity monitoring alert produced by Syscheck.
Integration component configuration
To start the custom integration, the ossec.conf file, including the block integration component, has to be modified in the manager. The following parameters can be used:
- name: Name of the script that performs the integration. In the case of a custom integration like the one discussed in this article, the name must start with “custom-“.
- hook_url: URL provided by the software API to connect to the API itself. Its use is optional, since it can be included in the script.
- api_key: Key of the API that enables us to use it. Its use is also optional for the same reason the use of the hook_url is optional.
- level: Sets a level filter so that the script will not receive alerts below a certain level.
- rule_id: Sets a filter for alert identifiers.
- group: Sets an alert group filter.
- event_location: Sets an alert source filter.
- alert_format: Indicates that the script receives the alerts in JSON format (recommended). By default, the script will receive the alerts in full_log format.
<integration> <name>custom-integration</name> <hook_url>WEBHOOK</hook_url> <level>10</level> <group>multiple_drops|authentication_failures</group> <alert_format>json</alert_format> </integration>
Creating the integration script
The first line of the integration script must indicate its interpreter or else Wazuh won’t know how to read and execute the script.
#!/usr/bin/env python
The script must check its arguments because it will receive configuration options from them. The first parameter includes the location of the file that contains the alert. The second parameter contains the api_key, and the third contains the hook_url option. If none of the above are indicated, the parameters will be received empty.
alert_file = open(sys.argv[1]) api_key = sys.argv[2].split(':')[1] hook_url = sys.argv[3]
The next step is to read the contents of the file indicated in the first parameter and extract, from the alert, the fields that are relevant for the integration. If JSON was used in the alert_format option, the information has to be loaded as a JSON object.
alert_level = alert_json['rule']['level'] ruleid = alert_json['rule']['id'] description = alert_json['rule']['description'] agentid = alert_json['agent']['id'] agentname = alert_json['agent']['name'] path = alert_json['syscheck']['path']
We recommend that you check the file /logs/alerts/alerts.json
before starting the development of the integration in order to find the format of the alerts to be interpreted.
Use case: integration with Jira
To carry out this use case, let’s presuppose an environment in Jira with the following configuration:
- A project whose identifying key is
WT
. - The existence of an issue type with id
10002
. - A domain address
https://testwazuhintegrator.atlassian.net
.
The configuration block takes the following considerations into account:
- The syscheck group contains the FIM alerts that are going to be reported.
- The URL indicated in
hook_url
contains the API address to work with the issues. - In order to authenticate in the Jira API it is necessary to provide an email in addition to the key of this API. Therefore, the
api_key
option will include the email address separated from the key by:
, in order to separate them in the script.
<integration> <name>custom-jira</name> <group>syscheck</group> <hook_url>https://testwazuhintegrator.atlassian.net/rest/api/3/issue/</hook_url> <api_key>integratortest@accountemail.com:api_key</api_key> <alert_format>json</alert_format> </integration>
With this information, we can develop a basic integration script with Jira which will create an issue for each FIM event, indicating the rule ID and level of the alert, the name and ID of the agent involved, and the type of event (modification, creation, or deletion). The configuration used here is for version 3 of the Jira API.
#!/usr/bin/env python import sys import json import requests from requests.auth import HTTPBasicAuth # Read configuration parameters alert_file = open(sys.argv[1]) user = sys.argv[2].split(':')[0] api_key = sys.argv[2].split(':')[1] hook_url = sys.argv[3] # Read the alert file alert_json = json.loads(alert_file.read()) alert_file.close() # Extract issue fields alert_level = alert_json['rule']['level'] ruleid = alert_json['rule']['id'] description = alert_json['rule']['description'] agentid = alert_json['agent']['id'] agentname = alert_json['agent']['name'] path = alert_json['syscheck']['path'] # Set the project attributes ===> This section needs to be manually configured before running! project_key = 'WT' # You can get this from the beggining of an issue key. For example, WS for issue key WS-5018 issuetypeid = '10002' # Check https://confluence.atlassian.com/jirakb/finding-the-id-for-issue-types-646186508.html. There's also an API endpoint to get it. # Generate request headers = {'content-type': 'application/json'} issue_data = { "update": {}, "fields": { "summary": 'FIM alert on [' + path + ']', "issuetype": { "id": issuetypeid }, "project": { "key": project_key }, "description": { 'version': 1, 'type': 'doc', 'content': [ { "type": "paragraph", "content": [ { "text": '- State: ' + description + '\n- Rule ID: ' + str(ruleid) + '\n- Alert level: ' + str(alert_level) + '\n- Agent: ' + str(agentid) + ' ' + agentname, "type": "text" } ] } ], }, } } # Send the request response = requests.post(hook_url, data=json.dumps(issue_data), headers=headers, auth=(user, api_key)) #print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": "))) # <--- Uncomment this line for debugging sys.exit(0)
In the following image, we can see a FIM issue generated by this integration.
Note that this script uses the requests
module as dependency, so it will need to be previously installed.
The Issue type ID can be queried to Jira API by running the following command.
curl --request GET \ --url 'https://your-domain.atlassian.net/rest/api/3/issuetype' \ --user 'email@example.com:<api_token>' \ --header 'Accept: application/json'
The script must be located in /var/ossec/integrations
with the same name indicated in the configuration block, contain execution permissions, and belong to the root
user of the ossec
group.
chmod 750 /var/ossec/integrations/custom-script chown root:ossec /var/ossec/integrations/custom-script
If we want the script to redirect outputs to a log file, it must have written permissions and belong to the ossec user of the ossec group.
As a final note, the following script can be used instead for other Wazuh alerts not related to the FIM module.
#!/usr/bin/env python import sys import json import requests from requests.auth import HTTPBasicAuth # Read configuration parameters alert_file = open(sys.argv[1]) user = sys.argv[2].split(':')[0] api_key = sys.argv[2].split(':')[1] hook_url = sys.argv[3] # Read the alert file alert_json = json.loads(alert_file.read()) alert_file.close() # Extract issue fields alert_level = alert_json['rule']['level'] ruleid = alert_json['rule']['id'] description = alert_json['rule']['description'] agentid = alert_json['agent']['id'] agentname = alert_json['agent']['name'] # Set the project attributes ===> This section needs to be manually configured before running! project_key = 'WT' # You can get this from the beggining of an issue key. For example, WS for issue key WS-5018 issuetypeid = '10002' # Check https://confluence.atlassian.com/jirakb/finding-the-id-for-issue-types-646186508.html. There's also an API endpoint to get it. # Generate request headers = {'content-type': 'application/json'} issue_data = { "update": {}, "fields": { "summary": 'Wazuh Alert: ' + description, "issuetype": { "id": issuetypeid }, "project": { "key": project_key }, "description": { 'version': 1, 'type': 'doc', 'content': [ { "type": "paragraph", "content": [ { "text": '- Rule ID: ' + str(ruleid) + '\n- Alert level: ' + str(alert_level) + '\n- Agent: ' + str(agentid) + ' ' + agentname, "type": "text" } ] } ], }, } } # Send the request response = requests.post(hook_url, data=json.dumps(issue_data), headers=headers, auth=(user, api_key)) #print(json.dumps(json.loads(response.text), sort_keys=True, indent=4, separators=(",", ": "))) # <--- Uncomment this line for debugging sys.exit(0)
Conclusion
This article has set forth the basic guidelines for developing an external software integration with Wazuh, Integrator and using Jira as an example.
If you have any questions about how to integrate external software using Integrator, don’t hesitate to check out our documentation or you can contribute to the Wazuh community by making your own integrations and sharing them through our GitHub repository, where they will be appreciated.