Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Content Manager settings

The Content Manager plugin is configured through settings in opensearch.yml. All settings use the plugins.content_manager prefix.

  • plugins.content_manager.cti.api (String, default https://api.pre.cloud.wazuh.com/api/v1) — base URL for the Wazuh CTI API. Covers both the catalog endpoints (snapshots, changes, plans) and the CTI Console ones (instance registration, environment lookup, resource-token exchange).

  • plugins.content_manager.catalog.sync_interval (Integer, default 60, range 10–1440, dynamic) — interval, in minutes, between scheduled synchronizations.

  • plugins.content_manager.setup_wait.max_retries (Integer, default 4, range 0–10) — number of retries the catalog sync job performs while waiting for the Setup plugin to report readiness on startup, before giving up until the next scheduled sync.

  • plugins.content_manager.setup_wait.backoff_base_seconds (Integer, default 20, range 1–120) — base delay, in seconds, for the exponential backoff between those retries (delay for retry n is base * 2^n; with the defaults, 20s/40s/80s/160s = 300s / 5 min worst case).

  • plugins.content_manager.max_items_per_bulk (Integer, default 1000, range 10–1000) — maximum documents per bulk indexing request.

  • plugins.content_manager.max_concurrent_bulks (Integer, default 5, range 1–5) — maximum concurrent bulk operations.

  • plugins.content_manager.max_bulk_bytes (Long, default 5242880 / 5 MB, range 1048576–104857600 / 1–100 MB) — maximum request body size, in bytes, for a single bulk indexing request.

  • plugins.content_manager.logtest.max_body_bytes (Long, default 1048576 / 1 MiB, range 1024–16777216 / 1 KiB–16 MiB, dynamic) — maximum size, in bytes, of a logtest request body (POST /logtest, /logtest/normalization, /logtest/detection). Requests whose body exceeds this are rejected with HTTP 413 at the REST layer, before parsing or dispatch, so an oversized event cannot be amplified into the response and exhaust the indexer’s heap.

  • plugins.content_manager.client.timeout (Long, default 10, range 10–50) — HTTP client timeout in seconds for CTI API requests.

  • plugins.content_manager.client.max_retries (Integer, default 3, range 0–10) — number of times a CTI API request is retried after an HTTP 429 (Too Many Requests) response, before the 429 is returned to the caller.

  • plugins.content_manager.client.retry_backoff_base_seconds (Integer, default 30, range 1–300) — base delay, in seconds, for the exponential backoff used between 429 retries when the response carries no usable Retry-After header (delay for retry n is base * 2^n).

  • plugins.content_manager.pit_keepalive (Long, default 120, range 60–600) — point-in-time keepalive in seconds used during paginated index scans.

  • plugins.content_manager.engine.mock (Boolean, default false) — bypasses real Engine socket calls, returning mocked responses instead. Intended for testing only.

  • plugins.content_manager.catalog.update_on_start (Boolean, default true) — trigger content sync when the plugin starts.

  • plugins.content_manager.catalog.update_on_schedule (Boolean, default true, dynamic) — enable the periodic sync job. When false the job is disabled and any scheduled run is refused, so no content is fetched from Wazuh CTI on a schedule. On-demand updates (POST /update) are unaffected — they are controlled by plugins.content_manager.catalog.update_on_demand.

  • plugins.content_manager.catalog.ruleset (String, default "") — full CTI consumer URL for ruleset content.

  • plugins.content_manager.catalog.iocs (String, default "") — full CTI consumer URL for IoC content.

  • plugins.content_manager.catalog.vulnerabilities (String, default "") — full CTI consumer URL for vulnerabilities content.

  • plugins.content_manager.catalog.create_detectors (Boolean, default true) — automatically create Ruleset Management detectors from CTI content.

  • plugins.content_manager.telemetry.enabled (Boolean, default true, dynamic) — enable or disable the daily Update check service ping.

  • plugins.content_manager.catalog.update_on_demand (Boolean, default true) — when false, on-demand content updates (POST /update) return 403 Forbidden for every caller, regardless of role.

  • plugins.content_manager.catalog.policy_update.enabled (Boolean, default true) — when false, policy updates (PUT /policy/{space}) return 403 Forbidden for every caller, regardless of role.

  • plugins.content_manager.max_integrations (Integer, default 100, minimum 0, no upper bound, dynamic) — maximum number of integrations that can be created. Requests that would exceed this limit are rejected with HTTP 400.

  • plugins.content_manager.max_decoders (Integer, default 200, minimum 0, no upper bound, dynamic) — maximum number of decoders that can be created. Requests that would exceed this limit are rejected with HTTP 400.

  • plugins.content_manager.max_rules (Integer, default 200, minimum 0, no upper bound, dynamic) — maximum number of rules that can be created. Requests that would exceed this limit are rejected with HTTP 400.

  • plugins.content_manager.max_kvdbs (Integer, default 100, minimum 0, no upper bound, dynamic) — maximum number of KVDBs that can be created. Requests that would exceed this limit are rejected with HTTP 400.

  • plugins.content_manager.max_filters (Integer, default 100, minimum 0, no upper bound, dynamic) — maximum number of filters that can be created per space. Requests that would exceed this limit are rejected with HTTP 400.

  • plugins.content_manager.cti.console.timeout (Integer, default 5, range 1–120) — request timeout, in seconds, for CTI Console calls (instance registration, plans, token exchange). The catalog client has its own timeout in plugins.content_manager.client.timeout; the Console endpoints resolve against plugins.content_manager.cti.api, so moving that setting moves both services.

  • plugins.content_manager.bulk.retry.shed.max_retries (Integer, default 3, range 0–10, dynamic) — number of times a bulk operation the cluster shed under load (circuit breaker trip, indexing-pressure rejection, HTTP 429/503) is re-submitted before its documents are counted as dropped.

  • plugins.content_manager.bulk.retry.shed.initial_backoff_millis (Long, default 1000, range 100–60000, dynamic) — delay, in milliseconds, before the first re-submission of a shed bulk operation. Each subsequent retry doubles it, capped at the max backoff.

  • plugins.content_manager.bulk.retry.shed.max_backoff_millis (Long, default 30000, range 100–60000, dynamic) — ceiling, in milliseconds, for the exponential backoff between shed bulk re-submissions.

  • plugins.content_manager.bulk.retry.topology.max_retries (Integer, default 5, range 0–10, dynamic) — number of times a bulk operation deferred by a transient cluster-topology change (an index recreated mid-load, a shard left unavailable, the node holding it leaving) is re-submitted before its documents are counted as dropped. The default was sized to outlast one rolling restart; raise it on clusters where a restart takes longer, otherwise a synchronization running across a restart drops documents.

  • plugins.content_manager.bulk.retry.topology.initial_backoff_millis (Long, default 5000, range 100–60000, dynamic) — delay, in milliseconds, before the first re-submission of a topology-deferred bulk operation. Each subsequent retry doubles it, capped at the max backoff.

  • plugins.content_manager.bulk.retry.topology.max_backoff_millis (Long, default 30000, range 100–60000, dynamic) — ceiling, in milliseconds, for the exponential backoff between topology-deferred bulk re-submissions.

  • plugins.content_manager.job_schedule.max_retries (Integer, default 3, range 0–10) — number of attempts to register a periodic job with the job scheduler before giving up and logging an error.

  • plugins.content_manager.job_schedule.retry_backoff_seconds (Integer, default 15, range 1–300) — base delay, in seconds, for the linear backoff between job-registration attempts (delay before attempt n is base * n).

  • plugins.content_manager.resource_lock.max_retries (Integer, default 20, range 1–100, dynamic) — number of attempts to acquire the resource-creation lock before the request is rejected with HTTP 503.

  • plugins.content_manager.resource_lock.retry_backoff_millis (Long, default 100, range 10–10000, dynamic) — delay, in milliseconds, between resource-creation lock acquisition attempts.

  • plugins.content_manager.resource_lock.stale_threshold_millis (Long, default 30000, range 5000–600000, dynamic) — age, in milliseconds, past which a held resource-creation lock is treated as orphaned by a crashed node and stolen by the next caller. Set this to the same value on every node, and above the worst-case resource-creation time: the lock is cluster-wide but the setting is read from each node’s own configuration, so nodes that disagree can both consider themselves inside the critical section. The holder never renews the lock, so a value below the time a creation takes lets every concurrent request steal it and the mutex stops working.

  • plugins.content_manager.user_overrides.max_update_attempts (Integer, default 3, range 1–20, dynamic) — number of attempts to write the shared user-overrides registry document before giving up. The registry is a single document, so concurrent writers are serialized optimistically and each version conflict re-reads and re-applies.

  • plugins.content_manager.integration.max_update_attempts (Integer, default 5, range 1–20, dynamic) — number of attempts to update an integration document on a version conflict, when linking or unlinking a resource, before the operation fails.

  • plugins.content_manager.engine.socket_path (String, default /usr/share/wazuh-indexer/engine/sockets/engine-api-http.sock) — filesystem path of the Unix domain socket the Engine API listens on.

  • plugins.content_manager.engine.reload_timeout_minutes (Integer, default 10, range 1–1440) — upper bound, in minutes, on how long a single Engine content reload may stay in flight. It guards against a lost callback wedging the single-flight guard forever.

  • plugins.content_manager.engine.not_ready_grace_minutes (Integer, default 5, range 1–1440) — how long, in minutes, the content indices may stay unable to serve reads before the deferral is escalated from a debug line to a warning.

  • plugins.content_manager.security_analytics.sync_timeout_seconds (Integer, default 60, range 1–3600) — how long, in seconds, a Ruleset Management bulk synchronization step (uploading rules or integrations) may run before it is abandoned and the pass reported as unsuccessful.

  • plugins.content_manager.security_analytics.detector_timeout_seconds (Integer, default 30, range 1–3600) — how long, in seconds, to wait for the first detector creation to complete.

  • plugins.content_manager.security_analytics.cleanup_timeout_seconds (Integer, default 120, range 120–3600) — how long, in seconds, to wait for the deletion of stale Ruleset Management rules and integrations after a content swap. The floor is the default: on timeout the deletion is not retried, so lowering it would make stale resources routine rather than exceptional.

  • plugins.content_manager.security_analytics.detector_interval (Integer, default 2, range 1–10080, dynamic) — detector schedule interval, in minutes, applied when the CTI integration document does not specify one, or specifies one outside the bounds Ruleset Management accepts.

  • plugins.content_manager.update_sub_batch_size (Integer, default 50, range 1–1000) — maximum number of UPDATE offsets batched into a single MultiGet + bulk request while applying a catalog changeset.

  • plugins.content_manager.offset_flush_interval (Integer, default 10, range 1–1000) — how many batches (changeset application) or bulks (snapshot load) are processed between consumer-offset checkpoints. A smaller value narrows the window of work replayed after a crash, at the cost of more writes to the consumer-state document.

  • plugins.content_manager.search_page_size (Integer, default 10000, range 100–10000) — page size used by the paginated IoC reconciliation scan. Lower values mean more round trips over the same documents, not fewer documents.

Settings marked dynamic can be changed on a running cluster and take effect on the next operation:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X PUT "https://127.0.0.1:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "plugins.content_manager.bulk.retry.topology.max_retries": 8,
    "plugins.content_manager.bulk.retry.topology.max_backoff_millis": 45000
  }
}'

