Wazuh indexer stores the full configuration of its Security plugin as a live system index. This “indexer security state” is critical to indexer cluster operations and accessibility, yet it is frequently omitted in the backup and restore of the Wazuh indexer central component. Neither filesystem backups nor standard index snapshots are well-suited for backing up this critical information, the loss of which could lock you out of your index data or force you to reconfigure the indexer cluster security from scratch. Also, clear visibility into how this security state changes over time is key to accountability and auditability, but this insight is not by default in easy reach. I wrote a suite of scripts to facilitate the backup and restore of the Wazuh index security state, as well as to provide concise diff comparisons between security state backups. In this blog, I cover what the scripts do, how to deploy them, and how to put them to work.
What comprises the Wazuh indexer security state
These are the files that represent the security state of the Wazuh indexer cluster at install time:
- action_groups.yml – named bundles of permissions to which roles can refer
- allowlist.yml – optional allowlist for the indexer REST API (empty by default)
- audit.yml – configuration of the security audit logging module
- config.yml – configuration for authentication and authorization of end users and between indexer nodes
- internal_users.yml – the internal user database
- nodes_dn.yml – list of certificate DNs required for transport-level trust between indexer nodes
- roles.yml – definitions of roles and their permissions
- roles_mapping.yml – mappings of roles to users
- tenants.yml – multitenancy configuration details
- whitelist.yml – somewhat legacy file allowing certain endpoints to have unauthenticated access (unconfigured by default)
Where the security state is stored
At Wazuh indexer install time, the above files are put in the /etc/wazuh-indexer/opensearch-security directory and then pushed to the indexer cluster to initialize its security state into the .opendistro_security system index. Thereafter, these files are not automatically updated when security settings are changed. The live and authoritative security state is solely in the .opendistro_security system index, a full copy of which is separately retained on every indexer node. A full export of this security state is not feasible to do at the filesystem or indexer API levels. Also, while it is possible to perform an index snapshot that includes the .opendistro_security index, this is not included as part of the “global state” of the indexer cluster. Furthermore, relying on a direct snapshot of .opendistro_security for backup/restore purposes is fraught with issues and strongly discouraged.
Deploy the wazuh-security-* helper scripts
I have shared the following scripts with the Wazuh community to accommodate more convenient backup and restore of the indexer security state, as well as better accounting for changes to that state between backups:
- wi-security-backup – backs up the indexer security state to a date-coded directory corresponding to the current date
- wi-security-restore – restores the indexer security state, usually from a specific day’s backup
- wi-security-diff – compares the indexer state backups for two different days, and outputs a compact but detailed account for what is different, with redaction of hashes, keys, and other secrets.
To deploy these on a Wazuh indexer:
sudo wget https://raw.githubusercontent.com/BlueWolfNinja/wazuh-resources/refs/heads/main/wi-security-backup -O /usr/local/bin/wi-security-backup
sudo wget https://raw.githubusercontent.com/BlueWolfNinja/wazuh-resources/refs/heads/main/wi-security-restore -O /usr/local/bin/wi-security-restore
sudo wget https://raw.githubusercontent.com/BlueWolfNinja/wazuh-resources/refs/heads/main/wi-security-diff -O /usr/local/bin/wi-security-diff
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/local/bin/yq
sudo chmod 744 /usr/local/bin/wi-security-{backup,restore,diff} /usr/local/bin/yq
This includes installing a static binary of the latest version of the yq tool for manipulating yaml data, which wi-security-diff depends on. The snap-installed yq package is not compatible with wi-security-diff.
Back up the security state
Simply run the following to back up the indexer security state to the /etc/wazuh-indexer/opensearch-security/YYYY-MM-DD/ directory corresponding to the current date.
sudo wi-security-backup
Automate daily backups
This can easily be done with a cron file like /etc/cron.d/wi-security-backup that looks like this:
0 1 * * * root /usr/local/bin/wi-security-backup &> /var/log/wi-security-backup.log
Restore the security state
To restore the current Wazuh indexer security state to what it was like as of the YYYY-MM-DD backup, run this, replacing <YYYY-MM-DD>
sudo wi-security-restore --date <YYYY-MM-DD>
In a more complex restore scenario, you could copy files from one or more /etc/wazuh-indexer/opensearch-security/YYYY-MM-DD/ directories to the top level backup directory /etc/wazuh-indexer/opensearch-security/, optionally making further manual edits to the files therein. Then you could restore from there by simply not specifying a date:
sudo wi-security-restore
Make sure all elements of the hybrid/modified configuration are consistent with each other. For example, a configuration directory containing a local user assigned a backend role that is not defined in the same directory would cause the restore to fail.
Security of your security state backups
While the security state information obviously has general blackhat reconnaissance value, the most common sensitive elements would be the hashed passwords of internal users. They are salted, but still represent a password-cracking risk unless you have assurance that all of your internal users have adequately complex passwords—something Wazuh does not enforce. Other secrets or keys, most likely involving advanced authentication methods and found in config.yml should also be carefully guarded. Store and restrict access to these backups accordingly.
Automatic deletion of old security state backups
If you wish to limit the total number of indexer security states to retain, consider adding this cron one-line to /etc/cron.d/wi-security-backup
30 0 * * * root find /etc/wazuh-indexer/opensearch-security -mindepth 1 -maxdepth 1 -type d -name '20[0-9][0-9]-[0-1][0-9]-[0-3][0-9]' -mtime +30 -exec rm -rf {} +
This will daily recursively delete all directories of the format /etc/wazuh-indexer/opensearch-security/
Comparing backups
Run wi-security-diff with a pair of dates corresponding to existing security state backups, and each of the security state files will be separately compared.
If there are no material differences between two backups, the output is empty and the return code is 0.
root@server:~# sudo wi-security-diff 2026-02-04 2026-02-05
root@server:~# echo $?
0
Otherwise, concise diff-like details will be printed and the return code will be 2.
root@server:~# sudo wi-security-diff 2026-01-21 2026-02-05
## config.yml (2026-01-21 -> 2026-02-05)
+ config.config.dynamic.authc.kerberos_auth_domain.http_enabled=true
============================================================
## internal_users.yml (2026-01-21 -> 2026-02-05)
- internal_users.admin.backend_roles.0=admin
- internal_users.admin.description=Demo admin user
+ internal_users.admin.description=super cool Demo admin user
- internal_users.netmon.hash=<REDACTED>
+ internal_users.netmon.hash=<REDACTED>
- internal_users.peter.backend_roles.0=admin
+ internal_users.peter.backend_roles.0=amazing
+ internal_users.surprise.backend_roles.0=admin
+ internal_users.surprise.hash=<REDACTED>
============================================================
## roles_mapping.yml (2026-01-21 -> 2026-02-05)
- roles_mapping.kibana_user.users.1=wazuh_admin
+ roles_mapping.kibana_user.users.1=wazuh_badmin
- roles_mapping.kibana_user.users.2=rouser
root@server:~# echo $?
2
You can also replace one of the two compare date parameters with a single period to represent the top level backup directory /etc/wazuh-indexer/opensearch-security/.
The script makes an effort to redact hashes, secrets, and keys. Review the source code to confirm it covers all of your redaction requirements.
Consider automating this script to be run each day after the daily security state backup, comparing the current day’s backup to the previous day’s backup. If the return code is 2, then email the output to an appropriate party for review.
Thanks for reading!
I would welcome your feedback on this blog or contributions to improve the value of the helper scripts. It would be nice if the wi-backup-diff script did not have to depend on a static yq binary but could be generalized to work across any modern Linux installation of yq without file permission issues. It would also be great to expand on the use of wi-backup-diff to produce a stream of Wazuh-friendly log output so that different kinds of security state changes could be alerted on by Wazuh with the help of a custom decoder and rule family. Please share your ideas or your own custom expansions on what I’ve shared here.
Also, if you are in need of direct expert Wazuh engineering assistance, I invite you to inquire of me by email, or click on the “Book a Call” link at the top of this page. I’d love to hear from you!



