diff --git a/README.md b/README.md index d0e055a..8ecd5d0 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ An Ansible role for infrastructure components as Docker containers. Handles task | [godns](https://github.com/TimothyYe/godns) | Dynamic DNS | No | | [Wireguard](https://github.com/linuxserver/docker-wireguard) | Remote access VPN | No | | [Unifi](https://github.com/linuxserver/docker-unifi-controller) | Unifi network management | No | +| [Wazuh](https://github.com/wazuh/wazuh) | Wazuh Security Platform | No | ## Installation @@ -54,6 +55,14 @@ infra_graylog_password_secret: "super-long-random-string-minimum-64-chars" # Hash of the password used for the root user [run `echo -n yourpassword | shasum -a 256`] infra_graylog_password_sha2: "sha256-sum-of-your-password" +# If `infra_use_wazuh` is true, then +# Password for the wazuh indexer 'admin' user (infra_wazuh_indexer_admin_user) +infra_wazuh_indexer_admin_password: ~ +# Password for the wazuh indexer 'dashboard' user (infra_wazuh_indexer_dashboard_user) +infra_wazuh_indexer_dashboard_password: ~ +# Password for the wazuh api user (infra_wazuh_api_username). NB: The password for Wazuh API users must be between 8 and 64 characters long. It must contain at least one uppercase and one lowercase letter, a number, and a symbol. +infra_wazuh_api_password: ~ + ``` ### Recommended configuration changes diff --git a/defaults/main/main.yml b/defaults/main/main.yml index de03f08..2763c8c 100644 --- a/defaults/main/main.yml +++ b/defaults/main/main.yml @@ -58,4 +58,6 @@ infra_use_authentik: true infra_use_godns: false # Configure uptime-kuma infra_use_uptimekuma: true +# Configure wazuh +infra_use_wazuh: false ... diff --git a/defaults/main/wazuh.yml b/defaults/main/wazuh.yml new file mode 100644 index 0000000..72664f3 --- /dev/null +++ b/defaults/main/wazuh.yml @@ -0,0 +1,171 @@ +--- +### wazuh ### +# Version of the wazuh Docker image to use (see 'infra_wazuh_manager_container_image', 'infra_wazuh_indexer_container_image', 'infra_wazuh_dashboard_container_image') +infra_wazuh_version: 4.8.1 +# Version of the wazuh Docker image to use (see 'infra_wazuh_cert_tool_container_image') +infra_wazuh_cert_tool_version: 0.0.2 + +## wazuh secrets +# Password for the wazuh indexer 'admin' user (infra_wazuh_indexer_admin_user) +infra_wazuh_indexer_admin_password: ~ +# Password for the wazuh indexer 'dashboard' user (infra_wazuh_indexer_dashboard_user) +infra_wazuh_indexer_dashboard_password: ~ +# Password for the wazuh api user (infra_wazuh_api_username). NB: The password for Wazuh API users must be between 8 and 64 characters long. It must contain at least one uppercase and one lowercase letter, a number, and a symbol. +infra_wazuh_api_password: ~ + +## wazuh users +# username for the indexer 'admin' user +infra_wazuh_indexer_admin_user: admin +# username for the indexer 'dashboard' user +infra_wazuh_indexer_dashboard_user: kibanaserver +# api username +infra_wazuh_api_user: wazuh + +## wazuh settings +# wazuh manager settings. See https://github.com/wazuh/wazuh-docker#manager +infra_wazuh_manager_settings: {} +# wazuh indexer settings. See https://github.com/wazuh/wazuh-docker#indexer +infra_wazuh_indexer_settings: {} +# wazuh dashboard settings. See https://github.com/wazuh/wazuh-docker#dashboard +infra_wazuh_dashboard_settings: {} + +## Directories +# Directory path +infra_wazuh_directory_path: "{{ infra_directory_path }}/wazuh" +# Directory user ownership +infra_wazuh_directory_owner: "{{ infra_directory_owner }}" +# Directory group ownership +infra_wazuh_directory_group: "{{ infra_directory_group }}" +# Directory mode +infra_wazuh_directory_mode: "{{ infra_directory_mode }}" +# wazuh config directory path +infra_wazuh_config_directory_path: "{{ infra_wazuh_directory_path }}/config" +# wazuh config directory user ownership +infra_wazuh_config_directory_owner: "{{ infra_directory_owner }}" +# wazuh configdirectory group ownership +infra_wazuh_config_directory_group: "{{ infra_directory_group }}" +# wazuh configdirectory mode +infra_wazuh_config_directory_mode: "{{ infra_directory_mode }}" +# wazuh certificates directory path +infra_wazuh_certificates_directory_path: "{{ infra_wazuh_config_directory_path }}/certificates" + +## File paths +# File mode +infra_wazuh_file_mode: "{{ infra_file_mode }}" +# wazuh compose file path +infra_wazuh_compose_path: "{{ infra_wazuh_directory_path }}/compose.yml" +# wazuh generate-certs compose file path +infra_wazuh_generate_certs_compose_path: "{{ infra_wazuh_directory_path }}/generate-certs.yml" +# wazuh env file path +infra_wazuh_manager_env_file_path: "{{ infra_wazuh_directory_path }}/wazuh-manager.env" +# wazuh env file path +infra_wazuh_indexer_env_file_path: "{{ infra_wazuh_directory_path }}/wazuh-indexer.env" +# wazuh env file path +infra_wazuh_dashboard_env_file_path: "{{ infra_wazuh_directory_path }}/wazuh-dashboard.env" +# Permissions (mode) for the env files (as an octal) +infra_wazuh_env_file_mode: "0600" +# wazuh certs.yml config file path +infra_wazuh_certs_conf_file_path: "{{ infra_wazuh_config_directory_path }}/certs.yml" +# wazuh manager config file path +infra_wazuh_manager_conf_file_path: "{{ infra_wazuh_config_directory_path }}/wazuh-manager.conf" +# wazuh indexer users config file path +infra_wazuh_indexer_internal_users_config_path: "{{ infra_wazuh_config_directory_path }}/internal_users.yml" +# wazuh dashboard config file path +infra_wazuh_dashboard_conf_file_path: "{{ infra_wazuh_config_directory_path }}/wazuh.yml" +# wazuh init file path (check if previously initialized) +infra_wazuh_init_file_path: "{{ infra_wazuh_directory_path }}/.initialized" + +## Docker service configs +# Base name of the wazuh Docker service +infra_wazuh_service_name: wazuh +# Name of the wazuh manager Docker service +infra_wazuh_manager_service_name: "{{ infra_wazuh_service_name }}.manager" +# Name of the wazuh indexer Docker service +infra_wazuh_indexer_service_name: "{{ infra_wazuh_service_name }}.indexer" +# Name of the wazuh dashboard Docker service +infra_wazuh_dashboard_service_name: "{{ infra_wazuh_service_name }}.dashboard" +# Container image to use for the wazuh manager service +infra_wazuh_manager_container_image: "docker.io/wazuh/wazuh-manager:{{ infra_wazuh_version }}" +# Container image to use for the wazuh indexer service +infra_wazuh_indexer_container_image: "docker.io/wazuh/wazuh-indexer:{{ infra_wazuh_version }}" +# Container image to use for the wazuh dashboard service +infra_wazuh_dashboard_container_image: "docker.io/wazuh/wazuh-dashboard:{{ infra_wazuh_version }}" +# Container image to use for the wazuh certificates tool +infra_wazuh_cert_tool_container_image: "docker.io/wazuh/wazuh-certs-generator:{{ infra_wazuh_cert_tool_version }}" +# Hostname of the wazuh manager Docker container +infra_wazuh_manager_container_hostname: "{{ infra_wazuh_manager_service_name }}" +# Hostname of the wazuh indexer Docker container +infra_wazuh_indexer_container_hostname: "{{ infra_wazuh_indexer_service_name }}" +# Hostname of the wazuh dashboard Docker container +infra_wazuh_dashboard_container_hostname: "{{ infra_wazuh_dashboard_service_name }}" +# Hostname of the wazuh cert-tool Docker container +infra_wazuh_cert_tool_hostname: "{{ infra_wazuh_service_name }}-certs-generator" +# FQDN of the wazuh dashboard Docker container +infra_wazuh_fqdn: "{{ infra_wazuh_service_name }}.{{ infra_domain }}" +# Restart policy for the wazuh Docker containers +infra_wazuh_restart_policy: "{{ infra_restart_policy }}" +# Memory limit for the wazuh manager container +infra_wazuh_manager_container_memory: 4g +# Memory limit for the wazuh indexer container +infra_wazuh_indexer_container_memory: 6g +# Memory limit for the wazuh dashboard container +infra_wazuh_dashboard_container_memory: 2g +# agentd port (secure event listener) for the wazuh manager +infra_wazuh_manager_port_agentd: 1514 +# agentAuthd port (agent enrollment service) for the wazuh manager +infra_wazuh_manager_port_auth: 1515 +# syslog port (event listener) for the wazuh manager +infra_wazuh_manager_port_syslog: 514 +# api port for the wazuh manager +infra_wazuh_manager_port_api: 55000 +# api port for the wazuh indexer +infra_wazuh_indexer_port_api: 9200 +# Log driver for the wazuh containers +infra_wazuh_log_driver: local +# Log driver options for the containers +infra_wazuh_log_options: + max-size: 20m + max-file: '5' + compress: 'true' +# soft ulimit for memlock +infra_wazuh_ulimit_memlock_soft: -1 +# hard ulimit for memlock +infra_wazuh_ulimit_memlock_hard: -1 +# soft ulimit for nofile +infra_wazuh_ulimit_nofile_soft: 65536 +# hard ulimit for nofile +infra_wazuh_ulimit_nofile_hard: 65536 + +## Docker volume configs +# Name of the manager api config Docker volume +infra_wazuh_manager_volume_name_api_config: "{{ infra_wazuh_service_name }}_api_config" +# Name of the manager ossec config Docker volume +infra_wazuh_manager_volume_name_ossec_config: "{{ infra_wazuh_service_name }}_ossec_config" +# Name of the manager logs Docker volume +infra_wazuh_manager_volume_name_ossec_logs: "{{ infra_wazuh_service_name }}_ossec_logs" +# Name of the manager ossec queue Docker volume +infra_wazuh_manager_volume_name_ossec_queue: "{{ infra_wazuh_service_name }}_ossec_queue" +# Name of the manager ossec multigroups Docker volume +infra_wazuh_manager_volume_name_ossec_multigroups: "{{ infra_wazuh_service_name }}_ossec_multigroups" +# Name of the manager ossec integrations Docker volume +infra_wazuh_manager_volume_name_ossec_integrations: "{{ infra_wazuh_service_name }}_ossec_integrations" +# Name of the manager ossec active-response Docker volume +infra_wazuh_manager_volume_name_ossec_active_response: "{{ infra_wazuh_service_name }}_ossec_active-response" +# Name of the manager ossec agentless Docker volume +infra_wazuh_manager_volume_name_ossec_agentless: "{{ infra_wazuh_service_name }}_ossec_agentless" +# Name of the manager ossec wodles Docker volume +infra_wazuh_manager_volume_name_ossec_wodles: "{{ infra_wazuh_service_name }}_ossec_wodles" +# Name of the manager filebeat etc Docker volume +infra_wazuh_manager_volume_name_filebeat_etc: "{{ infra_wazuh_service_name }}_filebeat_etc" +# Name of the manager filebeat var Docker volume +infra_wazuh_manager_volume_name_filebeat_var: "{{ infra_wazuh_service_name }}_filebeat_var" +# Name of the indexer data Docker volume +infra_wazuh_indexer_volume_name_data: "{{ infra_wazuh_service_name }}_indexer_data" +# Name of the dashboard config Docker volume +infra_wazuh_dashboard_volume_name_config: "{{ infra_wazuh_service_name }}_dashboard_config" +# Name of the dashboard custom plugins Docker volume +infra_wazuh_dashboard_volume_name_custom_plugins: "{{ infra_wazuh_service_name }}_dashboard_custom" +# Labels to attach to the Docker volumes for this service +infra_wazuh_volume_labels: + netr0m.infra.service: wazuh +... diff --git a/tasks/deploy_wazuh.yml b/tasks/deploy_wazuh.yml new file mode 100644 index 0000000..59e4b73 --- /dev/null +++ b/tasks/deploy_wazuh.yml @@ -0,0 +1,188 @@ +--- +- name: Ensure wazuh directory is present + ansible.builtin.file: + path: "{{ infra_wazuh_directory_path }}" + state: directory + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + mode: "{{ infra_wazuh_directory_mode }}" + +- name: Ensure wazuh config directory is present + ansible.builtin.file: + path: "{{ infra_wazuh_config_directory_path }}" + state: directory + owner: "{{ infra_wazuh_config_directory_owner }}" + group: "{{ infra_wazuh_config_directory_group }}" + mode: "{{ infra_wazuh_config_directory_mode }}" + +- name: Check if initial setup + block: + - name: Stat init file + ansible.builtin.stat: + path: "{{ infra_wazuh_init_file_path }}" + register: infra_wazuh_init_file_stat_output + + - name: Set init fact + ansible.builtin.set_fact: + _infra_wazuh_initial_setup: "{{ not infra_wazuh_init_file_stat_output.stat.exists }}" + + - name: Create init file + when: _infra_wazuh_initial_setup + ansible.builtin.file: + path: "{{ infra_wazuh_init_file_path }}" + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + state: touch + +- name: Manage wazuh configuration + block: + - name: Write wazuh manager config to file + ansible.builtin.template: + src: wazuh/wazuh_manager.conf.j2 + dest: "{{ infra_wazuh_manager_conf_file_path }}" + owner: "{{ infra_wazuh_config_directory_owner }}" + group: "{{ infra_wazuh_config_directory_group }}" + mode: "{{ infra_wazuh_file_mode }}" + backup: true + register: wazuh_manager_config_file_output + + - name: Configure wazuh indexer users + when: _infra_wazuh_initial_setup + block: + - name: Hash indexer user passwords + ansible.builtin.include_tasks: wazuh_password_hasher.yml + loop: + - user: "{{ infra_wazuh_indexer_admin_user }}" + password: "{{ infra_wazuh_indexer_admin_password }}" + - user: "{{ infra_wazuh_indexer_dashboard_user }}" + password: "{{ infra_wazuh_indexer_dashboard_password }}" + + - name: Write wazuh indexer users config to file + ansible.builtin.template: + src: wazuh/internal_users.yml.j2 + dest: "{{ infra_wazuh_indexer_internal_users_config_path }}" + owner: "{{ infra_wazuh_config_directory_owner }}" + group: "{{ infra_wazuh_config_directory_group }}" + mode: "{{ infra_wazuh_file_mode }}" + backup: true + register: wazuh_indexer_users_config_file_output + + - name: Write wazuh dashboard config to file + ansible.builtin.template: + src: wazuh/dashboard.yml.j2 + dest: "{{ infra_wazuh_dashboard_conf_file_path }}" + owner: "{{ infra_wazuh_config_directory_owner }}" + group: "{{ infra_wazuh_config_directory_group }}" + mode: "{{ infra_wazuh_file_mode }}" + backup: true + register: wazuh_dashboard_config_file_output + +- name: Manage service files + block: + - name: Write wazuh manager environment variables to file + ansible.builtin.template: + src: template.env.j2 + dest: "{{ infra_wazuh_manager_env_file_path }}" + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + mode: "{{ infra_wazuh_env_file_mode }}" + backup: true + vars: + _env_vars: "{{ infra_wazuh_manager_settings | combine(infra_wazuh_manager_env_vars) }}" + register: wazuh_manager_env_file_output + + - name: Write wazuh indexer environment variables to file + ansible.builtin.template: + src: template.env.j2 + dest: "{{ infra_wazuh_indexer_env_file_path }}" + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + mode: "{{ infra_wazuh_env_file_mode }}" + backup: true + vars: + _env_vars: "{{ infra_wazuh_indexer_settings | combine(infra_wazuh_indexer_env_vars) }}" + register: wazuh_indexer_env_file_output + + - name: Write wazuh dashboard environment variables to file + ansible.builtin.template: + src: template.env.j2 + dest: "{{ infra_wazuh_dashboard_env_file_path }}" + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + mode: "{{ infra_wazuh_env_file_mode }}" + backup: true + vars: + _env_vars: "{{ infra_wazuh_dashboard_settings | combine(infra_wazuh_dashboard_env_vars) }}" + register: wazuh_dashboard_env_file_output + + - name: Copy compose services file into place + ansible.builtin.template: + src: compose/wazuh.yml.j2 + dest: "{{ infra_wazuh_compose_path }}" + owner: "{{ infra_wazuh_directory_owner }}" + group: "{{ infra_wazuh_directory_group }}" + mode: "{{ infra_wazuh_file_mode }}" + backup: true + validate: docker compose -f %s config -q + register: compose_file_output + +- name: Take down services due to changed compose file + community.docker.docker_compose_v2: + project_src: "{{ infra_wazuh_directory_path }}" + files: "{{ compose_file_output.backup_file }}" + state: absent + remove_orphans: true + when: compose_file_output.backup_file is defined + +- name: Pull container images + community.docker.docker_image: + name: "{{ item }}" + source: pull + with_items: + - "{{ infra_wazuh_manager_container_image }}" + - "{{ infra_wazuh_indexer_container_image }}" + - "{{ infra_wazuh_dashboard_container_image }}" + +- name: Generate certificates + when: _infra_wazuh_initial_setup + block: + - name: Write wazuh certificates config to file + ansible.builtin.template: + src: wazuh/certs.yml.j2 + dest: "{{ infra_wazuh_certs_conf_file_path }}" + owner: "{{ infra_wazuh_config_directory_owner }}" + group: "{{ infra_wazuh_config_directory_group }}" + mode: "{{ infra_wazuh_file_mode }}" + backup: true + register: wazuh_certs_config_file_output + + - name: Pull container image + community.docker.docker_image: + name: "{{ infra_wazuh_cert_tool_container_image }}" + source: pull + + - name: Run wazuh certs-generator + community.docker.docker_container: + image: "{{ infra_wazuh_cert_tool_container_image }}" + name: "{{ infra_wazuh_cert_tool_hostname }}" + hostname: "{{ infra_wazuh_cert_tool_hostname }}" + volumes: + - "{{ infra_wazuh_certificates_directory_path }}:/certificates/" + - "{{ infra_wazuh_certs_conf_file_path }}:/config/certs.yml" + auto_remove: true + +- name: Deploy wazuh services + community.docker.docker_compose_v2: + project_src: "{{ infra_wazuh_directory_path }}" + state: "{{ 'present' if infra_use_wazuh else 'absent' }}" + wait: true + wait_timeout: "{{ infra_wazuh_compose_wait_timeout | default(infra_compose_wait_timeout) }}" + register: deploy_wazuh_services_output + +- name: Clean up init config files + ansible.builtin.file: + path: "{{ item }}" + state: absent + with_items: + - "{{ infra_wazuh_indexer_internal_users_config_path }}" +... diff --git a/tasks/main.yml b/tasks/main.yml index c4cb03b..c976f2d 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -47,4 +47,9 @@ - name: Include 'uptimekuma' tasks ansible.builtin.import_tasks: deploy_uptimekuma.yml when: infra_use_uptimekuma + +# Manage wazuh deployment +- name: Include 'wazuh' tasks + ansible.builtin.import_tasks: deploy_wazuh.yml + when: infra_use_wazuh ... diff --git a/tasks/wazuh_password_hasher.yml b/tasks/wazuh_password_hasher.yml new file mode 100644 index 0000000..47d22b6 --- /dev/null +++ b/tasks/wazuh_password_hasher.yml @@ -0,0 +1,37 @@ +--- +- name: Create password hash for user '{{ item.user }}' + block: + - name: Run hash.sh + community.docker.docker_container: + image: "{{ infra_wazuh_indexer_container_image }}" + name: "{{ infra_wazuh_indexer_service_name }}-pwhash" + command: bash /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p {{ item.password }} + interactive: true + detach: false + register: docker_hash_password_result + + - name: Set temporary password hash variable + ansible.builtin.set_fact: + __extracted_pw_hash: "{{ docker_hash_password_result.container.Output | trim | split('\n') | last }}" + when: docker_hash_password_result.container.Output + + - name: Set password hash fact for '{{ infra_wazuh_indexer_admin_user }}' + when: item.user == infra_wazuh_indexer_admin_user + ansible.builtin.set_fact: + __infra_wazuh_indexer_admin_password_hash: "{{ __extracted_pw_hash }}" + failed_when: + - __extracted_pw_hash is not regex('^\$.*') + + - name: Set password hash fact for '{{ infra_wazuh_indexer_dashboard_user }}' + when: item.user == infra_wazuh_indexer_dashboard_user + ansible.builtin.set_fact: + __infra_wazuh_indexer_dashboard_password_hash: "{{ __extracted_pw_hash }}" + failed_when: + - __extracted_pw_hash is not regex('^\$.*') + +- name: Cleanup password hashing container + community.docker.docker_container: + image: "{{ infra_wazuh_indexer_container_image }}" + name: "{{ infra_wazuh_indexer_service_name }}-pwhash" + state: absent +... diff --git a/templates/compose/wazuh.yml.j2 b/templates/compose/wazuh.yml.j2 new file mode 100644 index 0000000..8f69290 --- /dev/null +++ b/templates/compose/wazuh.yml.j2 @@ -0,0 +1,163 @@ +services: + {{ infra_wazuh_manager_service_name }}: + image: {{ infra_wazuh_manager_container_image }} + container_name: {{ infra_wazuh_manager_container_hostname }} + hostname: {{ infra_wazuh_manager_container_hostname }} + restart: {{ infra_wazuh_restart_policy | default(infra_restart_policy) }} + logging: + driver: {{ infra_wazuh_log_driver }} + options: {{ infra_wazuh_log_options }} + env_file: {{ infra_wazuh_manager_env_file_path }} + volumes: + - {{ infra_wazuh_manager_volume_name_api_config }}:/var/ossec/api/configuration + - {{ infra_wazuh_manager_volume_name_ossec_config }}:/var/ossec/etc + - {{ infra_wazuh_manager_volume_name_ossec_logs }}:/var/ossec/logs + - {{ infra_wazuh_manager_volume_name_ossec_queue }}:/var/ossec/queue + - {{ infra_wazuh_manager_volume_name_ossec_multigroups }}:/var/ossec/var/multigroups + - {{ infra_wazuh_manager_volume_name_ossec_integrations }}:/var/ossec/integrations + - {{ infra_wazuh_manager_volume_name_ossec_active_response }}:/var/ossec/active-response/bin + - {{ infra_wazuh_manager_volume_name_ossec_agentless }}:/var/ossec/agentless + - {{ infra_wazuh_manager_volume_name_ossec_wodles }}:/var/ossec/wodles + - {{ infra_wazuh_manager_volume_name_filebeat_etc }}:/etc/filebeat + - {{ infra_wazuh_manager_volume_name_filebeat_var }}:/var/lib/filebeat + - {{ infra_wazuh_manager_cert_root_ca_public_key_path }}:/etc/ssl/root-ca.pem + - {{ infra_wazuh_manager_cert_public_key_path }}:/etc/ssl/filebeat.pem + - {{ infra_wazuh_manager_cert_private_key_path }}:/etc/ssl/filebeat.key + - {{ infra_wazuh_manager_conf_file_path }}:/wazuh-config-mount/etc/ossec.conf + mem_limit: {{ infra_wazuh_manager_container_memory }} + networks: + - default + ports: + - {{ infra_wazuh_manager_port_agentd }}:1514 + - {{ infra_wazuh_manager_port_auth }}:1515 + - {{ infra_wazuh_manager_port_syslog }}:514/udp + - {{ infra_wazuh_manager_port_api }}:55000 + ulimits: + memlock: + soft: {{ infra_wazuh_ulimit_memlock_soft }} + hard: {{ infra_wazuh_ulimit_memlock_hard }} + nofile: + soft: {{ infra_wazuh_ulimit_nofile_soft }} + hard: {{ infra_wazuh_ulimit_nofile_hard }} + + {{ infra_wazuh_indexer_service_name }}: + image: {{ infra_wazuh_indexer_container_image }} + container_name: {{ infra_wazuh_indexer_container_hostname }} + hostname: {{ infra_wazuh_indexer_container_hostname }} + restart: {{ infra_wazuh_restart_policy | default(infra_restart_policy) }} + logging: + driver: {{ infra_wazuh_log_driver }} + options: {{ infra_wazuh_log_options }} + env_file: {{ infra_wazuh_indexer_env_file_path }} + volumes: + - {{ infra_wazuh_indexer_volume_name_data }}:/var/lib/wazuh-indexer + - {{ infra_wazuh_cert_root_ca_public_key_path }}:/usr/share/wazuh-indexer/certs/root-ca.pem + - {{ infra_wazuh_indexer_cert_public_key_path }}:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem + - {{ infra_wazuh_indexer_cert_private_key_path }}:/usr/share/wazuh-indexer/certs/wazuh.indexer.key + - {{ infra_wazuh_indexer_admin_cert_public_key_path }}:/usr/share/wazuh-indexer/certs/admin.pem + - {{ infra_wazuh_indexer_admin_cert_private_key_path }}:/usr/share/wazuh-indexer/certs/admin-key.pem +{% if _infra_wazuh_initial_setup %} + - {{ infra_wazuh_indexer_internal_users_config_path }}:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml +{% endif %} + mem_limit: {{ infra_wazuh_indexer_container_memory }} + networks: + - default + ports: + - {{ infra_wazuh_indexer_port_api }}:9200 + ulimits: + memlock: + soft: {{ infra_wazuh_ulimit_memlock_soft }} + hard: {{ infra_wazuh_ulimit_memlock_hard }} + nofile: + soft: {{ infra_wazuh_ulimit_nofile_soft }} + hard: {{ infra_wazuh_ulimit_nofile_hard }} + + {{ infra_wazuh_dashboard_service_name }}: + image: {{ infra_wazuh_dashboard_container_image }} + container_name: {{ infra_wazuh_dashboard_container_hostname }} + hostname: {{ infra_wazuh_dashboard_container_hostname }} + restart: {{ infra_wazuh_restart_policy | default(infra_restart_policy) }} + logging: + driver: {{ infra_wazuh_log_driver }} + options: {{ infra_wazuh_log_options }} + env_file: {{ infra_wazuh_dashboard_env_file_path }} + volumes: + - {{ infra_wazuh_dashboard_volume_name_config }}:/usr/share/wazuh-dashboard/data/wazuh/config + - {{ infra_wazuh_dashboard_volume_name_custom_plugins }}:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom + - {{ infra_wazuh_cert_root_ca_public_key_path }}:/usr/share/wazuh-dashboard/certs/root-ca.pem + - {{ infra_wazuh_dashboard_cert_public_key_path }}:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem + - {{ infra_wazuh_dashboard_cert_private_key_path }}:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem + - {{ infra_wazuh_dashboard_conf_file_path }}:/wazuh-config-mount/data/wazuh/config/wazuh.yml + mem_limit: {{ infra_wazuh_dashboard_container_memory }} + labels: + traefik.enable: 'true' + traefik.docker.network: {{ svc_docker_network_name }} + traefik.http.routers.{{ infra_wazuh_service_name }}-rtr.rule: "Host(\"{{ infra_wazuh_fqdn }}\")" + traefik.http.routers.{{ infra_wazuh_service_name }}-rtr.entrypoints: webSecure + traefik.http.services.{{ infra_wazuh_service_name }}-svc.loadbalancer.server.port: 5601 + traefik.http.services.{{ infra_wazuh_service_name }}-svc.loadbalancer.server.scheme: https + traefik.http.routers.{{ infra_wazuh_service_name }}-rtr.service: {{ infra_wazuh_service_name }}-svc + traefik.http.routers.{{ infra_wazuh_service_name }}-rtr.middlewares: lan-mwr@file + networks: + - default + - {{ svc_docker_network_name }} + depends_on: + - {{ infra_wazuh_indexer_service_name }} + ulimits: + memlock: + soft: {{ infra_wazuh_ulimit_memlock_soft }} + hard: {{ infra_wazuh_ulimit_memlock_hard }} + nofile: + soft: {{ infra_wazuh_ulimit_nofile_soft }} + hard: {{ infra_wazuh_ulimit_nofile_hard }} + +volumes: + {{ infra_wazuh_manager_volume_name_api_config }}: + name: {{ infra_wazuh_manager_volume_name_api_config }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_config }}: + name: {{ infra_wazuh_manager_volume_name_ossec_config }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_logs }}: + name: {{ infra_wazuh_manager_volume_name_ossec_logs }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_queue }}: + name: {{ infra_wazuh_manager_volume_name_ossec_queue }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_multigroups }}: + name: {{ infra_wazuh_manager_volume_name_ossec_multigroups }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_integrations }}: + name: {{ infra_wazuh_manager_volume_name_ossec_integrations }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_active_response }}: + name: {{ infra_wazuh_manager_volume_name_ossec_active_response }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_agentless }}: + name: {{ infra_wazuh_manager_volume_name_ossec_agentless }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_ossec_wodles }}: + name: {{ infra_wazuh_manager_volume_name_ossec_wodles }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_filebeat_etc }}: + name: {{ infra_wazuh_manager_volume_name_filebeat_etc }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_manager_volume_name_filebeat_var }}: + name: {{ infra_wazuh_manager_volume_name_filebeat_var }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_indexer_volume_name_data }}: + name: {{ infra_wazuh_indexer_volume_name_data }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_dashboard_volume_name_config }}: + name: {{ infra_wazuh_dashboard_volume_name_config }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + {{ infra_wazuh_dashboard_volume_name_custom_plugins }}: + name: {{ infra_wazuh_dashboard_volume_name_custom_plugins }} + labels: {{ infra_wazuh_volume_labels | combine(infra_docker_volume_shared_labels) }} + +networks: + default: + driver: bridge + internal: 'true' + {{ svc_docker_network_name }}: + external: 'true' diff --git a/templates/template.env.j2 b/templates/template.env.j2 index 9ca9e22..0d7fb87 100644 --- a/templates/template.env.j2 +++ b/templates/template.env.j2 @@ -1,4 +1,4 @@ # {{ ansible_managed }} {% for key, val in _env_vars.items() %} -{{ key }}={{ val }} +{{ key }}='{{ val }}' {% endfor %} diff --git a/templates/wazuh/certs.yml.j2 b/templates/wazuh/certs.yml.j2 new file mode 100644 index 0000000..8eacf5f --- /dev/null +++ b/templates/wazuh/certs.yml.j2 @@ -0,0 +1,13 @@ +# {{ ansible_managed }} +nodes: + indexer: + - name: {{ infra_wazuh_indexer_service_name }} + ip: {{ infra_wazuh_indexer_service_name }} + + server: + - name: {{ infra_wazuh_manager_service_name }} + ip: {{ infra_wazuh_manager_service_name }} + + dashboard: + - name: {{ infra_wazuh_dashboard_service_name }} + ip: {{ infra_wazuh_dashboard_service_name }} diff --git a/templates/wazuh/dashboard.yml.j2 b/templates/wazuh/dashboard.yml.j2 new file mode 100644 index 0000000..0ba629a --- /dev/null +++ b/templates/wazuh/dashboard.yml.j2 @@ -0,0 +1,8 @@ +# {{ ansible_managed }} +hosts: + - 1513629884013: + url: "https://{{ infra_wazuh_manager_service_name }}" + port: 55000 + username: "{{ infra_wazuh_indexer_dashboard_user }}" + password: "{{ infra_wazuh_indexer_dashboard_password }}" + run_as: false diff --git a/templates/wazuh/internal_users.yml.j2 b/templates/wazuh/internal_users.yml.j2 new file mode 100644 index 0000000..b72ad1f --- /dev/null +++ b/templates/wazuh/internal_users.yml.j2 @@ -0,0 +1,20 @@ +--- +# {{ ansible_managed }} +# This is the internal user database +# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh + +_meta: + type: "internalusers" + config_version: 2 + +{{ infra_wazuh_indexer_admin_user }}: + hash: "{{ __infra_wazuh_indexer_admin_password_hash }}" + reserved: true + backend_roles: + - "admin" + description: "Admin user" + +{{ infra_wazuh_indexer_dashboard_user }}: + hash: "{{ __infra_wazuh_indexer_dashboard_password_hash }}" + reserved: true + description: "Dashboard user" diff --git a/templates/wazuh/wazuh_manager.conf.j2 b/templates/wazuh/wazuh_manager.conf.j2 new file mode 100644 index 0000000..94775aa --- /dev/null +++ b/templates/wazuh/wazuh_manager.conf.j2 @@ -0,0 +1,308 @@ + + + yes + yes + no + no + no + smtp.example.wazuh.com + wazuh@example.wazuh.com + recipient@example.wazuh.com + 12 + alerts.log + 10m + 0 + + + + 3 + 12 + + + + + plain + + + + secure + 1514 + tcp + 131072 + + + + + no + yes + yes + yes + yes + yes + yes + yes + + + 43200 + + etc/rootcheck/rootkit_files.txt + etc/rootcheck/rootkit_trojans.txt + + yes + + + + yes + 1800 + 1d + yes + + wodles/java + wodles/ciscat + + + + + yes + yes + /var/log/osquery/osqueryd.results.log + /etc/osquery/osquery.conf + yes + + + + + no + 1h + yes + yes + yes + yes + yes + yes + yes + + + + 10 + + + + + yes + yes + 12h + yes + + + + yes + yes + 60m + + + + yes + + https://{{ infra_wazuh_indexer_service_name }}:9200 + + + + /etc/ssl/root-ca.pem + + /etc/ssl/filebeat.pem + /etc/ssl/filebeat.key + + + + + + no + + + 43200 + + yes + + + yes + + + no + + + /etc,/usr/bin,/usr/sbin + /bin,/sbin,/boot + + + /etc/mtab + /etc/hosts.deny + /etc/mail/statistics + /etc/random-seed + /etc/random.seed + /etc/adjtime + /etc/httpd/logs + /etc/utmpx + /etc/wtmpx + /etc/cups/certs + /etc/dumpdates + /etc/svc/volatile + + + .log$|.swp$ + + + /etc/ssl/private.key + + yes + yes + yes + yes + + + 10 + + + 100 + + + + yes + 5m + 1h + 10 + + + + + + 127.0.0.1 + ^localhost.localdomain$ + + + + disable-account + disable-account + yes + + + + restart-wazuh + restart-wazuh + + + + firewall-drop + firewall-drop + yes + + + + host-deny + host-deny + yes + + + + route-null + route-null + yes + + + + win_route-null + route-null.exe + yes + + + + netsh + netsh.exe + yes + + + + + + + command + df -P + 360 + + + + full_command + netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d + netstat listening ports + 360 + + + + full_command + last -n 20 + 360 + + + + + ruleset/decoders + ruleset/rules + 0215-policy_rules.xml + etc/lists/audit-keys + etc/lists/amazon/aws-eventnames + etc/lists/security-eventchannel + + + etc/decoders + etc/rules + + + + yes + 1 + 64 + 15m + + + + + no + 1515 + no + yes + no + HIGH:!ADH:!EXP:!MD5:!RC4:!3DES:!CAMELLIA:@STRENGTH + + no + etc/sslmanager.cert + etc/sslmanager.key + no + + + + wazuh + node01 + master + aa093264ef885029653eea20dfcf51ae + 1516 + 0.0.0.0 + + {{ infra_wazuh_manager_service_name }} + + no + yes + + + + + + + syslog + /var/ossec/logs/active-responses.log + + + \ No newline at end of file diff --git a/vars/main/wazuh.yml b/vars/main/wazuh.yml new file mode 100644 index 0000000..8e7aed8 --- /dev/null +++ b/vars/main/wazuh.yml @@ -0,0 +1,86 @@ +--- +# Environment variables for the wazuh manager container +infra_wazuh_manager_env_vars: + INDEXER_URL: "https://{{ infra_wazuh_indexer_service_name }}:9200" + INDEXER_USERNAME: "{{ infra_wazuh_indexer_admin_user }}" + INDEXER_PASSWORD: "{{ infra_wazuh_indexer_admin_password }}" + 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: "{{ infra_wazuh_api_user }}" + API_PASSWORD: "{{ infra_wazuh_api_password }}" + +# Environment variables for the wazuh indexer container +infra_wazuh_indexer_env_vars: + OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx{{ infra_wazuh_indexer_container_memory }}" + bootstrap.memory_lock: "true" + NODE_NAME: "{{ infra_wazuh_indexer_service_name }}" + CLUSTER_INITIAL_MASTER_NODES: "{{ infra_wazuh_indexer_service_name }}" + CLUSTER_NAME: "{{ infra_wazuh_service_name }}-cluster" + PATH_DATA: /var/lib/wazuh-indexer + PATH_LOGS: /var/log/wazuh-indexer + HTTP_PORT: 9200-9299 + TRANSPORT_TCP_PORT: 9300-9399 + COMPATIBILITY_OVERRIDE_MAIN_RESPONSE_VERSION: "true" + PLUGINS_SECURITY_SSL_HTTP_PEMCERT_FILEPATH: "/usr/share/wazuh-indexer/certs/{{ infra_wazuh_indexer_service_name }}.pem" + PLUGINS_SECURITY_SSL_HTTP_PEMKEY_FILEPATH: "/usr/share/wazuh-indexer/certs/{{ infra_wazuh_indexer_service_name }}.key" + PLUGINS_SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH: /usr/share/wazuh-indexer/certs/root-ca.pem + PLUGINS_SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH: "/usr/share/wazuh-indexer/certs/{{ infra_wazuh_indexer_service_name }}.pem" + PLUGINS_SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH: "/usr/share/wazuh-indexer/certs/{{ infra_wazuh_indexer_service_name }}.key" + PLUGINS_SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH: /usr/share/wazuh-indexer/certs/root-ca.pem + PLUGINS_SECURITY_SSL_HTTP_ENABLED: "true" + PLUGINS_SECURITY_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION: "false" + PLUGINS_SECURITY_SSL_TRANSPORT_RESOLVE_HOSTNAME: "false" + PLUGINS_SECURITY_AUTHCZ_ADMIN_DN: "CN=admin,OU=Wazuh,O=Wazuh,L=California,C=US" + PLUGINS_SECURITY_CHECK_SNAPSHOT_RESTORE_WRITE_PRIVILEGES: "true" + PLUGINS_SECURITY_ENABLE_SNAPSHOT_RESTORE_PRIVILEGE: "true" + PLUGINS_SECURITY_NODES_DN: "CN={{ infra_wazuh_indexer_service_name }},OU=Wazuh,O=Wazuh,L=California,C=US" + PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED: '["all_access", "security_rest_api_access"]' + PLUGINS_SECURITY_SYSTEM_INDICES_ENABLED: "true" + PLUGINS_SECURITY_SYSTEM_INDICES_INDICES: '[".opendistro-alerting-config", ".opendistro-alerting-alert*", ".opendistro-anomaly-results*", ".opendistro-anomaly-detector*", ".opendistro-anomaly-checkpoints", ".opendistro-anomaly-detection-state", ".opendistro-reports-*", ".opendistro-notifications-*", ".opendistro-notebooks", ".opensearch-observability", ".opendistro-asynchronous-search-response*", ".replication-metadata-store"]' + PLUGINS_SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX: "true" + CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED: "false" + +# Environment variables for the wazuh dashboard container +infra_wazuh_dashboard_env_vars: + WAZUH_API_URL: "https://{{ infra_wazuh_manager_service_name }}" + DASHBOARD_USERNAME: "{{ infra_wazuh_indexer_dashboard_user }}" + DASHBOARD_PASSWORD: "{{ infra_wazuh_indexer_dashboard_password }}" + API_USERNAME: "{{ infra_wazuh_api_user }}" + API_PASSWORD: "{{ infra_wazuh_api_password }}" + SERVER_HOST: 0.0.0.0 + SERVER_PORT: 5601 + OPENSEARCH_HOSTS: "https://{{ infra_wazuh_indexer_service_name }}:9200" + OPENSEARCH_SSL_VERIFICATIONMODE: certificate + OPENSEARCH_REQUESTHEADERSALLOWLIST: '["securitytenant","Authorization"]' + OPENSEARCH_SECURITY_MULTITENANCY_ENABLED: "false" + SERVER_SSL_ENABLED: "true" + OPENSEARCH_SECURITY_READONLY_MODE_ROLES: '["kibana_read_only"]' + SERVER_SSL_KEY: "/usr/share/wazuh-dashboard/certs/{{ infra_wazuh_dashboard_service_name }}-key.pem" + SERVER_SSL_CERTIFICATE: "/usr/share/wazuh-dashboard/certs/{{ infra_wazuh_indexer_service_name }}.pem" + OPENSEARCH_SSL_CERTIFICATEAUTHORITIES: '["/usr/share/wazuh-dashboard/certs/root-ca.pem"]' + UISETTINGS_OVERRIDES_DEFAULTROUTE: /app/wz-home + +## Certificate paths. See https://documentation.wazuh.com/current/user-manual/manager/wazuh-server-cluster.html#generating-wazuh-server-certificates +# Certificate path for the wazuh manager root CA +infra_wazuh_manager_cert_root_ca_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/root-ca-manager.pem" +# Certificate path for the wazuh manager public key +infra_wazuh_manager_cert_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_manager_service_name }}.pem" +# Certificate path for the wazuh manager private key +infra_wazuh_manager_cert_private_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_manager_service_name }}-key.pem" +# Certificate path for the wazuh root CA +infra_wazuh_cert_root_ca_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/root-ca.pem" +# Certificate path for the wazuh indexer public key +infra_wazuh_indexer_cert_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_indexer_service_name }}.pem" +# Certificate path for the wazuh indexer private key +infra_wazuh_indexer_cert_private_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_indexer_service_name }}-key.pem" +# Certificate path for the wazuh indexer admin public key +infra_wazuh_indexer_admin_cert_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/admin.pem" +# Certificate path for the wazuh indexer admin private key +infra_wazuh_indexer_admin_cert_private_key_path: "{{ infra_wazuh_certificates_directory_path }}/admin-key.pem" +# Certificate path for the wazuh dashboard public key +infra_wazuh_dashboard_cert_public_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_dashboard_service_name }}.pem" +# Certificate path for the wazuh indexer private key +infra_wazuh_dashboard_cert_private_key_path: "{{ infra_wazuh_certificates_directory_path }}/{{ infra_wazuh_dashboard_service_name }}-key.pem" +...