< back to notes

Installing, setting up and testing Wazuh and Automating Agent installations

Introduction

I have ran Wazuh in the past to just ingest logs from multiple machines and perform simple searches, however, I’d like to revisit it as it’s been neglected over the last 2 years it’s been running.

I’d like to spin up a new instance and utilize more of the features the platform offers.

VM Setup

Wazuh has an installation script that automates the installation front to back, so I will be running that.

curl -sO https://packages.wazuh.com/4.14/wazuh-install.sh && sudo bash ./wazuh-install.sh -a

User setup

I’m going to create a seperate admin account to get rid of the default “admin account”:

  • Index Management>Security>Internal Users>Create Internal User
    • Backend roles: admin
  • Server Management>Security>Role mapping>Create role mapping
    • Roles: administrator
    • Map internal users: (the internal user just created)

Securing agent enrollment

  • Set use_password option under ossec_config>auth in /var/ossec/etc/ossec.conf to true
  • Set a password via echo "[password]" > /var/ossec/etc/authd.pass
  • Set permissions via chmod 640 /var/ossec/etc/authd.pass and chown root:wazuh /var/ossec/etc/authd.pass
  • Restart the manager via systemctl restart wazuh-manager

Enable log retention

I want to use Wazuh to index all logs collected, not just alerts.

In the /var/ossec/etc/ossec.conf file:

  • Set logall to yes under global
  • Set logall_json to yes under global

Edit the filebeat config at /etc/filebeat/filebeat.yml

  • Adjust the config to enable archives:
filebeat.modules:
  - module: wazuh
    alerts:
      enabled: true
    archives:
      enabled: false

Restart filebeat via systemctl restart filebeat

Create the index on the dashboard:

  • Dashboards Management>Dashboards Management>Index patterns>Create index pattern
    • Name: wazuh-archives-*
    • Time field: @timestamp

Enrolling first agent to test changes

The first agent will be on Windows, and will be installed via the following PowerShell commands:

Invoke-WebRequest -Uri "https://packages.wazuh.com/4.x/windows/wazuh-agent-4.14.7-1.msi" -OutFile "$env:tmp\wazuh-agent.msi"

msiexec.exe /i "$env:tmp\wazuh-agent.msi" /q `
  WAZUH_MANAGER="wazuh-01.example.invalid" `
  WAZUH_REGISTRATION_SERVER="wazuh-01.example.invalid" `
  WAZUH_REGISTRATION_PASSWORD="[password]" `
  WAZUH_AGENT_NAME="myLaptop"

Start-Service wazuhsvc

The agent has appeared in Wazuh.

Logs are now viewable in Explore>Discover>wazuh-archives-*

Basic query

Log a basic event to the Windows event log:

Write-EventLog -LogName Application -Source "Application" -EventId 9999 -EntryType Information -Message "Wazuh test event - $(Get-Date)"

After waiting for ~30 seconds, we should be able to see it by searching the archive:

agent.name : "[Agent Name]"  and data.win.eventdata.data : *Wazuh*
test event

And testing user account creation, deletion, group membership modifications on the Windows host, those had appeared as alerts as well.

Automated agent installation using Ansible

I have 50+ agents to install, obviously manual installation might take all afternoon, so Ansible is going to be beneficial here.

I asked Claude to assist in generating a playbook to:

  1. Uninstall existing Wazuh agent (if present)
  2. Clean up any existing agent data on the file system
  3. Install and start the Agent

It came up with the following:

Main Task File:

---
- name: Fail fast on non-Debian targets
  ansible.builtin.fail:
    msg: "This role currently only supports Debian/Ubuntu hosts. ansible_os_family={{ ansible_os_family }}"
  when: ansible_os_family != "Debian"

- name: Run Debian/Ubuntu agent install flow
  include_tasks: linux_debian.yml

Linux/Debian Task File:

---
- name: Check if wazuh-agent package is currently installed
  ansible.builtin.command: dpkg-query -W -f='${Status}' wazuh-agent
  register: wazuh_pkg_check
  failed_when: false
  changed_when: false

- name: Set fact for existing install
  ansible.builtin.set_fact:
    wazuh_already_installed: "{{ 'install ok installed' in wazuh_pkg_check.stdout }}"

- name: Stop existing wazuh-agent service (pre-uninstall)
  ansible.builtin.systemd:
    name: wazuh-agent
    state: stopped
  when: wazuh_already_installed
  failed_when: false

- name: Purge existing wazuh-agent package
  ansible.builtin.apt:
    name: wazuh-agent
    state: absent
    purge: true
  when: wazuh_already_installed

- name: Remove leftover /var/ossec directory
  ansible.builtin.file:
    path: /var/ossec
    state: absent
  when: wazuh_already_installed

- name: Install prerequisite packages for repo/key management
  ansible.builtin.apt:
    name:
      - gnupg
      - ca-certificates
      - apt-transport-https
    state: present
    update_cache: true

- name: Remove legacy apt-key managed Wazuh key (if present)
  ansible.builtin.apt_key:
    url: https://packages.wazuh.com/key/GPG-KEY-WAZUH
    state: absent
  failed_when: false
  ignore_errors: true

- name: Ensure keyrings directory exists
  ansible.builtin.file:
    path: /usr/share/keyrings
    state: directory
    mode: '0755'