Offline configuration / disabling automatic updates

On offline installations, disable every task that requires an internet connection to prevent failures.

# opensearch.yml
plugins.content_manager.catalog.update_on_start: false
plugins.content_manager.catalog.update_on_schedule: false
plugins.content_manager.telemetry.enabled: false

plugins.content_manager.catalog.update_on_schedule and plugins.content_manager.telemetry.enabled are dynamic, so on a running deployment they can be applied without a restart:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X PUT "https://127.0.0.1:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "plugins.content_manager.catalog.update_on_schedule": false,
    "plugins.content_manager.telemetry.enabled": false
  }
}'

Disabling scheduled synchronization takes effect immediately and applies to an already-initialized node as well as a fresh one: the plugin updates the periodic job accordingly, and a run that is already scheduled is refused rather than reaching Wazuh CTI. See Job scheduler.

Disabling scheduled synchronization does not remove content that has already been downloaded. It only stops further updates from Wazuh CTI.

plugins.content_manager.catalog.update_on_start is not dynamic — it only governs the synchronization triggered as the plugin starts, so it takes effect on the next node start.

On online installations, manual synchronization can be performed on demand using the Content Manager API:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X POST "https://127.0.0.1:9200/_plugins/_content_manager/update"

Custom scheduled synchronization interval

