Files
computer-playbook/roles/web-app-wazuh/README.md
Prageeth Panicker 381d7862a8 docs(web-app-wazuh): note that the vulnerability data is not backed up
The manager's vulnerability detection data (queue/vd) is deliberately excluded from the backup and downloaded again after a restore.
2026-10-16 11:28:09 +00:00

8.0 KiB

Wazuh

Description

Wazuh is an open-source SIEM and XDR platform providing security monitoring, log analysis, intrusion and vulnerability detection, and compliance support.

Overview

This role deploys the single-node Wazuh stack (wazuh.manager, wazuh.indexer, wazuh.dashboard) with Docker Compose or Docker Swarm. A one-shot wazuh-certs-generator job creates the internal TLS material before the manager and the indexer start. The indexer is the stack's own datastore and holds the OpenSearch security configuration, so the role needs no shared database. Agent enrollment is not part of this role: the agent ports are declared in services.yml but not published. The manager's vulnerability detection data (/var/ossec/queue/vd, volume wazuh_manager_vd) is deliberately not backed up. The manager downloads it again after a restore, which can take a long time. This matters once agents are enrolled.

Cosmos

The diagram places Wazuh in the Infinito.Nexus cosmos: the components it deploys (capabilities), the central services it consumes (dependencies), and its outward reach (federation and bridged external networks).

flowchart LR
    subgraph deps [Dependencies]
        dep_svc_bkp_volume_2_local["svc-bkp-volume-2-local 💻"]
        dep_svc_db_openldap["svc-db-openldap 🐳🐝"]
        dep_svc_net_tor["svc-net-tor 🐳🐝"]
        dep_web_app_dashboard["web-app-dashboard 🐳🐝"]
        dep_web_app_keycloak["web-app-keycloak 🐳🐝"]
        dep_web_app_matomo["web-app-matomo 🐳🐝"]
        dep_web_app_prometheus["web-app-prometheus 🐳🐝"]
        dep_web_app_stalwart["web-app-stalwart 🐳🐝"]
        dep_web_svc_css["web-svc-css 💻"]
        dep_web_svc_logout["web-svc-logout 🐳🐝"]
    end
    subgraph role [web-app-wazuh 🐳🐝]
        svc_email["email ❌"]
        svc_sso["sso"]
        svc_ldap["ldap"]
        svc_logout["logout"]
        svc_dashboard["dashboard"]
        svc_matomo["matomo"]
        svc_prometheus["prometheus"]
        svc_css["css"]
        svc_container_backup["container_backup"]
        svc_wazuh["wazuh"]
        svc_certs_generator["certs_generator"]
        svc_manager["manager"]
        svc_indexer["indexer"]
        svc_console["console"]
        svc_tor["tor"]
    end
    dep_svc_bkp_volume_2_local -. "0..1" .-> svc_container_backup
    dep_svc_db_openldap -. "0..1" .-> svc_ldap
    dep_svc_net_tor -. "0..1" .-> svc_tor
    dep_web_app_dashboard -. "0..1" .-> svc_dashboard
    dep_web_app_keycloak -. "0..1" .-> svc_sso
    dep_web_app_matomo -. "0..1" .-> svc_matomo
    dep_web_app_prometheus -. "0..1" .-> svc_prometheus
    dep_web_app_stalwart -- "0..0" --> svc_email
    dep_web_svc_css -. "0..1" .-> svc_css
    dep_web_svc_logout -. "0..1" .-> svc_logout
    linkStyle 7 stroke:red;

Solid 1:1 edges are fixed relationships; dashed 0..1 edges are conditional (enabled only in matching deployments); red 0..0 edges are turned off in this role. Node markers show the role's deploy modes (💻 host, 🐳 compose, 🐝 swarm); ❌ marks a service that is explicitly turned off, and ⚙️ an Ansible role dependency declared in meta/main.yml.

Features

  • Single-node Wazuh stack: Manager, indexer, and dashboard at the versions pinned in services.yml.
  • Native OIDC login: The dashboard signs users in against Keycloak's discovery endpoint, without an oauth2-proxy sidecar.
  • LDAP authorization: LDAP group membership adds backend_roles to an already authenticated user and is never used to verify a password.
  • Three-tier RBAC: Administrator, Security Analyst, and Read-only Auditor, declared in rbac.yml and mapped onto OpenSearch security roles.
  • Generated credentials: Every service account password and the manager cluster key come from secrets.yml.

