Wazuh Dashboard + Nginx Proxy Manager

Server Wazuh Dashboard via docker behind NGINX Proxy Manager
Set up a docker network. Change the subnet to whatever you want.
docker network create \
--driver=bridge \
--subnet=10.0.1.0/24 \
--gateway=10.0.1.1 \
wazuh_npm
Clone and enter the Wazuh repo.
git clone https://github.com/wazuh/wazuh-docker.git -b v4.8.1 && \
cd wazuh-docker/single-node
Generate the Certificates
docker-compose -f generate-indexer-certs.yml run --rm generator
Now in, the docker-compose file, add static IPs to each container. I like setting a container name as well.
# single-node/docker-compose.yml
services:
wazuh.manager:
image: wazuh/wazuh-manager:4.8.1
hostname: wazuh.manager
container_name: wazuh-manager
networks:
wazuh_npm:
ipv4_address: 10.0.1.30
. . .
wazuh.indexer:
image: wazuh/wazuh-indexer:4.8.1
container_name: wazuh-indexer
hostname: wazuh.indexer
networks:
wazuh_npm:
ipv4_address: 10.0.1.31
. . .
wazuh.dashboard:
image: wazuh/wazuh-dashboard:4.8.1
container_name: wazuh-dashboard
hostname: wazuh.dashboard
networks:
wazuh_npm:
ipv4_address: 10.0.1.32
. . .
app:
image: jc21/nginx-proxy-manager:latest
networks:
wazuh_npm:
ipv4_address: 10.0.1.20
networks:
wazuh_npm:
name: wazuh_npm
external: true
Whatever IP you assign the Wazuh Dashboard needs to be put into single-node/config/wazuh_dashboard/opensearch_dashboards.yml
# single-node/config/wazuh_dashboard/opensearch_dashboards.yml
server.host: 10.0.1.32
server.port: 5601
. . .
Grab the certificate files generated for the dashboard and the root ca. You'll need to change the permissions to copy them first.
config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem
config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem
config/wazuh_indexer_ssl_certs/root-ca.pem
Start the containers.
docker-compose up -d
Go to the NPM web interface, import the SSL certificates, and create a proxy host.
IP: http://10.0.1.32:5601
Enable websockets support.
Should be good.
Comments
Join the discussionPutting the Wazuh dashboard behind NPM on a dedicated user-defined Docker network keeps TLS and Host routing at the edge while the dashboard container never publishes its UI port on the host. Attach both NPM and the dashboard service to that network, point the Proxy Host at the dashboard service name and internal port, enable Websockets Support for the live UI, and leave only NPM owning 80/443. Request a certificate only after the public A/AAAA for that hostname already lands on the box, and keep the NPM admin listener off the open internet.
Ops check once the host is live: leaf SAN matches the name, force-SSL is one hop, and Host plus X-Forwarded-Proto still reach the dashboard so auth redirects and absolute asset URLs stay on the public scheme. From inside the NPM container, curl the dashboard service by name on the internal port, then the same path through the public vhost; connect failures are network membership or a wrong internal port, while redirect loops usually mean the forwarded Host/Proto pair never arrived.


