dark detection with the wazuh alerting plugin image

Information Lifecycle Management (ILM) in the Wazuh SIEM is usually achieved by setting up Index State Management (ISM) policies and applying them to Wazuh indexes.  An ISM policy consists of one or more states through which a “policy managed” index will pass according to its age.  For example, a policy could be applied to wazuh-alerts-* indexes, that transitions them from a hot to a warm state at 30 days old by moving them from hot-tier to warm-tier indexer nodes, and then at 180 days old, transitions them to a deleted state by removing them entirely.  

That is the plan, but I have repeatedly encountered cases where past indexes, though managed by the intended ISM policy, were not transitioned out of their original ISM state even though they had reached and exceeded the age criteria in the policy. Re-indexing turns out to be the unexpected cause, and the effect can be an indexer cluster running out of shards or disk space due to older indexes not being deleted when intended.  The solution requires a different approach to Wazuh ILM.

Re-indexing throws ISM-based ILM a fatal curveball

At times it is necessary to re-index some or many of your Wazuh indexes to perform actions like:

  • Resolve critical mapping conflicts
  • Change the shard count of indexes
  • Retroactively transform field data

This re-indexing process creates a new index from an old one, setting the new index’s immutable creation_date attribute to the time when the new index is created, not the time when the original index was created.  The Wazuh ISM facility uses an index’s creation_date setting to determine its age, which means a year-old index that was recently re-indexed will look to an ISM policy like it has a young age, greatly skewing the intended timing of the ISM policy applied to it.

This is a fundamental shortcoming that Wazuh inherits from the OpenSearch ISM facility, and there is as yet no way to make an ISM policy accurately recognize the true age of re-indexed indexes. Fortunately, ISM is not the only way to achieve ILM of Wazuh indexes.

Curator-based ILM to the rescue

Curator is a powerful tool for managing Elasticsearch indexes, especially via automation. While not directly compatible with Wazuh or OpenSearch, the curator-opensearch project forked Elasticsearch’s Curator (version 5.8.4) and made it compatible with OpenSearch and thus also with Wazuh. Hereafter, I will mostly refer to curator-opensearch as simply Curator.

Curator empowers us via yaml job files, to implement most of the same kind of age-based actions taken by ISM policies, but unlike ISM, curator jobs can be configured to calculate index age based on the date suffix in the index name rather than by the creation_date attribute. Thus, Curator-based ILM can factor in the original age of re-indexed indexes, allowing us to fire off our intended ILM actions at the right time, even for re-indexed indexes.

Implementing Curator

Curator is fairly easy to implement on Wazuh SIEMs as an alternative to ISM, though this is accomplished at the CLI level instead of in Wazuh Dashboard. It is generally best to install it on the Wazuh indexer node of your choice, though this is not required. Python 3 is required, and a pip install of curator-opensearch will pull in all the necessary dependencies. In this blog, I provide an example of installing curator-opensearch in a Python virtual environment on an Ubuntu 24.04 server, with a recurring Curator job to delete indexes more than 365 days old. Fairly small variations on the following steps should make the same process work on different flavors and versions of Linux.

Unapply ISM policies and disable ISM plugin

To avoid any conflicts between ISM and Curator, bring all wazuh-alerts-* indexes out from under ISM management, and persistently disable the ISM plugin itself. It is best not to mix ISM and Curator functionality on the same Wazuh SIEM.

Navigate to ☰ ➜ Indexer management ➜ Dev Tools and execute these two API calls:

POST _plugins/_ism/remove/wazuh-alerts-*

PUT /_cluster/settings
{
  "persistent": {
    "plugins.index_state_management.enabled": "false"
  }
}'

Safely install curator-opensearch

This approach allows the many dependencies of curator-opensearch to be met without risk of creating conflicts with other Python-based programs installed on the same system that require different versions of those same dependencies. Run the following as root.

apt install python3-virtualenv
mkdir /venv
cd /venv
virtualenv curator
source curator/bin/activate
pip3 install curator-opensearch
deactivate
sed -i 1d /venv/curator/bin/curator /venv/curator/bin/curator_cli
sed -i '1i#!/venv/curator/bin/python3' /venv/curator/bin/curator /venv/curator/bin/curator_cli
ln -fs /venv/curator/bin/curator /usr/local/bin/curator
ln -fs /venv/curator/bin/curator_cli /usr/local/bin/curator_cli