The plugin checks for new content every 60 minutes by default, but this can be customized by changing the plugins.content_manager.catalog.sync_interval setting. The value is specified in minutes and must be between 10 and 1440 (24 hours).

# opensearch.yml
plugins.content_manager.catalog.sync_interval: 1440

The setting is dynamic, so the interval can also be changed on a running deployment:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X PUT "https://127.0.0.1:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "plugins.content_manager.catalog.sync_interval": 1440
  }
}'

The new interval is applied to the periodic job immediately; the next synchronization is scheduled from the moment of the change.

Setup readiness wait (startup race)

On node startup, the catalog sync job waits for the Setup plugin to finish creating its indices (signaled by the .wazuh-setup-status marker document) before it runs. If Setup has not finished within that wait, the sync is skipped for that run and deferred to the next scheduled run (plugins.content_manager.catalog.sync_interval minutes later, an hour by default) — on a slow-starting node (e.g. cluster still recovering shards), this can leave .wazuh-cti-consumers and the CTI content indices empty for up to that long.

The wait uses exponential backoff, controlled by plugins.content_manager.setup_wait.max_retries and plugins.content_manager.setup_wait.backoff_base_seconds. With the defaults (4 retries, 20s base), the schedule is 20s, 40s, 80s, 160s — 300s (5 min) total before giving up. Increase these on environments where Setup is known to take longer to initialize:

