Before exposing the Wazuh stack to a network, complete this security checklist to harden your deployment against common attack vectors. The default values inDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/rsol9000-01/wazuh/llms.txt
Use this file to discover all available pages before exploring further.
.env.example are intentionally illustrative — they must be replaced with strong, unique credentials before the stack handles any real monitored infrastructure.
Credential Rotation
Rotate all stack credentials whenever you provision a new environment, after a suspected compromise, or on your organization’s regular credential-rotation schedule.Edit .env with strong unique passwords
Open Update the following variables:
.env and replace every credential with a strong, randomly generated value:| Variable | Purpose |
|---|---|
INDEXER_PASSWORD | Password for the OpenSearch admin user |
API_PASSWORD | Password for the Wazuh REST API user (wazuh-wui) |
DASHBOARD_USERNAME / DASHBOARD_PASSWORD | Dashboard internal service account |
MY_PASSWORD | Password for your custom admin user (hashed into internal_users.yml) |
Rotate the Indexer admin hash
The password for any user defined in The relevant portion of the script that performs this step:
config/wazuh_indexer/internal_users.yml must be stored as a bcrypt hash. Run wazuh-dev.sh server up — the script calls the Indexer’s built-in hash.sh tool via docker run and automatically upserts the new hash for MY_USERNAME into internal_users.yml:Verify the Dashboard API connection is updated
wazuh-dev.sh automatically syncs config/wazuh_dashboard/wazuh.yml with the API_USERNAME and API_PASSWORD values from .env using a sed in-place substitution. Confirm the update was applied:API_USERNAME and API_PASSWORD values.Restart the stack
Bring the stack down and back up so all services pick up the new credentials and regenerated certs:Or simply let
wazuh-dev.sh server up handle the full cycle including cert regeneration.Verify login at the Dashboard
Open a browser and navigate to
https://<host>:6443. Log in with your updated credentials to confirm the rotation was successful. Check for “Could not connect to the Wazuh API” errors — if present, see Troubleshooting.Password Policy
Apply the following requirements to every credential stored in.env and internal_users.yml:
- Minimum length: 16 characters
- Complexity: mix of uppercase, lowercase, digits, and symbols
- Forbidden character: the
$character is not allowed inAPI_PASSWORDorAPI_USERNAME—wazuh-dev.shvalidates this and will exit with an error if it is present, because$interferes with the shell variable expansion used duringwazuh.ymlcredential injection - Uniqueness: use a different password for every role (
INDEXER_PASSWORD,API_PASSWORD,DASHBOARD_PASSWORD,MY_PASSWORD) - Storage: all passwords stored in
internal_users.ymlmust be bcrypt hashes — never store plain text passwords in that file
The
reserved: true flag on the admin and kibanaserver users in internal_users.yml prevents them from being deleted through the API. Custom users added by wazuh-dev.sh use reserved: false and can be managed via the Security plugin UI.Firewall Configuration
Thedocker-compose.yml exposes the following ports on the host. Lock them down to the minimum required network scope using ufw, nftables, or your cloud security group.
| Port | Protocol | Purpose | Recommendation |
|---|---|---|---|
| 6443 | TCP | Dashboard UI (HTTPS) | Restrict to admin IPs or VPN subnet |
| 55000 | TCP | Wazuh REST API | Restrict to trusted management hosts only |
| 9200 | TCP | Indexer (OpenSearch) API | Internal use only — block from all external hosts |
| 1514 | TCP | Agent event communication | Allow only from monitored host subnets |
| 1515 | TCP | Agent auto-enrollment | Allow only from monitored host subnets |
| 514 | UDP | Syslog ingestion | Allow only from designated log sources |
ufw rules:
TLS / HTTPS
Default behavior:wazuh-dev.sh server up runs docker compose -f generate-indexer-certs.yml run --rm generator to produce self-signed certificates in ./config/wazuh_indexer_ssl_certs/. These certs secure all inter-service communication (Manager ↔ Indexer via Filebeat, Dashboard ↔ Indexer) and are generated fresh on each wazuh-dev.sh invocation.
The certs are mounted at the following paths inside each container:
| Container | Cert files mounted |
|---|---|
wazuh.indexer | root-ca.pem, wazuh.indexer.pem, wazuh.indexer-key.pem, admin.pem, admin-key.pem |
wazuh.manager | root-ca-manager.pem, wazuh.manager.pem, wazuh.manager-key.pem |
wazuh.dashboard | root-ca.pem, wazuh.dashboard.pem, wazuh.dashboard-key.pem |
- Obtain certificates for each service node using your CA or ACME client.
- Name the files to match the expected filenames listed in
docker-compose.ymlvolume mounts (e.g.,wazuh.indexer.pem,wazuh.indexer-key.pem). - Place all certs in
./config/wazuh_indexer_ssl_certs/. - Restart the stack:
sudo docker compose down && sudo docker compose up -d.
The Wazuh Dashboard serves its own self-signed certificate to browsers on port
6443. This will trigger browser security warnings — this is expected behavior. Accept the exception or install the root-ca.pem into your browser’s or OS’s trusted certificate store to suppress the warning.Volume and File Permissions
Several files on the host contain sensitive credentials and must be protected from world-readable access:- Add
.envto.gitignore— it must never be committed. - Add
config/wazuh_indexer_ssl_certs/to.gitignore— private keys must never be committed. - The
.env.examplefile is safe to commit as a template (replace all real credential values with<strong-password>placeholders before committing).
Whitelist Configuration (Active Response)
The Manager’s active-response module can automatically block source IPs that trigger high-severity rules using thefirewall-drop command. To prevent accidentally blocking your own management hosts, add them to the whitelist in config/wazuh_cluster/wazuh_manager.conf.
The default whitelist in the deployed configuration:
<white_list> entries to the same <global> block:
sudo docker compose restart wazuh.manager.
Disabling Default Accounts
Theconfig/wazuh_indexer/internal_users.yml file ships with several demo accounts that are not needed in most production deployments:
| Account | Backend Role | Used by |
|---|---|---|
kibanaro | kibanauser, readall | Legacy Kibana read-only demo user |
logstash | logstash | Logstash pipeline ingestion (not used in this stack) |
readall | readall | Read-only API demo user |
snapshotrestore | snapshotrestore | Index snapshot/restore operations |
backend_roles and setting reserved: false. You can also simply omit the entries from the file entirely since wazuh-dev.sh will preserve only the entries present at deployment time.
Example — disabling kibanaro:
internal_users.yml, run sudo bash scripts/wazuh-dev.sh server up to apply the changes (the script mounts the file directly into the Indexer container on start).