Connect Curator to Wazuh Indexer

Create the file /root/.curator/curator.yml with this content:

client:
  hosts:
    - >>>INDEXER NAME<<<
  port: 9200
  url_prefix:
  use_ssl: True
  certificate:
  client_cert:
  client_key:
  ssl_no_validate: True
  http_auth: >>>USERNAME<<<:>>>PASSWORD<<<<
  timeout: 30
  master_only: False
logging:
  loglevel: INFO
  logfile: /var/log/curator.log
  logformat: default
  blacklist: ['elasticsearch', 'urllib3']

Replace the following in the above file

  • >>>INDEXER NAME<<< with the name or IP of your Wazuh indexer for Curator to manage
  • >>>USERNAME<<< with an admin-privileged Wazuh indexer username like “admin”
  • >>>PASSWORD<<<< with the password for the above username

Save your changes and exit the file.

Test Curator

Confirm that Curator is working by requesting an interactive listing of all Wazuh indexes.

/usr/local/bin/curator_cli show-indices

If the output consists of a clean index list, then continue. Otherwise revisit the curator.yml file, revise it, and retest until you get things working.

Configure a Curator job

To make Curator delete year-old indexes, create a file /root/.curator/prune.yml containing the following:

---
actions:
  1:
    action: delete_indices
    description: "Delete old alerts indices"
    options:
      ignore_empty_list: True
      disable_action: False
      timeout_override: 300
      continue_if_exception: False
    filters:
    - filtertype: pattern
      kind: prefix
      value: wazuh-alerts-
      exclude:
    - filtertype: age
      source: name
      direction: older
      timestring: '%Y.%m.%d'
      unit: days
      unit_count: 365
      exclude:

Test the Curator job

Make sure you really do want to delete all wazuh-alerts-* indexes that are more than a year old, adjusting the job if you want something different.  Also make sure you have a recent snapshot of all wazuh-alerts-* indexes.  Only after attending to these things, should you manually run the Curator job.  This may delete indexes!

curator /root/.curator/prune.yml

Watch the output of the above job execution, and examine further log output in /var/log/curator.log. If it appears to have run as intended, then you are ready to automate it.

Set up cron to run this job daily

Create /etc/cron.d/prune-indices to run this job daily at 5 minutes after midnight.

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Once per day, prune old indices from Wazuh-Indexer
5 0 * * * root curator /root/.curator/prune.yml

Remember to restart cron

systemctl restart cron

For further consideration

Curator is capable of much more than just deleting old indexes at the right time. It has a feature set similar to that of the ISM facility. For example:

alias             Add/Remove index(es) to/from an alias
allocation        Apply shard allocation filtering rules to indexes
close             Close index(es)
cluster_routing   Apply routing rules to the entire cluster
create_index      Create an index
delete_indices    Delete index(es)
delete_snapshots  Delete snapshot(s)
forcemerge        Force-merge index(es) to reduce segment count
freeze            Freeze index(es)
index_settings    Change settings for index(es)
open              Open index(es)
reindex           Re-index index(es)
replicas          Change replica count of index(es)
restore           Restore index(es) from a snapshot
rollover          Rollover index associated with alias
shrink            Shrink index(es) to have fewer shards
snapshot          Snapshot index(es) to a repository
unfreeze          Unfreeze index(es)

Read here for details about the broader set of actions Curator can take.

Thanks for reading!

When OpenSearch forked Elasticsearch, the original Elasticsearch ILM facility was excluded due to licensing restrictions. The OpenSearch team ultimately developed their own ISM solution, which has matured quite a bit in the last few years, even exceeding in flexibility and functionality Elasticsearch’s own ILM in some areas. One final area of immaturity in OpenSearch ISM which long ago was fixed in Elasticsearch ILM, is the inability to perform ISM actions based on the original age of re-indexed indexes. I’m sure eventually OpenSearch will fix this gap in ISM, making it the superior choice for automated Wazuh ILM in most cases. In the meantime, we can be thankful that the curator-opensearch project has given us a tool that can meet our needs in this area right now. Having performed extensive re-indexing on most of my clients’ Wazuh indexer clusters, this solution has met my own ILM needs quite well over the last year or so. I hope it proves useful to you as well.

As always, you are welcome to comment with general questions or to inquire of me by email about possible direct private assistance related to this or other Wazuh issues. I’d love to hear from you!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top