# opensearch.yml
plugins.content_manager.setup_wait.max_retries: 4
plugins.content_manager.setup_wait.backoff_base_seconds: 30

The example above raises the total worst-case wait to 30+60+120+240 = 450s (7.5 min).

CTI rate-limit retries (HTTP 429)

When the CTI API rate-limits a request it responds with HTTP 429 and a Retry-After header. The HTTP client honors that header: it waits the indicated time and retries the request, so a rate-limit no longer fails the synchronization pass. If the response carries no usable Retry-After header, it falls back to exponential backoff (base * 2^n).

The behavior is controlled by plugins.content_manager.client.max_retries and plugins.content_manager.client.retry_backoff_base_seconds. With the defaults (3 retries, 30s base), the fallback schedule is 30s, 60s, 120s — 210s (3.5 min) worst case before the 429 is surfaced and the pass defers to the next scheduled run:

# opensearch.yml
plugins.content_manager.client.max_retries: 3
plugins.content_manager.client.retry_backoff_base_seconds: 30

The Retry-After header, when present, always takes precedence over the backoff base — the base applies only when the header is missing or unparseable.

Custom CTI API endpoint

To point to a different CTI API (e.g., production):

# opensearch.yml
plugins.content_manager.cti.api: "https://cti.wazuh.com/api/v1"

The setting moves every CTI request the plugin makes, not just content retrieval. Besides the catalog endpoints (snapshots, changes, plans), it is also the base URL for the CTI Console calls: instance registration (POST /_plugins/_content_manager/subscription), environment and plan lookup, and the resource-token exchange that produces the HMAC-signed URLs used during synchronization.

A registered instance is not valid against a different CTI environment. After changing this setting, re-register the instance with POST /_plugins/_content_manager/subscription.

Custom catalog consumer URLs

To override default consumers, provide full HTTP(S) consumer URLs:

# opensearch.yml
plugins.content_manager.catalog.ruleset: "https://api.pre.cloud.wazuh.com/api/v1/catalog/contexts/beta-2-ruleset-5/consumers/public-ruleset-5"
plugins.content_manager.catalog.iocs: "https://api.pre.cloud.wazuh.com/api/v1/catalog/contexts/t1-iocs-5/consumers/public-iocs-5"
plugins.content_manager.catalog.vulnerabilities: "https://api.pre.cloud.wazuh.com/api/v1/catalog/contexts/t1-vulnerabilities-5/consumers/public-vulnerabilities-5"

Behavior:

  • Content Manager attempts remote snapshot initialization first. If a setting is empty, it uses the catalog URL of the subscription plan, the one stored by the previous sync, or, on a fresh install, the one recorded in the packaged snapshot’s manifest.
  • If remote initialization fails, it falls back to the last stable snapshot (the last one loaded in full), and then to the local packaged snapshot when available.

Tune bulk operations

For environments with limited resources, reduce the bulk operation concurrency:

# opensearch.yml
plugins.content_manager.max_items_per_bulk: 10
plugins.content_manager.max_concurrent_bulks: 2
plugins.content_manager.client.timeout: 30

Disable Ruleset Management detector creation

If you do not use the Ruleset Management plugin:

# opensearch.yml
plugins.content_manager.catalog.create_detectors: false

CTI communication headers

All HTTP clients that communicate with Wazuh CTI services send a custom User-Agent header, and an Accept-Encoding header so responses are transferred compressed:

User-Agent: Wazuh Indexer <version>
Accept-Encoding: gzip

For example: Wazuh Indexer 5.0.0. This applies to the Console API client, Catalog API client, Snapshot client, and Telemetry client. The version is read from VERSION.json at plugin startup.

Update check service behavior

The update check service is enabled by default and runs once per day, with an immediate first ping fired as soon as the job is registered in the scheduler.

  • It is implemented by a scheduled job (wazuh-telemetry-ping-job) in .wazuh-content-manager-jobs.
  • It sends a request to the CTI Update check API endpoint (/ping).
  • The request includes:
    • Deployment identifier (wazuh-uid: cluster UUID)
    • Running version (wazuh-tag: v<version>)
    • User agent (Wazuh Indexer <version>)
    • Accept-Encoding (gzip)

