Blog archive

Manage Wazuh Manager Configuration with Ansible (Part 3)

How I managed Wazuh manager ossec.conf using inventory-owned files, local wrapper roles, and a controlled restart flow.

Published · Republished on Medium

WazuhAnsibleDevOpsSecurityInfrastructure

Picture by Elina Emurlaeva on Unsplash

This article continues from Part 2: Integrating Wazuh SAML Authentication with Keycloak, where I covered Keycloak realm roles, SAML backend roles, and Wazuh RBAC mapping.

In this part, I will focus on Wazuh manager configuration.

After the base Wazuh deployment is running, the next thing I want to control is the manager-side ossec.conf.

The goal is simple: instead of editing the Wazuh manager configuration manually on the server, I want the configuration to be stored in the Ansible repository and applied consistently.

Version note: configuration sections and validation behavior can change between Wazuh releases. Review the ossec.conf reference for the same pinned version used by the manager before promoting a change.

Why Manage ossec.conf with Ansible?

The Wazuh manager is the core component that receives agent events, processes rules, manages alerts, and controls many runtime behaviors.

Its main configuration file is:

bash

/var/ossec/etc/ossec.conf

Editing this file manually works for quick testing, but it is not ideal for a maintainable setup.

Manual changes are hard to track, allow drift between hosts, make rollback ambiguous, and turn rebuilding the server into a memory exercise.

For me, non-secret configuration should live in the repository. An ossec.conf can contain API keys, webhook URLs, enrollment settings, or other sensitive integration values; render those from Ansible Vault or an external secret manager, prevent them from appearing in diffs and logs, and restrict the resulting file on the target.

Ansible gives a better flow:

text

repository config -> copy/template to target -> validate -> restart manager

This makes manager configuration repeatable and easier to review.

Repository Layout

The manager configuration is stored under the inventory-owned files directory.

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

This follows the same pattern from Part 1.

Environment-specific files should live inside the inventory, not be hidden inside a generic role.

The role should handle how to deploy the file, but the inventory owns what the file contains.

The separation looks like this:

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

This makes ownership clear.

If I want to update the Wazuh manager configuration, I update the inventory file first, then let Ansible apply it.

Example Structure

The relevant structure looks like this:

bash

inventories/lab/files/wazuh
└── config
    └── manager
        └── ossec.conf

If the repository later supports more environments, each environment can have its own manager config.

Example:

bash

inventories
├── lab
│   └── files/wazuh/config/manager/ossec.conf
├── staging
│   └── files/wazuh/config/manager/ossec.conf
└── production
    └── files/wazuh/config/manager/ossec.conf

That gives flexibility without changing the deployment role.

The role only needs to know which inventory is being used.

Ansible Role Flow

I use a local wrapper role to manage the manager config.

Example role name:

bash

roles/wazuh_manager_config

The role flow is simple:

text

1. Check source config exists on controller
2. Render a candidate without logging secret values
3. Check XML structure and required policy constraints
4. Back up the current runtime file with metadata
5. Install the candidate with root:wazuh ownership
6. Run the Wazuh daemon-specific configuration tests
7. Restart wazuh-manager only when config changes
8. Verify processes, API, agent connectivity, and alert flow
9. Restore the previous file automatically if acceptance fails

The playbook can stay thin:

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

The playbook only describes the intent.

The role handles the actual implementation.

Copying the Manager Config

The role copies the repository-owned file into the runtime path.

Conceptually:

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

This keeps the source path tied to the selected inventory.

If I run the playbook with inventories/lab/host.ini, the role uses the lab-owned config.

If I later run it with staging or production inventory, the same role can use a different source file.

Restart Handler

The manager should only restart when the config changes. A handler is useful, but delaying it until the end of a long play can leave validation and the service transition far apart. Flush the handler at an intentional maintenance boundary, then run acceptance checks immediately.

That is why I use a handler:

Screenshot from Wazuh Ansible Series Part 3: Managing Wazuh Manager Configuration with Ansible

This avoids unnecessary restarts when there is no config change.

For Wazuh, this matters because manager restart can temporarily affect event processing and agent communication. In a manager cluster, deploy serially and verify cluster synchronization before moving to the next node; do not restart every manager at once.

Validation Before Restart

Before restarting the manager, validate as much as the installed version supports. XML parsing catches malformed structure, but valid XML can still contain invalid Wazuh options. Wazuh documents checks for individual consumers of the runtime configuration:

bash

/var/ossec/bin/wazuh-analysisd -t
/var/ossec/bin/wazuh-logcollector -t
/var/ossec/bin/wazuh-modulesd -t
/var/ossec/bin/wazuh-syscheckd -t

Run the checks relevant to the sections you changed and fail on any non-zero exit. These tools inspect the installed runtime configuration, so the safe flow is: verify the candidate XML, create a protected backup, atomically install the candidate, run the daemon tests, and restore the backup before any restart if a test fails. An Ansible block with rescue and always makes that recovery path explicit.

There is no single test in the current reference that proves every ossec.conf section is operational. A successful syntax check is a gate, not the final acceptance test.

Example Deployment Command

The playbook can be executed like this:

bash

ansible-playbook -i inventories/lab/hosts.ini playbooks/wazuh-manager-config.yml --diff

Use --diff only when the rendered file cannot disclose secrets. After the playbook runs, verify the systemd unit and the individual Wazuh processes:

bash

systemctl is-active wazuh-manager
/var/ossec/bin/wazuh-control status

Or check logs:

bash

journalctl -u wazuh-manager -n 100 --no-pager

The manager merely starting is not enough. Confirm that expected agents reconnect, the Wazuh server API responds, a controlled test event reaches analysis, a resulting alert reaches the indexer, queues and error rates remain normal, and a second Ansible run reports no unintended change.

What Should Be Managed Here?

This article focuses only on manager-side configuration.

Examples include global manager settings, agent communication, log collection, integrations, vulnerability detection, and active response. Each change carries different risk: an invalid <remote> section can affect agent traffic, verbose event archiving can consume substantial disk, and active response can execute actions across endpoints. Review behavior and capacity, not just XML validity.

But I intentionally keep some things separate.

Custom suppression rules should not be mixed into the manager ossec.conf article.

They deserve their own flow and their own file ownership model.

For example:

text

Manager config:
inventories/lab/files/wazuh/config/manager/ossec.conf

Keeping those separate makes the repository easier to understand.

Managing Wazuh manager configuration with Ansible makes the setup easier to maintain.

Instead of editing /var/ossec/etc/ossec.conf manually, the source of truth lives in the repository:

bash

inventories/lab/files/wazuh/config/manager/ossec.conf

The Ansible role is responsible for copying, validating, and restarting the manager safely.

The key idea is simple:

config

inventory owns the config
role owns the deployment logic
playbook owns the execution flow

This keeps the Wazuh manager configuration predictable and easier to review.

In the next part, I will cover Wazuh agent configuration profiles, including how to manage different ossec.conf files for different host roles using Ansible.

References