- name: Download Wazuh GPG key
  ansible.builtin.get_url:
    url: https://packages.wazuh.com/key/GPG-KEY-WAZUH
    dest: /tmp/GPG-KEY-WAZUH
    mode: '0644'
    force: true

- name: Dearmor Wazuh GPG key into keyring
  ansible.builtin.command:
    cmd: gpg --yes --dearmor -o /usr/share/keyrings/wazuh.gpg /tmp/GPG-KEY-WAZUH
    creates: /usr/share/keyrings/wazuh.gpg

- name: Set permissions on Wazuh keyring
  ansible.builtin.file:
    path: /usr/share/keyrings/wazuh.gpg
    mode: '0644'

- name: Remove legacy Wazuh apt repository file (list format, unsigned)
  ansible.builtin.file:
    path: /etc/apt/sources.list.d/wazuh.list
    state: absent

- name: Add Wazuh apt repository (signed-by keyring)
  ansible.builtin.apt_repository:
    repo: "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main"
    filename: wazuh
    state: present

- name: Update apt cache
  ansible.builtin.apt:
    update_cache: true

- name: Install wazuh-agent with enrollment env vars
  ansible.builtin.apt:
    name: "wazuh-agent={{ wazuh_agent_version }}-*"
    state: present
  environment:
    WAZUH_MANAGER: "{{ wazuh_manager_ip }}"
    WAZUH_REGISTRATION_SERVER: "{{ wazuh_manager_ip }}"
    WAZUH_REGISTRATION_PASSWORD: "{{ wazuh_registration_password }}"
    WAZUH_AGENT_NAME: "{{ inventory_hostname }}"

- name: Reload systemd daemon
  ansible.builtin.systemd:
    daemon_reload: true

- name: Enable and start wazuh-agent
  ansible.builtin.systemd:
    name: wazuh-agent
    enabled: true
    state: started

For the first pass, I ran the playbook against ~20 hosts, and confirmed that logging was reaching Wazuh.

Syslog logging

Next I wanted the ability to monitor and decode FortiGate logs and generate alerts, by ingesting the syslog into Wazuh, however, I don’t want the Wazuh manager server listening for syslog directly, instead we will install a syslog server on a machine with the Wazuh agent along side it.

Setting up rsyslog

I ran the Ansible playbook to provision the agent on a new host, we will install rsyslog now

sudo apt install rsyslog -y

Create a new config file for rsyslog at /etc/rsyslog.d/01-wazuh-forward.conf:

module(load="imudp")
module(load="imtcp")

ruleset(name="networkOnly") {
    action(type="omfile" file="/var/log/syslogtowazuh.log")
    stop
}

input(type="imudp" port="514" ruleset="networkOnly")
input(type="imtcp" port="514" ruleset="networkOnly")

Restart rsyslog:

sudo systemctl restart rsyslog

Send syslog to server confirm syslog file exists:

logger -n localhost -P 514 -d "test message"

cat /var/log/syslogtowazuh.log
2026-08-10T22:20:52.423654+00:00 nsyslog-01 root test message

Create a logrotate at /etc/logrotate.d/wazuh-forward:

/var/log/syslogtowazuh.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        systemctl kill -s HUP rsyslog.service >/dev/null 2>&1 || true
    endscript
}

Test log rotation:

sudo logrotate -f /etc/logrotate.d/wazuh-forward
cat /var/log/syslogtowazuh.log

Setting up ossec to pick up logs

We will need to setup the agent to pick up the logs, and the easiest way to manage this with a lot of syslog forwarders, is to put all of the agents which are going to be fowarding syslog into a Agent Group in Wazuh.

Agents Management>Groups

  • Create a group and ensure agents are a member of that group
  • Edit the Agent Group for your syslog forwarders to pick up the log file:
<agent_config>
  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/syslogtowazuh.log</location>
  </localfile>
</agent_config>
  • Save
  • Wait a few moments

Send another test message and confirm you see it in the archive index.

Setting up FortiGate to log to the syslog server

Wazuh already has decoders built in for FortiGate, so all should need to be done is to setup syslog within the FortiGate:

config log syslogd setting
    set status en
    set server [the syslog server]
    set mode reliable
end

Watch the syslog flood into the server:

tail -f /var/log/syslogtowazuh.log

Important note to get traffic logs working

I had to adjust the built in FortiGate log decoders to be indexed, for some reason the decoders clash with Elastic Search’s service field.

I found this in the Filebeat log:

mapper_parsing_exception: object mapping for [data.service] tried to parse field [service] as object, but found a concrete value

I fixed this by renaming instances of service to service_name

root@wazuh-01:~# cp /var/ossec/ruleset/decoders/0100-fortigate_decoders.xml /var/ossec/ruleset/decoders/0100-fortigate_decoders.xml.bak
root@wazuh-01:~# grep -n "order>service<" /var/ossec/ruleset/decoders/0100-fortigate_decoders.xml
631:  <order>service</order>
1425:  <order>service</order>
root@wazuh-01:~# sed -i 's/<order>service<\/order>/<order>service_name<\/order>/' /var/ossec/ruleset/decoders/0100-fortigate_decoders.xml
root@wazuh-01:~# systemctl restart wazuh-manager

This is a modification to the built in rule set, so this may be overwritten during updates later on.

Conclusion

That’s it, Wazuh is up and running. It’s slowly being rolled out to all of the hosts on the network (again), and ingesting logging and creating alerts based off the built in rules.