In Wazuh software architecture we can clearly identify four layers, four levels of abstraction. These four layers, sorted from the lowest to the highest level, are: Core, Framework, API, and APP. In this article we will focus on the Framework layer and we will see how to use it to our advantage and how to write our own Wazuh scripts and APPs.

Wazuh software: Internal structure of Wazuh at abstraction layer level

What is the Wazuh Framework

The Wazuh Framework is an abstraction layer added on top of the Wazuh Core. It was created so that developers don’t have to worry about the low-level mechanics of our software like communication with all Wazuh daemons through Unix sockets. In this way, we can use it to develop more advanced features in a shorter-term. These are some of the main benefits we gain with our framework:

  • Allows performing complex operations easily: Wazuh Framework provides the basic functionality to support our distributed API and the cluster of managers. These features would be much harder to achieve without the tools we find in our framework.
  • Speed of development: As a result of the above, since we can work with a higher level of abstraction, we can also develop faster and deliver new features to the community in less time.
  • Security: Security in communications with the Wazuh Core is guaranteed, and all internal operations are performed safely and efficiently to avoid possible vulnerabilities in the code.
Wazuh software: What is the Wazuh framework

How embedded Python works

Python makes it possible to implement the functionalities of our application in Python instead of in C. But above all, it allows us to have all the necessary packages self-contained in the installation of Wazuh in order to eliminate dependencies and provide us with everything necessary for Wazuh to work. In addition, it makes the installation easier for the user.

Flowchart of internal communication Framework-Core

Make your own scripts with Wazuh Python

The first thing we’re going to do is check that the embedded python is working properly. To do this, we must go to the Wazuh installation directory, by default /var/ossec, and then run framework/python/bin/python3 --version. The result must be the following:

framework/python/bin/python3 --version
Python 3.7.2

It’s time for action. Let’s think of a possible development that could come in handy… How about eliminating agents by groups?

The first thing we are going to do is have a look at the code of our Framework. For our functionality, we will have to look inside the agents’ module, agent.py. Inside this, we can see that there is a function called get_agent_group. In its docstring, we can see all the agents that belong to a certain group, which must be passed by parameter.

Therefore we need a function that gives us a list of system groups to ensure that we work with a valid group. This function is get_all_groups. Once we have the agents of a group we need a function that removes them. This is nothing more and nothing less than remove_agent. Passing the ID of our agent by parameter will remove it.

With this in mind, we can start with our script. The first thing we’ll do is import the agent module. It is the agents module that we will use to interact and extract information from the agents of the system:

import argparse
from wazuh.agent import Agent

The next thing we’ll do is define a function that checks if a certain group exists, and if it doesn’t exist, it will notify us. We’ll do this with the get_all_groups method which returns all the groups in our system. Then we check that the group given by parameter is one of the groups obtained:

def check_group(group):
   groups = list()
   for group_info in Agent.get_all_groups()['items']:
      groups.append(group_info['name'])
   if group in groups:
      return True

   print('[INFO] The specified group does not exist')
   return False

Once we are certain that our group exists, we need the agents it contains. We will do this through the get_agent_group method of our Framework, which returns a dictionary where we can find information for all the agents that meet the condition of belonging to a given group:

def check_agents(group):
   agents = None
   if check_group(group):
      agents = Agent.get_agent_group(group)
      if agents['totalItems'] == 0:
         print('[INFO] Group {} do not have agents'.format(group))
         return False

   return agents

We already have everything. Not only do we know that our group exists and that it has agents, but we also know information about those agents. Now, we only have to eliminate them:

def delete_agents_in_group(group):
   deleted = list()
   agents = check_agents(group)
   if agents:
      agents = Agent.get_agent_group(group)
      for agent in agents['items']:
         Agent.remove_agent(agent['id'])
         deleted.append(agent['id'])
      return deleted

   return False

We’re almost done. We’ll just put everything together in our main function. Then, we’ll add the parameters of our scripts and finally check that it works:

if __name__ == '__main__':
   parser = argparse.ArgumentParser()
   parser.add_argument('-d', '--delete', type=str, default='', help='All the agents of the indicated group are going to be removed.')
   args = parser.parse_args()
   if args.delete == '':
      print('[ERROR] You need to specify the type of action (-d or --delete)')
   else:
      if args.delete != '':
         result = delete_agents_in_group(args.delete)
         if result:
            print('[INFO] Successful operation, the following agents have been removed: {}'.format(', '.join(result)))

The final moment, will it work?

This is our client.keys file before running our script:

adriiiprodri@Wazuh:/# cat /var/ossec/etc/client.keys 
001 5bb604b8284f any b10670e5c782ee173dd3899dde1019bf0b16e6a96be8c78b7c5621b68c6e095b
002 27575504f3b5 any 774a6e02c0f9c94029403cc2211235f0ba206d4097a49f4c95acd9c80444e972
003 c963e6a7692e any d38be9dc14179267a3e2974ddef638ad10f0a9b6e1faa0886d69130f36df7e3f
004 e95083978b44 any 214cd270d01b5b3b28a00720a0b4878161c55c131ea334df73e4db342e8ee0c5
005 7184251f0f6f any 9e25c20b510742dab4bd9d7c1efb00757f35b617ad33ce4facea2f12518f00af

Let’s test our script. We have a group called “testing” that contains four agents (001, 002, 003, 004). Let’s eliminate these agents while keeping the group:

adriiiprodri@Wazuh:/# /var/ossec/framework/python/bin/python3 /var/ossec/framework/python/bin/blog/operations_groups.py -d testing
[INFO] Successful operation, the following agents have been removed: 001, 002, 003, 004
adriiiprodri@Wazuh:/# cat /var/ossec/etc/client.keys 
005 7184251f0f6f any 9e25c20b510742dab4bd9d7c1efb00757f35b617ad33ce4facea2f12518f00af
adriiiprodri@Wazuh:/# curl -u foo:bar -X GET "https://localhost:55000/agents/groups/testing?pretty"
{
   "error": 0,
   "data": {
      "items": [],
      "totalItems": 0
   }
}

Try it out and see for yourself

As we can see, using the Wazuh Framework is a great idea because the tool provides us very interesting potential and functionalities without the need to program the most complex functions such as obtaining all the agents.

Of course all of the above is not only used to create scripts, but we can also build APPs for Wazuh following the same methodology that we explored in this post.

We encourage you to try this feature of Wazuh and show us your creations.

References

If you have any questions about Wazuh Scripts, don’t hesitate to check out our documentation to learn more about Wazuh or join our community where our team and contributors will help you.