Quick Setup

Development

Clone, set up the workstation, and deploy Wazuh onto the local stack:

git clone https://github.com/infinito-nexus/core.git
cd core
make onboard
make compose-deploy mode=reinstall apps=web-app-wazuh full_cycle=false

Production

Run the published image to provision the inventory and deploy Wazuh to a managed server (the mounted volume persists the inventory):

APP=web-app-wazuh
HOST=<your-server>
DOMAIN=<your-domain>
TLS_MODE=self_signed
SSH_PUBLIC_KEY="<your-ssh-public-key>"

docker run --rm -it \
  -v "$PWD/inventories:/etc/infinito.nexus/inventories" \
  -e APP="$APP" -e HOST="$HOST" -e DOMAIN="$DOMAIN" -e TLS_MODE="$TLS_MODE" -e SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" \
  ghcr.io/infinito-nexus/core/debian bash -c '
    INVENTORY=/etc/infinito.nexus/inventories/production
    infinito administration inventory provision "$INVENTORY" \
      --inventory-file "$INVENTORY/devices.yml" \
      --host "$HOST" \
      --include "$APP" \
      --vars "{\"TLS_MODE\": \"$TLS_MODE\", \"DOMAIN_PRIMARY\": \"$DOMAIN\", \"users\": {\"administrator\": {\"authorized_keys\": [\"$SSH_PUBLIC_KEY\"]}}}" &&
    infinito administration deploy dedicated "$INVENTORY/devices.yml" \
      --password-file "$INVENTORY/.password" \
      --diff -vv'

Auth flow

sequenceDiagram
    actor U as Browser (biber)
    participant D as wazuh.dashboard
    participant I as wazuh.indexer (OpenSearch Security)
    participant K as Keycloak
    participant L as LDAP (authz only)

    U->>D: GET / (no session)
    D->>I: forward request
    Note over I: basic_internal_auth_domain, order 0<br/>no Authorization header, falls through
    Note over I: openid_auth_domain, order 1, challenge true
    I-->>D: 401, redirect to Keycloak
    D-->>U: redirect to Keycloak login
    U->>K: submit credentials
    K-->>U: redirect back with auth code
    U->>D: callback with auth code
    D->>K: exchange code for tokens
    K-->>D: id_token, groups claim, full.path=true
    D->>I: authenticated request, bearer token
    Note over I: subject_key=preferred_username<br/>roles_key=groups maps to backend_roles
    I->>L: enrich backend_roles via ldap_authz_domain<br/>CN lookup, skips admin and kibanaserver
    L-->>I: additional backend_roles, if matched
    Note over I: roles_mapping.yml maps backend_roles<br/>to all_access, security_analyst, or readonly_auditor
    I-->>D: authorized response
    D-->>U: authenticated dashboard, RBAC-scoped

The internal admin and kibanaserver service accounts authenticate with HTTP basic auth against internal_users.yml and bypass OIDC and LDAP. With sso disabled, these two accounts are the only way to sign in.

RBAC

Role OpenSearch role backend_roles (OIDC) backend_roles (LDAP)
Administrator all_access /roles/web-app-wazuh/administrator web-app-wazuh-administrator
Security Analyst security_analyst /roles/web-app-wazuh/security-analyst web-app-wazuh-security-analyst
Read-only Auditor readonly_auditor, kibana_read_only /roles/web-app-wazuh/readonly-auditor web-app-wazuh-readonly-auditor
  • Grant a role by adding the user to the matching Keycloak group or LDAP group.
  • A group change applies on the user's next request, because the indexer's auth cache is disabled.
  • Only Administrators reach the OpenSearch Dashboards Security app.
  • Every role lands on /app/home after login.

Developer Notes

Variant matrix: variants.yml. Service flags and image pins: services.yml. Credentials: secrets.yml. RBAC roles: rbac.yml.

Further Resources

Credits

Implemented by Prageeth Panicker. Part of the Infinito.Nexus Project and maintained by Kevin Veen-Birkenbach. Licensed under the Infinito.Nexus Community License (Non-Commercial).