Compare commits

..

3 Commits

Author SHA1 Message Date
695b7b017e updatd with wazah deploymnt 2026-09-16 09:39:56 -06:00
3e7939b682 feat: install wazuh agent in packages role; remove unused deployment files
Wazuh agent:
- packages role now copies the local wazuh-agent RPM to the host, installs
  it via dnf, points /var/ossec/etc/ossec.conf at wazuh.boxswapper.io,
  and enables/starts the wazuh-agent service
- new vars: wazuh_agent_rpm, wazuh_server_address (manager version 4.14.1
  matches the agent RPM)

Cleanup of files not used by the ansible deployment:
- removed wazuh-docker/docker-compose.yml, generate-indexer-certs.yml,
  README.md (role generates certs via podman run, config/ dir is the
  only part referenced)
- removed committed .omo/ session data (already gitignored)

Done by Sisyphus.
2026-09-16 09:33:42 -06:00
170146efb1 refactor: split monolithic playbook into 9 focused ansible roles
Refactored the single ansible-playbook.yml into granular roles under roles/:
- packages: dnf update + package installs
- directories: /opt/apps/* directory creation
- sysctl: ip_forward, src_valid_mark, vm.max_map_count tuning
- network: npm_internal podman network
- wireguard: kernel module, wg-easy container, nftables hook patching
- npm: Nginx Proxy Manager container
- gitea: Gitea container
- jellyfin: Jellyfin container
- wazuh: cert generation + indexer/manager/dashboard containers

Top-level playbook is now a role dispatcher with image pre-pull kept
upfront. n8n deployment kept as commented-out guidance with an enable
checklist. All variables remain in top-level vars.yml.

Done by Sisyphus.
2026-09-16 09:24:32 -06:00
16 changed files with 412 additions and 586 deletions

View File

@@ -1,10 +0,0 @@
{
"sessionID": "ses_f9c7a2a4bffeGbArqDu2FhIlC5",
"updatedAt": "2026-09-02T19:11:53.267Z",
"sources": {
"background-task": {
"state": "idle",
"updatedAt": "2026-09-02T19:11:53.267Z"
}
}
}

View File

@@ -1,10 +0,0 @@
{
"sessionID": "ses_f9c868146ffeKpqM2tbofhzWO3",
"updatedAt": "2026-09-02T19:03:22.170Z",
"sources": {
"background-task": {
"state": "idle",
"updatedAt": "2026-09-02T19:03:22.170Z"
}
}
}

View File

@@ -1,433 +1,22 @@
---
- hosts: internal-apps
become: true
vars_files:
vars_files:
- ./vars.yml
tasks:
- name: Update Packages
ansible.builtin.dnf:
update_cache: yes
- name: Install Packages
ansible.builtin.dnf:
name:
- vim
- podman
- fail2ban
- python3-libdnf5
state: present
- name: Create directories
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop: "{{ directories }}"
- name: Create Jellyfin directories
file:
path: "{{ item }}"
state: directory
mode: '0777'
loop: "{{ directories_jellyfin }}"
- name: Create internal podman network
containers.podman.podman_network:
name: npm_internal
ip_range: 10.10.0.128/25
subnet: 10.10.0.0/24
gateway: 10.10.0.1
state: present
- name: Ensure WireGuard kernel module is loaded
ansible.builtin.modprobe:
name: wireguard
state: present
- name: Persist WireGuard kernel module across reboots
ansible.builtin.copy:
dest: /etc/modules-load.d/wg-easy.conf
content: |
wireguard
nft_masq
mode: '0644'
- name: Enable IP forwarding on host
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
state: present
- name: Enable source valid mark on host
ansible.posix.sysctl:
name: net.ipv4.conf.all.src_valid_mark
value: '1'
sysctl_set: true
state: present
reload: true
- name: Raise vm.max_map_count for Wazuh indexer
ansible.posix.sysctl:
name: vm.max_map_count
value: '262144'
sysctl_set: true
state: present
reload: true
- name: pull images
- name: Pull images
containers.podman.podman_image:
name: "{{ item }}"
loop: "{{ images }}"
- name: Deploy Nginx Proxy Manager
containers.podman.podman_container:
image: "docker.io/jc21/nginx-proxy-manager:latest"
name: npm
restart_policy: always
recreate: true
state: started
ports:
- "80:80"
- "81:81"
- "443:443"
volumes:
- "/opt/apps/npm:/data:z"
- "/opt/apps/letsencrypt:/etc/letsencrypt:z"
network:
- npm_internal
ip: '10.10.0.19'
- name: Deploy Gitea
containers.podman.podman_container:
image: docker.gitea.com/gitea:nightly
name: gitea
env:
USER_UID: 1000
USER_GID: 1000
restart_policy: always
recreate: true
state: started
volumes:
- /opt/apps/gitea:/data:z
- /usr/share/zoneinfo/America/Denver:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
network:
- npm_internal
ip: '10.10.0.25'
ports:
# - "3000:3000"
- "222:22"
- name: Deploy Jellyfin
containers.podman.podman_container:
image: lscr.io/linuxserver/jellyfin:latest
name: jellyfin
env:
USER_UID: 1000
USER_GID: 1000
JELLYFIN_PublishedServerUrl: http://jellyfin.boxswapper.io
restart_policy: always
recreate: true
state: started
volumes:
- /opt/apps/jellyfin/config:/config:z
- /opt/apps/jellyfin/tvseries:/data/tvshows:z
- /opt/apps/jellyfin/movies:/data/movies:z
- /usr/share/zoneinfo/America/Denver:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
network:
- npm_internal
ip: '10.10.0.30'
# ports:
# - 8096:8096
# - 8920:8920 #optional
# - 7359:7359/udp #optional
# - 1900:1900/udp #optional
# WireGuard Easy VPN (v15) — UI at 10.10.0.36:51821 on npm_internal, fronted by NPM
# (vpn.boxswapper.io -> http://10.10.0.36:51821). Login: admin / wireguard_ui_password from vars.yml.
# v15 init is ONE-TIME: the INIT_* envs below are consumed only when /etc/wireguard/wg-easy.db is ABSENT.
# Force a re-init (wipes clients + server keys): podman rm -f wg-easy && rm -rf /opt/apps/wireguard, then re-run.
# NAT from VPN clients to LAN/apps (10.10.1.0/24 -> 10.10.0.0/24) uses NFTABLES hooks —
# wg-easy's iptables-legacy hooks cannot init the nat table inside a podman netns
# ("Table does not exist"), which makes wg-quick up fail and every client API call
# 500 with "Unable to access interface". scripts/patch-wg-hooks.py rewrites the hooks
# to nft rules and the tasks below restart wg-easy to apply them. See the official
# wg-easy "Podman + nftables" tutorial.
# EXTERNAL ACCESS: router MUST forward UDP 51820 -> 192.168.0.98. Test from LTE,
# not your LAN — NAT hairpin breaks public-IP handshakes from inside the network.
- name: Deploy WireGuard Easy (wg-easy)
containers.podman.podman_container:
name: wg-easy
image: "{{ wireguard_image }}"
restart_policy: always
recreate: true
state: started
env:
INIT_ENABLED: "true"
INIT_USERNAME: "{{ wireguard_ui_username }}"
INIT_PASSWORD: "{{ wireguard_ui_password }}"
INIT_HOST: "{{ wireguard_host }}"
INIT_PORT: "{{ wireguard_port }}"
INIT_DNS: "{{ wireguard_default_dns }}"
INIT_IPV4_CIDR: "{{ wireguard_cidr }}"
INIT_IPV6_CIDR: "{{ wireguard_ipv6_cidr }}"
INIT_ALLOWED_IPS: "{{ wireguard_allowed_ips }}"
PORT: "{{ wireguard_ui_port }}"
INSECURE: "true"
DISABLE_VERSION_CHECK: "true"
ports:
- "{{ wireguard_port }}:{{ wireguard_port }}/udp"
- "{{ wireguard_ui_port }}:{{ wireguard_ui_port }}/tcp"
volumes:
- "/opt/apps/wireguard:/etc/wireguard:z"
- "/lib/modules:/lib/modules:ro"
cap_add:
- NET_ADMIN
- SYS_MODULE
- NET_RAW
sysctl:
net.ipv4.ip_forward: "1"
net.ipv4.conf.all.src_valid_mark: "1"
net.ipv6.conf.all.disable_ipv6: "0"
net.ipv6.conf.all.forwarding: "1"
net.ipv6.conf.default.forwarding: "1"
network:
- npm_internal
ip: '{{ wireguard_ip }}'
- name: Copy wg-easy hook repair script
ansible.builtin.copy:
src: ./scripts/patch-wg-hooks.py
dest: /opt/apps/wireguard/patch-wg-hooks.py
owner: root
group: root
mode: '0755'
- name: Replace iptables hooks with nftables (iptables-legacy broken in podman)
ansible.builtin.command: python3 /opt/apps/wireguard/patch-wg-hooks.py
register: wg_hooks_patch
changed_when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Restart wg-easy to apply nft hooks
ansible.builtin.command: podman restart wg-easy
when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Wait for WireGuard Easy web UI
ansible.builtin.wait_for:
host: '{{ wireguard_ip }}'
port: '{{ wireguard_ui_port }}'
delay: 5
timeout: 120
# Wazuh SIEM (single-node: indexer + manager + dashboard) on npm_internal
# Dashboard is NOT exposed on host ports — NPM proxies wazuh.boxswapper.io to https://10.10.0.40:5601
# DNS record must be grey-cloud (Cloudflare 523 lesson from vpn.boxswapper.io), cert via NPM
# Config is copied from ./wazuh-docker/config before first deploy
- name: Copy Wazuh config files to host
ansible.builtin.copy:
src: ./wazuh-docker/config/
dest: /opt/apps/wazuh/config/
owner: root
group: root
mode: preserve
force: true
- name: Check for stale Wazuh certificate residue
ansible.builtin.stat:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
register: wazuh_cert_residue
- name: Remove stale Wazuh certificate directories
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: absent
when: wazuh_cert_residue.stat.exists and wazuh_cert_residue.stat.isdir
- name: Recreate Wazuh certificates directory
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: directory
mode: '0755'
owner: root
group: root
- name: Generate Wazuh certificates
ansible.builtin.command: podman run --rm -e CERT_TOOL_VERSION=4.14 -v /opt/apps/wazuh/config/certs.yml:/config/certs.yml:z -v /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/:/certificates:z docker.io/wazuh/wazuh-certs-generator:0.0.3
args:
creates: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
- name: Copy root CA as manager root CA
ansible.builtin.copy:
src: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
dest: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca-manager.pem
remote_src: true
force: false
- name: Deploy Wazuh indexer
containers.podman.podman_container:
name: wazuh.indexer
image: "docker.io/wazuh/wazuh-indexer:{{ wazuh_version }}"
hostname: wazuh.indexer
restart_policy: always
recreate: true
state: started
env:
OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g"
ulimit:
- "memlock=-1:-1"
- "nofile=65536:65536"
volumes:
- "wazuh-indexer-data:/var/lib/wazuh-indexer"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml:z"
- "/opt/apps/wazuh/config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml:z"
network:
- npm_internal
ip: '{{ wazuh_indexer_ip }}'
- name: Wait for Wazuh indexer to be ready
ansible.builtin.wait_for:
host: '{{ wazuh_indexer_ip }}'
port: 9200
delay: 10
timeout: 300
- name: Deploy Wazuh manager
containers.podman.podman_container:
name: wazuh.manager
image: "docker.io/wazuh/wazuh-manager:{{ wazuh_version }}"
hostname: wazuh.manager
restart_policy: always
recreate: true
state: started
env:
INDEXER_URL: "https://wazuh.indexer:9200"
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
FILEBEAT_SSL_VERIFICATION_MODE: "full"
SSL_CERTIFICATE_AUTHORITIES: "/etc/ssl/root-ca.pem"
SSL_CERTIFICATE: "/etc/ssl/filebeat.pem"
SSL_KEY: "/etc/ssl/filebeat.key"
API_USERNAME: "wazuh-wui"
API_PASSWORD: "MyS3cr37P450r.*-"
ulimit:
- "memlock=-1:-1"
- "nofile=655360:655360"
ports:
- "1514:1514"
- "1515:1515"
- "514:514/udp"
- "55000:55000"
volumes:
- "wazuh_api_configuration:/var/ossec/api/configuration"
- "wazuh_etc:/var/ossec/etc"
- "wazuh_logs:/var/ossec/logs"
- "wazuh_queue:/var/ossec/queue"
- "wazuh_var_multigroups:/var/ossec/var/multigroups"
- "wazuh_integrations:/var/ossec/integrations"
- "wazuh_agentless:/var/ossec/agentless"
- "wazuh_active_response:/var/ossec/active-response/bin"
- "wazuh_wodles:/var/ossec/wodles"
- "filebeat_etc:/etc/filebeat"
- "filebeat_var:/var/lib/filebeat"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key:z"
- "/opt/apps/wazuh/config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf:z"
network:
- npm_internal
ip: '{{ wazuh_manager_ip }}'
- name: Deploy Wazuh dashboard
containers.podman.podman_container:
name: wazuh.dashboard
image: "docker.io/wazuh/wazuh-dashboard:{{ wazuh_version }}"
hostname: wazuh.dashboard
restart_policy: always
recreate: true
state: started
env:
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
WAZUH_API_URL: "https://wazuh.manager"
DASHBOARD_USERNAME: "kibanaserver"
DASHBOARD_PASSWORD: "kibanaserver"
API_USERNAME: "wazuh-wui"
API_PASSWORD: "MyS3cr37P450r.*-"
volumes:
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml:z"
- "/opt/apps/wazuh/config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml:z"
- "wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config"
- "wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom"
network:
- npm_internal
ip: '{{ wazuh_dashboard_ip }}'
#----N8N deployment, no GPU avail for deployment ------#
# - name: Deploy n8n
# containers.podman.podman_container:
# image: docker.n8n.io/n8nio/n8n
# name: n8n
# env:
# N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: true
# N8N_RUNNERS_ENABLED: true
# restart_policy: always
# recreate: true
# state: started
# volumes:
# - /opt/apps/n8n/data:/home/node/.n8n:z
# - /opt/apps/n8n/files:/files:z
# - /usr/share/zoneinfo/America/Denver:/etc/timezone:ro
# - /etc/localtime:/etc/localtime:ro
# network:
# - npm_internal
# ip: '10.10.0.35'
# # ports:
# # - 5678:5678
roles:
- packages
- directories
- sysctl
- network
- wireguard
- npm
- gitea
- jellyfin
- wazuh

View File

@@ -0,0 +1,14 @@
---
- name: Create directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: '0755'
loop: "{{ directories }}"
- name: Create Jellyfin directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: '0777'
loop: "{{ directories_jellyfin }}"

View File

@@ -0,0 +1,20 @@
---
- name: Deploy Gitea
containers.podman.podman_container:
image: docker.gitea.com/gitea:nightly
name: gitea
env:
USER_UID: 1000
USER_GID: 1000
restart_policy: always
recreate: true
state: started
volumes:
- /opt/apps/gitea:/data:z
- /usr/share/zoneinfo/America/Denver:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
network:
- npm_internal
ip: '10.10.0.25'
ports:
- "222:22"

View File

@@ -0,0 +1,21 @@
---
- name: Deploy Jellyfin
containers.podman.podman_container:
image: lscr.io/linuxserver/jellyfin:latest
name: jellyfin
env:
USER_UID: 1000
USER_GID: 1000
JELLYFIN_PublishedServerUrl: http://jellyfin.boxswapper.io
restart_policy: always
recreate: true
state: started
volumes:
- /opt/apps/jellyfin/config:/config:z
- /opt/apps/jellyfin/tvseries:/data/tvshows:z
- /opt/apps/jellyfin/movies:/data/movies:z
- /usr/share/zoneinfo/America/Denver:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
network:
- npm_internal
ip: '10.10.0.30'

View File

@@ -0,0 +1,8 @@
---
- name: Create internal podman network
containers.podman.podman_network:
name: npm_internal
ip_range: 10.10.0.128/25
subnet: 10.10.0.0/24
gateway: 10.10.0.1
state: present

18
roles/npm/tasks/main.yml Normal file
View File

@@ -0,0 +1,18 @@
---
- name: Deploy Nginx Proxy Manager
containers.podman.podman_container:
image: "docker.io/jc21/nginx-proxy-manager:latest"
name: npm
restart_policy: always
recreate: true
state: started
ports:
- "80:80"
- "81:81"
- "443:443"
volumes:
- "/opt/apps/npm:/data:z"
- "/opt/apps/letsencrypt:/etc/letsencrypt:z"
network:
- npm_internal
ip: '10.10.0.19'

View File

@@ -0,0 +1,45 @@
---
- name: Update Packages
ansible.builtin.dnf:
update_cache: yes
- name: Apply security updates
ansible.builtin.dnf:
name: "*"
state: latest
security: yes
update_cache: yes
- name: Install Packages
ansible.builtin.dnf:
name:
- vim
- podman
- fail2ban
- python3-libdnf5
state: present
- name: Copy Wazuh agent RPM to host
ansible.builtin.copy:
src: "{{ playbook_dir }}/{{ wazuh_agent_rpm }}"
dest: "/tmp/{{ wazuh_agent_rpm }}"
owner: root
group: root
mode: '0644'
- name: Install Wazuh agent RPM
ansible.builtin.dnf:
name: "/tmp/{{ wazuh_agent_rpm }}"
state: present
- name: Point Wazuh agent at Wazuh manager
ansible.builtin.replace:
path: /var/ossec/etc/ossec.conf
regexp: '<address>MANAGER_IP</address>'
replace: '<address>{{ wazuh_server_address }}</address>'
- name: Enable and start Wazuh agent
ansible.builtin.systemd:
name: wazuh-agent
enabled: true
state: started

View File

@@ -0,0 +1,23 @@
---
- name: Enable IP forwarding on host
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: '1'
sysctl_set: true
state: present
- name: Enable source valid mark on host
ansible.posix.sysctl:
name: net.ipv4.conf.all.src_valid_mark
value: '1'
sysctl_set: true
state: present
reload: true
- name: Raise vm.max_map_count for Wazuh indexer
ansible.posix.sysctl:
name: vm.max_map_count
value: '262144'
sysctl_set: true
state: present
reload: true

157
roles/wazuh/tasks/main.yml Normal file
View File

@@ -0,0 +1,157 @@
---
# Wazuh SIEM (single-node: indexer + manager + dashboard) on npm_internal
# Dashboard is NOT exposed on host ports — NPM proxies wazuh.boxswapper.io to https://10.10.0.40:5601
# DNS record must be grey-cloud (Cloudflare 523 lesson from vpn.boxswapper.io), cert via NPM
# Config is copied from ./wazuh-docker/config before first deploy
- name: Copy Wazuh config files to host
ansible.builtin.copy:
src: ./wazuh-docker/config/
dest: /opt/apps/wazuh/config/
owner: root
group: root
mode: preserve
force: true
- name: Check for stale Wazuh certificate residue
ansible.builtin.stat:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
register: wazuh_cert_residue
- name: Remove stale Wazuh certificate directories
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: absent
when: wazuh_cert_residue.stat.exists and wazuh_cert_residue.stat.isdir
- name: Recreate Wazuh certificates directory
ansible.builtin.file:
path: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs
state: directory
mode: '0755'
owner: root
group: root
- name: Generate Wazuh certificates
ansible.builtin.command: >
podman run --rm
-e CERT_TOOL_VERSION=4.14
-v /opt/apps/wazuh/config/certs.yml:/config/certs.yml:z
-v /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/:/certificates:z
docker.io/wazuh/wazuh-certs-generator:0.0.3
args:
creates: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
- name: Copy root CA as manager root CA
ansible.builtin.copy:
src: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem
dest: /opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca-manager.pem
remote_src: true
force: false
- name: Deploy Wazuh indexer
containers.podman.podman_container:
name: wazuh.indexer
image: "docker.io/wazuh/wazuh-indexer:{{ wazuh_version }}"
hostname: wazuh.indexer
restart_policy: always
recreate: true
state: started
env:
OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g"
ulimit:
- "memlock=-1:-1"
- "nofile=65536:65536"
volumes:
- "wazuh-indexer-data:/var/lib/wazuh-indexer"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml:z"
- "/opt/apps/wazuh/config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml:z"
network:
- npm_internal
ip: '{{ wazuh_indexer_ip }}'
- name: Wait for Wazuh indexer to be ready
ansible.builtin.wait_for:
host: '{{ wazuh_indexer_ip }}'
port: 9200
delay: 10
timeout: 300
- name: Deploy Wazuh manager
containers.podman.podman_container:
name: wazuh.manager
image: "docker.io/wazuh/wazuh-manager:{{ wazuh_version }}"
hostname: wazuh.manager
restart_policy: always
recreate: true
state: started
env:
INDEXER_URL: "https://wazuh.indexer:9200"
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
FILEBEAT_SSL_VERIFICATION_MODE: "full"
SSL_CERTIFICATE_AUTHORITIES: "/etc/ssl/root-ca.pem"
SSL_CERTIFICATE: "/etc/ssl/filebeat.pem"
SSL_KEY: "/etc/ssl/filebeat.key"
API_USERNAME: "wazuh-wui"
API_PASSWORD: "MyS3cr37P450r.*-"
ulimit:
- "memlock=-1:-1"
- "nofile=655360:655360"
ports:
- "1514:1514"
- "1515:1515"
- "514:514/udp"
- "55000:55000"
volumes:
- "wazuh_api_configuration:/var/ossec/api/configuration"
- "wazuh_etc:/var/ossec/etc"
- "wazuh_logs:/var/ossec/logs"
- "wazuh_queue:/var/ossec/queue"
- "wazuh_var_multigroups:/var/ossec/var/multigroups"
- "wazuh_integrations:/var/ossec/integrations"
- "wazuh_agentless:/var/ossec/agentless"
- "wazuh_active_response:/var/ossec/active-response/bin"
- "wazuh_wodles:/var/ossec/wodles"
- "filebeat_etc:/etc/filebeat"
- "filebeat_var:/var/lib/filebeat"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key:z"
- "/opt/apps/wazuh/config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf:z"
network:
- npm_internal
ip: '{{ wazuh_manager_ip }}'
- name: Deploy Wazuh dashboard
containers.podman.podman_container:
name: wazuh.dashboard
image: "docker.io/wazuh/wazuh-dashboard:{{ wazuh_version }}"
hostname: wazuh.dashboard
restart_policy: always
recreate: true
state: started
env:
INDEXER_USERNAME: "admin"
INDEXER_PASSWORD: "*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv"
WAZUH_API_URL: "https://wazuh.manager"
DASHBOARD_USERNAME: "kibanaserver"
DASHBOARD_PASSWORD: "kibanaserver"
API_USERNAME: "wazuh-wui"
API_PASSWORD: "MyS3cr37P450r.*-"
volumes:
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem:z"
- "/opt/apps/wazuh/config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem:z"
- "/opt/apps/wazuh/config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml:z"
- "/opt/apps/wazuh/config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml:z"
- "wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config"
- "wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom"
network:
- npm_internal
ip: '{{ wazuh_dashboard_ip }}'

View File

@@ -0,0 +1,89 @@
---
- name: Ensure WireGuard kernel module is loaded
ansible.builtin.modprobe:
name: wireguard
state: present
- name: Persist WireGuard kernel module across reboots
ansible.builtin.copy:
dest: /etc/modules-load.d/wg-easy.conf
content: |
wireguard
nft_masq
mode: '0644'
# WireGuard Easy VPN (v15) — UI at 10.10.0.36:51821 on npm_internal, fronted by NPM
# (vpn.boxswapper.io -> http://10.10.0.36:51821). Login: admin / wireguard_ui_password from vars.yml.
# v15 init is ONE-TIME: the INIT_* envs below are consumed only when /etc/wireguard/wg-easy.db is ABSENT.
# Force a re-init (wipes clients + server keys): podman rm -f wg-easy && rm -rf /opt/apps/wireguard, then re-run.
# NAT from VPN clients to LAN/apps (10.10.1.0/24 -> 10.10.0.0/24) uses NFTABLES hooks —
# wg-easy's iptables-legacy hooks cannot init the nat table inside a podman netns
# ("Table does not exist"), which makes wg-quick up fail and every client API call
# 500 with "Unable to access interface". scripts/patch-wg-hooks.py rewrites the hooks
# to nft rules and the tasks below restart wg-easy to apply them. See the official
# wg-easy "Podman + nftables" tutorial.
# EXTERNAL ACCESS: router MUST forward UDP 51820 -> 192.168.0.98. Test from LTE,
# not your LAN — NAT hairpin breaks public-IP handshakes from inside the network.
- name: Deploy WireGuard Easy (wg-easy)
containers.podman.podman_container:
name: wg-easy
image: "{{ wireguard_image }}"
restart_policy: always
recreate: true
state: started
env:
INIT_ENABLED: "true"
INIT_USERNAME: "{{ wireguard_ui_username }}"
INIT_PASSWORD: "{{ wireguard_ui_password }}"
INIT_HOST: "{{ wireguard_host }}"
INIT_PORT: "{{ wireguard_port }}"
INIT_DNS: "{{ wireguard_default_dns }}"
INIT_IPV4_CIDR: "{{ wireguard_cidr }}"
INIT_IPV6_CIDR: "{{ wireguard_ipv6_cidr }}"
INIT_ALLOWED_IPS: "{{ wireguard_allowed_ips }}"
PORT: "{{ wireguard_ui_port }}"
INSECURE: "true"
DISABLE_VERSION_CHECK: "true"
ports:
- "{{ wireguard_port }}:{{ wireguard_port }}/udp"
- "{{ wireguard_ui_port }}:{{ wireguard_ui_port }}/tcp"
volumes:
- "/opt/apps/wireguard:/etc/wireguard:z"
- "/lib/modules:/lib/modules:ro"
cap_add:
- NET_ADMIN
- SYS_MODULE
- NET_RAW
sysctl:
net.ipv4.ip_forward: "1"
net.ipv4.conf.all.src_valid_mark: "1"
net.ipv6.conf.all.disable_ipv6: "0"
net.ipv6.conf.all.forwarding: "1"
net.ipv6.conf.default.forwarding: "1"
network:
- npm_internal
ip: '{{ wireguard_ip }}'
- name: Copy wg-easy hook repair script
ansible.builtin.copy:
src: ./scripts/patch-wg-hooks.py
dest: /opt/apps/wireguard/patch-wg-hooks.py
owner: root
group: root
mode: '0755'
- name: Replace iptables hooks with nftables (iptables-legacy broken in podman)
ansible.builtin.command: python3 /opt/apps/wireguard/patch-wg-hooks.py
register: wg_hooks_patch
changed_when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Restart wg-easy to apply nft hooks
ansible.builtin.command: podman restart wg-easy
when: "'patched hooks' in wg_hooks_patch.stdout"
- name: Wait for WireGuard Easy web UI
ansible.builtin.wait_for:
host: '{{ wireguard_ip }}'
port: '{{ wireguard_ui_port }}'
delay: 5
timeout: 120

View File

@@ -62,3 +62,8 @@ wazuh_dashboard_ip: 10.10.0.40
wazuh_manager_ip: 10.10.0.41
wazuh_indexer_ip: 10.10.0.42
## Wazuh agent (installed on this host by packages role)
## Server address the agent reports to (manager reachable on ports 1514/1515 via router forwarding)
wazuh_agent_rpm: wazuh-agent-4.14.1-1.x86_64.rpm
wazuh_server_address: wazuh.boxswapper.io

View File

@@ -1,24 +0,0 @@
# Deploy Wazuh Docker in single node configuration
This deployment is defined in the `docker-compose.yml` file with one Wazuh manager containers, one Wazuh indexer containers, and one Wazuh dashboard container. It can be deployed by following these steps:
1) Increase max_map_count on your host (Linux). This command must be run with root permissions:
```
$ sysctl -w vm.max_map_count=262144
```
2) Run the certificate creation script:
```
$ docker compose -f generate-indexer-certs.yml run --rm generator
```
3) Start the environment with docker compose:
- In the foregroud:
```
$ docker compose up
```
- In the background:
```
$ docker compose up -d
```
The environment takes about 1 minute to get up (depending on your Docker host) for the first time since Wazuh Indexer must be started for the first time and the indexes and index patterns must be generated.

View File

@@ -1,109 +0,0 @@
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
services:
wazuh.manager:
image: wazuh/wazuh-manager:5.0.0
hostname: wazuh.manager
restart: always
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 655360
hard: 655360
ports:
- "1514:1514"
- "1515:1515"
- "514:514/udp"
- "55000:55000"
environment:
- INDEXER_URL=https://wazuh.indexer:9200
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=*WD*oW9m6iKf4hDovy7BbMmhLxC9D?Tv
- FILEBEAT_SSL_VERIFICATION_MODE=full
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
- SSL_KEY=/etc/ssl/filebeat.key
- API_USERNAME=wazuh-wui
- API_PASSWORD=MyS3cr37P450r.*-
volumes:
- wazuh_api_configuration:/var/ossec/api/configuration
- wazuh_etc:/var/ossec/etc
- wazuh_logs:/var/ossec/logs
- wazuh_queue:/var/ossec/queue
- wazuh_var_multigroups:/var/ossec/var/multigroups
- wazuh_active_response:/var/ossec/active-response/bin
- wazuh_wodles:/var/ossec/wodles
- filebeat_etc:/etc/filebeat
- filebeat_var:/var/lib/filebeat
- ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key
- ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf
wazuh.indexer:
image: wazuh/wazuh-indexer:5.0.0
hostname: wazuh.indexer
restart: always
ports:
- "9200:9200"
environment:
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
volumes:
- wazuh-indexer-data:/var/lib/wazuh-indexer
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/config/certs/root-ca.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.key
- ./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/config/certs/wazuh.indexer.pem
- ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/config/certs/admin.pem
- ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/config/certs/admin-key.pem
- ./config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/config/opensearch.yml
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/config/opensearch-security/internal_users.yml
wazuh.dashboard:
image: wazuh/wazuh-dashboard:5.0.0
hostname: wazuh.dashboard
restart: always
ports:
- 443:5601
environment:
- INDEXER_USERNAME=admin
- INDEXER_PASSWORD=SecretPassword
- WAZUH_API_URL=https://wazuh.manager
- DASHBOARD_USERNAME=kibanaserver
- DASHBOARD_PASSWORD=kibanaserver
- API_USERNAME=wazuh-wui
- API_PASSWORD=MyS3cr37P450r.*-
volumes:
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
- ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
- ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
- wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config
- wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
depends_on:
- wazuh.indexer
links:
- wazuh.indexer:wazuh.indexer
- wazuh.manager:wazuh.manager
volumes:
wazuh_api_configuration:
wazuh_etc:
wazuh_logs:
wazuh_queue:
wazuh_var_multigroups:
wazuh_active_response:
wazuh_wodles:
filebeat_etc:
filebeat_var:
wazuh-indexer-data:
wazuh-dashboard-config:
wazuh-dashboard-custom:

View File

@@ -1,10 +0,0 @@
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
services:
generator:
image: wazuh/wazuh-certs-generator:0.0.3
hostname: wazuh-certs-generator
environment:
- CERT_TOOL_VERSION=4.14
volumes:
- ./config/wazuh_indexer_ssl_certs/:/certificates/
- ./config/certs.yml:/config/certs.yml