This data allows Wazuh to determine if a newer version is available and notify users in the update check UI.

The service only sends deployment identification/version metadata required for update checks. It does not send rules, events, or log payloads.

Enable or disable the update check service dynamically

The update check service can be enabled or disabled at runtime without restarting the node using the Cluster Settings API:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X PUT "https://127.0.0.1:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "plugins.content_manager.telemetry.enabled": false
  }
}'

Protecting sensitive configuration

Some endpoints modify configuration with a high impact on the platform and are protected by two independent controls:

  • PUT /_plugins/_content_manager/policy/{space} — permission cluster:admin/content_manager/policy/update.
  • POST /_plugins/_content_manager/update — permission cluster:admin/content_manager/update/trigger.
  • PUT /_plugins/_setup/settings — permission plugin:wazuh/settings/write.
  1. RBAC — each endpoint is gated by a cluster permission (the action name above), enforced by the security plugin. No default role holds these permissions: dashboard_server and wazuh_manager are both excluded, so out of the box only the superuser admin (role all_access, cluster wildcard *) can call them. To delegate any of these actions without granting full superuser, create a dedicated role for the permission(s) above. See the access control reference.

  2. Per-endpoint disable settings — each endpoint can be disabled independently with its own node setting; when disabled it returns 403 Forbidden for every caller, including admin / all_access. This is intended for externally managed (e.g. Wazuh Cloud) deployments.

    • POST /_plugins/_content_manager/update — disable via plugins.content_manager.catalog.update_on_demand: false.
    • PUT /_plugins/_content_manager/policy/{space} — disable via plugins.content_manager.catalog.policy_update.enabled: false.
    • PUT /_plugins/_setup/settings — disable via plugins.setup.settings_update.enabled: false.
# opensearch.yml — disable sensitive configuration endpoints on a managed deployment
plugins.content_manager.catalog.update_on_demand: false
plugins.content_manager.catalog.policy_update.enabled: false
plugins.setup.settings_update.enabled: false

Resource creation limits

The plugin enforces configurable upper bounds on the number of resources that can be created. Each limit applies to POST (creation) requests only — existing resources are not affected when a limit is lowered. The count is checked against the relevant index at request time; if the index does not yet exist, the check is skipped and creation proceeds.

All limit settings are dynamic and can be changed at runtime:

curl -sk -u admin:$WAZUH_INDEXER_ADMIN_PASSWORD -X PUT "https://127.0.0.1:9200/_cluster/settings" \
  -H 'Content-Type: application/json' \
  -d '{
    "persistent": {
      "plugins.content_manager.max_integrations": 50,
      "plugins.content_manager.max_decoders": 100,
      "plugins.content_manager.max_rules": 100,
      "plugins.content_manager.max_kvdbs": 50,
      "plugins.content_manager.max_filters": 50
    }
  }'

Setting a limit to 0 blocks all new creation of that resource type.

Notes

  • Changes to opensearch.yml require a restart of the Wazuh Indexer to take effect, except for dynamic settings, which can be updated at runtime via the OpenSearch API. Dynamic settings include plugins.content_manager.catalog.update_on_schedule, plugins.content_manager.catalog.sync_interval, plugins.content_manager.telemetry.enabled, plugins.content_manager.logtest.max_body_bytes, and all five resource creation limits (max_integrations, max_decoders, max_rules, max_kvdbs, max_filters).
  • The catalog URL settings (plugins.content_manager.catalog.ruleset, plugins.content_manager.catalog.iocs, and plugins.content_manager.catalog.vulnerabilities) should only be changed if instructed by Wazuh support or documentation, and must point to valid absolute HTTP(S) CTI consumer endpoints.
  • The sync interval is enforced by the OpenSearch Job Scheduler. The actual sync timing may vary slightly depending on cluster load.
  • plugins.content_manager.catalog.update_on_schedule and plugins.content_manager.catalog.sync_interval are reconciled with the periodic job on every node start and whenever either setting changes, so a value set in opensearch.yml applies to an existing deployment and not only to a fresh install.
  • The update check service runs with a fixed interval of 1 day when enabled. The first ping is sent immediately after the job is registered (on node start or when the setting is dynamically enabled); subsequent pings follow the 1-day interval.
  • Detector configuration: the settings for Ruleset Management detectors (interval, enabled status, and source indices) are managed directly via CTI integration files. If an integration’s detector object is missing in the CTI source, the system will use built-in safety defaults.