diff --git a/.github/workflows/Test_installation_assistant.yml b/.github/workflows/Test_installation_assistant.yml new file mode 100644 index 0000000..6393629 --- /dev/null +++ b/.github/workflows/Test_installation_assistant.yml @@ -0,0 +1,62 @@ +run-name: Test installation assistant - System ${{ inputs.SYSTEM }} - Launched by @${{ github.actor }} +name: Test installation assistant + +on: + pull_request: + paths: + - 'cert_tool/**' + - 'common_functions/**' + - 'config/**' + - 'install_functions/**' + - 'passwords_tool/**' + - 'tests/**' + workflow_dispatch: + inputs: + REPOSITORY: + description: 'Repository environment' + required: true + default: 'pre-release' + type: choice + options: + - staging + - pre-release + AUTOMATION_REFERENCE: + description: 'wazuh-automation reference' + required: true + default: 'v4.10.0' + SYSTEM: + description: 'Operating System' + required: true + default: 'CentOS 8' + type: choice + options: + - CentOS 7 + - CentOS 8 + - Amazon Linux 2 + - Ubuntu 16 + - Ubuntu 18 + - Ubuntu 20 + - Ubuntu 22 + - RHEL7 + - RHEL8 + DEBUG: + description: 'Debug mode' + required: true + default: false + type: boolean + DESTROY: + description: 'Destroy instances after run' + required: true + default: true + type: boolean + +env: + LABEL: ubuntu-latest + +jobs: + initialize-environment: + runs-on: $LABEL + + steps: + - name: Set up Git + uses: actions/checkout@v3 diff --git a/.github/workflows/Test_installation_assistant_distributed.yml b/.github/workflows/Test_installation_assistant_distributed.yml new file mode 100644 index 0000000..b2d91aa --- /dev/null +++ b/.github/workflows/Test_installation_assistant_distributed.yml @@ -0,0 +1,47 @@ +run-name: (Distributed) Test installation assistant - Launched by @${{ github.actor }} +name: (Distributed) Test installation assistant + +on: + pull_request: + paths: + - 'cert_tool/**' + - 'common_functions/**' + - 'config/**' + - 'install_functions/**' + - 'passwords_tool/**' + - 'tests/**' + workflow_dispatch: + inputs: + REPOSITORY: + description: 'Repository environment' + required: true + default: 'pre-release' + type: choice + options: + - staging + - pre-release + AUTOMATION_REFERENCE: + description: 'wazuh-automation reference' + required: true + default: 'v4.10.0' + DEBUG: + description: 'Debug mode' + required: true + default: false + type: boolean + DESTROY: + description: 'Destroy instances after run' + required: true + default: true + type: boolean + +env: + LABEL: ubuntu-latest + +jobs: + initialize-environment: + runs-on: $LABEL + + steps: + - name: Set up Git + uses: actions/checkout@v3 diff --git a/.github/workflows/Test_installation_assistant_tier.yml b/.github/workflows/Test_installation_assistant_tier.yml new file mode 100644 index 0000000..32170b0 --- /dev/null +++ b/.github/workflows/Test_installation_assistant_tier.yml @@ -0,0 +1,84 @@ +run-name: (Tier) Test installation assistant - Launched by @${{ github.actor }} +name: (Tier) Test installation assistant + +on: + workflow_dispatch: + inputs: + REPOSITORY: + description: 'Repository environment' + required: true + default: 'pre-release' + type: choice + options: + - staging + - pre-release + AUTOMATION_REFERENCE: + description: 'wazuh-automation reference' + required: true + default: 'v4.10.0' + CentOS_7: + description: 'CentOS 7' + required: true + default: false + type: boolean + CentOS_8: + description: 'CentOS 8' + required: true + default: true + type: boolean + Amazon_Linux_2: + description: 'Amazon Linux 2' + required: true + default: false + type: boolean + Ubuntu_16: + description: 'Ubuntu 16' + required: true + default: false + type: boolean + Ubuntu_18: + description: 'Ubuntu 18' + required: true + default: false + type: boolean + Ubuntu_20: + description: 'Ubuntu 20' + required: true + default: false + type: boolean + Ubuntu_22: + description: 'Ubuntu 22' + required: true + default: false + type: boolean + RHEL_7: + description: 'RHEL 7' + required: true + default: false + type: boolean + RHEL_8: + description: 'RHEL 8' + required: true + default: false + type: boolean + DEBUG: + description: 'Debug mode' + required: true + default: false + type: boolean + DESTROY: + description: 'Destroy instances after run' + required: true + default: true + type: boolean + +env: + LABEL: ubuntu-latest + +jobs: + launch-tests: + runs-on: $LABEL + + steps: + - name: Set up Git + uses: actions/checkout@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..85e2091 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,172 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## [4.10.0] + +### Changed + +- Added post-install validations for the Wazuh manager and Filebeat. ([#3059](https://github.com/wazuh/wazuh-packages/pull/3059)) + +### Fixed + +- Fixed Wazuh API validation ([#29](https://github.com/wazuh/wazuh-installation-assistant/pull/29)) + +## [4.9.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.9.1 + +## [4.9.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.9.0 + +## [4.8.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.8.1 + +## [4.8.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.8.0 + +## [4.7.5] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.5 + +## [4.7.4] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.4 + +## [4.7.3] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.3 + +## [4.7.2] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.2 + +## [4.7.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.1 + +## [v4.7.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.7.0 + +## [v4.6.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.6.0 + +## [v4.5.4] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.5.4 + +## [v4.5.3] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.5.3 + +## [v4.5.2] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.5.2 + +## [v4.5.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.5.1 + +## [v4.5.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.5.0 + +## [v4.4.5] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.5 + +## [v4.4.4] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.4 + +## [v4.4.3] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.3 + +## [v4.4.2] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.2 + +## [v4.3.11] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.11 + +## [v4.4.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.1 + +## [v4.4.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.4.0 + +## [v4.3.10] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.10 + +## [v4.3.9] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.9 + +## [v4.3.8] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.8 + +## [v4.3.7] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.7 + +## [v4.3.6] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.6 + +## [v4.3.5] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.5 + +## [v4.3.4] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.4 + +## [v4.3.3] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.3 + +## [v4.3.2] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.2 + +## [v4.2.7] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.7 + +## [v4.3.1] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.1 + +## [v4.3.0] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.3.0 + +## [v4.2.7] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.7 + +## [v4.2.6] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.7 + +## [v4.2.5] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.5 + +## [v4.2.4] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.4 + +## [v4.2.3] + +- https://github.com/wazuh/wazuh-packages/releases/tag/v4.2.3 diff --git a/README.md b/README.md index 280bb7a..dad0bdc 100644 --- a/README.md +++ b/README.md @@ -1 +1,176 @@ -# wazuh-installation-assistant \ No newline at end of file +# Wazuh installation assistant + +[![Slack](https://img.shields.io/badge/slack-join-blue.svg)](https://wazuh.com/community/join-us-on-slack/) +[![Email](https://img.shields.io/badge/email-join-blue.svg)](https://groups.google.com/forum/#!forum/wazuh) +[![Documentation](https://img.shields.io/badge/docs-view-green.svg)](https://documentation.wazuh.com) +[![Documentation](https://img.shields.io/badge/web-view-green.svg)](https://wazuh.com) +[![Twitter](https://img.shields.io/twitter/follow/wazuh?style=social)](https://twitter.com/wazuh) +[![YouTube](https://img.shields.io/youtube/views/peTSzcAueEc?style=social)](https://www.youtube.com/watch?v=peTSzcAueEc) + +## Table of Contents +1. [Overview](#overview) +2. [Tools](#tools) +3. [User Guide](#user-guide) +4. [Use Cases](#use-cases) +5. [Options Table](#options-table) +6. [Contribute](#contribute) +7. [Development Guide](#development-guide) +7. [More Information](#more-information) +9. [Authors](#authors) + +## Overview + +The Wazuh installation Assistant is a tool designed to simplify the deployment of Wazuh. It guides users through the process of installing Wazuh components. Key features include: + +- **Guided Installation**: Step-by-step instructions for easy setup. +- **Component Selection**: Install only the Wazuh components you need. +- **System Requirements Check**: Automatically checks if your system meets the necessary requirements. +- **Automated Configuration**: Reduces errors by automating most of the setup. +- **Multi-Platform Support**: Compatible with various Linux distributions like Ubuntu, CentOS, and Debian. + +## Tools + +The Wazuh installation assistant uses the following tools to enhance security during the installation process: + +- **Wazuh password tool**: Securely generate and manage passwords. [Learn more](https://documentation.wazuh.com/current/user-manual/user-administration/password-management.html). +- **Wazuh cert tool**: Manage SSL/TLS certificates for secure communications. [Learn more](https://documentation.wazuh.com/current/user-manual/wazuh-dashboard/certificates.html). + + + +## User Guide + +### Downloads +- [Download the Wazuh installation assistant.](https://packages.wazuh.com/4.10/wazuh-install.sh) +- [Download the Wazuh password tool.](https://packages.wazuh.com/4.10/wazuh-passwords-tool.sh) +- [Download the Wazuh cert tool.](https://packages.wazuh.com/4.10/wazuh-certs-tool.sh) + +### Build the scripts +As an alternative to downloading, use the `builder.sh` script to build the Wazuh installation assistant and tools: + + +1. Build the Wazuh installation assistant - `wazuh-install.sh`: + ```bash + bash builder.sh -i + ``` + +2. Build the Wazuh password tool - `wazuh-passwords-tool.sh`: + ```bash + bash builder.sh -p + ``` + +3. Build the Wazuh cert tool - `wazuh-certs-tool.sh`: + ```bash + bash builder.sh -c + ``` + +## Use Cases + +Start by downloading the [configuration file](https://packages.wazuh.com/4.10/config.yml) and replace the node names and IP values with the corresponding ones. + +> [!NOTE] +> It is not necessary to download the Wazuh password tool and the Wazuh cert tool to use the Wazuh installation assistant. The Wazuh installation assistant has embedded the previous tools. + +### Common commands + +1. Generate the passwords and certificates. Needs the [configuration file](https://packages.wazuh.com/4.10/config.yml). + ```bash + bash wazuh-install.sh -g + ``` +2. Install all central components on the local machine: + ```bash + bash wazuh-install.sh -a + ``` + +3. Uninstall all central components: + ```bash + bash wazuh-install.sh -u + ``` + +4. Install the Wazuh indexer specifying the same name as specified in the configuration file: + ```bash + bash wazuh-install.sh --wazuh-indexer + ``` + +5. Initialize the Wazuh indexer cluster: + ```bash + bash wazuh-install.sh --start-cluster + ``` + +6. Install the Wazuh server specifying the same name as specified in the configuration file: + ```bash + bash wazuh-install.sh --wazuh-server + ``` + +7. Install the Wazuh dashboard specifying the same name as specified in the configuration file: + ```bash + bash wazuh-install.sh --wazuh-dashboard + ``` + +8. Display all options and help: + ```bash + bash wazuh-install.sh -h + ``` + +## Options Table + +All the options for the Wazuh installation assistant are listed in the following table: +| Option | Description | +|---------------------------------------|----------------------------------------| +| `-a`, `--all-in-one` | Install and configure Wazuh server, Wazuh indexer, Wazuh dashboard. | +| `-c`, `--config-file ` | Path to the configuration file used to generate `wazuh-install-files.tar` file containing the files needed for installation. By default, the Wazuh installation assistant will search for a file named `config.yml` in the same path as the script. | +| `-dw`, `--download-wazuh ` | Download all the packages necessary for offline installation. Specify the type of packages to download for offline installation (`rpm`, `deb`). | +| `-fd`, `--force-install-dashboard` | Force Wazuh dashboard installation to continue even when it is not capable of connecting to the Wazuh indexer. | +| `-g`, `--generate-config-files` | Generate `wazuh-install-files.tar` file containing the files needed for installation from `config.yml`. In distributed deployments, you will need to copy this file to all hosts. | +| `-h`, `--help` | Display this help and exit. | +| `-i`, `--ignore-check` | Ignore the check for minimum hardware requirements. | +| `-o`, `--overwrite` | Overwrite previously installed components. This will erase all the existing configuration and data. | +| `-of`, `--offline-installation` | Perform an offline installation. This option must be used with `-a`, `-ws`, `-s`, `-wi`, or `-wd`. | +| `-p`, `--port` | Specify the Wazuh web user interface port. Default is the `443` TCP port. Recommended ports are: `8443`, `8444`, `8080`, `8888`, `9000`. | +| `-s`, `--start-cluster` | Initialize Wazuh indexer cluster security settings. | +| `-t`, `--tar ` | Path to tar file containing certificate files. By default, the Wazuh installation assistant will search for a file named `wazuh-install-files.tar` in the same path as the script. | +| `-u`, `--uninstall` | Uninstall all Wazuh components. This will erase all the existing configuration and data. | +| `-v`, `--verbose` | Show the complete installation output. | +| `-V`, `--version` | Show the version of the script and Wazuh packages. | +| `-wd`, `--wazuh-dashboard ` | Install and configure Wazuh dashboard, used for distributed deployments. | +| `-wi`, `--wazuh-indexer ` | Install and configure Wazuh indexer, used for distributed deployments. | +| `-ws`, `--wazuh-server ` | Install and configure Wazuh manager and Filebeat, used for distributed deployments. | + + +## Contribute + +If you want to contribute to our repository, please fork our GitHub repository and submit a pull request. Alternatively, you can share ideas through [our users' mailing list](https://groups.google.com/d/forum/wazuh). + +## Development Guide + +To ensure consistency in development, please follow these guidelines: + +- Write functions with a single objective and limited arguments. +- Use libraries selectively (e.g., `install_functions`). +- Main functions should not depend on specific implementations. +- Use descriptive names for variables and functions. +- Use `${var}` instead of `$(var)` and `$(command)` instead of backticks. +- Always quote variables: `"${var}"`. +- Use the `common_logger` function instead of `echo`. +- Check command results with `$?` or `PIPESTATUS`. +- Use timeouts for long commands. +- Ensure all necessary resources are available both online and offline. +- Check command existence with `command -v`. +- Parametrize all package versions. +- Use `| grep -q` instead of `| grep`. +- Use standard `$((..))` instead of old `$[]`. + +> [!TIP] +> *Additional check*: Run unit [tests](/tests/unit/README) before preparing a pull request. + +Some useful links and acknowledgment: +- [Bash meets solid](https://codewizardly.com/bash-meets-solid/) +- [Shellcheck](https://github.com/koalaman/shellcheck#gallery-of-bad-code) + +## More Information + +For more detailed instructions and advanced use cases, please refer to the [Wazuh Quickstart Guide](https://documentation.wazuh.com/current/quickstart.html). + + +## Authors + +Wazuh Copyright (C) 2015-2023 Wazuh Inc. (License GPLv2) \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..100f730 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,49 @@ +# Wazuh Open Source Project Security Policy + +Version: 2023-06-12 + +## Introduction +This document outlines the Security Policy for Wazuh's open source projects. It emphasizes our commitment to maintain a secure environment for our users and contributors, and reflects our belief in the power of collaboration to identify and resolve security vulnerabilities. + +## Scope +This policy applies to all open source projects developed, maintained, or hosted by Wazuh. + +## Reporting Security Vulnerabilities +If you believe you've discovered a potential security vulnerability in one of our open source projects, we strongly encourage you to report it to us responsibly. + +Please submit your findings as [security advisories](https://github.com/wazuh/wazuh-installation-assistant/security/advisories) under the "Security" tab in the relevant GitHub repository. Alternatively, you may send the details of your findings to security@wazuh.com. + +## Vulnerability Disclosure Policy +Upon receiving a report of a potential vulnerability, our team will initiate an investigation. If the reported issue is confirmed as a vulnerability, we will take the following steps: + +1. Acknowledgment: We will acknowledge the receipt of your vulnerability report and begin our investigation. + +2. Validation: We will validate the issue and work on reproducing it in our environment. + +3. Remediation: We will work on a fix and thoroughly test it + +4. Release & Disclosure: After 90 days from the discovery of the vulnerability, or as soon as a fix is ready and thoroughly tested (whichever comes first), we will release a security update for the affected project. We will also publicly disclose the vulnerability by publishing a CVE (Common Vulnerabilities and Exposures) and acknowledging the discovering party. + +5. Exceptions: In order to preserve the security of the Wazuh community at large, we might extend the disclosure period to allow users to patch their deployments. + +This 90-day period allows for end-users to update their systems and minimizes the risk of widespread exploitation of the vulnerability. + +## Automatic Scanning +We leverage GitHub Actions to perform automated scans of our supply chain. These scans assist us in identifying vulnerabilities and outdated dependencies in a proactive and timely manner. + +## Credit +We believe in giving credit where credit is due. If you report a security vulnerability to us, and we determine that it is a valid vulnerability, we will publicly credit you for the discovery when we disclose the vulnerability. If you wish to remain anonymous, please indicate so in your initial report. + +We do appreciate and encourage feedback from our community, but currently we do not have a bounty program. We might start bounty programs in the future. + +## Compliance with this Policy +We consider the discovery and reporting of security vulnerabilities an important public service. We encourage responsible reporting of any vulnerabilities that may be found in our site or applications. + +Furthermore, we will not take legal action against or suspend or terminate access to the site or services of those who discover and report security vulnerabilities in accordance with this policy because of the fact. + +We ask that all users and contributors respect this policy and the security of our community's users by disclosing vulnerabilities to us in accordance with this policy. + +## Changes to this Security Policy +This policy may be revised from time to time. Each version of the policy will be identified at the top of the page by its effective date. + +If you have any questions about this Security Policy, please contact us at security@wazuh.com diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..2da4316 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +4.10.0 diff --git a/builder.sh b/builder.sh new file mode 100755 index 0000000..f007604 --- /dev/null +++ b/builder.sh @@ -0,0 +1,332 @@ +#!/bin/bash + +# Tool to create wazuh-install.sh, wazuh-cert-tool.sh +# and wazuh-passwords-tool.sh +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +readonly base_path_builder="$(dirname "$(readlink -f "$0")")" +readonly resources_installer="${base_path_builder}/install_functions" +readonly resources_config="${base_path_builder}/config" +readonly resources_certs="${base_path_builder}/cert_tool" +readonly resources_passwords="${base_path_builder}/passwords_tool" +readonly resources_common="${base_path_builder}/common_functions" +readonly resources_download="${base_path_builder}/downloader" +source_branch="4.10.0" + +function getHelp() { + + echo -e "" + echo -e "NAME" + echo -e " $(basename "$0") - Builds the Wazuh installation assistant and tools." + echo -e "" + echo -e "SYNOPSIS" + echo -e " $(basename "$0") [-v] -i | -c | -p" + echo -e "" + echo -e "DESCRIPTION" + echo -e " -i, --installer" + echo -e " Builds the unattended installer single file wazuh-install.sh" + echo -e "" + echo -e " -c, --cert-tool" + echo -e " Builds the certificate creation tool wazuh-cert-tool.sh" + echo -e "" + echo -e " -d [pre-release|staging], --development" + echo -e " Use development repositories. By default it uses the pre-release package repository. If staging is specified, it will use that repository." + echo -e "" + echo -e " -p, --password-tool" + echo -e " Builds the password creation and modification tool wazuh-password-tool.sh" + echo -e "" + echo -e " -h, --help" + echo -e " Shows help." + exit 1 + +} + +function buildInstaller() { + + checkDistDetectURL + + output_script_path="${base_path_builder}/wazuh-install.sh" + + ## Create installer script + echo -n > "${output_script_path}" + + ## License + echo "#!/bin/bash + +# Wazuh installer +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation." >> "${output_script_path}" + echo >> "${output_script_path}" + + ## Installation variables + if [ -n "${development}" ]; then + echo 'readonly development=1' >> "${output_script_path}" + echo 'readonly repogpg="https://packages-dev.wazuh.com/key/GPG-KEY-WAZUH"' >> "${output_script_path}" + echo 'readonly repobaseurl="https://packages-dev.wazuh.com/'${devrepo}'"' >> "${output_script_path}" + echo 'readonly reporelease="unstable"' >> "${output_script_path}" + echo 'readonly filebeat_wazuh_module="${repobaseurl}/filebeat/wazuh-filebeat-0.4.tar.gz"' >> "${output_script_path}" + echo 'readonly bucket="packages-dev.wazuh.com"' >> "${output_script_path}" + echo 'readonly repository="'"${devrepo}"'"' >> "${output_script_path}" + sed -i 's|v${wazuh_version}|${wazuh_version}|g' "${resources_installer}/installVariables.sh" + else + echo 'readonly repogpg="https://packages.wazuh.com/key/GPG-KEY-WAZUH"' >> "${output_script_path}" + echo 'readonly repobaseurl="https://packages.wazuh.com/4.x"' >> "${output_script_path}" + echo 'readonly reporelease="stable"' >> "${output_script_path}" + echo 'readonly filebeat_wazuh_module="${repobaseurl}/filebeat/wazuh-filebeat-0.4.tar.gz"' >> "${output_script_path}" + echo 'readonly bucket="packages.wazuh.com"' >> "${output_script_path}" + echo 'readonly repository="4.x"' >> "${output_script_path}" + fi + echo >> "${output_script_path}" + grep -Ev '^#|^\s*$' ${resources_common}/commonVariables.sh >> "${output_script_path}" + grep -Ev '^#|^\s*$' ${resources_installer}/installVariables.sh >> "${output_script_path}" + echo >> "${output_script_path}" + + ## Configuration files as variables + configuration_files=($(find "${resources_config}" -type f)) + config_file_name=($(eval "echo "${configuration_files[@]}" | sed 's|${resources_config}||g;s|/|_|g;s|.yml||g'")) + for index in "${!config_file_name[@]}"; do + echo "config_file${config_file_name[$index]}=\"$(cat "${configuration_files[$index]}" | sed 's|\"|\\\"|g;s|\$|\\\$|g')\"" >> "${output_script_path}" + echo >> "${output_script_path}" + done + + ## Sigint trap + echo "trap installCommon_cleanExit SIGINT" >> "${output_script_path}" + + ## JAVA_HOME + echo "export JAVA_HOME=\"/usr/share/wazuh-indexer/jdk/\"" >> "${output_script_path}" + + ## Functions for all install function modules + install_modules=($(find "${resources_installer}" -type f)) + install_modules_names=($(eval "echo \"${install_modules[*]}\" | sed 's,${resources_installer}/,,g'")) + for i in "${!install_modules[@]}"; do + if [ "${install_modules_names[$i]}" != "installVariables.sh" ]; then + echo "# ------------ ${install_modules_names[$i]} ------------ " >> "${output_script_path}" + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' ${install_modules[$i]} >> "${output_script_path}" + echo >> "${output_script_path}" + fi + done + + ## dist-detect.sh + echo "function dist_detect() {" >> "${output_script_path}" + curl -s "https://raw.githubusercontent.com/wazuh/wazuh/${source_branch}/src/init/dist-detect.sh" | sed '/^#/d' >> "${output_script_path}" + echo "}" >> "${output_script_path}" + + ## Common functions + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${resources_common}/common.sh" >> "${output_script_path}" + + ## Certificate tool library functions + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${resources_certs}/certFunctions.sh" >> "${output_script_path}" + + ## Passwords tool library functions + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${resources_passwords}/passwordsFunctions.sh" >> "${output_script_path}" + + ## Main function and call to it + echo >> "${output_script_path}" + echo "main \"\$@\"" >> "${output_script_path}" + + checkFilebeatURL + +} + +function buildPasswordsTool() { + output_script_path="${base_path_builder}/wazuh-passwords-tool.sh" + + ## Create installer script + echo -n > "${output_script_path}" + + ## License + echo "#!/bin/bash + +# Wazuh installer +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation." >> "${output_script_path}" + + ## Common and Passwords tool variables + grep -Ev '^#|^\s*$' ${resources_common}/commonVariables.sh >> "${output_script_path}" + grep -Ev '^#|^\s*$' "${resources_passwords}/passwordsVariables.sh" >> "${output_script_path}" + echo >> "${output_script_path}" + + ## Functions for all password function modules + passwords_modules=($(find "${resources_passwords}" -type f)) + passwords_modules_names=($(eval "echo "${passwords_modules[@]}" | sed 's,${resources_passwords}/,,g'")) + for i in "${!passwords_modules[@]}"; do + if [ "${passwords_modules[$i]}" != "passwordsVariables.sh" ]; then + echo "# ------------ ${passwords_modules_names[$i]} ------------ " >> "${output_script_path}" + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${passwords_modules[$i]}" >> "${output_script_path}" + echo >> "${output_script_path}" + fi + done + + ## Common functions + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${resources_common}/common.sh" >> "${output_script_path}" + + ## Call to main function + echo >> "${output_script_path}" + echo "main \"\$@\"" >> "${output_script_path}" +} + +function buildCertsTool() { + output_script_path="${base_path_builder}/wazuh-certs-tool.sh" + + ## Create installer script + echo -n > "${output_script_path}" + + ## License + echo "#!/bin/bash + +# Wazuh installer +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation." >> "${output_script_path}" + + ## Common and Certs tool variables + grep -Ev '^#|^\s*$' ${resources_common}/commonVariables.sh >> "${output_script_path}" + grep -Ev '^#|^\s*$' "${resources_certs}/certVariables.sh" >> "${output_script_path}" + echo >> "${output_script_path}" + + ## Functions for all certs tool function modules + certs_modules=($(find "${resources_certs}" -type f)) + certs_modules_names=($(eval "echo "${certs_modules[@]}" | sed 's,${resources_certs}/,,g'")) + for i in "${!certs_modules[@]}"; do + if [ "${certs_modules[$i]}" != "certVariables.sh" ]; then + echo "# ------------ ${certs_modules_names[$i]} ------------ " >> "${output_script_path}" + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${certs_modules[$i]}" >> "${output_script_path}" + echo >> "${output_script_path}" + fi + done + + ## Common functions + sed -n '/^function [a-zA-Z_]\(\)/,/^}/p' "${resources_common}/common.sh" >> "${output_script_path}" + + ## Call to main function + echo >> "${output_script_path}" + echo "main \"\$@\"" >> "${output_script_path}" + +} + +function builder_main() { + + umask 066 + + while [ -n "${1}" ] + do + case "${1}" in + "-i"|"--installer") + installer=1 + shift 1 + ;; + "-c"|"--cert-tool") + certTool=1 + shift 1 + ;; + "-d"|"--development") + development=1 + if [ -n "${2}" ] && [ "${2}" = "staging" ]; then + devrepo="staging" + shift 2 + elif [ -n "${2}" ] && [ "${2}" = "pre-release" ]; then + devrepo="pre-release" + shift 2 + else + devrepo="pre-release" + shift 1 + fi + ;; + "-p"|"--password-tool") + passwordsTool=1 + shift 1 + ;; + "-h"|"--help") + getHelp + ;; + *) + echo "Unknow option: \"${1}\"" + getHelp + esac + done + + if [ -n "${installer}" ]; then + buildInstaller + chmod 500 ${output_script_path} + if [ -n "${change_filebeat_url}" ]; then + sed -i -E "s|(https.+)master(.+wazuh-template.json)|\1\\$\\{source_branch\\}\2|" "${resources_installer}/installVariables.sh" + fi + if [ -n "${development}" ]; then + sed -i 's|${wazuh_version}|v${wazuh_version}|g' "${resources_installer}/installVariables.sh" + fi + fi + + if [ -n "${passwordsTool}" ]; then + buildPasswordsTool + chmod 500 ${output_script_path} + fi + + if [ -n "${certTool}" ]; then + buildCertsTool + chmod 644 ${output_script_path} + fi +} + +function checkDistDetectURL() { + + urls=("https://raw.githubusercontent.com/wazuh/wazuh/${source_branch}/src/init/dist-detect.sh" + "https://raw.githubusercontent.com/wazuh/wazuh/v${source_branch}/src/init/dist-detect.sh" + "https://raw.githubusercontent.com/wazuh/wazuh/master/src/init/dist-detect.sh") + + for url in "${urls[@]}"; do + eval "curl -s -o /dev/null '${url}' --retry 5 --retry-delay 5 --max-time 300 --fail" + e_code="${PIPESTATUS[0]}" + + if [ "${e_code}" -eq 0 ]; then + source_branch=$(echo "${url}" | awk -F'/' '{print $(NF-3)}') + break + fi + done + + if [ "${e_code}" -ne 0 ]; then + echo -e "Error: Could not get the dist-detect file." + exit 1 + fi + +} + +function checkFilebeatURL() { + + # Import variables + eval "$(grep -E "filebeat_wazuh_template=" "${resources_installer}/installVariables.sh")" + new_filebeat_url="https://raw.githubusercontent.com/wazuh/wazuh/master/extensions/elasticsearch/7.x/wazuh-template.json" + + # Get the response of the URL and check it + response=$(curl -I --write-out '%{http_code}' --silent --output /dev/null $filebeat_wazuh_template) + if [ "${response}" != "200" ]; then + response=$(curl -I --write-out '%{http_code}' --silent --output /dev/null $new_filebeat_url) + + # Display error if both URLs do not get the resource + if [ "${response}" != "200" ]; then + echo -e "Error: Could not get the Filebeat Wazuh template. " + # If matches, replace the variable of installVariables to the new one + else + echo -e "Changing Filebeat URL..." + sed -i -E "s|filebeat_wazuh_template=.*|filebeat_wazuh_template=\"${new_filebeat_url}\"|g" "${resources_installer}/installVariables.sh" + change_filebeat_url=1 + fi + fi +} + +builder_main "$@" diff --git a/cert_tool/certFunctions.sh b/cert_tool/certFunctions.sh new file mode 100644 index 0000000..fc2bf51 --- /dev/null +++ b/cert_tool/certFunctions.sh @@ -0,0 +1,469 @@ +# Certificate tool - Library functions +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + + +function cert_cleanFiles() { + + common_logger -d "Cleaning certificate files." + eval "rm -f ${cert_tmp_path}/*.csr ${debug}" + eval "rm -f ${cert_tmp_path}/*.srl ${debug}" + eval "rm -f ${cert_tmp_path}/*.conf ${debug}" + eval "rm -f ${cert_tmp_path}/admin-key-temp.pem ${debug}" + +} + +function cert_checkOpenSSL() { + + common_logger -d "Checking if OpenSSL is installed." + + if [ -z "$(command -v openssl)" ]; then + common_logger -e "OpenSSL not installed." + exit 1 + fi + +} + +function cert_checkRootCA() { + + common_logger -d "Checking if the root CA exists." + + if [[ -n ${rootca} || -n ${rootcakey} ]]; then + # Verify variables match keys + if [[ ${rootca} == *".key" ]]; then + ca_temp=${rootca} + rootca=${rootcakey} + rootcakey=${ca_temp} + fi + # Validate that files exist + if [[ -e ${rootca} ]]; then + eval "cp ${rootca} ${cert_tmp_path}/root-ca.pem ${debug}" + else + common_logger -e "The file ${rootca} does not exists" + cert_cleanFiles + exit 1 + fi + if [[ -e ${rootcakey} ]]; then + eval "cp ${rootcakey} ${cert_tmp_path}/root-ca.key ${debug}" + else + common_logger -e "The file ${rootcakey} does not exists" + cert_cleanFiles + exit 1 + fi + else + cert_generateRootCAcertificate + fi + +} + +# Executes and analyze the output of the command. It prints the output +# in case of an error +function cert_executeAndValidate() { + + command_output=$(eval "$@" 2>&1) + e_code="${PIPESTATUS[0]}" + + if [ "${e_code}" -ne 0 ]; then + common_logger -e "Error generating the certificates." + common_logger -d "Error executing command: $@" + common_logger -d "Error output: ${command_output}" + cert_cleanFiles + exit 1 + fi + +} + +function cert_generateAdmincertificate() { + + common_logger "Generating Admin certificates." + common_logger -d "Generating Admin private key." + cert_executeAndValidate "openssl genrsa -out ${cert_tmp_path}/admin-key-temp.pem 2048" + common_logger -d "Converting Admin private key to PKCS8 format." + cert_executeAndValidate "openssl pkcs8 -inform PEM -outform PEM -in ${cert_tmp_path}/admin-key-temp.pem -topk8 -nocrypt -v1 PBE-SHA1-3DES -out ${cert_tmp_path}/admin-key.pem" + common_logger -d "Generating Admin CSR." + cert_executeAndValidate "openssl req -new -key ${cert_tmp_path}/admin-key.pem -out ${cert_tmp_path}/admin.csr -batch -subj '/C=US/L=California/O=Wazuh/OU=Wazuh/CN=admin'" + common_logger -d "Creating Admin certificate." + cert_executeAndValidate "openssl x509 -days 3650 -req -in ${cert_tmp_path}/admin.csr -CA ${cert_tmp_path}/root-ca.pem -CAkey ${cert_tmp_path}/root-ca.key -CAcreateserial -sha256 -out ${cert_tmp_path}/admin.pem" + +} + +function cert_generateCertificateconfiguration() { + + common_logger -d "Generating certificate configuration." + cat > "${cert_tmp_path}/${1}.conf" <<- EOF + [ req ] + prompt = no + default_bits = 2048 + default_md = sha256 + distinguished_name = req_distinguished_name + x509_extensions = v3_req + + [req_distinguished_name] + C = US + L = California + O = Wazuh + OU = Wazuh + CN = cname + + [ v3_req ] + authorityKeyIdentifier=keyid,issuer + basicConstraints = CA:FALSE + keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment + subjectAltName = @alt_names + + [alt_names] + IP.1 = cip + EOF + + + conf="$(awk '{sub("CN = cname", "CN = '"${1}"'")}1' "${cert_tmp_path}/${1}.conf")" + echo "${conf}" > "${cert_tmp_path}/${1}.conf" + + if [ "${#@}" -gt 1 ]; then + sed -i '/IP.1/d' "${cert_tmp_path}/${1}.conf" + for (( i=2; i<=${#@}; i++ )); do + isIP=$(echo "${!i}" | grep -P "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") + isDNS=$(echo "${!i}" | grep -P "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\.([A-Za-z]{2,})$" ) j=$((i-1)) + if [ "${isIP}" ]; then + printf '%s\n' " IP.${j} = ${!i}" >> "${cert_tmp_path}/${1}.conf" + elif [ "${isDNS}" ]; then + printf '%s\n' " DNS.${j} = ${!i}" >> "${cert_tmp_path}/${1}.conf" + else + common_logger -e "Invalid IP or DNS ${!i}" + exit 1 + fi + done + else + common_logger -e "No IP or DNS specified" + exit 1 + fi + +} + +function cert_generateIndexercertificates() { + + if [ ${#indexer_node_names[@]} -gt 0 ]; then + common_logger "Generating Wazuh indexer certificates." + + for i in "${!indexer_node_names[@]}"; do + indexer_node_name=${indexer_node_names[$i]} + common_logger -d "Creating the certificates for ${indexer_node_name} indexer node." + cert_generateCertificateconfiguration "${indexer_node_name}" "${indexer_node_ips[i]}" + common_logger -d "Creating the Wazuh indexer tmp key pair." + cert_executeAndValidate "openssl req -new -nodes -newkey rsa:2048 -keyout ${cert_tmp_path}/${indexer_node_name}-key.pem -out ${cert_tmp_path}/${indexer_node_name}.csr -config ${cert_tmp_path}/${indexer_node_name}.conf" + common_logger -d "Creating the Wazuh indexer certificates." + cert_executeAndValidate "openssl x509 -req -in ${cert_tmp_path}/${indexer_node_name}.csr -CA ${cert_tmp_path}/root-ca.pem -CAkey ${cert_tmp_path}/root-ca.key -CAcreateserial -out ${cert_tmp_path}/${indexer_node_name}.pem -extfile ${cert_tmp_path}/${indexer_node_name}.conf -extensions v3_req -days 3650" + done + else + return 1 + fi + +} + +function cert_generateFilebeatcertificates() { + + if [ ${#server_node_names[@]} -gt 0 ]; then + common_logger "Generating Filebeat certificates." + + for i in "${!server_node_names[@]}"; do + server_name="${server_node_names[i]}" + common_logger -d "Generating the certificates for ${server_name} server node." + j=$((i+1)) + declare -a server_ips=(server_node_ip_"$j"[@]) + cert_generateCertificateconfiguration "${server_name}" "${!server_ips}" + common_logger -d "Creating the Wazuh server tmp key pair." + cert_executeAndValidate "openssl req -new -nodes -newkey rsa:2048 -keyout ${cert_tmp_path}/${server_name}-key.pem -out ${cert_tmp_path}/${server_name}.csr -config ${cert_tmp_path}/${server_name}.conf" + common_logger -d "Creating the Wazuh server certificates." + cert_executeAndValidate "openssl x509 -req -in ${cert_tmp_path}/${server_name}.csr -CA ${cert_tmp_path}/root-ca.pem -CAkey ${cert_tmp_path}/root-ca.key -CAcreateserial -out ${cert_tmp_path}/${server_name}.pem -extfile ${cert_tmp_path}/${server_name}.conf -extensions v3_req -days 3650" + done + else + return 1 + fi + +} + +function cert_generateDashboardcertificates() { + if [ ${#dashboard_node_names[@]} -gt 0 ]; then + common_logger "Generating Wazuh dashboard certificates." + + for i in "${!dashboard_node_names[@]}"; do + dashboard_node_name="${dashboard_node_names[i]}" + cert_generateCertificateconfiguration "${dashboard_node_name}" "${dashboard_node_ips[i]}" + common_logger -d "Creating the Wazuh dashboard tmp key pair." + cert_executeAndValidate "openssl req -new -nodes -newkey rsa:2048 -keyout ${cert_tmp_path}/${dashboard_node_name}-key.pem -out ${cert_tmp_path}/${dashboard_node_name}.csr -config ${cert_tmp_path}/${dashboard_node_name}.conf" + common_logger -d "Creating the Wazuh dashboard certificates." + cert_executeAndValidate "openssl x509 -req -in ${cert_tmp_path}/${dashboard_node_name}.csr -CA ${cert_tmp_path}/root-ca.pem -CAkey ${cert_tmp_path}/root-ca.key -CAcreateserial -out ${cert_tmp_path}/${dashboard_node_name}.pem -extfile ${cert_tmp_path}/${dashboard_node_name}.conf -extensions v3_req -days 3650" + done + else + return 1 + fi + +} + +function cert_generateRootCAcertificate() { + + common_logger "Generating the root certificate." + cert_executeAndValidate "openssl req -x509 -new -nodes -newkey rsa:2048 -keyout ${cert_tmp_path}/root-ca.key -out ${cert_tmp_path}/root-ca.pem -batch -subj '/OU=Wazuh/O=Wazuh/L=California/' -days 3650" + +} + +function cert_parseYaml() { + + local prefix=$2 + local separator=${3:-_} + local indexfix + # Detect awk flavor + if awk --version 2>&1 | grep -q "GNU Awk" ; then + # GNU Awk detected + indexfix=-1 + elif awk -Wv 2>&1 | grep -q "mawk" ; then + # mawk detected + indexfix=0 + fi + + local s='[[:space:]]*' sm='[ \t]*' w='[a-zA-Z0-9_]*' fs=${fs:-$(echo @|tr @ '\034')} i=${i:- } + cat $1 2>/dev/null | \ + awk -F$fs "{multi=0; + if(match(\$0,/$sm\|$sm$/)){multi=1; sub(/$sm\|$sm$/,\"\");} + if(match(\$0,/$sm>$sm$/)){multi=2; sub(/$sm>$sm$/,\"\");} + while(multi>0){ + str=\$0; gsub(/^$sm/,\"\", str); + indent=index(\$0,str); + indentstr=substr(\$0, 0, indent+$indexfix) \"$i\"; + obuf=\$0; + getline; + while(index(\$0,indentstr)){ + obuf=obuf substr(\$0, length(indentstr)+1); + if (multi==1){obuf=obuf \"\\\\n\";} + if (multi==2){ + if(match(\$0,/^$sm$/)) + obuf=obuf \"\\\\n\"; + else obuf=obuf \" \"; + } + getline; + } + sub(/$sm$/,\"\",obuf); + print obuf; + multi=0; + if(match(\$0,/$sm\|$sm$/)){multi=1; sub(/$sm\|$sm$/,\"\");} + if(match(\$0,/$sm>$sm$/)){multi=2; sub(/$sm>$sm$/,\"\");} + } + print}" | \ + sed -e "s|^\($s\)?|\1-|" \ + -ne "s|^$s#.*||;s|$s#[^\"']*$||;s|^\([^\"'#]*\)#.*|\1|;t1;t;:1;s|^$s\$||;t2;p;:2;d" | \ + sed -ne "s|,$s\]$s\$|]|" \ + -e ":1;s|^\($s\)\($w\)$s:$s\(&$w\)\?$s\[$s\(.*\)$s,$s\(.*\)$s\]|\1\2: \3[\4]\n\1$i- \5|;t1" \ + -e "s|^\($s\)\($w\)$s:$s\(&$w\)\?$s\[$s\(.*\)$s\]|\1\2: \3\n\1$i- \4|;" \ + -e ":2;s|^\($s\)-$s\[$s\(.*\)$s,$s\(.*\)$s\]|\1- [\2]\n\1$i- \3|;t2" \ + -e "s|^\($s\)-$s\[$s\(.*\)$s\]|\1-\n\1$i- \2|;p" | \ + sed -ne "s|,$s}$s\$|}|" \ + -e ":1;s|^\($s\)-$s{$s\(.*\)$s,$s\($w\)$s:$s\(.*\)$s}|\1- {\2}\n\1$i\3: \4|;t1" \ + -e "s|^\($s\)-$s{$s\(.*\)$s}|\1-\n\1$i\2|;" \ + -e ":2;s|^\($s\)\($w\)$s:$s\(&$w\)\?$s{$s\(.*\)$s,$s\($w\)$s:$s\(.*\)$s}|\1\2: \3 {\4}\n\1$i\5: \6|;t2" \ + -e "s|^\($s\)\($w\)$s:$s\(&$w\)\?$s{$s\(.*\)$s}|\1\2: \3\n\1$i\4|;p" | \ + sed -e "s|^\($s\)\($w\)$s:$s\(&$w\)\(.*\)|\1\2:\4\n\3|" \ + -e "s|^\($s\)-$s\(&$w\)\(.*\)|\1- \3\n\2|" | \ + sed -ne "s|^\($s\):|\1|" \ + -e "s|^\($s\)\(---\)\($s\)||" \ + -e "s|^\($s\)\(\.\.\.\)\($s\)||" \ + -e "s|^\($s\)-$s[\"']\(.*\)[\"']$s\$|\1$fs$fs\2|p;t" \ + -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p;t" \ + -e "s|^\($s\)-$s\(.*\)$s\$|\1$fs$fs\2|" \ + -e "s|^\($s\)\($w\)$s:$s[\"']\?\(.*\)$s\$|\1$fs\2$fs\3|" \ + -e "s|^\($s\)[\"']\?\([^&][^$fs]\+\)[\"']$s\$|\1$fs$fs$fs\2|" \ + -e "s|^\($s\)[\"']\?\([^&][^$fs]\+\)$s\$|\1$fs$fs$fs\2|" \ + -e "s|$s\$||p" | \ + awk -F$fs "{ + gsub(/\t/,\" \",\$1); + gsub(\"name: \", \"\"); + if(NF>3){if(value!=\"\"){value = value \" \";}value = value \$4;} + else { + if(match(\$1,/^&/)){anchor[substr(\$1,2)]=full_vn;getline}; + indent = length(\$1)/length(\"$i\"); + vname[indent] = \$2; + value= \$3; + for (i in vname) {if (i > indent) {delete vname[i]; idx[i]=0}} + if(length(\$2)== 0){ vname[indent]= ++idx[indent] }; + vn=\"\"; for (i=0; i0)&&index(val, ref)==1){ + tmpval=assignment[val]; + sub(ref,full_vn,val); + if(match(val,\"$separator\$\")){ + gsub(ref,full_vn,tmpval); + } else if (length(tmpval) > 0) { + printf(\"%s=\\\"%s\\\"\n\", val, tmpval); + } + assignment[val]=tmpval; + } + } + } + } else if (length(value) > 0) { + printf(\"%s=\\\"%s\\\"\n\", full_vn, value); + } + }END{ + for(val in assignment){ + if(match(val,\"$separator\$\")) + printf(\"%s=\\\"%s\\\"\n\", val, assignment[val]); + } + }" + +} + +function cert_checkPrivateIp() { + + local ip=$1 + common_logger -d "Checking if ${ip} is private." + + # Check private IPv4 ranges + if [[ $ip =~ ^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^(127\.) ]]; then + return 0 + fi + + # Check private IPv6 ranges (fc00::/7 prefix) + if [[ $ip =~ ^fc ]]; then + return 0 + fi + + return 1 + +} + +function cert_readConfig() { + + common_logger -d "Reading configuration file." + + if [ -f "${config_file}" ]; then + if [ ! -s "${config_file}" ]; then + common_logger -e "File ${config_file} is empty" + exit 1 + fi + eval "$(cert_convertCRLFtoLF "${config_file}")" + + eval "indexer_node_names=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+indexer[_]+[0-9]+=" | cut -d = -f 2 ) )" + eval "server_node_names=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+server[_]+[0-9]+=" | cut -d = -f 2 ) )" + eval "dashboard_node_names=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+dashboard[_]+[0-9]+=" | cut -d = -f 2) )" + eval "indexer_node_ips=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+indexer[_]+[0-9]+[_]+ip=" | cut -d = -f 2) )" + eval "server_node_ips=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+server[_]+[0-9]+[_]+ip=" | cut -d = -f 2) )" + eval "dashboard_node_ips=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+dashboard[_]+[0-9]+[_]+ip=" | cut -d = -f 2 ) )" + eval "server_node_types=( $(cert_parseYaml "${config_file}" | grep -E "nodes[_]+server[_]+[0-9]+[_]+node_type=" | cut -d = -f 2 ) )" + eval "number_server_ips=( $(cert_parseYaml "${config_file}" | grep -o -E 'nodes[_]+server[_]+[0-9]+[_]+ip' | sort -u | wc -l) )" + all_ips=("${indexer_node_ips[@]}" "${server_node_ips[@]}" "${dashboard_node_ips[@]}") + + for ip in "${all_ips[@]}"; do + isIP=$(echo "${ip}" | grep -P "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$") + if [[ -n "${isIP}" ]]; then + if ! cert_checkPrivateIp "$ip"; then + common_logger -e "The IP ${ip} is public." + exit 1 + fi + fi + done + + for i in $(seq 1 "${number_server_ips}"); do + nodes_server="nodes[_]+server[_]+${i}[_]+ip" + eval "server_node_ip_$i=( $( cert_parseYaml "${config_file}" | grep -E "${nodes_server}" | sed '/\./!d' | cut -d = -f 2 | sed -r 's/\s+//g') )" + done + + unique_names=($(echo "${indexer_node_names[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_names[@]}" -ne "${#indexer_node_names[@]}" ]; then + common_logger -e "Duplicated indexer node names." + exit 1 + fi + + unique_ips=($(echo "${indexer_node_ips[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_ips[@]}" -ne "${#indexer_node_ips[@]}" ]; then + common_logger -e "Duplicated indexer node ips." + exit 1 + fi + + unique_names=($(echo "${server_node_names[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_names[@]}" -ne "${#server_node_names[@]}" ]; then + common_logger -e "Duplicated Wazuh server node names." + exit 1 + fi + + unique_ips=($(echo "${server_node_ips[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_ips[@]}" -ne "${#server_node_ips[@]}" ]; then + common_logger -e "Duplicated Wazuh server node ips." + exit 1 + fi + + unique_names=($(echo "${dashboard_node_names[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_names[@]}" -ne "${#dashboard_node_names[@]}" ]; then + common_logger -e "Duplicated dashboard node names." + exit 1 + fi + + unique_ips=($(echo "${dashboard_node_ips[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + if [ "${#unique_ips[@]}" -ne "${#dashboard_node_ips[@]}" ]; then + common_logger -e "Duplicated dashboard node ips." + exit 1 + fi + + for i in "${server_node_types[@]}"; do + if ! echo "$i" | grep -ioq master && ! echo "$i" | grep -ioq worker; then + common_logger -e "Incorrect node_type $i must be master or worker" + exit 1 + fi + done + + if [ "${#server_node_names[@]}" -le 1 ]; then + if [ "${#server_node_types[@]}" -ne 0 ]; then + common_logger -e "The tag node_type can only be used with more than one Wazuh server." + exit 1 + fi + elif [ "${#server_node_names[@]}" -gt "${#server_node_types[@]}" ]; then + common_logger -e "The tag node_type needs to be specified for all Wazuh server nodes." + exit 1 + elif [ "${#server_node_names[@]}" -lt "${#server_node_types[@]}" ]; then + common_logger -e "Found extra node_type tags." + exit 1 + elif [ "$(grep -io master <<< "${server_node_types[*]}" | wc -l)" -ne 1 ]; then + common_logger -e "Wazuh cluster needs a single master node." + exit 1 + elif [ "$(grep -io worker <<< "${server_node_types[*]}" | wc -l)" -ne $(( ${#server_node_types[@]} - 1 )) ]; then + common_logger -e "Incorrect number of workers." + exit 1 + fi + + if [ "${#dashboard_node_names[@]}" -ne "${#dashboard_node_ips[@]}" ]; then + common_logger -e "Different number of dashboard node names and IPs." + exit 1 + fi + + else + common_logger -e "No configuration file found." + exit 1 + fi + +} + +function cert_setpermisions() { + eval "chmod -R 744 ${cert_tmp_path} ${debug}" +} + +function cert_convertCRLFtoLF() { + if [[ ! -d "/tmp/wazuh-install-files" ]]; then + eval "mkdir /tmp/wazuh-install-files ${debug}" + fi + eval "chmod -R 755 /tmp/wazuh-install-files ${debug}" + eval "tr -d '\015' < $1 > /tmp/wazuh-install-files/new_config.yml" + eval "mv /tmp/wazuh-install-files/new_config.yml $1 ${debug}" +} diff --git a/cert_tool/certMain.sh b/cert_tool/certMain.sh new file mode 100644 index 0000000..64ba285 --- /dev/null +++ b/cert_tool/certMain.sh @@ -0,0 +1,261 @@ +# Certificate tool - Main functions +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function getHelp() { + + echo -e "" + echo -e "NAME" + echo -e " wazuh-cert-tool.sh - Manages the creation of certificates of the Wazuh components." + echo -e "" + echo -e "SYNOPSIS" + echo -e " wazuh-cert-tool.sh [OPTIONS]" + echo -e "" + echo -e "DESCRIPTION" + echo -e " -a, --admin-certificates " + echo -e " Creates the admin certificates, add root-ca.pem and root-ca.key." + echo -e "" + echo -e " -A, --all " + echo -e " Creates certificates specified in config.yml and admin certificates. Add a root-ca.pem and root-ca.key or leave it empty so a new one will be created." + echo -e "" + echo -e " -ca, --root-ca-certificates" + echo -e " Creates the root-ca certificates." + echo -e "" + echo -e " -v, --verbose" + echo -e " Enables verbose mode." + echo -e "" + echo -e " -wd, --wazuh-dashboard-certificates " + echo -e " Creates the Wazuh dashboard certificates, add root-ca.pem and root-ca.key." + echo -e "" + echo -e " -wi, --wazuh-indexer-certificates " + echo -e " Creates the Wazuh indexer certificates, add root-ca.pem and root-ca.key." + echo -e "" + echo -e " -ws, --wazuh-server-certificates " + echo -e " Creates the Wazuh server certificates, add root-ca.pem and root-ca.key." + echo -e "" + echo -e " -tmp, --cert_tmp_path " + echo -e " Modifies the default tmp directory (/tmp/wazuh-ceritificates) to the specified one." + echo -e " Must be used along with one of these options: -a, -A, -ca, -wi, -wd, -ws" + echo -e "" + + exit 1 + +} + +function main() { + + umask 177 + + cert_checkOpenSSL + + if [ -n "${1}" ]; then + while [ -n "${1}" ] + do + case "${1}" in + "-a"|"--admin-certificates") + if [[ -z "${2}" || -z "${3}" ]]; then + common_logger -e "Error on arguments. Probably missing after -a|--admin-certificates" + getHelp + exit 1 + else + cadmin=1 + rootca="${2}" + rootcakey="${3}" + shift 3 + fi + ;; + "-A"|"--all") + if [[ -n "${2}" && "${2}" != "-v" && "${2}" != "-tmp" ]]; then + # Validate that the user has entered the 2 files + if [[ -z ${3} ]]; then + if [[ ${2} == *".key" ]]; then + common_logger -e "You have not entered a root-ca.pem" + exit 1 + else + common_logger -e "You have not entered a root-ca.key" + exit 1 + fi + fi + all=1 + rootca="${2}" + rootcakey="${3}" + shift 3 + else + all=1 + shift 1 + fi + ;; + "-ca"|"--root-ca-certificate") + ca=1 + shift 1 + ;; + "-h"|"--help") + getHelp + ;; + "-v"|"--verbose") + debugEnabled=1 + shift 1 + ;; + "-wd"|"--wazuh-dashboard-certificates") + if [[ -z "${2}" || -z "${3}" ]]; then + common_logger -e "Error on arguments. Probably missing after -wd|--wazuh-dashboard-certificates" + getHelp + exit 1 + else + cdashboard=1 + rootca="${2}" + rootcakey="${3}" + shift 3 + fi + ;; + "-wi"|"--wazuh-indexer-certificates") + if [[ -z "${2}" || -z "${3}" ]]; then + common_logger -e "Error on arguments. Probably missing after -wi|--wazuh-indexer-certificates" + getHelp + exit 1 + else + cindexer=1 + rootca="${2}" + rootcakey="${3}" + shift 3 + fi + ;; + "-ws"|"--wazuh-server-certificates") + if [[ -z "${2}" || -z "${3}" ]]; then + common_logger -e "Error on arguments. Probably missing after -ws|--wazuh-server-certificates" + getHelp + exit 1 + else + cserver=1 + rootca="${2}" + rootcakey="${3}" + shift 3 + fi + ;; + "-tmp"|"--cert_tmp_path") + if [[ -n "${3}" || ( "${cadmin}" == 1 || "${all}" == 1 || "${ca}" == 1 || "${cdashboard}" == 1 || "${cindexer}" == 1 || "${cserver}" == 1 ) ]]; then + if [[ -z "${2}" || ! "${2}" == /* ]]; then + common_logger -e "Error on arguments. Probably missing or path does not start with '/'." + getHelp + exit 1 + else + cert_tmp_path="${2}" + shift 2 + fi + else + common_logger -e "Error: -tmp must be used along with one of these options: -a, -A, -ca, -wi, -wd, -ws" + getHelp + exit 1 + fi + ;; + *) + echo "Unknow option: ${1}" + getHelp + esac + done + + common_logger "Verbose logging redirected to ${logfile}" + + if [[ -d "${base_path}"/wazuh-certificates ]]; then + if [ -n "$(ls -A "${base_path}"/wazuh-certificates)" ]; then + common_logger -e "Directory wazuh-certificates already exists in the same path as the script. Please, remove the certs directory to create new certificates." + exit 1 + fi + fi + + if [[ ! -d "${cert_tmp_path}" ]]; then + mkdir -p "${cert_tmp_path}" + chmod 744 "${cert_tmp_path}" + fi + + cert_readConfig + + if [ -n "${debugEnabled}" ]; then + debug="2>&1 | tee -a ${logfile}" + fi + + if [[ -n "${cadmin}" ]]; then + cert_checkRootCA + cert_generateAdmincertificate + common_logger "Admin certificates created." + cert_cleanFiles + cert_setpermisions + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + fi + + if [[ -n "${all}" ]]; then + cert_checkRootCA + cert_generateAdmincertificate + common_logger "Admin certificates created." + if cert_generateIndexercertificates; then + common_logger "Wazuh indexer certificates created." + fi + if cert_generateFilebeatcertificates; then + common_logger "Wazuh Filebeat certificates created." + fi + if cert_generateDashboardcertificates; then + common_logger "Wazuh dashboard certificates created." + fi + cert_cleanFiles + cert_setpermisions + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + fi + + if [[ -n "${ca}" ]]; then + cert_generateRootCAcertificate + common_logger "Authority certificates created." + cert_cleanFiles + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + fi + + if [[ -n "${cindexer}" ]]; then + if [ ${#indexer_node_names[@]} -gt 0 ]; then + cert_checkRootCA + cert_generateIndexercertificates + common_logger "Wazuh indexer certificates created." + cert_cleanFiles + cert_setpermisions + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + else + common_logger -e "Indexer node not present in config.yml." + exit 1 + fi + fi + + if [[ -n "${cserver}" ]]; then + if [ ${#server_node_names[@]} -gt 0 ]; then + cert_checkRootCA + cert_generateFilebeatcertificates + common_logger "Wazuh Filebeat certificates created." + cert_cleanFiles + cert_setpermisions + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + else + common_logger -e "Server node not present in config.yml." + exit 1 + fi + fi + + if [[ -n "${cdashboard}" ]]; then + if [ ${#dashboard_node_names[@]} -gt 0 ]; then + cert_checkRootCA + cert_generateDashboardcertificates + common_logger "Wazuh dashboard certificates created." + cert_cleanFiles + cert_setpermisions + eval "mv ${cert_tmp_path} ${base_path}/wazuh-certificates ${debug}" + else + common_logger -e "Dashboard node not present in config.yml." + exit 1 + fi + fi + + else + getHelp + fi + +} \ No newline at end of file diff --git a/cert_tool/certVariables.sh b/cert_tool/certVariables.sh new file mode 100644 index 0000000..7d86d37 --- /dev/null +++ b/cert_tool/certVariables.sh @@ -0,0 +1,14 @@ +# Certificate tool - Variables +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +readonly base_path="$(dirname "$(readlink -f "$0")")" +readonly config_file="${base_path}/config.yml" +readonly logfile="${base_path}/wazuh-certificates-tool.log" +cert_tmp_path="/tmp/wazuh-certificates" +debug=">> ${logfile} 2>&1" +readonly cert_tool_script_name=".*certs.*\.sh" \ No newline at end of file diff --git a/common_functions/common.sh b/common_functions/common.sh new file mode 100644 index 0000000..359624b --- /dev/null +++ b/common_functions/common.sh @@ -0,0 +1,221 @@ +# Common functions for Wazuh installation assistant, +# wazuh-passwords-tool and wazuh-cert-tool +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function common_checkAptLock() { + + attempt=0 + seconds=30 + max_attempts=10 + + while fuser "${apt_lockfile}" >/dev/null 2>&1 && [ "${attempt}" -lt "${max_attempts}" ]; do + attempt=$((attempt+1)) + common_logger "Another process is using APT. Waiting for it to release the lock. Next retry in ${seconds} seconds (${attempt}/${max_attempts})" + sleep "${seconds}" + done + +} + +function common_logger() { + + now=$(date +'%d/%m/%Y %H:%M:%S') + mtype="INFO:" + debugLogger= + nolog= + if [ -n "${1}" ]; then + while [ -n "${1}" ]; do + case ${1} in + "-e") + mtype="ERROR:" + shift 1 + ;; + "-w") + mtype="WARNING:" + shift 1 + ;; + "-d") + debugLogger=1 + mtype="DEBUG:" + shift 1 + ;; + "-nl") + nolog=1 + shift 1 + ;; + *) + message="${1}" + shift 1 + ;; + esac + done + fi + + if [ -z "${debugLogger}" ] || { [ -n "${debugLogger}" ] && [ -n "${debugEnabled}" ]; }; then + if [ -z "${nolog}" ] && { [ "$EUID" -eq 0 ] || [[ "$(basename "$0")" =~ $cert_tool_script_name ]]; }; then + printf "%s\n" "${now} ${mtype} ${message}" | tee -a ${logfile} + else + printf "%b\n" "${now} ${mtype} ${message}" + fi + fi + +} + +function common_checkRoot() { + + common_logger -d "Checking root permissions." + if [ "$EUID" -ne 0 ]; then + echo "This script must be run as root." + exit 1; + fi + + common_logger -d "Checking sudo package." + if ! command -v sudo > /dev/null; then + common_logger -e "The sudo package is not installed and it is necessary for the installation." + exit 1; + fi +} + +function common_checkInstalled() { + + common_logger -d "Checking Wazuh installation." + wazuh_installed="" + indexer_installed="" + filebeat_installed="" + dashboard_installed="" + + if [ "${sys_type}" == "yum" ]; then + eval "rpm -q wazuh-manager --quiet && wazuh_installed=1" + elif [ "${sys_type}" == "apt-get" ]; then + wazuh_installed=$(apt list --installed 2>/dev/null | grep wazuh-manager) + fi + + if [ -d "/var/ossec" ]; then + common_logger -d "There are Wazuh remaining files." + wazuh_remaining_files=1 + fi + + if [ "${sys_type}" == "yum" ]; then + eval "rpm -q wazuh-indexer --quiet && indexer_installed=1" + + elif [ "${sys_type}" == "apt-get" ]; then + indexer_installed=$(apt list --installed 2>/dev/null | grep wazuh-indexer) + fi + + if [ -d "/var/lib/wazuh-indexer/" ] || [ -d "/usr/share/wazuh-indexer" ] || [ -d "/etc/wazuh-indexer" ] || [ -f "${base_path}/search-guard-tlstool*" ]; then + common_logger -d "There are Wazuh indexer remaining files." + indexer_remaining_files=1 + fi + + if [ "${sys_type}" == "yum" ]; then + eval "rpm -q filebeat --quiet && filebeat_installed=1" + elif [ "${sys_type}" == "apt-get" ]; then + filebeat_installed=$(apt list --installed 2>/dev/null | grep filebeat) + fi + + if [ -d "/var/lib/filebeat/" ] || [ -d "/usr/share/filebeat" ] || [ -d "/etc/filebeat" ]; then + common_logger -d "There are Filebeat remaining files." + filebeat_remaining_files=1 + fi + + if [ "${sys_type}" == "yum" ]; then + eval "rpm -q wazuh-dashboard --quiet && dashboard_installed=1" + elif [ "${sys_type}" == "apt-get" ]; then + dashboard_installed=$(apt list --installed 2>/dev/null | grep wazuh-dashboard) + fi + + if [ -d "/var/lib/wazuh-dashboard/" ] || [ -d "/usr/share/wazuh-dashboard" ] || [ -d "/etc/wazuh-dashboard" ] || [ -d "/run/wazuh-dashboard/" ]; then + common_logger -d "There are Wazuh dashboard remaining files." + dashboard_remaining_files=1 + fi + +} + +function common_checkSystem() { + + if [ -n "$(command -v yum)" ]; then + sys_type="yum" + sep="-" + common_logger -d "YUM package manager will be used." + elif [ -n "$(command -v apt-get)" ]; then + sys_type="apt-get" + sep="=" + common_logger -d "APT package manager will be used." + else + common_logger -e "Couldn't find YUM or APT package manager. Try installing the one corresponding to your operating system and then, launch the installation assistant again." + exit 1 + fi + +} + +function common_checkWazuhConfigYaml() { + + common_logger -d "Checking Wazuh YAML configuration file." + filecorrect=$(cert_parseYaml "${config_file}" | grep -Ev '^#|^\s*$' | grep -Pzc "\A(\s*(nodes_indexer__name|nodes_indexer__ip|nodes_server__name|nodes_server__ip|nodes_server__node_type|nodes_dashboard__name|nodes_dashboard__ip)=.*?)+\Z") + if [[ "${filecorrect}" -ne 1 ]]; then + common_logger -e "The configuration file ${config_file} does not have a correct format." + exit 1 + fi + +} + +# Retries even if the --retry-connrefused is not available +function common_curl() { + + if [ -n "${curl_has_connrefused}" ]; then + eval "curl $@ --retry-connrefused" + e_code="${PIPESTATUS[0]}" + else + retries=0 + eval "curl $@" + e_code="${PIPESTATUS[0]}" + while [ "${e_code}" -eq 7 ] && [ "${retries}" -ne 12 ]; do + retries=$((retries+1)) + sleep 5 + eval "curl $@" + e_code="${PIPESTATUS[0]}" + done + fi + return "${e_code}" + +} + +function common_remove_gpg_key() { + + common_logger -d "Removing GPG key from system." + if [ "${sys_type}" == "yum" ]; then + if { rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n' | grep "Wazuh"; } >/dev/null ; then + key=$(rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n' | grep "Wazuh Signing Key" | awk '{print $1}' ) + rpm -e "${key}" + else + common_logger "Wazuh GPG key not found in the system" + return 1 + fi + elif [ "${sys_type}" == "apt-get" ]; then + if [ -f "/usr/share/keyrings/wazuh.gpg" ]; then + rm -rf "/usr/share/keyrings/wazuh.gpg" "${debug}" + else + common_logger "Wazuh GPG key not found in the system" + return 1 + fi + fi + +} + +function common_checkYumLock() { + + attempt=0 + seconds=30 + max_attempts=10 + + while [ -f "${yum_lockfile}" ] && [ "${attempt}" -lt "${max_attempts}" ]; do + attempt=$((attempt+1)) + common_logger "Another process is using YUM. Waiting for it to release the lock. Next retry in ${seconds} seconds (${attempt}/${max_attempts})" + sleep "${seconds}" + done + +} diff --git a/common_functions/commonVariables.sh b/common_functions/commonVariables.sh new file mode 100644 index 0000000..d43ffa8 --- /dev/null +++ b/common_functions/commonVariables.sh @@ -0,0 +1,10 @@ +# Common variables +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +adminpem="/etc/wazuh-indexer/certs/admin.pem" +adminkey="/etc/wazuh-indexer/certs/admin-key.pem" diff --git a/config/certificate/config.yml b/config/certificate/config.yml new file mode 100644 index 0000000..c61a756 --- /dev/null +++ b/config/certificate/config.yml @@ -0,0 +1,26 @@ +nodes: + # Wazuh indexer nodes + indexer: + - name: indexer-1 + ip: "" + - name: indexer-2 + ip: "" + - name: indexer-3 + ip: "" + server: + - name: server-1 + ip: "" + node_type: master + - name: server-2 + ip: "" + node_type: worker + - name: server-3 + ip: "" + node_type: worker + dashboard: + - name: dashboard-1 + ip: "" + - name: dashboard-2 + ip: "" + - name: dashboard-3 + ip: "" diff --git a/config/certificate/config_aio.yml b/config/certificate/config_aio.yml new file mode 100644 index 0000000..8f73564 --- /dev/null +++ b/config/certificate/config_aio.yml @@ -0,0 +1,10 @@ +nodes: + indexer: + - name: wazuh-indexer + ip: 127.0.0.1 + server: + - name: wazuh-server + ip: 127.0.0.1 + dashboard: + - name: wazuh-dashboard + ip: 127.0.0.1 diff --git a/config/dashboard/dashboard.yml b/config/dashboard/dashboard.yml new file mode 100644 index 0000000..8123ca9 --- /dev/null +++ b/config/dashboard/dashboard.yml @@ -0,0 +1,15 @@ +server.host: "" +opensearch.hosts: https://:9200 +server.port: 443 +opensearch.ssl.verificationMode: certificate +# opensearch.username: kibanaserver +# opensearch.password: kibanaserver +opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"] +opensearch_security.multitenancy.enabled: false +opensearch_security.readonly_mode.roles: ["kibana_read_only"] +server.ssl.enabled: true +server.ssl.key: "/etc/wazuh-dashboard/certs/kibana-key.pem" +server.ssl.certificate: "/etc/wazuh-dashboard/certs/kibana.pem" +opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"] +server.defaultRoute: /app/wz-home +opensearch_security.cookie.secure: true diff --git a/config/dashboard/dashboard_all_in_one.yml b/config/dashboard/dashboard_all_in_one.yml new file mode 100644 index 0000000..7d48482 --- /dev/null +++ b/config/dashboard/dashboard_all_in_one.yml @@ -0,0 +1,15 @@ +server.host: 0.0.0.0 +server.port: 443 +opensearch.hosts: https://localhost:9200 +opensearch.ssl.verificationMode: certificate +# opensearch.username: kibanaserver +# opensearch.password: kibanaserver +opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"] +opensearch_security.multitenancy.enabled: false +opensearch_security.readonly_mode.roles: ["kibana_read_only"] +server.ssl.enabled: true +server.ssl.key: "/etc/wazuh-dashboard/certs/kibana-key.pem" +server.ssl.certificate: "/etc/wazuh-dashboard/certs/kibana.pem" +opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"] +uiSettings.overrides.defaultRoute: /app/wz-home +opensearch_security.cookie.secure: true diff --git a/config/dashboard/dashboard_assistant.yml b/config/dashboard/dashboard_assistant.yml new file mode 100644 index 0000000..1a48157 --- /dev/null +++ b/config/dashboard/dashboard_assistant.yml @@ -0,0 +1,15 @@ +server.host: 0.0.0.0 +opensearch.hosts: https://127.0.0.1:9200 +server.port: 443 +opensearch.ssl.verificationMode: certificate +# opensearch.username: kibanaserver +# opensearch.password: kibanaserver +opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"] +opensearch_security.multitenancy.enabled: false +opensearch_security.readonly_mode.roles: ["kibana_read_only"] +server.ssl.enabled: true +server.ssl.key: "/etc/wazuh-dashboard/certs/dashboard-key.pem" +server.ssl.certificate: "/etc/wazuh-dashboard/certs/dashboard.pem" +opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"] +uiSettings.overrides.defaultRoute: /app/wz-home +opensearch_security.cookie.secure: true diff --git a/config/dashboard/dashboard_assistant_distributed.yml b/config/dashboard/dashboard_assistant_distributed.yml new file mode 100644 index 0000000..50dbb8a --- /dev/null +++ b/config/dashboard/dashboard_assistant_distributed.yml @@ -0,0 +1,13 @@ +server.port: 443 +opensearch.ssl.verificationMode: certificate +# opensearch.username: kibanaserver +# opensearch.password: kibanaserver +opensearch.requestHeadersAllowlist: ["securitytenant","Authorization"] +opensearch_security.multitenancy.enabled: false +opensearch_security.readonly_mode.roles: ["kibana_read_only"] +server.ssl.enabled: true +server.ssl.key: "/etc/wazuh-dashboard/certs/dashboard-key.pem" +server.ssl.certificate: "/etc/wazuh-dashboard/certs/dashboard.pem" +opensearch.ssl.certificateAuthorities: ["/etc/wazuh-dashboard/certs/root-ca.pem"] +uiSettings.overrides.defaultRoute: /app/wz-home +opensearch_security.cookie.secure: true diff --git a/config/filebeat/filebeat.yml b/config/filebeat/filebeat.yml new file mode 100644 index 0000000..000afd2 --- /dev/null +++ b/config/filebeat/filebeat.yml @@ -0,0 +1,31 @@ +# Wazuh - Filebeat configuration file +output.elasticsearch: + hosts: [":9200"] + protocol: https + username: ${username} + password: ${password} + ssl.certificate_authorities: + - /etc/filebeat/certs/root-ca.pem + ssl.certificate: "/etc/filebeat/certs/filebeat.pem" + ssl.key: "/etc/filebeat/certs/filebeat-key.pem" +setup.template.json.enabled: true +setup.template.json.path: '/etc/filebeat/wazuh-template.json' +setup.template.json.name: 'wazuh' +setup.ilm.overwrite: true +setup.ilm.enabled: false + +filebeat.modules: + - module: wazuh + alerts: + enabled: true + archives: + enabled: false + +logging.metrics.enabled: false + +seccomp: + default_action: allow + syscalls: + - action: allow + names: + - rseq diff --git a/config/filebeat/filebeat_all_in_one.yml b/config/filebeat/filebeat_all_in_one.yml new file mode 100644 index 0000000..34ad970 --- /dev/null +++ b/config/filebeat/filebeat_all_in_one.yml @@ -0,0 +1,31 @@ +# Wazuh - Filebeat configuration file +output.elasticsearch: + hosts: ["127.0.0.1:9200"] + protocol: https + username: ${username} + password: ${password} + ssl.certificate_authorities: + - /etc/filebeat/certs/root-ca.pem + ssl.certificate: "/etc/filebeat/certs/filebeat.pem" + ssl.key: "/etc/filebeat/certs/filebeat-key.pem" +setup.template.json.enabled: true +setup.template.json.path: '/etc/filebeat/wazuh-template.json' +setup.template.json.name: 'wazuh' +setup.ilm.overwrite: true +setup.ilm.enabled: false + +filebeat.modules: + - module: wazuh + alerts: + enabled: true + archives: + enabled: false + +logging.metrics.enabled: false + +seccomp: + default_action: allow + syscalls: + - action: allow + names: + - rseq diff --git a/config/filebeat/filebeat_assistant.yml b/config/filebeat/filebeat_assistant.yml new file mode 100644 index 0000000..17927ab --- /dev/null +++ b/config/filebeat/filebeat_assistant.yml @@ -0,0 +1,43 @@ +# Wazuh - Filebeat configuration file +output.elasticsearch.hosts: + - 127.0.0.1:9200 +# - :9200 +# - :9200 + +output.elasticsearch: + protocol: https + username: ${username} + password: ${password} + ssl.certificate_authorities: + - /etc/filebeat/certs/root-ca.pem + ssl.certificate: "/etc/filebeat/certs/filebeat.pem" + ssl.key: "/etc/filebeat/certs/filebeat-key.pem" +setup.template.json.enabled: true +setup.template.json.path: '/etc/filebeat/wazuh-template.json' +setup.template.json.name: 'wazuh' +setup.ilm.overwrite: true +setup.ilm.enabled: false + +filebeat.modules: + - module: wazuh + alerts: + enabled: true + archives: + enabled: false + +logging.level: info +logging.to_files: true +logging.files: + path: /var/log/filebeat + name: filebeat + keepfiles: 7 + permissions: 0644 + +logging.metrics.enabled: false + +seccomp: + default_action: allow + syscalls: + - action: allow + names: + - rseq diff --git a/config/filebeat/filebeat_distributed.yml b/config/filebeat/filebeat_distributed.yml new file mode 100644 index 0000000..e6f122f --- /dev/null +++ b/config/filebeat/filebeat_distributed.yml @@ -0,0 +1,38 @@ +# Wazuh - Filebeat configuration file +output.elasticsearch: + protocol: https + username: ${username} + password: ${password} + ssl.certificate_authorities: + - /etc/filebeat/certs/root-ca.pem + ssl.certificate: "/etc/filebeat/certs/filebeat.pem" + ssl.key: "/etc/filebeat/certs/filebeat-key.pem" +setup.template.json.enabled: true +setup.template.json.path: '/etc/filebeat/wazuh-template.json' +setup.template.json.name: 'wazuh' +setup.ilm.overwrite: true +setup.ilm.enabled: false + +filebeat.modules: + - module: wazuh + alerts: + enabled: true + archives: + enabled: false + +logging.level: info +logging.to_files: true +logging.files: + path: /var/log/filebeat + name: filebeat + keepfiles: 7 + permissions: 0644 + +logging.metrics.enabled: false + +seccomp: + default_action: allow + syscalls: + - action: allow + names: + - rseq diff --git a/config/filebeat/filebeat_elastic_cluster.yml b/config/filebeat/filebeat_elastic_cluster.yml new file mode 100644 index 0000000..459ec99 --- /dev/null +++ b/config/filebeat/filebeat_elastic_cluster.yml @@ -0,0 +1,31 @@ +# Wazuh - Filebeat configuration file +output.elasticsearch: + hosts: [":9200", ":9200", ":9200"] + protocol: https + username: ${username} + password: ${password} + ssl.certificate_authorities: + - /etc/filebeat/certs/root-ca.pem + ssl.certificate: "/etc/filebeat/certs/filebeat.pem" + ssl.key: "/etc/filebeat/certs/filebeat-key.pem" +setup.template.json.enabled: true +setup.template.json.path: '/etc/filebeat/wazuh-template.json' +setup.template.json.name: 'wazuh' +setup.ilm.overwrite: true +setup.ilm.enabled: false + +filebeat.modules: + - module: wazuh + alerts: + enabled: true + archives: + enabled: false + +logging.metrics.enabled: false + +seccomp: + default_action: allow + syscalls: + - action: allow + names: + - rseq diff --git a/config/indexer/indexer.yml b/config/indexer/indexer.yml new file mode 100644 index 0000000..11b14fc --- /dev/null +++ b/config/indexer/indexer.yml @@ -0,0 +1,36 @@ +network.host: 0.0.0.0 +node.name: node-1 +cluster.initial_master_nodes: node-1 + +plugins.security.ssl.transport.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.transport.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem +plugins.security.ssl.transport.enforce_hostname_verification: false +plugins.security.ssl.transport.resolve_hostname: false +plugins.security.ssl.http.enabled: true +plugins.security.ssl.http.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.http.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.http.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem +plugins.security.ssl.http.enabled_ciphers: + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" +plugins.security.ssl.http.enabled_protocols: + - "TLSv1.2" +plugins.security.nodes_dn: +- CN=node-1,OU=Wazuh,O=Wazuh,L=California,C=US +plugins.security.authcz.admin_dn: +- CN=admin,OU=Wazuh,O=Wazuh,L=California,C=US + +plugins.security.enable_snapshot_restore_privilege: true +plugins.security.check_snapshot_restore_write_privileges: true +plugins.security.restapi.roles_enabled: ["all_access", "security_rest_api_access"] +cluster.routing.allocation.disk.threshold_enabled: false +node.max_local_storage_nodes: 3 + +path.data: /var/lib/elasticsearch +path.logs: /var/log/elasticsearch + +### Option to allow Filebeat-oss 7.10.2 to work ### +compatibility.override_main_response_version: true \ No newline at end of file diff --git a/config/indexer/indexer_all_in_one.yml b/config/indexer/indexer_all_in_one.yml new file mode 100644 index 0000000..0ae85fe --- /dev/null +++ b/config/indexer/indexer_all_in_one.yml @@ -0,0 +1,41 @@ +network.host: "127.0.0.1" +node.name: "node-1" +cluster.initial_master_nodes: +- "node-1" +cluster.name: "wazuh-cluster" + +node.max_local_storage_nodes: "3" +path.data: /var/lib/wazuh-indexer +path.logs: /var/log/wazuh-indexer + +plugins.security.ssl.http.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.http.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.http.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem +plugins.security.ssl.transport.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.transport.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/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.ssl.http.enabled_ciphers: + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" +plugins.security.ssl.http.enabled_protocols: + - "TLSv1.2" +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=indexer,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"] + +### Option to allow Filebeat-oss 7.10.2 to work ### +compatibility.override_main_response_version: true diff --git a/config/indexer/indexer_assistant_distributed.yml b/config/indexer/indexer_assistant_distributed.yml new file mode 100644 index 0000000..cd63d2e --- /dev/null +++ b/config/indexer/indexer_assistant_distributed.yml @@ -0,0 +1,41 @@ +node.master: true +node.data: true +node.ingest: true + +cluster.name: wazuh-indexer-cluster +cluster.routing.allocation.disk.threshold_enabled: false + +node.max_local_storage_nodes: "3" +path.data: /var/lib/wazuh-indexer +path.logs: /var/log/wazuh-indexer + + +plugins.security.ssl.http.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.http.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.http.pemtrustedcas_filepath: /etc/wazuh-indexer/certs/root-ca.pem +plugins.security.ssl.transport.pemcert_filepath: /etc/wazuh-indexer/certs/indexer.pem +plugins.security.ssl.transport.pemkey_filepath: /etc/wazuh-indexer/certs/indexer-key.pem +plugins.security.ssl.transport.pemtrustedcas_filepath: /etc/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.ssl.http.enabled_ciphers: + - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384" + - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" +plugins.security.ssl.http.enabled_protocols: + - "TLSv1.2" +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.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"] + +### Option to allow Filebeat-oss 7.10.2 to work ### +compatibility.override_main_response_version: true diff --git a/config/indexer/roles/internal_users.yml b/config/indexer/roles/internal_users.yml new file mode 100644 index 0000000..1ff2c8c --- /dev/null +++ b/config/indexer/roles/internal_users.yml @@ -0,0 +1,56 @@ +--- +# 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 + +# Define your internal users here + +## Demo users + +admin: + hash: "$2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG" + reserved: true + backend_roles: + - "admin" + description: "Demo admin user" + +kibanaserver: + hash: "$2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H." + reserved: true + description: "Demo kibanaserver user" + +kibanaro: + hash: "$2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC" + reserved: false + backend_roles: + - "kibanauser" + - "readall" + attributes: + attribute1: "value1" + attribute2: "value2" + attribute3: "value3" + description: "Demo kibanaro user" + +logstash: + hash: "$2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2" + reserved: false + backend_roles: + - "logstash" + description: "Demo logstash user" + +readall: + hash: "$2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2" + reserved: false + backend_roles: + - "readall" + description: "Demo readall user" + +snapshotrestore: + hash: "$2y$12$DpwmetHKwgYnorbgdvORCenv4NAK8cPUg8AI6pxLCuWf/ALc0.v7W" + reserved: false + backend_roles: + - "snapshotrestore" + description: "Demo snapshotrestore user" diff --git a/config/indexer/roles/roles.yml b/config/indexer/roles/roles.yml new file mode 100644 index 0000000..860ae00 --- /dev/null +++ b/config/indexer/roles/roles.yml @@ -0,0 +1,149 @@ +_meta: + type: "roles" + config_version: 2 + +# Restrict users so they can only view visualization and dashboard on kibana +kibana_read_only: + reserved: true + +# The security REST API access role is used to assign specific users access to change the security settings through the REST API. +security_rest_api_access: + reserved: true + +# Allows users to view monitors, destinations and alerts +alerting_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/alerting/alerts/get' + - 'cluster:admin/opendistro/alerting/destination/get' + - 'cluster:admin/opendistro/alerting/monitor/get' + - 'cluster:admin/opendistro/alerting/monitor/search' + +# Allows users to view and acknowledge alerts +alerting_ack_alerts: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/alerting/alerts/*' + +# Allows users to use all alerting functionality +alerting_full_access: + reserved: true + cluster_permissions: + - 'cluster_monitor' + - 'cluster:admin/opendistro/alerting/*' + index_permissions: + - index_patterns: + - '*' + allowed_actions: + - 'indices_monitor' + - 'indices:admin/aliases/get' + - 'indices:admin/mappings/get' + +# Allow users to read Anomaly Detection detectors and results +anomaly_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/ad/detector/info' + - 'cluster:admin/opendistro/ad/detector/search' + - 'cluster:admin/opendistro/ad/detectors/get' + - 'cluster:admin/opendistro/ad/result/search' + - 'cluster:admin/opendistro/ad/tasks/search' + +# Allows users to use all Anomaly Detection functionality +anomaly_full_access: + reserved: true + cluster_permissions: + - 'cluster_monitor' + - 'cluster:admin/opendistro/ad/*' + index_permissions: + - index_patterns: + - '*' + allowed_actions: + - 'indices_monitor' + - 'indices:admin/aliases/get' + - 'indices:admin/mappings/get' + +# Allows users to read Notebooks +notebooks_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/notebooks/list' + - 'cluster:admin/opendistro/notebooks/get' + +# Allows users to all Notebooks functionality +notebooks_full_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/notebooks/create' + - 'cluster:admin/opendistro/notebooks/update' + - 'cluster:admin/opendistro/notebooks/delete' + - 'cluster:admin/opendistro/notebooks/get' + - 'cluster:admin/opendistro/notebooks/list' + +# Allows users to read and download Reports +reports_instances_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/reports/instance/list' + - 'cluster:admin/opendistro/reports/instance/get' + - 'cluster:admin/opendistro/reports/menu/download' + +# Allows users to read and download Reports and Report-definitions +reports_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/reports/definition/get' + - 'cluster:admin/opendistro/reports/definition/list' + - 'cluster:admin/opendistro/reports/instance/list' + - 'cluster:admin/opendistro/reports/instance/get' + - 'cluster:admin/opendistro/reports/menu/download' + +# Allows users to all Reports functionality +reports_full_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/reports/definition/create' + - 'cluster:admin/opendistro/reports/definition/update' + - 'cluster:admin/opendistro/reports/definition/on_demand' + - 'cluster:admin/opendistro/reports/definition/delete' + - 'cluster:admin/opendistro/reports/definition/get' + - 'cluster:admin/opendistro/reports/definition/list' + - 'cluster:admin/opendistro/reports/instance/list' + - 'cluster:admin/opendistro/reports/instance/get' + - 'cluster:admin/opendistro/reports/menu/download' + +# Allows users to use all asynchronous-search functionality +asynchronous_search_full_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/asynchronous_search/*' + index_permissions: + - index_patterns: + - '*' + allowed_actions: + - 'indices:data/read/search*' + +# Allows users to read stored asynchronous-search results +asynchronous_search_read_access: + reserved: true + cluster_permissions: + - 'cluster:admin/opendistro/asynchronous_search/get' + +# Wazuh monitoring and statistics index permissions +manage_wazuh_index: + reserved: true + hidden: false + cluster_permissions: [] + index_permissions: + - index_patterns: + - "wazuh-*" + dls: "" + fls: [] + masked_fields: [] + allowed_actions: + - "read" + - "delete" + - "manage" + - "index" + tenant_permissions: [] + static: false diff --git a/config/indexer/roles/roles_mapping.yml b/config/indexer/roles/roles_mapping.yml new file mode 100644 index 0000000..4c28846 --- /dev/null +++ b/config/indexer/roles/roles_mapping.yml @@ -0,0 +1,87 @@ +--- +# In this file users, backendroles and hosts can be mapped to Open Distro Security roles. +# Permissions for Opendistro roles are configured in roles.yml + +_meta: + type: "rolesmapping" + config_version: 2 + +# Define your roles mapping here + +## Default roles mapping + +all_access: + reserved: true + hidden: false + backend_roles: + - "admin" + hosts: [] + users: [] + and_backend_roles: [] + description: "Maps admin to all_access" + +own_index: + reserved: false + hidden: false + backend_roles: [] + hosts: [] + users: + - "*" + and_backend_roles: [] + description: "Allow full access to an index named like the username" + +logstash: + reserved: false + hidden: false + backend_roles: + - "logstash" + hosts: [] + users: [] + and_backend_roles: [] + +readall: + reserved: true + hidden: false + backend_roles: + - "readall" + hosts: [] + users: [] + and_backend_roles: [] + +manage_snapshots: + reserved: true + hidden: false + backend_roles: + - "snapshotrestore" + hosts: [] + users: [] + and_backend_roles: [] + +kibana_server: + reserved: true + hidden: false + backend_roles: [] + hosts: [] + users: + - "kibanaserver" + and_backend_roles: [] + +kibana_user: + reserved: false + hidden: false + backend_roles: + - "kibanauser" + hosts: [] + users: [] + and_backend_roles: [] + description: "Maps kibanauser to kibana_user" + +# Wazuh monitoring and statistics index permissions +manage_wazuh_index: + reserved: true + hidden: false + backend_roles: [] + hosts: [] + users: + - "kibanaserver" + and_backend_roles: [] diff --git a/install_functions/checks.sh b/install_functions/checks.sh new file mode 100644 index 0000000..0d42d2a --- /dev/null +++ b/install_functions/checks.sh @@ -0,0 +1,495 @@ +# Wazuh installer - checks.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function checks_arch() { + + common_logger -d "Checking system architecture." + arch=$(uname -m) + + if [ "${arch}" != "x86_64" ]; then + common_logger -e "Uncompatible system. This script must be run on a 64-bit system." + exit 1 + fi +} + +function checks_arguments() { + + # -------------- Port option validation --------------------- + + if [ -n "${port_specified}" ]; then + if [ -z "${AIO}" ] && [ -z "${dashboard}" ]; then + common_logger -e "The argument -p|--port can only be used with -a|--all-in-one or -wd|--wazuh-dashboard." + exit 1 + fi + fi + + # -------------- Offline installation --------------------- + + if [ -n "${offline_install}" ]; then + if [ -z "${AIO}" ] && [ -z "${dashboard}" ] && [ -z "${indexer}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ]; then + common_logger -e "The -of|--offline-installation option must be used with -a, -ws, -s, -wi, or -wd." + exit 1 + fi + fi + + # -------------- Configurations --------------------------------- + + if [ -f "${tar_file}" ]; then + if [ -n "${AIO}" ]; then + rm -f "${tar_file}" + fi + if [ -n "${configurations}" ]; then + common_logger -e "File ${tar_file} already exists. Please remove it if you want to use a new configuration." + exit 1 + fi + fi + + if [[ -n "${configurations}" && ( -n "${AIO}" || -n "${indexer}" || -n "${dashboard}" || -n "${wazuh}" || -n "${overwrite}" || -n "${start_indexer_cluster}" || -n "${tar_conf}" || -n "${uninstall}" ) ]]; then + common_logger -e "The argument -g|--generate-config-files can't be used with -a|--all-in-one, -o|--overwrite, -s|--start-cluster, -t|--tar, -u|--uninstall, -wd|--wazuh-dashboard, -wi|--wazuh-indexer, or -ws|--wazuh-server." + exit 1 + fi + + # -------------- Overwrite -------------------------------------- + + if [ -n "${overwrite}" ] && [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ]; then + common_logger -e "The argument -o|--overwrite must be used in conjunction with -a|--all-in-one, -wd|--wazuh-dashboard, -wi|--wazuh-indexer, or -ws|--wazuh-server." + exit 1 + fi + + # -------------- Uninstall -------------------------------------- + + if [ -n "${uninstall}" ]; then + + if [ -n "$AIO" ] || [ -n "$indexer" ] || [ -n "$dashboard" ] || [ -n "$wazuh" ]; then + common_logger -e "It is not possible to uninstall and install in the same operation. If you want to overwrite the components use -o|--overwrite." + exit 1 + fi + + if [ -z "${wazuh_installed}" ] && [ -z "${wazuh_remaining_files}" ]; then + common_logger "Wazuh manager not found in the system so it was not uninstalled." + fi + + if [ -z "${filebeat_installed}" ] && [ -z "${filebeat_remaining_files}" ]; then + common_logger "Filebeat not found in the system so it was not uninstalled." + fi + + if [ -z "${indexer_installed}" ] && [ -z "${indexer_remaining_files}" ]; then + common_logger "Wazuh indexer not found in the system so it was not uninstalled." + fi + + if [ -z "${dashboard_installed}" ] && [ -z "${dashboard_remaining_files}" ]; then + common_logger "Wazuh dashboard not found in the system so it was not uninstalled." + fi + + fi + + # -------------- All-In-One ------------------------------------- + + if [ -n "${AIO}" ]; then + + if [ -n "$indexer" ] || [ -n "$dashboard" ] || [ -n "$wazuh" ]; then + common_logger -e "Argument -a|--all-in-one is not compatible with -wi|--wazuh-indexer, -wd|--wazuh-dashboard or -ws|--wazuh-server." + exit 1 + fi + + if [ -n "${overwrite}" ]; then + installCommon_rollBack + fi + + if [ -z "${overwrite}" ] && { [ -n "${wazuh_installed}" ] || [ -n "${wazuh_remaining_files}" ]; }; then + common_logger -e "Wazuh manager already installed." + installedComponent=1 + fi + if [ -z "${overwrite}" ] && { [ -n "${indexer_installed}" ] || [ -n "${indexer_remaining_files}" ]; };then + common_logger -e "Wazuh indexer already installed." + installedComponent=1 + fi + if [ -z "${overwrite}" ] && { [ -n "${dashboard_installed}" ] || [ -n "${dashboard_remaining_files}" ]; }; then + common_logger -e "Wazuh dashboard already installed." + installedComponent=1 + fi + if [ -z "${overwrite}" ] && { [ -n "${filebeat_installed}" ] || [ -n "${filebeat_remaining_files}" ]; }; then + common_logger -e "Filebeat already installed." + installedComponent=1 + fi + if [ -n "${installedComponent}" ]; then + common_logger "If you want to overwrite the current installation, run this script adding the option -o/--overwrite. This will erase all the existing configuration and data." + exit 1 + fi + + fi + + # -------------- Indexer ---------------------------------- + + if [ -n "${indexer}" ]; then + + if [ -n "${indexer_installed}" ] || [ -n "${indexer_remaining_files}" ]; then + if [ -n "${overwrite}" ]; then + installCommon_rollBack + else + common_logger -e "Wazuh indexer is already installed in this node or some of its files have not been removed. Use option -o|--overwrite to overwrite all components." + exit 1 + fi + fi + fi + + # -------------- Wazuh dashboard -------------------------------- + + if [ -n "${dashboard}" ]; then + if [ -n "${dashboard_installed}" ] || [ -n "${dashboard_remaining_files}" ]; then + if [ -n "${overwrite}" ]; then + installCommon_rollBack + else + common_logger -e "Wazuh dashboard is already installed in this node or some of its files have not been removed. Use option -o|--overwrite to overwrite all components." + exit 1 + fi + fi + fi + + # -------------- Wazuh ------------------------------------------ + + if [ -n "${wazuh}" ]; then + if [ -n "${wazuh_installed}" ] || [ -n "${wazuh_remaining_files}" ] || [ -n "${filebeat_installed}" ] || [ -n "${filebeat_remaining_files}" ]; then + if [ -n "${overwrite}" ]; then + installCommon_rollBack + else + common_logger -e "Wazuh server components (wazuh-manager and filebeat) are already installed in this node or some of their files have not been removed. Use option -o|--overwrite to overwrite all components." + exit 1 + fi + fi + fi + + # -------------- Cluster start ---------------------------------- + + if [[ -n "${start_indexer_cluster}" && ( -n "${AIO}" || -n "${indexer}" || -n "${dashboard}" || -n "${wazuh}" || -n "${overwrite}" || -n "${configurations}" || -n "${tar_conf}" || -n "${uninstall}") ]]; then + common_logger -e "The argument -s|--start-cluster can't be used with -a|--all-in-one, -g|--generate-config-files,-o|--overwrite , -u|--uninstall, -wi|--wazuh-indexer, -wd|--wazuh-dashboard, -s|--start-cluster, -ws|--wazuh-server." + exit 1 + fi + + # -------------- Global ----------------------------------------- + + if [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ] && [ -z "${configurations}" ] && [ -z "${uninstall}" ] && [ -z "${download}" ]; then + common_logger -e "At least one of these arguments is necessary -a|--all-in-one, -g|--generate-config-files, -wi|--wazuh-indexer, -wd|--wazuh-dashboard, -s|--start-cluster, -ws|--wazuh-server, -u|--uninstall, -dw|--download-wazuh." + exit 1 + fi + + if [ -n "${force}" ] && [ -z "${dashboard}" ]; then + common_logger -e "The -fd|--force-install-dashboard argument needs to be used alongside -wd|--wazuh-dashboard." + exit 1 + fi + +} + +# Checks if the --retry-connrefused is available in curl +function check_curlVersion() { + + common_logger -d "Checking curl tool version." + # --retry-connrefused was added in 7.52.0 + curl_version=$(curl -V | head -n 1 | awk '{ print $2 }') + if [ $(check_versions ${curl_version} 7.52.0) == "0" ]; then + curl_has_connrefused=0 + fi + +} + +function check_dist() { + common_logger -d "Checking system distribution." + dist_detect + if [ "${DIST_NAME}" != "centos" ] && [ "${DIST_NAME}" != "rhel" ] && + [ "${DIST_NAME}" != "amzn" ] && [ "${DIST_NAME}" != "ubuntu" ] && [ "${DIST_NAME}" != "rocky" ]; then + notsupported=1 + fi + if [ "${DIST_NAME}" == "centos" ] && { [ "${DIST_VER}" -ne "7" ] && [ "${DIST_VER}" -ne "8" ]; }; then + notsupported=1 + fi + if [ "${DIST_NAME}" == "rhel" ] && { [ "${DIST_VER}" -ne "7" ] && [ "${DIST_VER}" -ne "8" ] && [ "${DIST_VER}" -ne "9" ]; }; then + notsupported=1 + fi + + if [ "${DIST_NAME}" == "amzn" ]; then + if [ "${DIST_VER}" != "2" ] && + [ "${DIST_VER}" != "2023" ] && + [ "${DIST_VER}" != "2018.03" ]; then + notsupported=1 + fi + if [ "${DIST_VER}" -eq "2023" ]; then + checks_specialDepsAL2023 + fi + fi + + if [ "${DIST_NAME}" == "ubuntu" ]; then + if [ "${DIST_VER}" == "16" ] || [ "${DIST_VER}" == "18" ] || + [ "${DIST_VER}" == "20" ] || [ "${DIST_VER}" == "22" ] || + [ "${DIST_VER}" == "24" ]; then + if [ "${DIST_SUBVER}" != "04" ]; then + notsupported=1 + fi + else + notsupported=1 + fi + fi + + if [ "${DIST_NAME}" == "rocky" ]; then + if [ "${DIST_VER}" != "9" ] || [ "${DIST_SUBVER}" != "4" ]; then + notsupported=1 + fi + fi + + if [ -n "${notsupported}" ]; then + common_logger "The recommended systems are: Red Hat Enterprise Linux 7, 8, 9; CentOS 7, 8; Amazon Linux 2; Ubuntu 16.04, 18.04, 20.04, 22.04." + common_logger -w "The current system does not match with the list of recommended systems. The installation may not work properly." + fi + common_logger -d "Detected distribution name: ${DIST_NAME}" + common_logger -d "Detected distribution version: ${DIST_VER}" + +} + +function checks_health() { + + checks_specifications + + common_logger -d "CPU cores detected: ${cores}" + common_logger -d "Free RAM memory detected: ${ram_gb}" + + if [ -n "${indexer}" ]; then + if [ "${cores}" -lt 2 ] || [ "${ram_gb}" -lt 3700 ]; then + common_logger -e "Your system does not meet the recommended minimum hardware requirements of 4Gb of RAM and 2 CPU cores. If you want to proceed with the installation use the -i option to ignore these requirements." + exit 1 + fi + fi + + if [ -n "${dashboard}" ]; then + if [ "${cores}" -lt 2 ] || [ "${ram_gb}" -lt 3700 ]; then + common_logger -e "Your system does not meet the recommended minimum hardware requirements of 4Gb of RAM and 2 CPU cores. If you want to proceed with the installation use the -i option to ignore these requirements." + exit 1 + fi + fi + + if [ -n "${wazuh}" ]; then + if [ "${cores}" -lt 2 ] || [ "${ram_gb}" -lt 1700 ]; then + common_logger -e "Your system does not meet the recommended minimum hardware requirements of 2Gb of RAM and 2 CPU cores . If you want to proceed with the installation use the -i option to ignore these requirements." + exit 1 + fi + fi + + if [ -n "${AIO}" ]; then + if [ "${cores}" -lt 2 ] || [ "${ram_gb}" -lt 3700 ]; then + common_logger -e "Your system does not meet the recommended minimum hardware requirements of 4Gb of RAM and 2 CPU cores. If you want to proceed with the installation use the -i option to ignore these requirements." + exit 1 + fi + fi + +} + +# This function ensures different names in the config.yml file. +function checks_names() { + + common_logger -d "Checking node names in the configuration file." + if [ -n "${indxname}" ] && [ -n "${dashname}" ] && [ "${indxname}" == "${dashname}" ]; then + common_logger -e "The node names for Wazuh indexer and Wazuh dashboard must be different." + exit 1 + fi + + if [ -n "${indxname}" ] && [ -n "${winame}" ] && [ "${indxname}" == "${winame}" ]; then + common_logger -e "The node names for Elastisearch and Wazuh must be different." + exit 1 + fi + + if [ -n "${winame}" ] && [ -n "${dashname}" ] && [ "${winame}" == "${dashname}" ]; then + common_logger -e "The node names for Wazuh server and Wazuh indexer must be different." + exit 1 + fi + + if [ -n "${winame}" ] && ! echo "${server_node_names[@]}" | grep -w -q "${winame}"; then + common_logger -e "The Wazuh server node name ${winame} does not appear on the configuration file." + exit 1 + fi + + if [ -n "${indxname}" ] && ! echo "${indexer_node_names[@]}" | grep -w -q "${indxname}"; then + common_logger -e "The Wazuh indexer node name ${indxname} does not appear on the configuration file." + exit 1 + fi + + if [ -n "${dashname}" ] && ! echo "${dashboard_node_names[@]}" | grep -w -q "${dashname}"; then + common_logger -e "The Wazuh dashboard node name ${dashname} does not appear on the configuration file." + exit 1 + fi + + if [[ "${dashname}" == -* ]] || [[ "${indxname}" == -* ]] || [[ "${winame}" == -* ]]; then + common_logger -e "Node name cannot start with \"-\"" + exit 1 + fi + +} + +# This function checks if the target certificates are created before to start the installation. +function checks_previousCertificate() { + common_logger -d "Checking previous certificate existence." + if [ ! -f "${tar_file}" ]; then + common_logger -e "Cannot find ${tar_file}. Run the script with the option -g|--generate-config-files to create it or copy it from another node." + exit 1 + fi + + if [ -n "${indxname}" ]; then + if ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${indxname}".pem || ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${indxname}"-key.pem; then + common_logger -e "There is no certificate for the indexer node ${indxname} in ${tar_file}." + exit 1 + fi + fi + + if [ -n "${dashname}" ]; then + if ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${dashname}".pem || ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${dashname}"-key.pem; then + common_logger -e "There is no certificate for the Wazuh dashboard node ${dashname} in ${tar_file}." + exit 1 + fi + fi + + if [ -n "${winame}" ]; then + if ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${winame}".pem || ! tar -tf "${tar_file}" | grep -q -E ^wazuh-install-files/"${winame}"-key.pem; then + common_logger -e "There is no certificate for the wazuh server node ${winame} in ${tar_file}." + exit 1 + fi + fi +} + +# Manages the special dependencies in case of AL2023 +function checks_specialDepsAL2023() { + + # Change curl for curl-minimal + wia_yum_dependencies=( "${wia_yum_dependencies[@]/curl/curl-minimal}" ) + + # In containers, coreutils is replaced for coreutils-single + if [ -f "/.dockerenv" ]; then + wia_yum_dependencies=( "${wia_yum_dependencies[@]/coreutils/coreutils-single}" ) + fi +} + +function checks_specifications() { + + cores=$(grep -c processor /proc/cpuinfo) + ram_gb=$(free -m | awk 'FNR == 2 {print $2}') + +} + +function checks_ports() { + + if [ -z "${offline_install}" ]; then + dep="lsof" + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstallList "${dep}" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstallList "${dep}" + fi + + if [ "${#not_installed[@]}" -gt 0 ]; then + wia_dependencies_installed+=("${dep}") + fi + fi + + common_logger -d "Checking ports availability." + used_port=0 + ports=("$@") + + checks_firewall "${ports[@]}" + + if command -v lsof > /dev/null; then + port_command="lsof -sTCP:LISTEN -i:" + else + common_logger -w "Cannot find lsof. Port checking will be skipped." + return 1 + fi + + for i in "${!ports[@]}"; do + if eval "${port_command}""${ports[i]}" > /dev/null; then + used_port=1 + common_logger -e "Port ${ports[i]} is being used by another process. Please, check it before installing Wazuh." + fi + done + + if [ "${used_port}" -eq 1 ]; then + common_logger "The installation can not continue due to port usage by other processes." + installCommon_rollBack + exit 1 + fi + +} + +# Checks if the first version is greater equal than to second one +function check_versions() { + + if test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; then + echo 0 + else + echo 1 + fi +} + +function checks_available_port() { + chosen_port="$1" + shift + ports_list=("$@") + + if [ "$chosen_port" -ne "${http_port}" ]; then + for port in "${ports_list[@]}"; do + if [ "$chosen_port" -eq "$port" ]; then + common_logger -e "Port ${chosen_port} is reserved by Wazuh. Please, choose another port." + exit 1 + fi + done + fi +} + +function checks_firewall(){ + ports_list=("$@") + f_ports="" + f_message="The system has firewall enabled. Please ensure that traffic is allowed on " + firewalld_installed=0 + ufw_installed=0 + + + # Record of the ports that must be exposed according to the installation + if [ -n "${AIO}" ]; then + f_message+="these ports: 1515, 1514, ${http_port}" + elif [ -n "${dashboard}" ]; then + f_message+="this port: ${http_port}" + else + f_message+="these ports:" + for port in "${ports_list[@]}"; do + f_message+=" ${port}," + done + + # Deletes last comma + f_message="${f_message%,}" + fi + + # Check if the firewall is installed + if [ "${sys_type}" == "yum" ]; then + eval "rpm -q firewalld --quiet && firewalld_installed=1" + eval "rpm -q ufw --quiet && ufw_installed=1" + elif [ "${sys_type}" == "apt-get" ]; then + if apt list --installed 2>/dev/null | grep -q -E ^"firewalld"\/; then + firewalld_installed=1 + fi + if apt list --installed 2>/dev/null | grep -q -E ^"ufw"\/; then + ufw_installed=1 + fi + fi + + # Check if the firewall is running + if [ "${firewalld_installed}" == "1" ]; then + if firewall-cmd --state 2>/dev/null | grep -q -w "running"; then + common_logger -w "${f_message/firewall/Firewalld}." + fi + fi + if [ "${ufw_installed}" == "1" ]; then + if ufw status 2>/dev/null | grep -q -w "active"; then + common_logger -w "${f_message/firewall/UFW}." + fi + fi + +} diff --git a/install_functions/dashboard.sh b/install_functions/dashboard.sh new file mode 100644 index 0000000..97455fb --- /dev/null +++ b/install_functions/dashboard.sh @@ -0,0 +1,226 @@ +# Wazuh installer - dashboard.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function dashboard_changePort() { + + chosen_port="$1" + http_port="${chosen_port}" + wazuh_dashboard_port=( "${http_port}" ) + wazuh_aio_ports=(9200 9300 1514 1515 1516 55000 "${http_port}") + + sed -i 's/server\.port: [0-9]\+$/server.port: '"${chosen_port}"'/' "$0" + common_logger "Wazuh web interface port will be ${chosen_port}." +} + +function dashboard_configure() { + + common_logger -d "Configuring Wazuh dashboard." + if [ -n "${AIO}" ]; then + eval "installCommon_getConfig dashboard/dashboard_assistant.yml /etc/wazuh-dashboard/opensearch_dashboards.yml ${debug}" + dashboard_copyCertificates "${debug}" + else + eval "installCommon_getConfig dashboard/dashboard_assistant_distributed.yml /etc/wazuh-dashboard/opensearch_dashboards.yml ${debug}" + dashboard_copyCertificates "${debug}" + if [ "${#dashboard_node_names[@]}" -eq 1 ]; then + pos=0 + ip=${dashboard_node_ips[0]} + else + for i in "${!dashboard_node_names[@]}"; do + if [[ "${dashboard_node_names[i]}" == "${dashname}" ]]; then + pos="${i}"; + fi + done + ip=${dashboard_node_ips[pos]} + fi + + if [[ "${ip}" != "127.0.0.1" ]]; then + echo "server.host: ${ip}" >> /etc/wazuh-dashboard/opensearch_dashboards.yml + else + echo 'server.host: '0.0.0.0'' >> /etc/wazuh-dashboard/opensearch_dashboards.yml + fi + + if [ "${#indexer_node_names[@]}" -eq 1 ]; then + echo "opensearch.hosts: https://${indexer_node_ips[0]}:9200" >> /etc/wazuh-dashboard/opensearch_dashboards.yml + else + echo "opensearch.hosts:" >> /etc/wazuh-dashboard/opensearch_dashboards.yml + for i in "${indexer_node_ips[@]}"; do + echo " - https://${i}:9200" >> /etc/wazuh-dashboard/opensearch_dashboards.yml + done + fi + fi + + sed -i 's/server\.port: [0-9]\+$/server.port: '"${chosen_port}"'/' /etc/wazuh-dashboard/opensearch_dashboards.yml + + common_logger "Wazuh dashboard post-install configuration finished." + +} + +function dashboard_copyCertificates() { + + common_logger -d "Copying Wazuh dashboard certificates." + eval "rm -f ${dashboard_cert_path}/* ${debug}" + name=${dashboard_node_names[pos]} + + if [ -f "${tar_file}" ]; then + if ! tar -tvf "${tar_file}" | grep -q "${name}" ; then + common_logger -e "Tar file does not contain certificate for the node ${name}." + installCommon_rollBack + exit 1; + fi + eval "mkdir ${dashboard_cert_path} ${debug}" + eval "sed -i s/dashboard.pem/${name}.pem/ /etc/wazuh-dashboard/opensearch_dashboards.yml ${debug}" + eval "sed -i s/dashboard-key.pem/${name}-key.pem/ /etc/wazuh-dashboard/opensearch_dashboards.yml ${debug}" + eval "tar -xf ${tar_file} -C ${dashboard_cert_path} wazuh-install-files/${name}.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${dashboard_cert_path} wazuh-install-files/${name}-key.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${dashboard_cert_path} wazuh-install-files/root-ca.pem --strip-components 1 ${debug}" + eval "chown -R wazuh-dashboard:wazuh-dashboard /etc/wazuh-dashboard/ ${debug}" + eval "chmod 500 ${dashboard_cert_path} ${debug}" + eval "chmod 400 ${dashboard_cert_path}/* ${debug}" + eval "chown wazuh-dashboard:wazuh-dashboard ${dashboard_cert_path}/* ${debug}" + common_logger -d "Wazuh dashboard certificate setup finished." + else + common_logger -e "No certificates found. Wazuh dashboard could not be initialized." + installCommon_rollBack + exit 1 + fi + +} + +function dashboard_initialize() { + + common_logger "Initializing Wazuh dashboard web application." + installCommon_getPass "admin" + j=0 + + if [ "${#dashboard_node_names[@]}" -eq 1 ]; then + nodes_dashboard_ip=${dashboard_node_ips[0]} + else + for i in "${!dashboard_node_names[@]}"; do + if [[ "${dashboard_node_names[i]}" == "${dashname}" ]]; then + pos="${i}"; + fi + done + nodes_dashboard_ip=${dashboard_node_ips[pos]} + fi + + if [ "${nodes_dashboard_ip}" == "localhost" ] || [[ "${nodes_dashboard_ip}" == 127.* ]]; then + print_ip="" + else + print_ip="${nodes_dashboard_ip}" + fi + + until [ "$(curl -XGET https://"${nodes_dashboard_ip}":"${http_port}"/status -uadmin:"${u_pass}" -k -w %"{http_code}" -s -o /dev/null)" -eq "200" ] || [ "${j}" -eq "12" ]; do + sleep 10 + j=$((j+1)) + common_logger -d "Retrying Wazuh dashboard connection..." + done + + if [ ${j} -lt 12 ]; then + common_logger -d "Wazuh dashboard connection was successful." + if [ "${#server_node_names[@]}" -eq 1 ]; then + wazuh_api_address=${server_node_ips[0]} + else + for i in "${!server_node_types[@]}"; do + if [[ "${server_node_types[i]}" == "master" ]]; then + wazuh_api_address=${server_node_ips[i]} + fi + done + fi + if [ -f "/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml" ]; then + eval "sed -i 's,url: https://localhost,url: https://${wazuh_api_address},g' /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml ${debug}" + fi + + common_logger "Wazuh dashboard web application initialized." + common_logger -nl "--- Summary ---" + common_logger -nl "You can access the web interface https://${print_ip}:${http_port}\n User: admin\n Password: ${u_pass}" + + else + flag="-w" + if [ -z "${force}" ]; then + flag="-e" + fi + failed_nodes=() + common_logger "${flag}" "Cannot connect to Wazuh dashboard." + + for i in "${!indexer_node_ips[@]}"; do + curl=$(common_curl -XGET https://"${indexer_node_ips[i]}":9200/ -uadmin:"${u_pass}" -k -s --max-time 300 --retry 5 --retry-delay 5 --fail) + exit_code=${PIPESTATUS[0]} + if [[ "${exit_code}" -eq "7" ]]; then + failed_connect=1 + failed_nodes+=("${indexer_node_names[i]}") + elif [ "${exit_code}" -eq "22" ]; then + sec_not_initialized=1 + fi + done + if [ -n "${failed_connect}" ]; then + common_logger "${flag}" "Failed to connect with ${failed_nodes[*]}. Connection refused." + fi + + if [ -n "${sec_not_initialized}" ]; then + common_logger "${flag}" "Wazuh indexer security settings not initialized. Please run the installation assistant using -s|--start-cluster in one of the wazuh indexer nodes." + fi + + if [ -z "${force}" ]; then + common_logger "If you want to install Wazuh dashboard without waiting for the Wazuh indexer cluster, use the -fd option" + installCommon_rollBack + exit 1 + else + common_logger -nl "--- Summary ---" + common_logger -nl "When Wazuh dashboard is able to connect to your Wazuh indexer cluster, you can access the web interface https://${print_ip}\n User: admin\n Password: ${u_pass}" + fi + fi + +} + +function dashboard_initializeAIO() { + + wazuh_api_address=${server_node_ips[0]} + common_logger "Initializing Wazuh dashboard web application." + installCommon_getPass "admin" + http_code=$(curl -XGET https://localhost:"${http_port}"/status -uadmin:"${u_pass}" -k -w %"{http_code}" -s -o /dev/null) + retries=0 + max_dashboard_initialize_retries=20 + while [ "${http_code}" -ne "200" ] && [ "${retries}" -lt "${max_dashboard_initialize_retries}" ] + do + http_code=$(curl -XGET https://localhost:"${http_port}"/status -uadmin:"${u_pass}" -k -w %"{http_code}" -s -o /dev/null) + common_logger "Wazuh dashboard web application not yet initialized. Waiting..." + retries=$((retries+1)) + sleep 15 + done + if [ "${http_code}" -eq "200" ]; then + if [ -f "/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml" ]; then + eval "sed -i 's,url: https://localhost,url: https://${wazuh_api_address},g' /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml ${debug}" + fi + common_logger "Wazuh dashboard web application initialized." + common_logger -nl "--- Summary ---" + common_logger -nl "You can access the web interface https://:${http_port}\n User: admin\n Password: ${u_pass}" + else + common_logger -e "Wazuh dashboard installation failed." + installCommon_rollBack + exit 1 + fi +} + +function dashboard_install() { + + common_logger "Starting Wazuh dashboard installation." + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstall "wazuh-dashboard" "${wazuh_version}-*" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstall "wazuh-dashboard" "${wazuh_version}-*" + fi + common_checkInstalled + if [ "$install_result" != 0 ] || [ -z "${dashboard_installed}" ]; then + common_logger -e "Wazuh dashboard installation failed." + installCommon_rollBack + exit 1 + else + common_logger "Wazuh dashboard installation finished." + fi + +} diff --git a/install_functions/filebeat.sh b/install_functions/filebeat.sh new file mode 100644 index 0000000..30f039f --- /dev/null +++ b/install_functions/filebeat.sh @@ -0,0 +1,132 @@ +# Wazuh installer - filebeat.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function filebeat_checkService() { + common_logger "Checking Filebeat connection" + + if filebeat test output | grep -q -i -w "ERROR"; then + common_logger -e "Filebeat connection Error." + eval "filebeat test output x ${debug}" + installCommon_rollBack + exit 1 + else + common_logger "Filebeat connection successful" + fi +} + +function filebeat_configure(){ + + common_logger -d "Configuring Filebeat." + + if [ -z "${offline_install}" ]; then + eval "common_curl -sSo /etc/filebeat/wazuh-template.json ${filebeat_wazuh_template} --max-time 300 --retry 5 --retry-delay 5 --fail" + if [ ! -f "/etc/filebeat/wazuh-template.json" ]; then + common_logger -e "Error downloading wazuh-template.json file." + installCommon_rollBack + exit 1 + fi + common_logger -d "Filebeat template was download successfully." + + eval "(common_curl -sS ${filebeat_wazuh_module} --max-time 300 --retry 5 --retry-delay 5 --fail | tar -xvz -C /usr/share/filebeat/module) ${debug}" + if [ ! -d "/usr/share/filebeat/module" ]; then + common_logger -e "Error downloading wazuh filebeat module." + installCommon_rollBack + exit 1 + fi + common_logger -d "Filebeat module was downloaded successfully." + else + eval "cp ${offline_files_path}/wazuh-template.json /etc/filebeat/wazuh-template.json ${debug}" + eval "tar -xvzf ${offline_files_path}/wazuh-filebeat-*.tar.gz -C /usr/share/filebeat/module ${debug}" + fi + + eval "chmod go+r /etc/filebeat/wazuh-template.json ${debug}" + if [ -n "${AIO}" ]; then + eval "installCommon_getConfig filebeat/filebeat_assistant.yml /etc/filebeat/filebeat.yml ${debug}" + else + eval "installCommon_getConfig filebeat/filebeat_distributed.yml /etc/filebeat/filebeat.yml ${debug}" + if [ ${#indexer_node_names[@]} -eq 1 ]; then + echo -e "\noutput.elasticsearch.hosts:" >> /etc/filebeat/filebeat.yml + echo " - ${indexer_node_ips[0]}:9200" >> /etc/filebeat/filebeat.yml + else + echo -e "\noutput.elasticsearch.hosts:" >> /etc/filebeat/filebeat.yml + for i in "${indexer_node_ips[@]}"; do + echo " - ${i}:9200" >> /etc/filebeat/filebeat.yml + done + fi + fi + + eval "mkdir /etc/filebeat/certs ${debug}" + filebeat_copyCertificates + + eval "filebeat keystore create ${debug}" + eval "(echo admin | filebeat keystore add username --force --stdin)" "${debug}" + eval "(echo admin | filebeat keystore add password --force --stdin)" "${debug}" + + common_logger "Filebeat post-install configuration finished." +} + +function filebeat_copyCertificates() { + + common_logger -d "Copying Filebeat certificates." + if [ -f "${tar_file}" ]; then + if [ -n "${AIO}" ]; then + if ! tar -tvf "${tar_file}" | grep -q "${server_node_names[0]}" ; then + common_logger -e "Tar file does not contain certificate for the node ${server_node_names[0]}." + installCommon_rollBack + exit 1 + fi + eval "sed -i s/filebeat.pem/${server_node_names[0]}.pem/ /etc/filebeat/filebeat.yml ${debug}" + eval "sed -i s/filebeat-key.pem/${server_node_names[0]}-key.pem/ /etc/filebeat/filebeat.yml ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} --wildcards wazuh-install-files/${server_node_names[0]}.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} --wildcards wazuh-install-files/${server_node_names[0]}-key.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} wazuh-install-files/root-ca.pem --strip-components 1 ${debug}" + eval "rm -rf ${filebeat_cert_path}/wazuh-install-files/ ${debug}" + else + if ! tar -tvf "${tar_file}" | grep -q "${winame}" ; then + common_logger -e "Tar file does not contain certificate for the node ${winame}." + installCommon_rollBack + exit 1 + fi + eval "sed -i s/filebeat.pem/${winame}.pem/ /etc/filebeat/filebeat.yml ${debug}" + eval "sed -i s/filebeat-key.pem/${winame}-key.pem/ /etc/filebeat/filebeat.yml ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} wazuh-install-files/${winame}.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} wazuh-install-files/${winame}-key.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${filebeat_cert_path} wazuh-install-files/root-ca.pem --strip-components 1 ${debug}" + eval "rm -rf ${filebeat_cert_path}/wazuh-install-files/ ${debug}" + fi + eval "chmod 500 ${filebeat_cert_path} ${debug}" + eval "chmod 400 ${filebeat_cert_path}/* ${debug}" + eval "chown root:root ${filebeat_cert_path}/* ${debug}" + else + common_logger -e "No certificates found. Could not initialize Filebeat" + installCommon_rollBack + exit 1 + fi + +} + +function filebeat_install() { + + common_logger "Starting Filebeat installation." + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstall "filebeat" "${filebeat_version}" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstall "filebeat" "${filebeat_version}" + fi + + install_result="${PIPESTATUS[0]}" + common_checkInstalled + if [ "$install_result" != 0 ] || [ -z "${filebeat_installed}" ]; then + common_logger -e "Filebeat installation failed." + installCommon_rollBack + exit 1 + else + common_logger "Filebeat installation finished." + fi + +} diff --git a/install_functions/indexer.sh b/install_functions/indexer.sh new file mode 100644 index 0000000..d224e5e --- /dev/null +++ b/install_functions/indexer.sh @@ -0,0 +1,216 @@ +# Wazuh installer - indexer.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function indexer_configure() { + + common_logger -d "Configuring Wazuh indexer." + eval "export JAVA_HOME=/usr/share/wazuh-indexer/jdk/" + + # Configure JVM options for Wazuh indexer + ram_gb=$(free -m | awk 'FNR == 2 {print $2}') + ram="$(( ram_mb / 2 ))" + + if [ "${ram}" -eq "0" ]; then + ram=1024; + fi + eval "sed -i "s/-Xms1g/-Xms${ram}m/" /etc/wazuh-indexer/jvm.options ${debug}" + eval "sed -i "s/-Xmx1g/-Xmx${ram}m/" /etc/wazuh-indexer/jvm.options ${debug}" + + if [ -n "${AIO}" ]; then + eval "installCommon_getConfig indexer/indexer_all_in_one.yml /etc/wazuh-indexer/opensearch.yml ${debug}" + else + eval "installCommon_getConfig indexer/indexer_assistant_distributed.yml /etc/wazuh-indexer/opensearch.yml ${debug}" + if [ "${#indexer_node_names[@]}" -eq 1 ]; then + pos=0 + { + echo "node.name: ${indxname}" + echo "network.host: ${indexer_node_ips[0]}" + echo "cluster.initial_master_nodes: ${indxname}" + echo "plugins.security.nodes_dn:" + echo ' - CN='"${indxname}"',OU=Wazuh,O=Wazuh,L=California,C=US' + } >> /etc/wazuh-indexer/opensearch.yml + else + echo "node.name: ${indxname}" >> /etc/wazuh-indexer/opensearch.yml + echo "cluster.initial_master_nodes:" >> /etc/wazuh-indexer/opensearch.yml + for i in "${indexer_node_names[@]}"; do + echo " - ${i}" >> /etc/wazuh-indexer/opensearch.yml + done + + echo "discovery.seed_hosts:" >> /etc/wazuh-indexer/opensearch.yml + for i in "${indexer_node_ips[@]}"; do + echo " - ${i}" >> /etc/wazuh-indexer/opensearch.yml + done + + for i in "${!indexer_node_names[@]}"; do + if [[ "${indexer_node_names[i]}" == "${indxname}" ]]; then + pos="${i}"; + fi + done + + echo "network.host: ${indexer_node_ips[pos]}" >> /etc/wazuh-indexer/opensearch.yml + + echo "plugins.security.nodes_dn:" >> /etc/wazuh-indexer/opensearch.yml + for i in "${indexer_node_names[@]}"; do + echo " - CN=${i},OU=Wazuh,O=Wazuh,L=California,C=US" >> /etc/wazuh-indexer/opensearch.yml + done + fi + fi + + indexer_copyCertificates + + jv=$(java -version 2>&1 | grep -o -m1 '1.8.0' ) + if [ "$jv" == "1.8.0" ]; then + { + echo "wazuh-indexer hard nproc 4096" + echo "wazuh-indexer soft nproc 4096" + echo "wazuh-indexer hard nproc 4096" + echo "wazuh-indexer soft nproc 4096" + } >> /etc/security/limits.conf + echo -ne "\nbootstrap.system_call_filter: false" >> /etc/wazuh-indexer/opensearch.yml + fi + + common_logger "Wazuh indexer post-install configuration finished." +} + +function indexer_copyCertificates() { + + common_logger -d "Copying Wazuh indexer certificates." + eval "rm -f ${indexer_cert_path}/* ${debug}" + name=${indexer_node_names[pos]} + + if [ -f "${tar_file}" ]; then + if ! tar -tvf "${tar_file}" | grep -q "${name}" ; then + common_logger -e "Tar file does not contain certificate for the node ${name}." + installCommon_rollBack + exit 1; + fi + eval "mkdir ${indexer_cert_path} ${debug}" + eval "sed -i s/indexer.pem/${name}.pem/ /etc/wazuh-indexer/opensearch.yml ${debug}" + eval "sed -i s/indexer-key.pem/${name}-key.pem/ /etc/wazuh-indexer/opensearch.yml ${debug}" + eval "tar -xf ${tar_file} -C ${indexer_cert_path} wazuh-install-files/${name}.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${indexer_cert_path} wazuh-install-files/${name}-key.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${indexer_cert_path} wazuh-install-files/root-ca.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${indexer_cert_path} wazuh-install-files/admin.pem --strip-components 1 ${debug}" + eval "tar -xf ${tar_file} -C ${indexer_cert_path} wazuh-install-files/admin-key.pem --strip-components 1 ${debug}" + eval "rm -rf ${indexer_cert_path}/wazuh-install-files/ ${debug}" + eval "chown -R wazuh-indexer:wazuh-indexer ${indexer_cert_path} ${debug}" + eval "chmod 500 ${indexer_cert_path} ${debug}" + eval "chmod 400 ${indexer_cert_path}/* ${debug}" + else + common_logger -e "No certificates found. Could not initialize Wazuh indexer" + installCommon_rollBack + exit 1; + fi + +} + +function indexer_initialize() { + + common_logger "Initializing Wazuh indexer cluster security settings." + eval "common_curl -XGET https://"${indexer_node_ips[pos]}":9200/ -uadmin:admin -k --max-time 120 --silent --output /dev/null" + e_code="${PIPESTATUS[0]}" + + if [ "${e_code}" -ne "0" ]; then + common_logger -e "Cannot initialize Wazuh indexer cluster." + installCommon_rollBack + exit 1 + fi + + if [ -n "${AIO}" ]; then + eval "sudo -u wazuh-indexer JAVA_HOME=/usr/share/wazuh-indexer/jdk/ OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -cd /etc/wazuh-indexer/opensearch-security -icl -p 9200 -nhnv -cacert ${indexer_cert_path}/root-ca.pem -cert ${indexer_cert_path}/admin.pem -key ${indexer_cert_path}/admin-key.pem -h 127.0.0.1 ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The Wazuh indexer cluster security configuration could not be initialized." + installCommon_rollBack + exit 1 + else + common_logger "Wazuh indexer cluster security configuration initialized." + fi + fi + + if [ "${#indexer_node_names[@]}" -eq 1 ] && [ -z "${AIO}" ]; then + installCommon_changePasswords + fi + + common_logger "Wazuh indexer cluster initialized." + +} + +function indexer_install() { + + common_logger "Starting Wazuh indexer installation." + + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstall "wazuh-indexer" "${wazuh_version}-*" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstall "wazuh-indexer" "${wazuh_version}-*" + fi + + common_checkInstalled + if [ "$install_result" != 0 ] || [ -z "${indexer_installed}" ]; then + common_logger -e "Wazuh indexer installation failed." + installCommon_rollBack + exit 1 + else + common_logger "Wazuh indexer installation finished." + fi + + eval "sysctl -q -w vm.max_map_count=262144 ${debug}" + +} + +function indexer_startCluster() { + + common_logger -d "Starting Wazuh indexer cluster." + for ip_to_test in "${indexer_node_ips[@]}"; do + eval "common_curl -XGET https://"${ip_to_test}":9200/ -k -s -o /dev/null" + e_code="${PIPESTATUS[0]}" + + if [ "${e_code}" -eq "7" ]; then + common_logger -e "Connectivity check failed on node ${ip_to_test} port 9200. Possible causes: Wazuh indexer not installed on the node, the Wazuh indexer service is not running or you have connectivity issues with that node. Please check this before trying again." + exit 1 + fi + done + + eval "wazuh_indexer_ip=( $(cat /etc/wazuh-indexer/opensearch.yml | grep network.host | sed 's/network.host:\s//') )" + eval "sudo -u wazuh-indexer JAVA_HOME=/usr/share/wazuh-indexer/jdk/ OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -cd /etc/wazuh-indexer/opensearch-security -icl -p 9200 -nhnv -cacert /etc/wazuh-indexer/certs/root-ca.pem -cert /etc/wazuh-indexer/certs/admin.pem -key /etc/wazuh-indexer/certs/admin-key.pem -h ${wazuh_indexer_ip} ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The Wazuh indexer cluster security configuration could not be initialized." + installCommon_rollBack + exit 1 + else + common_logger "Wazuh indexer cluster security configuration initialized." + fi + + # Validate Wazuh indexer security admin it is initialized + indexer_security_admin_comm="common_curl -XGET https://"${indexer_node_ips[pos]}":9200/ -uadmin:admin -k --max-time 120 --silent -w \"%{http_code}\" --output /dev/null" + http_status=$(eval "${indexer_security_admin_comm}") + retries=0 + max_retries=5 + while [ "${http_status}" -ne 200 ]; do + common_logger -d "Waiting for Wazuh indexer to be ready. wazuh-indexer status: ${http_status}" + sleep 5 + retries=$((retries+1)) + if [ "${retries}" -eq "${max_retries}" ]; then + common_logger -e "The Wazuh indexer cluster security configuration could not be initialized." + exit 1 + fi + http_status=$(eval "${indexer_security_admin_comm}") + done + + # Wazuh alerts template injection + if [ -n "${offline_install}" ]; then + filebeat_wazuh_template="file://${offline_files_path}/wazuh-template.json" + fi + http_status=$(eval "common_curl --silent '${filebeat_wazuh_template}' --max-time 300 --retry 5 --retry-delay 5" | eval "common_curl -X PUT 'https://${indexer_node_ips[pos]}:9200/_template/wazuh' -H \'Content-Type: application/json\' -d @- -uadmin:admin -k --max-time 300 --silent --retry 5 --retry-delay 5 -w "%{http_code}" -o /dev/null") + if [ "${http_status}" -ne 200 ]; then + common_logger -e "The wazuh-alerts template could not be inserted into the Wazuh indexer cluster." + exit 1 + else + common_logger -d "Inserted wazuh-alerts template into the Wazuh indexer cluster." + fi +} diff --git a/install_functions/installCommon.sh b/install_functions/installCommon.sh new file mode 100644 index 0000000..6bbd2c9 --- /dev/null +++ b/install_functions/installCommon.sh @@ -0,0 +1,938 @@ +# Wazuh installer - common.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function installCommon_addCentOSRepository() { + + local repo_name="$1" + local repo_description="$2" + local repo_baseurl="$3" + + echo "[$repo_name]" >> "${centos_repo}" + echo "name=${repo_description}" >> "${centos_repo}" + echo "baseurl=${repo_baseurl}" >> "${centos_repo}" + echo 'gpgcheck=1' >> "${centos_repo}" + echo 'enabled=1' >> "${centos_repo}" + echo "gpgkey=file://${centos_key}" >> "${centos_repo}" + echo '' >> "${centos_repo}" + +} + +function installCommon_cleanExit() { + + rollback_conf="" + + if [ -n "$spin_pid" ]; then + eval "kill -9 $spin_pid ${debug}" + fi + + until [[ "${rollback_conf}" =~ ^[N|Y|n|y]$ ]]; do + echo -ne "\nDo you want to remove the ongoing installation?[Y/N]" + read -r rollback_conf + done + if [[ "${rollback_conf}" =~ [N|n] ]]; then + exit 1 + else + common_checkInstalled + installCommon_rollBack + exit 1 + fi + +} + +function installCommon_addWazuhRepo() { + + common_logger -d "Adding the Wazuh repository." + + if [ -n "${development}" ]; then + if [ "${sys_type}" == "yum" ]; then + eval "rm -f /etc/yum.repos.d/wazuh.repo ${debug}" + elif [ "${sys_type}" == "apt-get" ]; then + eval "rm -f /etc/apt/sources.list.d/wazuh.list ${debug}" + fi + fi + + if [ ! -f "/etc/yum.repos.d/wazuh.repo" ] && [ ! -f "/etc/zypp/repos.d/wazuh.repo" ] && [ ! -f "/etc/apt/sources.list.d/wazuh.list" ] ; then + if [ "${sys_type}" == "yum" ]; then + eval "rpm --import ${repogpg} ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "Cannot import Wazuh GPG key" + exit 1 + fi + eval "(echo -e '[wazuh]\ngpgcheck=1\ngpgkey=${repogpg}\nenabled=1\nname=EL-\${releasever} - Wazuh\nbaseurl='${repobaseurl}'/yum/\nprotect=1' | tee /etc/yum.repos.d/wazuh.repo)" "${debug}" + eval "chmod 644 /etc/yum.repos.d/wazuh.repo ${debug}" + elif [ "${sys_type}" == "apt-get" ]; then + eval "common_curl -s ${repogpg} --max-time 300 --retry 5 --retry-delay 5 --fail | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import - ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "Cannot import Wazuh GPG key" + exit 1 + fi + eval "chmod 644 /usr/share/keyrings/wazuh.gpg ${debug}" + eval "(echo \"deb [signed-by=/usr/share/keyrings/wazuh.gpg] ${repobaseurl}/apt/ ${reporelease} main\" | tee /etc/apt/sources.list.d/wazuh.list)" "${debug}" + eval "apt-get update -q ${debug}" + eval "chmod 644 /etc/apt/sources.list.d/wazuh.list ${debug}" + fi + else + common_logger -d "Wazuh repository already exists. Skipping addition." + fi + + if [ -n "${development}" ]; then + common_logger "Wazuh development repository added." + else + common_logger "Wazuh repository added." + fi +} + +function installCommon_aptInstall() { + + package="${1}" + version="${2}" + attempt=0 + if [ -n "${version}" ]; then + installer=${package}${sep}${version} + else + installer=${package} + fi + + # Offline installation case: get package name and install it + if [ -n "${offline_install}" ]; then + package_name=$(ls ${offline_packages_path} | grep ${package}) + installer="${offline_packages_path}/${package_name}" + fi + + command="DEBIAN_FRONTEND=noninteractive apt-get install ${installer} -y -q" + common_checkAptLock + + if [ "${attempt}" -ne "${max_attempts}" ]; then + apt_output=$(eval "${command} 2>&1") + install_result="${PIPESTATUS[0]}" + eval "echo \${apt_output} ${debug}" + fi + +} + +function installCommon_aptInstallList(){ + + dependencies=("$@") + not_installed=() + + for dep in "${dependencies[@]}"; do + if ! apt list --installed 2>/dev/null | grep -q -E ^"${dep}"\/; then + not_installed+=("${dep}") + for wia_dep in "${wia_apt_dependencies[@]}"; do + if [ "${wia_dep}" == "${dep}" ]; then + wia_dependencies_installed+=("${dep}") + fi + done + fi + done + + if [ "${#not_installed[@]}" -gt 0 ]; then + common_logger "--- Dependencies ----" + for dep in "${not_installed[@]}"; do + common_logger "Installing $dep." + installCommon_aptInstall "${dep}" + if [ "${install_result}" != 0 ]; then + common_logger -e "Cannot install dependency: ${dep}." + installCommon_rollBack + exit 1 + fi + done + fi + +} + +function installCommon_changePasswordApi() { + + common_logger -d "Changing API passwords." + + #Change API password tool + if [ -n "${changeall}" ]; then + for i in "${!api_passwords[@]}"; do + if [ -n "${wazuh}" ] || [ -n "${AIO}" ]; then + passwords_getApiUserId "${api_users[i]}" + WAZUH_PASS_API='{\"password\":\"'"${api_passwords[i]}"'\"}' + eval 'common_curl -s -k -X PUT -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" -d "$WAZUH_PASS_API" "https://localhost:55000/security/users/${user_id}" -o /dev/null --max-time 300 --retry 5 --retry-delay 5 --fail' + if [ "${api_users[i]}" == "${adminUser}" ]; then + sleep 1 + adminPassword="${api_passwords[i]}" + passwords_getApiToken + fi + fi + if [ "${api_users[i]}" == "wazuh-wui" ] && { [ -n "${dashboard}" ] || [ -n "${AIO}" ]; }; then + passwords_changeDashboardApiPassword "${api_passwords[i]}" + fi + done + else + if [ -n "${wazuh}" ] || [ -n "${AIO}" ]; then + passwords_getApiUserId "${nuser}" + WAZUH_PASS_API='{\"password\":\"'"${password}"'\"}' + eval 'common_curl -s -k -X PUT -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" -d "$WAZUH_PASS_API" "https://localhost:55000/security/users/${user_id}" -o /dev/null --max-time 300 --retry 5 --retry-delay 5 --fail' + fi + if [ "${nuser}" == "wazuh-wui" ] && { [ -n "${dashboard}" ] || [ -n "${AIO}" ]; }; then + passwords_changeDashboardApiPassword "${password}" + fi + fi + +} + +function installCommon_createCertificates() { + + common_logger -d "Creating Wazuh certificates." + if [ -n "${AIO}" ]; then + eval "installCommon_getConfig certificate/config_aio.yml ${config_file} ${debug}" + fi + + cert_readConfig + + if [ -d /tmp/wazuh-certificates/ ]; then + eval "rm -rf /tmp/wazuh-certificates/ ${debug}" + fi + eval "mkdir /tmp/wazuh-certificates/ ${debug}" + + cert_tmp_path="/tmp/wazuh-certificates/" + + cert_generateRootCAcertificate + cert_generateAdmincertificate + cert_generateIndexercertificates + cert_generateFilebeatcertificates + cert_generateDashboardcertificates + cert_cleanFiles + eval "chmod 400 /tmp/wazuh-certificates/* ${debug}" + eval "mv /tmp/wazuh-certificates/* /tmp/wazuh-install-files ${debug}" + eval "rm -rf /tmp/wazuh-certificates/ ${debug}" + +} + +function installCommon_createClusterKey() { + + openssl rand -hex 16 >> "/tmp/wazuh-install-files/clusterkey" + +} + +function installCommon_createInstallFiles() { + + if [ -d /tmp/wazuh-install-files ]; then + eval "rm -rf /tmp/wazuh-install-files ${debug}" + fi + + if eval "mkdir /tmp/wazuh-install-files ${debug}"; then + common_logger "Generating configuration files." + + dep="openssl" + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstallList "${dep}" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstallList "${dep}" + fi + + if [ "${#not_installed[@]}" -gt 0 ]; then + wia_dependencies_installed+=("${dep}") + fi + + if [ -n "${configurations}" ]; then + cert_checkOpenSSL + fi + installCommon_createCertificates + if [ -n "${server_node_types[*]}" ]; then + installCommon_createClusterKey + fi + gen_file="/tmp/wazuh-install-files/wazuh-passwords.txt" + passwords_generatePasswordFile + eval "cp '${config_file}' '/tmp/wazuh-install-files/config.yml' ${debug}" + eval "chown root:root /tmp/wazuh-install-files/* ${debug}" + eval "tar -zcf '${tar_file}' -C '/tmp/' wazuh-install-files/ ${debug}" + eval "rm -rf '/tmp/wazuh-install-files' ${debug}" + eval "rm -rf ${config_file} ${debug}" + common_logger "Created ${tar_file_name}. It contains the Wazuh cluster key, certificates, and passwords necessary for installation." + else + common_logger -e "Unable to create /tmp/wazuh-install-files" + exit 1 + fi +} + +function installCommon_changePasswords() { + + common_logger -d "Setting Wazuh indexer cluster passwords." + if [ -f "${tar_file}" ]; then + eval "tar -xf ${tar_file} -C /tmp wazuh-install-files/wazuh-passwords.txt ${debug}" + p_file="/tmp/wazuh-install-files/wazuh-passwords.txt" + common_checkInstalled + if [ -n "${start_indexer_cluster}" ] || [ -n "${AIO}" ]; then + changeall=1 + passwords_readUsers + else + no_indexer_backup=1 + fi + if { [ -n "${wazuh}" ] || [ -n "${AIO}" ]; } && { [ "${server_node_types[pos]}" == "master" ] || [ "${#server_node_names[@]}" -eq 1 ]; }; then + passwords_getApiToken + passwords_getApiUsers + passwords_getApiIds + else + api_users=( wazuh wazuh-wui ) + fi + installCommon_readPasswordFileUsers + else + common_logger -e "Cannot find passwords file. Exiting" + installCommon_rollBack + exit 1 + fi + if [ -n "${start_indexer_cluster}" ] || [ -n "${AIO}" ]; then + passwords_getNetworkHost + passwords_generateHash + fi + + passwords_changePassword + + if [ -n "${start_indexer_cluster}" ] || [ -n "${AIO}" ]; then + passwords_runSecurityAdmin + fi + if [ -n "${wazuh}" ] || [ -n "${dashboard}" ] || [ -n "${AIO}" ]; then + if [ "${server_node_types[pos]}" == "master" ] || [ "${#server_node_names[@]}" -eq 1 ] || [ -n "${dashboard_installed}" ]; then + installCommon_changePasswordApi + fi + fi + +} + +# Adds the CentOS repository to install lsof. +function installCommon_configureCentOSRepositories() { + + centos_repos_configured=1 + centos_key="/etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial" + eval "common_curl -sLo ${centos_key} 'https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official' --max-time 300 --retry 5 --retry-delay 5 --fail" + + if [ ! -f "${centos_key}" ]; then + common_logger -w "The CentOS key could not be added. Some dependencies may not be installed." + else + centos_repo="/etc/yum.repos.d/centos.repo" + eval "touch ${centos_repo} ${debug}" + common_logger -d "CentOS repository file created." + + if [ "${DIST_VER}" == "9" ]; then + installCommon_addCentOSRepository "appstream" "CentOS Stream \$releasever - AppStream" "https://mirror.stream.centos.org/9-stream/AppStream/\$basearch/os/" + installCommon_addCentOSRepository "baseos" "CentOS Stream \$releasever - BaseOS" "https://mirror.stream.centos.org/9-stream/BaseOS/\$basearch/os/" + elif [ "${DIST_VER}" == "8" ]; then + installCommon_addCentOSRepository "extras" "CentOS Linux \$releasever - Extras" "http://vault.centos.org/centos/\$releasever/extras/\$basearch/os/" + installCommon_addCentOSRepository "baseos" "CentOS Linux \$releasever - BaseOS" "http://vault.centos.org/centos/\$releasever/BaseOS/\$basearch/os/" + installCommon_addCentOSRepository "appstream" "CentOS Linux \$releasever - AppStream" "http://vault.centos.org/centos/\$releasever/AppStream/\$basearch/os/" + fi + + common_logger -d "CentOS repositories added." + fi + +} + +function installCommon_extractConfig() { + + common_logger -d "Extracting Wazuh configuration." + if ! tar -tf "${tar_file}" | grep -q wazuh-install-files/config.yml; then + common_logger -e "There is no config.yml file in ${tar_file}." + exit 1 + fi + eval "tar -xf ${tar_file} -C /tmp wazuh-install-files/config.yml ${debug}" + +} + +function installCommon_getConfig() { + + if [ "$#" -ne 2 ]; then + common_logger -e "installCommon_getConfig should be called with two arguments" + exit 1 + fi + + config_name="config_file_$(eval "echo ${1} | sed 's|/|_|g;s|.yml||'")" + if [ -z "$(eval "echo \${${config_name}}")" ]; then + common_logger -e "Unable to find configuration file ${1}. Exiting." + installCommon_rollBack + exit 1 + fi + eval "echo \"\${${config_name}}\"" > "${2}" +} + +function installCommon_getPass() { + + for i in "${!users[@]}"; do + if [ "${users[i]}" == "${1}" ]; then + u_pass=${passwords[i]} + fi + done +} + +function installCommon_installCheckDependencies() { + + common_logger -d "Installing check dependencies." + if [ "${sys_type}" == "yum" ]; then + if [[ "${DIST_NAME}" == "rhel" ]] && [[ "${DIST_VER}" == "8" || "${DIST_VER}" == "9" ]]; then + installCommon_configureCentOSRepositories + fi + installCommon_yumInstallList "${wia_yum_dependencies[@]}" + + # In RHEL cases, remove the CentOS repositories configuration + if [ "${centos_repos_configured}" == 1 ]; then + installCommon_removeCentOSrepositories + fi + + elif [ "${sys_type}" == "apt-get" ]; then + eval "apt-get update -q ${debug}" + installCommon_aptInstallList "${wia_apt_dependencies[@]}" + fi + +} + +function installCommon_installPrerequisites() { + + message="Installing prerequisites dependencies." + if [ "${sys_type}" == "yum" ]; then + if [ "${1}" == "AIO" ]; then + deps=($(echo "${indexer_yum_dependencies[@]}" "${dashboard_yum_dependencies[@]}" | tr ' ' '\n' | sort -u)) + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_yumInstallList "${deps[@]}" + else + offline_checkPrerequisites "${deps[@]}" + fi + fi + if [ "${1}" == "indexer" ]; then + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_yumInstallList "${indexer_yum_dependencies[@]}" + else + offline_checkPrerequisites "${indexer_yum_dependencies[@]}" + fi + fi + if [ "${1}" == "dashboard" ]; then + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_yumInstallList "${dashboard_yum_dependencies[@]}" + else + offline_checkPrerequisites "${dashboard_yum_dependencies[@]}" + fi + fi + elif [ "${sys_type}" == "apt-get" ]; then + if [ -z "${offline_install}" ]; then + eval "apt-get update -q ${debug}" + fi + if [ "${1}" == "AIO" ]; then + deps=($(echo "${wazuh_apt_dependencies[@]}" "${indexer_apt_dependencies[@]}" "${dashboard_apt_dependencies[@]}" | tr ' ' '\n' | sort -u)) + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_aptInstallList "${deps[@]}" + else + offline_checkPrerequisites "${deps[@]}" + fi + fi + if [ "${1}" == "indexer" ]; then + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_aptInstallList "${indexer_apt_dependencies[@]}" + else + offline_checkPrerequisites "${indexer_apt_dependencies[@]}" + fi + fi + if [ "${1}" == "dashboard" ]; then + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_aptInstallList "${dashboard_apt_dependencies[@]}" + else + offline_checkPrerequisites "${dashboard_apt_dependencies[@]}" + fi + fi + if [ "${1}" == "wazuh" ]; then + if [ -z "${offline_install}" ]; then + common_logger -d "${message}" + installCommon_aptInstallList "${wazuh_apt_dependencies[@]}" + else + offline_checkPrerequisites "${wazuh_apt_dependencies[@]}" + fi + fi + fi + +} + +function installCommon_readPasswordFileUsers() { + + filecorrect=$(grep -Ev '^#|^\s*$' "${p_file}" | grep -Pzc "\A(\s*(indexer_username|api_username|indexer_password|api_password):[ \t]+[\'\"]?[\w.*+?-]+[\'\"]?)+\Z") + if [[ "${filecorrect}" -ne 1 ]]; then + common_logger -e "The password file does not have a correct format or password uses invalid characters. Allowed characters: A-Za-z0-9.*+? + +For Wazuh indexer users, the file must have this format: + +# Description + indexer_username: + indexer_password: + +For Wazuh API users, the file must have this format: + +# Description + api_username: + api_password: + +" + installCommon_rollBack + exit 1 + fi + + sfileusers=$(grep indexer_username: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + sfilepasswords=$(grep indexer_password: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + + sfileapiusers=$(grep api_username: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + sfileapipasswords=$(grep api_password: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + + + mapfile -t fileusers < <(printf '%s\n' "${sfileusers}") + mapfile -t filepasswords < <(printf '%s\n' "${sfilepasswords}") + mapfile -t fileapiusers < <(printf '%s\n' "${sfileapiusers}") + mapfile -t fileapipasswords < <(printf '%s\n' "${sfileapipasswords}") + + if [ -n "${changeall}" ]; then + for j in "${!fileusers[@]}"; do + supported=false + for i in "${!users[@]}"; do + if [[ ${users[i]} == "${fileusers[j]}" ]]; then + passwords_checkPassword "${filepasswords[j]}" + passwords[i]=${filepasswords[j]} + supported=true + fi + done + if [ "${supported}" = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e -d "The given user ${fileusers[j]} does not exist" + fi + done + + for j in "${!fileapiusers[@]}"; do + supported=false + for i in "${!api_users[@]}"; do + if [[ "${api_users[i]}" == "${fileapiusers[j]}" ]]; then + passwords_checkPassword "${fileapipasswords[j]}" + api_passwords[i]=${fileapipasswords[j]} + supported=true + fi + done + if [ "${supported}" = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The Wazuh API user ${fileapiusers[j]} does not exist" + fi + done + else + finalusers=() + finalpasswords=() + + finalapiusers=() + finalapipasswords=() + + if [ -n "${dashboard_installed}" ] && [ -n "${dashboard}" ]; then + users=( kibanaserver admin ) + fi + + if [ -n "${filebeat_installed}" ] && [ -n "${wazuh}" ]; then + users=( admin ) + fi + + for j in "${!fileusers[@]}"; do + supported=false + for i in "${!users[@]}"; do + if [[ "${users[i]}" == "${fileusers[j]}" ]]; then + passwords_checkPassword "${filepasswords[j]}" + finalusers+=(${fileusers[j]}) + finalpasswords+=(${filepasswords[j]}) + supported=true + fi + done + if [ "${supported}" = "false" ] && [ -n "${indexer_installed}" ] && [ -n "${changeall}" ]; then + common_logger -e -d "The given user ${fileusers[j]} does not exist" + fi + done + + for j in "${!fileapiusers[@]}"; do + supported=false + for i in "${!api_users[@]}"; do + if [[ "${api_users[i]}" == "${fileapiusers[j]}" ]]; then + passwords_checkPassword "${fileapipasswords[j]}" + finalapiusers+=("${fileapiusers[j]}") + finalapipasswords+=("${fileapipasswords[j]}") + supported=true + fi + done + if [ ${supported} = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The Wazuh API user ${fileapiusers[j]} does not exist" + fi + done + + users=() + mapfile -t users < <(printf '%s\n' "${finalusers[@]}") + mapfile -t passwords < <(printf '%s\n' "${finalpasswords[@]}") + mapfile -t api_users < <(printf '%s\n' "${finalapiusers[@]}") + mapfile -t api_passwords < <(printf '%s\n' "${finalapipasswords[@]}") + changeall=1 + fi + +} + +function installCommon_restoreWazuhrepo() { + + common_logger -d "Restoring Wazuh repository." + if [ -n "${development}" ]; then + if [ "${sys_type}" == "yum" ] && [ -f "/etc/yum.repos.d/wazuh.repo" ]; then + file="/etc/yum.repos.d/wazuh.repo" + elif [ "${sys_type}" == "apt-get" ] && [ -f "/etc/apt/sources.list.d/wazuh.list" ]; then + file="/etc/apt/sources.list.d/wazuh.list" + else + common_logger -w -d "Wazuh repository does not exists." + fi + eval "sed -i 's/-dev//g' ${file} ${debug}" + eval "sed -i 's/pre-release/4.x/g' ${file} ${debug}" + eval "sed -i 's/unstable/stable/g' ${file} ${debug}" + fi + +} + +function installCommon_removeCentOSrepositories() { + + eval "rm -f ${centos_repo} ${debug}" + eval "rm -f ${centos_key} ${debug}" + eval "yum clean all ${debug}" + centos_repos_configured=0 + common_logger -d "CentOS repositories and key deleted." + +} + +function installCommon_rollBack() { + + if [ -z "${uninstall}" ]; then + common_logger "--- Removing existing Wazuh installation ---" + fi + + if [ -f "/etc/yum.repos.d/wazuh.repo" ]; then + eval "rm /etc/yum.repos.d/wazuh.repo ${debug}" + elif [ -f "/etc/zypp/repos.d/wazuh.repo" ]; then + eval "rm /etc/zypp/repos.d/wazuh.repo ${debug}" + elif [ -f "/etc/apt/sources.list.d/wazuh.list" ]; then + eval "rm /etc/apt/sources.list.d/wazuh.list ${debug}" + fi + + if [[ -n "${wazuh_installed}" && ( -n "${wazuh}" || -n "${AIO}" || -n "${uninstall}" ) ]];then + common_logger "Removing Wazuh manager." + if [ "${sys_type}" == "yum" ]; then + common_checkYumLock + if [ "${attempt}" -ne "${max_attempts}" ]; then + eval "yum remove wazuh-manager -y ${debug}" + eval "rpm -q wazuh-manager --quiet && manager_installed=1" + fi + elif [ "${sys_type}" == "apt-get" ]; then + common_checkAptLock + eval "apt-get remove --purge wazuh-manager -y ${debug}" + manager_installed=$(apt list --installed 2>/dev/null | grep wazuh-manager) + fi + + if [ -n "${manager_installed}" ]; then + common_logger -w "The Wazuh manager package could not be removed." + else + common_logger "Wazuh manager removed." + fi + + fi + + if [[ ( -n "${wazuh_remaining_files}" || -n "${wazuh_installed}" ) && ( -n "${wazuh}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + eval "rm -rf /var/ossec/ ${debug}" + fi + + if [[ -n "${indexer_installed}" && ( -n "${indexer}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + common_logger "Removing Wazuh indexer." + if [ "${sys_type}" == "yum" ]; then + common_checkYumLock + if [ "${attempt}" -ne "${max_attempts}" ]; then + eval "yum remove wazuh-indexer -y ${debug}" + eval "rpm -q wazuh-indexer --quiet && indexer_installed=1" + fi + elif [ "${sys_type}" == "apt-get" ]; then + common_checkAptLock + eval "apt-get remove --purge wazuh-indexer -y ${debug}" + indexer_installed=$(apt list --installed 2>/dev/null | grep wazuh-indexer) + fi + + if [ -n "${indexer_installed}" ]; then + common_logger -w "The Wazuh indexer package could not be removed." + else + common_logger "Wazuh indexer removed." + fi + fi + + if [[ ( -n "${indexer_remaining_files}" || -n "${indexer_installed}" ) && ( -n "${indexer}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + eval "rm -rf /var/lib/wazuh-indexer/ ${debug}" + eval "rm -rf /usr/share/wazuh-indexer/ ${debug}" + eval "rm -rf /etc/wazuh-indexer/ ${debug}" + fi + + if [[ -n "${filebeat_installed}" && ( -n "${wazuh}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + common_logger "Removing Filebeat." + if [ "${sys_type}" == "yum" ]; then + common_checkYumLock + if [ "${attempt}" -ne "${max_attempts}" ]; then + eval "yum remove filebeat -y ${debug}" + eval "rpm -q filebeat --quiet && filebeat_installed=1" + fi + elif [ "${sys_type}" == "apt-get" ]; then + common_checkAptLock + eval "apt-get remove --purge filebeat -y ${debug}" + filebeat_installed=$(apt list --installed 2>/dev/null | grep filebeat) + fi + + if [ -n "${filebeat_installed}" ]; then + common_logger -w "The Filebeat package could not be removed." + else + common_logger "Filebeat removed." + fi + fi + + if [[ ( -n "${filebeat_remaining_files}" || -n "${filebeat_installed}" ) && ( -n "${wazuh}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + eval "rm -rf /var/lib/filebeat/ ${debug}" + eval "rm -rf /usr/share/filebeat/ ${debug}" + eval "rm -rf /etc/filebeat/ ${debug}" + fi + + if [[ -n "${dashboard_installed}" && ( -n "${dashboard}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + common_logger "Removing Wazuh dashboard." + if [ "${sys_type}" == "yum" ]; then + common_checkYumLock + if [ "${attempt}" -ne "${max_attempts}" ]; then + eval "yum remove wazuh-dashboard -y ${debug}" + eval "rpm -q wazuh-dashboard --quiet && dashboard_installed=1" + fi + elif [ "${sys_type}" == "apt-get" ]; then + common_checkAptLock + eval "apt-get remove --purge wazuh-dashboard -y ${debug}" + dashboard_installed=$(apt list --installed 2>/dev/null | grep wazuh-dashboard) + fi + + if [ -n "${dashboard_installed}" ]; then + common_logger -w "The Wazuh dashboard package could not be removed." + else + common_logger "Wazuh dashboard removed." + fi + fi + + if [[ ( -n "${dashboard_remaining_files}" || -n "${dashboard_installed}" ) && ( -n "${dashboard}" || -n "${AIO}" || -n "${uninstall}" ) ]]; then + eval "rm -rf /var/lib/wazuh-dashboard/ ${debug}" + eval "rm -rf /usr/share/wazuh-dashboard/ ${debug}" + eval "rm -rf /etc/wazuh-dashboard/ ${debug}" + eval "rm -rf /run/wazuh-dashboard/ ${debug}" + fi + + elements_to_remove=( "/var/log/wazuh-indexer/" + "/var/log/filebeat/" + "/etc/systemd/system/opensearch.service.wants/" + "/securityadmin_demo.sh" + "/etc/systemd/system/multi-user.target.wants/wazuh-manager.service" + "/etc/systemd/system/multi-user.target.wants/filebeat.service" + "/etc/systemd/system/multi-user.target.wants/opensearch.service" + "/etc/systemd/system/multi-user.target.wants/wazuh-dashboard.service" + "/etc/systemd/system/wazuh-dashboard.service" + "/lib/firewalld/services/dashboard.xml" + "/lib/firewalld/services/opensearch.xml" ) + + eval "rm -rf ${elements_to_remove[*]} ${debug}" + + common_remove_gpg_key + + installCommon_removeWIADependencies + + eval "systemctl daemon-reload ${debug}" + + if [ -z "${uninstall}" ]; then + if [ -n "${rollback_conf}" ] || [ -n "${overwrite}" ]; then + common_logger "Installation cleaned." + else + common_logger "Installation cleaned. Check the ${logfile} file to learn more about the issue." + fi + fi + +} + +function installCommon_startService() { + + if [ "$#" -ne 1 ]; then + common_logger -e "installCommon_startService must be called with 1 argument." + exit 1 + fi + + common_logger "Starting service ${1}." + + if [[ -d /run/systemd/system ]]; then + eval "systemctl daemon-reload ${debug}" + eval "systemctl enable ${1}.service ${debug}" + eval "systemctl start ${1}.service ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + installCommon_rollBack + exit 1 + else + common_logger "${1} service started." + fi + elif ps -p 1 -o comm= | grep "init"; then + eval "chkconfig ${1} on ${debug}" + eval "service ${1} start ${debug}" + eval "/etc/init.d/${1} start ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + installCommon_rollBack + exit 1 + else + common_logger "${1} service started." + fi + elif [ -x "/etc/rc.d/init.d/${1}" ] ; then + eval "/etc/rc.d/init.d/${1} start ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + installCommon_rollBack + exit 1 + else + common_logger "${1} service started." + fi + else + common_logger -e "${1} could not start. No service manager found on the system." + exit 1 + fi + +} + +function installCommon_yumInstallList(){ + + dependencies=("$@") + not_installed=() + for dep in "${dependencies[@]}"; do + if ! rpm -q "${dep}" --quiet;then + not_installed+=("${dep}") + for wia_dep in "${wia_yum_dependencies[@]}"; do + if [ "${wia_dep}" == "${dep}" ]; then + wia_dependencies_installed+=("${dep}") + fi + done + fi + done + + if [ "${#not_installed[@]}" -gt 0 ]; then + common_logger "--- Dependencies ---" + for dep in "${not_installed[@]}"; do + common_logger "Installing $dep." + installCommon_yumInstall "${dep}" + yum_code="${PIPESTATUS[0]}" + + eval "echo \${yum_output} ${debug}" + if [ "${yum_code}" != 0 ]; then + common_logger -e "Cannot install dependency: ${dep}." + installCommon_rollBack + exit 1 + fi + done + fi + +} + +function installCommon_removeWIADependencies() { + + if [ "${sys_type}" == "yum" ]; then + installCommon_yumRemoveWIADependencies + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptRemoveWIADependencies + fi + +} + +function installCommon_yumRemoveWIADependencies(){ + + if [ "${#wia_dependencies_installed[@]}" -gt 0 ]; then + common_logger "--- Dependencies ---" + for dep in "${wia_dependencies_installed[@]}"; do + if [ "${dep}" != "systemd" ]; then + common_logger "Removing $dep." + yum_output=$(yum remove ${dep} -y 2>&1) + yum_code="${PIPESTATUS[0]}" + + eval "echo \${yum_output} ${debug}" + if [ "${yum_code}" != 0 ]; then + common_logger -e "Cannot remove dependency: ${dep}." + exit 1 + fi + fi + done + fi + +} + +function installCommon_aptRemoveWIADependencies(){ + + if [ "${#wia_dependencies_installed[@]}" -gt 0 ]; then + common_logger "--- Dependencies ----" + for dep in "${wia_dependencies_installed[@]}"; do + if [ "${dep}" != "systemd" ]; then + common_logger "Removing $dep." + apt_output=$(apt-get remove --purge ${dep} -y 2>&1) + apt_code="${PIPESTATUS[0]}" + + eval "echo \${apt_output} ${debug}" + if [ "${apt_code}" != 0 ]; then + common_logger -e "Cannot remove dependency: ${dep}." + exit 1 + fi + fi + done + fi + +} +function installCommon_yumInstall() { + + package="${1}" + version="${2}" + install_result=1 + if [ -n "${version}" ]; then + installer="${package}-${version}" + else + installer="${package}" + fi + + # Offline installation case: get package name and install it + if [ -n "${offline_install}" ]; then + package_name=$(ls ${offline_packages_path} | grep ${package}) + installer="${offline_packages_path}/${package_name}" + command="rpm -ivh ${installer}" + common_logger -d "Installing local package: ${installer}" + else + command="yum install ${installer} -y" + fi + common_checkYumLock + + if [ "${attempt}" -ne "${max_attempts}" ]; then + yum_output=$(eval "${command} 2>&1") + install_result="${PIPESTATUS[0]}" + eval "echo \${yum_output} ${debug}" + fi + +} + + +function installCommon_checkAptLock() { + + attempt=0 + seconds=30 + max_attempts=10 + + while fuser "${apt_lockfile}" >/dev/null 2>&1 && [ "${attempt}" -lt "${max_attempts}" ]; do + attempt=$((attempt+1)) + common_logger "Another process is using APT. Waiting for it to release the lock. Next retry in ${seconds} seconds (${attempt}/${max_attempts})" + sleep "${seconds}" + done + +} diff --git a/install_functions/installMain.sh b/install_functions/installMain.sh new file mode 100755 index 0000000..c10dff0 --- /dev/null +++ b/install_functions/installMain.sh @@ -0,0 +1,425 @@ +# Wazuh installer - main functions +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function getHelp() { + + echo -e "" + echo -e "NAME" + echo -e " $(basename "$0") - Install and configure Wazuh central components: Wazuh server, Wazuh indexer, and Wazuh dashboard." + echo -e "" + echo -e "SYNOPSIS" + echo -e " $(basename "$0") [OPTIONS] -a | -c | -s | -wi | -wd | -ws " + echo -e "" + echo -e "DESCRIPTION" + echo -e " -a, --all-in-one" + echo -e " Install and configure Wazuh server, Wazuh indexer, Wazuh dashboard." + echo -e "" + echo -e " -c, --config-file " + echo -e " Path to the configuration file used to generate wazuh-install-files.tar file containing the files that will be needed for installation. By default, the Wazuh installation assistant will search for a file named config.yml in the same path as the script." + echo -e "" + echo -e " -dw, --download-wazuh " + echo -e " Download all the packages necessary for offline installation. Type of packages to download for offline installation (rpm, deb)" + echo -e "" + echo -e " -fd, --force-install-dashboard" + echo -e " Force Wazuh dashboard installation to continue even when it is not capable of connecting to the Wazuh indexer." + echo -e "" + echo -e " -g, --generate-config-files" + echo -e " Generate wazuh-install-files.tar file containing the files that will be needed for installation from config.yml. In distributed deployments you will need to copy this file to all hosts." + echo -e "" + echo -e " -h, --help" + echo -e " Display this help and exit." + echo -e "" + echo -e " -i, --ignore-check" + echo -e " Ignore the check for minimum hardware requirements." + echo -e "" + echo -e " -o, --overwrite" + echo -e " Overwrites previously installed components. This will erase all the existing configuration and data." + echo -e "" + echo -e " -of, --offline-installation" + echo -e " Perform an offline installation. This option must be used with -a, -ws, -s, -wi, or -wd." + echo -e "" + echo -e " -p, --port" + echo -e " Specifies the Wazuh web user interface port. By default is the 443 TCP port. Recommended ports are: 8443, 8444, 8080, 8888, 9000." + echo -e "" + echo -e " -s, --start-cluster" + echo -e " Initialize Wazuh indexer cluster security settings." + echo -e "" + echo -e " -t, --tar " + echo -e " Path to tar file containing certificate files. By default, the Wazuh installation assistant will search for a file named wazuh-install-files.tar in the same path as the script." + echo -e "" + echo -e " -u, --uninstall" + echo -e " Uninstalls all Wazuh components. This will erase all the existing configuration and data." + echo -e "" + echo -e " -v, --verbose" + echo -e " Shows the complete installation output." + echo -e "" + echo -e " -V, --version" + echo -e " Shows the version of the script and Wazuh packages." + echo -e "" + echo -e " -wd, --wazuh-dashboard " + echo -e " Install and configure Wazuh dashboard, used for distributed deployments." + echo -e "" + echo -e " -wi, --wazuh-indexer " + echo -e " Install and configure Wazuh indexer, used for distributed deployments." + echo -e "" + echo -e " -ws, --wazuh-server " + echo -e " Install and configure Wazuh manager and Filebeat, used for distributed deployments." + exit 1 + +} + + +function main() { + umask 177 + + if [ -z "${1}" ]; then + getHelp + fi + + while [ -n "${1}" ] + do + case "${1}" in + "-a"|"--all-in-one") + AIO=1 + shift 1 + ;; + "-c"|"--config-file") + if [ -z "${2}" ]; then + common_logger -e "Error on arguments. Probably missing after -c|--config-file" + getHelp + exit 1 + fi + file_conf=1 + config_file="${2}" + shift 2 + ;; + "-fd"|"--force-install-dashboard") + force=1 + shift 1 + ;; + "-g"|"--generate-config-files") + configurations=1 + shift 1 + ;; + "-h"|"--help") + getHelp + ;; + "-i"|"--ignore-check") + ignore=1 + shift 1 + ;; + "-o"|"--overwrite") + overwrite=1 + shift 1 + ;; + "-of"|"--offline-installation") + offline_install=1 + shift 1 + ;; + "-p"|"--port") + if [ -z "${2}" ]; then + common_logger -e "Error on arguments. Probably missing after -p|--port" + getHelp + exit 1 + fi + port_specified=1 + port_number="${2}" + shift 2 + ;; + "-s"|"--start-cluster") + start_indexer_cluster=1 + shift 1 + ;; + "-t"|"--tar") + if [ -z "${2}" ]; then + common_logger -e "Error on arguments. Probably missing after -t|--tar" + getHelp + exit 1 + fi + tar_conf=1 + tar_file="${2}" + shift 2 + ;; + "-u"|"--uninstall") + uninstall=1 + shift 1 + ;; + "-v"|"--verbose") + debugEnabled=1 + debug="2>&1 | tee -a ${logfile}" + shift 1 + ;; + "-V"|"--version") + showVersion=1 + shift 1 + ;; + "-wd"|"--wazuh-dashboard") + if [ -z "${2}" ]; then + common_logger -e "Error on arguments. Probably missing after -wd|---wazuh-dashboard" + getHelp + exit 1 + fi + dashboard=1 + dashname="${2}" + shift 2 + ;; + "-wi"|"--wazuh-indexer") + if [ -z "${2}" ]; then + common_logger -e "Arguments contain errors. Probably missing after -wi|--wazuh-indexer." + getHelp + exit 1 + fi + indexer=1 + indxname="${2}" + shift 2 + ;; + "-ws"|"--wazuh-server") + if [ -z "${2}" ]; then + common_logger -e "Error on arguments. Probably missing after -ws|--wazuh-server" + getHelp + exit 1 + fi + wazuh=1 + winame="${2}" + shift 2 + ;; + "-dw"|"--download-wazuh") + if [ "${2}" != "deb" ] && [ "${2}" != "rpm" ]; then + common_logger -e "Error on arguments. Probably missing after -dw|--download-wazuh" + getHelp + exit 1 + fi + download=1 + package_type="${2}" + shift 2 + ;; + *) + echo "Unknow option: ${1}" + getHelp + esac + done + + cat /dev/null > "${logfile}" + + if [ -z "${download}" ] && [ -z "${showVersion}" ]; then + common_checkRoot + fi + + if [ -n "${showVersion}" ]; then + common_logger "Wazuh version: ${wazuh_version}" + common_logger "Filebeat version: ${filebeat_version}" + common_logger "Wazuh installation assistant version: ${wazuh_install_vesion}" + exit 0 + fi + + common_logger "Starting Wazuh installation assistant. Wazuh version: ${wazuh_version}" + common_logger "Verbose logging redirected to ${logfile}" + +# -------------- Uninstall case ------------------------------------ + + common_checkSystem + + if [ -z "${download}" ]; then + check_dist + fi + + if [ -z "${uninstall}" ] && [ -z "${offline_install}" ]; then + installCommon_installCheckDependencies + elif [ -n "${offline_install}" ]; then + offline_checkDependencies + fi + + common_checkInstalled + checks_arguments + if [ -n "${uninstall}" ]; then + installCommon_rollBack + exit 0 + fi + + checks_arch + if [ -n "${ignore}" ]; then + common_logger -w "Hardware checks ignored." + else + common_logger "Verifying that your system meets the recommended minimum hardware requirements." + checks_health + fi + +# -------------- Preliminary checks and Prerequisites -------------------------------- + + if [ -z "${configurations}" ] && [ -z "${AIO}" ] && [ -z "${download}" ]; then + checks_previousCertificate + fi + + if [ -n "${port_specified}" ]; then + checks_available_port "${port_number}" "${wazuh_aio_ports[@]}" + dashboard_changePort "${port_number}" + elif [ -n "${AIO}" ] || [ -n "${dashboard}" ]; then + dashboard_changePort "${http_port}" + fi + + if [ -n "${AIO}" ]; then + rm -f "${tar_file}" + checks_ports "${wazuh_aio_ports[@]}" + installCommon_installPrerequisites "AIO" + fi + + if [ -n "${indexer}" ]; then + checks_ports "${wazuh_indexer_ports[@]}" + installCommon_installPrerequisites "indexer" + fi + + if [ -n "${wazuh}" ]; then + checks_ports "${wazuh_manager_ports[@]}" + installCommon_installPrerequisites "wazuh" + fi + + if [ -n "${dashboard}" ]; then + checks_ports "${wazuh_dashboard_port}" + installCommon_installPrerequisites "dashboard" + fi + + +# -------------- Wazuh repo ---------------------- + + # Offline installation case: extract the compressed files + if [ -n "${offline_install}" ]; then + offline_checkPreinstallation + offline_extractFiles + fi + + if [ -n "${AIO}" ] || [ -n "${indexer}" ] || [ -n "${dashboard}" ] || [ -n "${wazuh}" ]; then + check_curlVersion + if [ -z "${offline_install}" ]; then + installCommon_addWazuhRepo + fi + fi + +# -------------- Configuration creation case ----------------------- + + # Creation certificate case: Only AIO and -g option can create certificates. + if [ -n "${configurations}" ] || [ -n "${AIO}" ]; then + common_logger "--- Configuration files ---" + installCommon_createInstallFiles + fi + + if [ -z "${configurations}" ] && [ -z "${download}" ]; then + installCommon_extractConfig + config_file="/tmp/wazuh-install-files/config.yml" + cert_readConfig + fi + + # Distributed architecture: node names must be different + if [[ -z "${AIO}" && -z "${download}" && ( -n "${indexer}" || -n "${dashboard}" || -n "${wazuh}" ) ]]; then + checks_names + fi + + if [ -n "${configurations}" ]; then + installCommon_removeWIADependencies + fi + +# -------------- Wazuh indexer case ------------------------------- + + if [ -n "${indexer}" ]; then + common_logger "--- Wazuh indexer ---" + indexer_install + indexer_configure + installCommon_startService "wazuh-indexer" + indexer_initialize + installCommon_removeWIADependencies + fi + +# -------------- Start Wazuh indexer cluster case ------------------ + + if [ -n "${start_indexer_cluster}" ]; then + indexer_startCluster + installCommon_changePasswords + installCommon_removeWIADependencies + fi + +# -------------- Wazuh dashboard case ------------------------------ + + if [ -n "${dashboard}" ]; then + common_logger "--- Wazuh dashboard ----" + dashboard_install + dashboard_configure + installCommon_startService "wazuh-dashboard" + installCommon_changePasswords + dashboard_initialize + installCommon_removeWIADependencies + + fi + +# -------------- Wazuh server case --------------------------------------- + + if [ -n "${wazuh}" ]; then + common_logger "--- Wazuh server ---" + manager_install + manager_configure + if [ -n "${server_node_types[*]}" ]; then + manager_startCluster + fi + installCommon_startService "wazuh-manager" + manager_checkService + filebeat_install + filebeat_configure + installCommon_changePasswords + installCommon_startService "filebeat" + filebeat_checkService + installCommon_removeWIADependencies + fi + +# -------------- AIO case ------------------------------------------ + + if [ -n "${AIO}" ]; then + + common_logger "--- Wazuh indexer ---" + indexer_install + indexer_configure + installCommon_startService "wazuh-indexer" + indexer_initialize + common_logger "--- Wazuh server ---" + manager_install + manager_configure + installCommon_startService "wazuh-manager" + manager_checkService + filebeat_install + filebeat_configure + installCommon_startService "filebeat" + filebeat_checkService + common_logger "--- Wazuh dashboard ---" + dashboard_install + dashboard_configure + installCommon_startService "wazuh-dashboard" + installCommon_changePasswords + dashboard_initializeAIO + installCommon_removeWIADependencies + + fi + +# -------------- Offline case ------------------------------------------ + + if [ -n "${download}" ]; then + common_logger "--- Download Packages ---" + offline_download + fi + + +# ------------------------------------------------------------------- + + if [ -z "${configurations}" ] && [ -z "${download}" ] && [ -z "${offline_install}" ]; then + installCommon_restoreWazuhrepo + fi + + if [ -n "${AIO}" ] || [ -n "${indexer}" ] || [ -n "${dashboard}" ] || [ -n "${wazuh}" ]; then + eval "rm -rf /tmp/wazuh-install-files ${debug}" + common_logger "Installation finished." + elif [ -n "${start_indexer_cluster}" ]; then + common_logger "Wazuh indexer cluster started." + fi + +} diff --git a/install_functions/installVariables.sh b/install_functions/installVariables.sh new file mode 100644 index 0000000..19626c1 --- /dev/null +++ b/install_functions/installVariables.sh @@ -0,0 +1,68 @@ +# Wazuh installer - variables +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +## Package vars +readonly wazuh_major="4.10" +readonly wazuh_version="4.10.0" +readonly filebeat_version="7.10.2" +readonly wazuh_install_vesion="0.1" +readonly source_branch="v${wazuh_version}" + +## Links and paths to resources +readonly resources="https://${bucket}/${wazuh_major}" +readonly base_url="https://${bucket}/${repository}" +base_path="$(dirname "$(readlink -f "$0")")" +readonly base_path +config_file="${base_path}/config.yml" +readonly tar_file_name="wazuh-install-files.tar" +tar_file="${base_path}/${tar_file_name}" + +filebeat_wazuh_template="https://raw.githubusercontent.com/wazuh/wazuh/${source_branch}/extensions/elasticsearch/7.x/wazuh-template.json" + +readonly dashboard_cert_path="/etc/wazuh-dashboard/certs" +readonly filebeat_cert_path="/etc/filebeat/certs" +readonly indexer_cert_path="/etc/wazuh-indexer/certs" + +readonly logfile="/var/log/wazuh-install.log" +debug=">> ${logfile} 2>&1" +readonly yum_lockfile="/var/run/yum.pid" +readonly apt_lockfile="/var/lib/dpkg/lock" + +## Offline Installation vars +readonly base_dest_folder="wazuh-offline" +readonly manager_deb_base_url="${base_url}/apt/pool/main/w/wazuh-manager" +readonly filebeat_deb_base_url="${base_url}/apt/pool/main/f/filebeat" +readonly filebeat_deb_package="filebeat-oss-${filebeat_version}-amd64.deb" +readonly indexer_deb_base_url="${base_url}/apt/pool/main/w/wazuh-indexer" +readonly dashboard_deb_base_url="${base_url}/apt/pool/main/w/wazuh-dashboard" +readonly manager_rpm_base_url="${base_url}/yum" +readonly filebeat_rpm_base_url="${base_url}/yum" +readonly filebeat_rpm_package="filebeat-oss-${filebeat_version}-x86_64.rpm" +readonly indexer_rpm_base_url="${base_url}/yum" +readonly dashboard_rpm_base_url="${base_url}/yum" +readonly wazuh_gpg_key="https://${bucket}/key/GPG-KEY-WAZUH" +readonly filebeat_config_file="${resources}/tpl/wazuh/filebeat/filebeat.yml" + +adminUser="wazuh" +adminPassword="wazuh" + +http_port=443 +wazuh_aio_ports=( 9200 9300 1514 1515 1516 55000 "${http_port}") +readonly wazuh_indexer_ports=( 9200 9300 ) +readonly wazuh_manager_ports=( 1514 1515 1516 55000 ) +wazuh_dashboard_port="${http_port}" +# `lsof` and `openssl` are installed separately +wia_yum_dependencies=( systemd grep tar coreutils sed procps-ng gawk curl ) +readonly wia_apt_dependencies=( systemd grep tar coreutils sed procps gawk curl ) +readonly wazuh_yum_dependencies=( libcap ) +readonly wazuh_apt_dependencies=( apt-transport-https libcap2-bin software-properties-common gnupg ) +readonly indexer_yum_dependencies=( coreutils ) +readonly indexer_apt_dependencies=( debconf adduser procps gnupg apt-transport-https ) +readonly dashboard_yum_dependencies=( libcap ) +readonly dashboard_apt_dependencies=( debhelper tar curl libcap2-bin gnupg apt-transport-https ) +wia_dependencies_installed=() diff --git a/install_functions/manager.sh b/install_functions/manager.sh new file mode 100644 index 0000000..862f76b --- /dev/null +++ b/install_functions/manager.sh @@ -0,0 +1,102 @@ +# Wazuh installer - manager.sh functions. +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function manager_startCluster() { + + common_logger -d "Starting Wazuh manager cluster." + for i in "${!server_node_names[@]}"; do + if [[ "${server_node_names[i]}" == "${winame}" ]]; then + pos="${i}"; + fi + done + + for i in "${!server_node_types[@]}"; do + if [[ "${server_node_types[i],,}" == "master" ]]; then + master_address=${server_node_ips[i]} + fi + done + + key=$(tar -axf "${tar_file}" wazuh-install-files/clusterkey -O) + bind_address="0.0.0.0" + port="1516" + hidden="no" + disabled="no" + lstart=$(grep -n "" /var/ossec/etc/ossec.conf | cut -d : -f 1) + lend=$(grep -n "" /var/ossec/etc/ossec.conf | cut -d : -f 1) + + eval 'sed -i -e "${lstart},${lend}s/.*<\/name>/wazuh_cluster<\/name>/" \ + -e "${lstart},${lend}s/.*<\/node_name>/${winame}<\/node_name>/" \ + -e "${lstart},${lend}s/.*<\/node_type>/${server_node_types[pos],,}<\/node_type>/" \ + -e "${lstart},${lend}s/.*<\/key>/${key}<\/key>/" \ + -e "${lstart},${lend}s/.*<\/port>/${port}<\/port>/" \ + -e "${lstart},${lend}s/.*<\/bind_addr>/${bind_address}<\/bind_addr>/" \ + -e "${lstart},${lend}s/.*<\/node>/${master_address}<\/node>/" \ + -e "${lstart},${lend}s/.*<\/hidden>/${hidden}<\/hidden>/" \ + -e "${lstart},${lend}s/.*<\/disabled>/${disabled}<\/disabled>/" \ + /var/ossec/etc/ossec.conf' + +} + +function manager_checkService() { + common_logger "Checking Wazuh API connection" + eval "TOKEN=$(curl -k -s -X POST -u "wazuh-wui:wazuh-wui" https://127.0.0.1:55000/security/user/authenticate/run_as?raw=true -d '{"user_name":"wzread"}' -H "content-type:application/json")" + wm_error=$(curl -k -s -X GET "https://127.0.0.1:55000/agents/outdated?pretty=true" -H "Authorization: Bearer ${TOKEN}") + + if [[ ${wm_error,,} =~ '"error": 0' ]]; then + common_logger "Wazuh API connection successful" + else + common_logger -e "Wazuh API connection Error. $wm_error" + eval "/var/ossec/bin/wazuh-control status ${debug}" + installCommon_rollBack + exit 1 + fi +} + +function manager_configure(){ + + common_logger -d "Configuring Wazuh manager." + + if [ ${#indexer_node_names[@]} -eq 1 ]; then + eval "sed -i 's/.*<\/host>/https:\/\/${indexer_node_ips[0]}:9200<\/host>/g' /var/ossec/etc/ossec.conf ${debug}" + else + lstart=$(grep -n "" /var/ossec/etc/ossec.conf | cut -d : -f 1) + lend=$(grep -n "" /var/ossec/etc/ossec.conf | cut -d : -f 1) + for i in "${!indexer_node_ips[@]}"; do + if [ $i -eq 0 ]; then + eval "sed -i 's/.*<\/host>/https:\/\/${indexer_node_ips[0]}:9200<\/host>/g' /var/ossec/etc/ossec.conf ${debug}" + else + eval "sed -i '//a\ https:\/\/${indexer_node_ips[$i]}:9200<\/host>' /var/ossec/etc/ossec.conf" + fi + done + fi + eval "sed -i s/filebeat.pem/${server_node_names[0]}.pem/ /var/ossec/etc/ossec.conf ${debug}" + eval "sed -i s/filebeat-key.pem/${server_node_names[0]}-key.pem/ /var/ossec/etc/ossec.conf ${debug}" + common_logger -d "Setting provisional Wazuh indexer password." + eval "/var/ossec/bin/wazuh-keystore -f indexer -k username -v admin" + eval "/var/ossec/bin/wazuh-keystore -f indexer -k password -v admin" + common_logger "Wazuh manager vulnerability detection configuration finished." +} + +function manager_install() { + + common_logger "Starting the Wazuh manager installation." + if [ "${sys_type}" == "yum" ]; then + installCommon_yumInstall "wazuh-manager" "${wazuh_version}-*" + elif [ "${sys_type}" == "apt-get" ]; then + installCommon_aptInstall "wazuh-manager" "${wazuh_version}-*" + fi + + common_checkInstalled + if [ "$install_result" != 0 ] || [ -z "${wazuh_installed}" ]; then + common_logger -e "Wazuh installation failed." + installCommon_rollBack + exit 1 + else + common_logger "Wazuh manager installation finished." + fi +} diff --git a/install_functions/wazuh-offline-download.sh b/install_functions/wazuh-offline-download.sh new file mode 100755 index 0000000..cd81f19 --- /dev/null +++ b/install_functions/wazuh-offline-download.sh @@ -0,0 +1,171 @@ +#!/bin/bash + +# Wazuh installer: offline download +# Copyright (C) 2021, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function offline_download() { + + common_logger "Starting Wazuh packages download." + common_logger "Downloading Wazuh ${package_type} packages for ${arch}." + dest_path="${base_dest_folder}/wazuh-packages" + + if [ -d "${dest_path}" ]; then + eval "rm -f ${dest_path}/* ${debug}" # Clean folder before downloading specific versions + eval "chmod 700 ${dest_path} ${debug}" + else + eval "mkdir -m700 -p ${dest_path} ${debug}" # Create folder if it does not exist + fi + + packages_to_download=( "manager" "filebeat" "indexer" "dashboard" ) + + manager_revision="1" + indexer_revision="1" + dashboard_revision="1" + + if [ "${package_type}" == "rpm" ]; then + manager_rpm_package="wazuh-manager-${wazuh_version}-${manager_revision}.x86_64.${package_type}" + indexer_rpm_package="wazuh-indexer-${wazuh_version}-${indexer_revision}.x86_64.${package_type}" + dashboard_rpm_package="wazuh-dashboard-${wazuh_version}-${dashboard_revision}.x86_64.${package_type}" + manager_base_url="${manager_rpm_base_url}" + indexer_base_url="${indexer_rpm_base_url}" + dashboard_base_url="${dashboard_rpm_base_url}" + manager_package="${manager_rpm_package}" + indexer_package="${indexer_rpm_package}" + dashboard_package="${dashboard_rpm_package}" + elif [ "${package_type}" == "deb" ]; then + manager_deb_package="wazuh-manager_${wazuh_version}-${manager_revision}_amd64.${package_type}" + indexer_deb_package="wazuh-indexer_${wazuh_version}-${indexer_revision}_amd64.${package_type}" + dashboard_deb_package="wazuh-dashboard_${wazuh_version}-${dashboard_revision}_amd64.${package_type}" + manager_base_url="${manager_deb_base_url}" + indexer_base_url="${indexer_deb_base_url}" + dashboard_base_url="${dashboard_deb_base_url}" + manager_package="${manager_deb_package}" + indexer_package="${indexer_deb_package}" + dashboard_package="${dashboard_deb_package}" + else + common_logger "Unsupported package type: ${package_type}" + exit 1 + fi + + while common_curl -s -I -o /dev/null -w "%{http_code}" "${manager_base_url}/${manager_package}" --max-time 300 --retry 5 --retry-delay 5 --fail | grep -q "200"; do + manager_revision=$((manager_revision+1)) + if [ "${package_type}" == "rpm" ]; then + manager_rpm_package="wazuh-manager-${wazuh_version}-${manager_revision}.x86_64.rpm" + manager_package="${manager_rpm_package}" + else + manager_deb_package="wazuh-manager_${wazuh_version}-${manager_revision}_amd64.deb" + manager_package="${manager_deb_package}" + fi + done + if [ "$manager_revision" -gt 1 ] && [ "$(common_curl -s -I -o /dev/null -w "%{http_code}" "${manager_base_url}/${manager_package}" --max-time 300 --retry 5 --retry-delay 5 --fail)" -ne "200" ]; then + manager_revision=$((manager_revision-1)) + if [ "${package_type}" == "rpm" ]; then + manager_rpm_package="wazuh-manager-${wazuh_version}-${manager_revision}.x86_64.rpm" + else + manager_deb_package="wazuh-manager_${wazuh_version}-${manager_revision}_amd64.deb" + fi + fi + common_logger -d "Wazuh manager package revision fetched." + + while common_curl -s -I -o /dev/null -w "%{http_code}" "${indexer_base_url}/${indexer_package}" --max-time 300 --retry 5 --retry-delay 5 --fail | grep -q "200"; do + indexer_revision=$((indexer_revision+1)) + if [ "${package_type}" == "rpm" ]; then + indexer_rpm_package="wazuh-indexer-${wazuh_version}-${indexer_revision}.x86_64.rpm" + indexer_package="${indexer_rpm_package}" + else + indexer_deb_package="wazuh-indexer_${wazuh_version}-${indexer_revision}_amd64.deb" + indexer_package="${indexer_deb_package}" + fi + done + if [ "$indexer_revision" -gt 1 ] && [ "$(common_curl -s -I -o /dev/null -w "%{http_code}" "${indexer_base_url}/${indexer_package}" --max-time 300 --retry 5 --retry-delay 5 --fail)" -ne "200" ]; then + indexer_revision=$((indexer_revision-1)) + if [ "${package_type}" == "rpm" ]; then + indexer_rpm_package="wazuh-indexer-${wazuh_version}-${indexer_revision}.x86_64.rpm" + else + indexer_deb_package="wazuh-indexer_${wazuh_version}-${indexer_revision}_amd64.deb" + fi + fi + common_logger -d "Wazuh indexer package revision fetched." + + while common_curl -s -I -o /dev/null -w "%{http_code}" "${dashboard_base_url}/${dashboard_package}" --max-time 300 --retry 5 --retry-delay 5 --fail | grep -q "200"; do + dashboard_revision=$((dashboard_revision+1)) + if [ "${package_type}" == "rpm" ]; then + dashboard_rpm_package="wazuh-dashboard-${wazuh_version}-${dashboard_revision}.x86_64.rpm" + dashboard_package="${dashboard_rpm_package}" + else + dashboard_deb_package="wazuh-dashboard_${wazuh_version}-${dashboard_revision}_amd64.deb" + dashboard_package="${dashboard_deb_package}" + fi + done + if [ "$dashboard_revision" -gt 1 ] && [ "$(common_curl -s -I -o /dev/null -w "%{http_code}" "${dashboard_base_url}/${dashboard_package}" --max-time 300 --retry 5 --retry-delay 5 --fail)" -ne "200" ]; then + dashboard_revision=$((dashboard_revision-1)) + if [ "${package_type}" == "rpm" ]; then + dashboard_rpm_package="wazuh-dashboard-${wazuh_version}-${dashboard_revision}.x86_64.rpm" + else + dashboard_deb_package="wazuh-dashboard_${wazuh_version}-${dashboard_revision}_amd64.deb" + fi + fi + common_logger -d "Wazuh dashboard package revision fetched." + + for package in "${packages_to_download[@]}" + do + common_logger -d "Downloading Wazuh ${package} package..." + package_name="${package}_${package_type}_package" + eval "package_base_url=${package}_${package_type}_base_url" + + if output=$(common_curl -sSo "${dest_path}/${!package_name}" "${!package_base_url}/${!package_name}" --max-time 300 --retry 5 --retry-delay 5 --fail 2>&1); then + common_logger "The ${package} package was downloaded." + else + common_logger -e "The ${package} package could not be downloaded. Exiting." + eval "echo \${output} ${debug}" + exit 1 + fi + + done + + common_logger "The packages are in ${dest_path}" + +# -------------------------------------------------- + + common_logger "Downloading configuration files and assets." + dest_path="${base_dest_folder}/wazuh-files" + + if [ -d "${dest_path}" ]; then + eval "rm -f ${dest_path}/* ${debug}" # Clean folder before downloading specific versions + eval "chmod 700 ${dest_path} ${debug}" + else + eval "mkdir -m700 -p ${dest_path} ${debug}" # Create folder if it does not exist + fi + + files_to_download=( "${wazuh_gpg_key}" "${filebeat_config_file}" "${filebeat_wazuh_template}" "${filebeat_wazuh_module}" ) + + eval "cd ${dest_path}" + for file in "${files_to_download[@]}" + do + common_logger -d "Downloading ${file}..." + if output=$(common_curl -sSO ${file} --max-time 300 --retry 5 --retry-delay 5 --fail 2>&1); then + common_logger "The resource ${file} was downloaded." + else + common_logger -e "The resource ${file} could not be downloaded. Exiting." + eval "echo \${output} ${debug}" + exit 1 + fi + + done + eval "cd - > /dev/null" + + eval "chmod 500 ${base_dest_folder} ${debug}" + + common_logger "The configuration files and assets are in wazuh-offline.tar.gz" + + eval "tar -czf ${base_dest_folder}.tar.gz ${base_dest_folder} ${debug}" + eval "chmod -R 700 ${base_dest_folder} && rm -rf ${base_dest_folder} ${debug}" + + common_logger "You can follow the installation guide here https://documentation.wazuh.com/current/deployment-options/offline-installation.html" + +} \ No newline at end of file diff --git a/install_functions/wazuh-offline-installation.sh b/install_functions/wazuh-offline-installation.sh new file mode 100644 index 0000000..dab1f82 --- /dev/null +++ b/install_functions/wazuh-offline-installation.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Wazuh installer: offline download +# Copyright (C) 2023, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +# Checks the necessary dependencies for the installation +function offline_checkDependencies() { + + dependencies=( curl tar gnupg openssl lsof ) + + common_logger "Checking installed dependencies for Offline installation." + for dep in "${dependencies[@]}"; do + if [ "${sys_type}" == "yum" ]; then + eval "yum list installed 2>/dev/null | grep -q -E ^"${dep}"\\." + elif [ "${sys_type}" == "apt-get" ]; then + eval "apt list --installed 2>/dev/null | grep -q -E ^"${dep}"\/" + fi + + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${dep} is necessary for the offline installation." + exit 1 + fi + done + common_logger -d "Offline dependencies are installed." + +} + +# Checks the necessary packages needed for a Wazuh component +function offline_checkPrerequisites(){ + + dependencies=("$@") + common_logger "Checking prerequisites for Offline installation." + for dep in "${dependencies[@]}"; do + if [ "${sys_type}" == "yum" ]; then + eval "yum list installed 2>/dev/null | grep -q -E ^"${dep}"\\." + elif [ "${sys_type}" == "apt-get" ]; then + eval "apt list --installed 2>/dev/null | grep -q -E ^"${dep}"\/" + fi + + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${dep} is necessary for the offline installation." + exit 1 + fi + done + common_logger -d "Offline prerequisites are installed." +} + +# Checks the necessary files for the installation +function offline_checkPreinstallation() { + + offline_tarfile="${base_dest_folder}.tar.gz" + common_logger "Checking ${offline_tarfile} file." + if [ ! -f "${base_path}/${offline_tarfile}" ]; then + common_logger -e "The ${offline_tarfile} file was not found in ${base_path}." + exit 1 + fi + common_logger -d "${offline_tarfile} was found correctly." + +} + +# Extracts the files for the offline installation and check its content +function offline_extractFiles() { + + common_logger -d "Extracting files from ${offline_tarfile}" + if [ ! -d "${base_path}/wazuh-offline/" ]; then + eval "tar -xzf ${offline_tarfile} ${debug}" + + if [ ! -d "${base_path}/wazuh-offline/" ]; then + common_logger -e "The ${offline_tarfile} file could not be decompressed." + exit 1 + fi + fi + + offline_files_path="${base_path}/wazuh-offline/wazuh-files" + offline_packages_path="${base_path}/wazuh-offline/wazuh-packages" + + required_files=( + "${offline_files_path}/filebeat.yml" + "${offline_files_path}/GPG-KEY-WAZUH" + "${offline_files_path}/wazuh-filebeat-*.tar.gz" + "${offline_files_path}/wazuh-template.json" + ) + + if [ "${sys_type}" == "apt-get" ]; then + required_files+=("${offline_packages_path}/filebeat-oss-*.deb" "${offline_packages_path}/wazuh-dashboard_*.deb" "${offline_packages_path}/wazuh-indexer_*.deb" "${offline_packages_path}/wazuh-manager_*.deb") + elif [ "${sys_type}" == "rpm" ]; then + required_files+=("${offline_packages_path}/filebeat-oss-*.rpm" "${offline_packages_path}/wazuh-dashboard_*.rpm" "${offline_packages_path}/wazuh-indexer_*.rpm" "${offline_packages_path}/wazuh-manager_*.rpm") + fi + + for file in "${required_files[@]}"; do + if ! compgen -G "${file}" > /dev/null; then + common_logger -e "Missing necessary offline file: ${file}" + exit 1 + fi + done + + common_logger -d "Offline files extracted successfully." +} diff --git a/passwords_tool/passwordsFunctions.sh b/passwords_tool/passwordsFunctions.sh new file mode 100644 index 0000000..2316640 --- /dev/null +++ b/passwords_tool/passwordsFunctions.sh @@ -0,0 +1,647 @@ +# Passwords tool - library functions +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function passwords_changePassword() { + + if [ -n "${changeall}" ]; then + if [ -n "${indexer_installed}" ] && [ -z ${no_indexer_backup} ]; then + eval "mkdir /etc/wazuh-indexer/backup/ ${debug}" + eval "cp /etc/wazuh-indexer/opensearch-security/* /etc/wazuh-indexer/backup/ ${debug}" + passwords_createBackUp + fi + for i in "${!passwords[@]}" + do + if [ -n "${indexer_installed}" ] && [ -f "/etc/wazuh-indexer/backup/internal_users.yml" ]; then + awk -v new='"'"${hashes[i]}"'"' 'prev=="'${users[i]}':"{sub(/\042.*/,""); $0=$0 new} {prev=$1} 1' /etc/wazuh-indexer/backup/internal_users.yml > internal_users.yml_tmp && mv -f internal_users.yml_tmp /etc/wazuh-indexer/backup/internal_users.yml + fi + + if [ "${users[i]}" == "admin" ]; then + adminpass=${passwords[i]} + elif [ "${users[i]}" == "kibanaserver" ]; then + dashpass=${passwords[i]} + fi + + done + else + if [ -z "${api}" ] && [ -n "${indexer_installed}" ]; then + eval "mkdir /etc/wazuh-indexer/backup/ ${debug}" + eval "cp /etc/wazuh-indexer/opensearch-security/* /etc/wazuh-indexer/backup/ ${debug}" + passwords_createBackUp + fi + if [ -n "${indexer_installed}" ] && [ -f "/etc/wazuh-indexer/backup/internal_users.yml" ]; then + awk -v new='"'"${hash}"'"' 'prev=="'${nuser}':"{sub(/\042.*/,""); $0=$0 new} {prev=$1} 1' /etc/wazuh-indexer/backup/internal_users.yml > internal_users.yml_tmp && mv -f internal_users.yml_tmp /etc/wazuh-indexer/backup/internal_users.yml + fi + + if [ "${nuser}" == "admin" ]; then + adminpass=${password} + elif [ "${nuser}" == "kibanaserver" ]; then + dashpass=${password} + fi + + fi + + if [ "${nuser}" == "admin" ] || [ -n "${changeall}" ]; then + if [ -n "${filebeat_installed}" ]; then + file_username=$(grep "username:" /etc/filebeat/filebeat.yml | awk '{print $2}') + file_password=$(grep "password:" /etc/filebeat/filebeat.yml | awk '{print $2}') + if [ "$file_username" != "\${username}" ] || [ "$file_password" != "\${password}" ]; then + common_logger -w "The user and password configured in the filebeat.yml file will be updated and stored in Filebeat Keystore." + fi + eval "echo ${adminpass} | filebeat keystore add password --force --stdin ${debug}" + conf="$(awk '{sub("password: .*", "password: ${password}")}1' /etc/filebeat/filebeat.yml)" + echo "${conf}" > /etc/filebeat/filebeat.yml + eval "echo admin | filebeat keystore add username --force --stdin ${debug}" + conf="$(awk '{sub("username: .*", "username: ${username}")}1' /etc/filebeat/filebeat.yml)" + echo "${conf}" > /etc/filebeat/filebeat.yml + common_logger "The filebeat.yml file has been updated to use the Filebeat Keystore username and password." + passwords_restartService "filebeat" + eval "/var/ossec/bin/wazuh-keystore -f indexer -k password -v ${adminpass}" + passwords_restartService "wazuh-manager" + fi + fi + + if [ "$nuser" == "kibanaserver" ] || [ -n "$changeall" ]; then + if [ -n "${dashboard_installed}" ] && [ -n "${dashpass}" ]; then + if /usr/share/wazuh-dashboard/bin/opensearch-dashboards-keystore --allow-root list | grep -q opensearch.password; then + eval "echo ${dashpass} | /usr/share/wazuh-dashboard/bin/opensearch-dashboards-keystore --allow-root add -f --stdin opensearch.password ${debug_pass} > /dev/null 2>&1" + else + wazuhdashold=$(grep "password:" /etc/wazuh-dashboard/opensearch_dashboards.yml ) + rk="opensearch.password: " + wazuhdashold="${wazuhdashold//$rk}" + conf="$(awk '{sub("opensearch.password: .*", "opensearch.password: '"${dashpass}"'")}1' /etc/wazuh-dashboard/opensearch_dashboards.yml)" + echo "${conf}" > /etc/wazuh-dashboard/opensearch_dashboards.yml + fi + passwords_restartService "wazuh-dashboard" + fi + fi + +} + +function passwords_changePasswordApi() { + #Change API password tool + if [ -n "${changeall}" ]; then + for i in "${!api_passwords[@]}"; do + if [ -n "${wazuh_installed}" ]; then + passwords_getApiUserId "${api_users[i]}" + WAZUH_PASS_API='{\"password\":\"'"${api_passwords[i]}"'\"}' + eval 'common_curl -s -k -X PUT -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" -d "$WAZUH_PASS_API" "https://localhost:55000/security/users/${user_id}" -o /dev/null --max-time 300 --retry 5 --retry-delay 5 --fail' + if [ "${api_users[i]}" == "${adminUser}" ]; then + sleep 1 + adminPassword="${api_passwords[i]}" + passwords_getApiToken + fi + if [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ]; then + common_logger -nl $"The password for Wazuh API user ${api_users[i]} is ${api_passwords[i]}" + fi + fi + if [ "${api_users[i]}" == "wazuh-wui" ] && [ -n "${dashboard_installed}" ]; then + passwords_changeDashboardApiPassword "${api_passwords[i]}" + fi + done + else + if [ -n "${wazuh_installed}" ]; then + passwords_getApiUserId "${nuser}" + WAZUH_PASS_API='{\"password\":\"'"${password}"'\"}' + eval 'common_curl -s -k -X PUT -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" -d "$WAZUH_PASS_API" "https://localhost:55000/security/users/${user_id}" -o /dev/null --max-time 300 --retry 5 --retry-delay 5 --fail' + if [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ]; then + common_logger -nl $"The password for Wazuh API user ${nuser} is ${password}" + fi + fi + if [ "${nuser}" == "wazuh-wui" ] && [ -n "${dashboard_installed}" ]; then + passwords_changeDashboardApiPassword "${password}" + fi + fi +} + +function passwords_changeDashboardApiPassword() { + + j=0 + until [ -n "${file_exists}" ] || [ "${j}" -eq "12" ]; do + if [ -f "/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml" ]; then + eval "sed -i 's|password: .*|password: \"${1}\"|g' /usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml ${debug}" + if [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ]; then + common_logger "Updated wazuh-wui user password in wazuh dashboard. Remember to restart the service." + fi + file_exists=1 + fi + sleep 5 + j=$((j+1)) + done + +} + +function passwords_checkUser() { + + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + for i in "${!api_users[@]}"; do + if [ "${api_users[i]}" == "${nuser}" ]; then + exists=1 + fi + done + else + for i in "${!users[@]}"; do + if [ "${users[i]}" == "${nuser}" ]; then + exists=1 + fi + done + fi + + if [ -z "${exists}" ]; then + common_logger -e "The given user does not exist" + exit 1; + fi + +} + +function passwords_checkPassword() { + + if ! echo "$1" | grep -q "[A-Z]" || ! echo "$1" | grep -q "[a-z]" || ! echo "$1" | grep -q "[0-9]" || ! echo "$1" | grep -q "[.*+?-]" || [ "${#1}" -lt 8 ] || [ "${#1}" -gt 64 ]; then + common_logger -e "The password must have a length between 8 and 64 characters and contain at least one upper and lower case letter, a number and a symbol(.*+?-)." + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1 + fi + +} + +function passwords_createBackUp() { + + if [ -z "${indexer_installed}" ] && [ -z "${dashboard_installed}" ] && [ -z "${filebeat_installed}" ]; then + common_logger -e "Cannot find Wazuh indexer, Wazuh dashboard or Filebeat on the system." + exit 1; + else + if [ -n "${indexer_installed}" ]; then + capem=$(grep "plugins.security.ssl.transport.pemtrustedcas_filepath: " /etc/wazuh-indexer/opensearch.yml ) + rcapem="plugins.security.ssl.transport.pemtrustedcas_filepath: " + capem="${capem//$rcapem}" + fi + fi + + common_logger -d "Creating password backup." + if [ ! -d "/etc/wazuh-indexer/backup" ]; then + eval "mkdir /etc/wazuh-indexer/backup ${debug}" + fi + eval "JAVA_HOME=/usr/share/wazuh-indexer/jdk/ OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -backup /etc/wazuh-indexer/backup -icl -p 9200 -nhnv -cacert ${capem} -cert ${adminpem} -key ${adminkey} -h ${IP} ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The backup could not be created" + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + fi + common_logger -d "Password backup created in /etc/wazuh-indexer/backup." + +} + +function passwords_generateHash() { + + if [ -n "${changeall}" ]; then + common_logger -d "Generating password hashes." + for i in "${!passwords[@]}" + do + nhash=$(bash /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p "${passwords[i]}" 2>&1 | grep -A 2 'issues' | tail -n 1) + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "Hash generation failed." + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + fi + hashes+=("${nhash}") + done + common_logger -d "Password hashes generated." + else + common_logger "Generating password hash" + hash=$(bash /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p "${password}" 2>&1 | grep -A 2 'issues' | tail -n 1) + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "Hash generation failed." + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + fi + common_logger -d "Password hash generated." + fi + +} + +function passwords_generatePassword() { + + if [ -n "${nuser}" ]; then + common_logger -d "Generating random password." + pass=$(< /dev/urandom tr -dc "A-Za-z0-9.*+?" | head -c "${1:-28}";echo;) + special_char=$(< /dev/urandom tr -dc ".*+?" | head -c "${1:-1}";echo;) + minus_char=$(< /dev/urandom tr -dc "a-z" | head -c "${1:-1}";echo;) + mayus_char=$(< /dev/urandom tr -dc "A-Z" | head -c "${1:-1}";echo;) + number_char=$(< /dev/urandom tr -dc "0-9" | head -c "${1:-1}";echo;) + password="$(echo "${pass}${special_char}${minus_char}${mayus_char}${number_char}" | fold -w1 | shuf | tr -d '\n')" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The password could not been generated." + exit 1; + fi + else + common_logger -d "Generating random passwords." + for i in "${!users[@]}"; do + pass=$(< /dev/urandom tr -dc "A-Za-z0-9.*+?" | head -c "${1:-28}";echo;) + special_char=$(< /dev/urandom tr -dc ".*+?" | head -c "${1:-1}";echo;) + minus_char=$(< /dev/urandom tr -dc "a-z" | head -c "${1:-1}";echo;) + mayus_char=$(< /dev/urandom tr -dc "A-Z" | head -c "${1:-1}";echo;) + number_char=$(< /dev/urandom tr -dc "0-9" | head -c "${1:-1}";echo;) + passwords+=("$(echo "${pass}${special_char}${minus_char}${mayus_char}${number_char}" | fold -w1 | shuf | tr -d '\n')") + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The password could not been generated." + exit 1; + fi + done + for i in "${!api_users[@]}"; do + pass=$(< /dev/urandom tr -dc "A-Za-z0-9.*+?" | head -c "${1:-28}";echo;) + special_char=$(< /dev/urandom tr -dc ".*+?" | head -c "${1:-1}";echo;) + minus_char=$(< /dev/urandom tr -dc "a-z" | head -c "${1:-1}";echo;) + mayus_char=$(< /dev/urandom tr -dc "A-Z" | head -c "${1:-1}";echo;) + number_char=$(< /dev/urandom tr -dc "0-9" | head -c "${1:-1}";echo;) + api_passwords+=("$(echo "${pass}${special_char}${minus_char}${mayus_char}${number_char}" | fold -w1 | shuf | tr -d '\n')") + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "The password could not been generated." + exit 1; + fi + done + fi +} + +function passwords_generatePasswordFile() { + + common_logger -d "Generating password file." + users=( admin anomalyadmin kibanaserver kibanaro logstash readall snapshotrestore ) + api_users=( wazuh wazuh-wui ) + user_description=( + "Admin user for the web user interface and Wazuh indexer. Use this user to log in to Wazuh dashboard" + "Anomaly detection user for the web user interface" + "Wazuh dashboard user for establishing the connection with Wazuh indexer" + "Regular Dashboard user, only has read permissions to all indices and all permissions on the .kibana index" + "Filebeat user for CRUD operations on Wazuh indices" + "User with READ access to all indices" + "User with permissions to perform snapshot and restore operations" + "Admin user used to communicate with Wazuh API" + "Regular user to query Wazuh API" + ) + api_user_description=( + "Password for wazuh API user" + "Password for wazuh-wui API user" + ) + passwords_generatePassword + + for i in "${!users[@]}"; do + { + echo "# ${user_description[${i}]}" + echo " indexer_username: '${users[${i}]}'" + echo " indexer_password: '${passwords[${i}]}'" + echo "" + } >> "${gen_file}" + done + + for i in "${!api_users[@]}"; do + { + echo "# ${api_user_description[${i}]}" + echo " api_username: '${api_users[${i}]}'" + echo " api_password: '${api_passwords[${i}]}'" + echo "" + } >> "${gen_file}" + done + +} + +function passwords_getApiToken() { + retries=0 + max_internal_error_retries=20 + + TOKEN_API=$(curl -s -u "${adminUser}":"${adminPassword}" -k -X POST "https://localhost:55000/security/user/authenticate?raw=true" --max-time 300 --retry 5 --retry-delay 5) + while [[ "${TOKEN_API}" =~ "Wazuh Internal Error" ]] && [ "${retries}" -lt "${max_internal_error_retries}" ] + do + common_logger "There was an error accessing the API. Retrying..." + TOKEN_API=$(curl -s -u "${adminUser}":"${adminPassword}" -k -X POST "https://localhost:55000/security/user/authenticate?raw=true" --max-time 300 --retry 5 --retry-delay 5) + retries=$((retries+1)) + sleep 10 + done + if [[ ${TOKEN_API} =~ "Wazuh Internal Error" ]]; then + common_logger -e "There was an error while trying to get the API token." + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1 + elif [[ ${TOKEN_API} =~ "Invalid credentials" ]]; then + common_logger -e "Invalid admin user credentials" + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1 + fi + +} + +function passwords_getApiUsers() { + + mapfile -t api_users < <(common_curl -s -k -X GET -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" \"https://localhost:55000/security/users?pretty=true\" --max-time 300 --retry 5 --retry-delay 5 | grep username | awk -F': ' '{print $2}' | sed -e "s/[\'\",]//g") + +} + +function passwords_getApiIds() { + + mapfile -t api_ids < <(common_curl -s -k -X GET -H \"Authorization: Bearer $TOKEN_API\" -H \"Content-Type: application/json\" \"https://localhost:55000/security/users?pretty=true\" --max-time 300 --retry 5 --retry-delay 5 | grep id | awk -F': ' '{print $2}' | sed -e "s/[\'\",]//g") + +} + +function passwords_getApiUserId() { + + user_id="noid" + for u in "${!api_users[@]}"; do + if [ "${1}" == "${api_users[u]}" ]; then + user_id="${api_ids[u]}" + fi + done + + if [ "${user_id}" == "noid" ]; then + common_logger -e "User ${1} is not registered in Wazuh API" + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1 + fi + +} + + +function passwords_getNetworkHost() { + + IP=$(grep -hr "^network.host:" /etc/wazuh-indexer/opensearch.yml) + NH="network.host: " + IP="${IP//$NH}" + + # Remove surrounding double quotes if present + IP="${IP//\"}" + + #allow to find ip with an interface + if [[ ${IP} =~ _.*_ ]]; then + interface="${IP//_}" + IP=$(ip -o -4 addr list "${interface}" | awk '{print $4}' | cut -d/ -f1) + fi + + if [ "${IP}" == "0.0.0.0" ]; then + IP="localhost" + fi +} + +function passwords_readFileUsers() { + + filecorrect=$(grep -Ev '^#|^\s*$' "${p_file}" | grep -Pzc "\A(\s*(indexer_username|api_username|indexer_password|api_password):[ \t]+[\'\"]?[\w.*+?-]+[\'\"]?)+\Z") + if [[ "${filecorrect}" -ne 1 ]]; then + common_logger -e "The password file does not have a correct format or password uses invalid characters. Allowed characters: A-Za-z0-9.*+? + +For Wazuh indexer users, the file must have this format: + +# Description + indexer_username: + indexer_password: + +For Wazuh API users, the file must have this format: + +# Description + api_username: + api_password: + +" + exit 1 + fi + + sfileusers=$(grep indexer_username: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + sfilepasswords=$(grep indexer_password: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + + sfileapiusers=$(grep api_username: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + sfileapipasswords=$(grep api_password: "${p_file}" | awk '{ print substr( $2, 1, length($2) ) }' | sed -e "s/[\'\"]//g") + + mapfile -t fileusers <<< "${sfileusers}" + mapfile -t filepasswords <<< "${sfilepasswords}" + + mapfile -t fileapiusers <<< "${sfileapiusers}" + mapfile -t fileapipasswords <<< "${sfileapipasswords}" + + if [ -n "${changeall}" ]; then + for j in "${!fileusers[@]}"; do + supported=false + for i in "${!users[@]}"; do + if [[ "${users[i]}" == "${fileusers[j]}" ]]; then + passwords_checkPassword "${filepasswords[j]}" + passwords[i]=${filepasswords[j]} + supported=true + fi + done + if [ "${supported}" = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The user ${fileusers[j]} does not exist" + fi + done + + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + for j in "${!fileapiusers[@]}"; do + supported=false + for i in "${!api_users[@]}"; do + if [[ "${api_users[i]}" == "${fileapiusers[j]}" ]]; then + passwords_checkPassword "${fileapipasswords[j]}" + api_passwords[i]=${fileapipasswords[j]} + supported=true + fi + done + if [ "${supported}" = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The Wazuh API user ${fileapiusers[j]} does not exist" + fi + done + fi + else + finalusers=() + finalpasswords=() + + finalapiusers=() + finalapipasswords=() + + for j in "${!fileusers[@]}"; do + supported=false + for i in "${!users[@]}"; do + if [[ "${users[i]}" == "${fileusers[j]}" ]]; then + passwords_checkPassword "${filepasswords[j]}" + finalusers+=("${fileusers[j]}") + finalpasswords+=("${filepasswords[j]}") + supported=true + fi + done + if [ ${supported} = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The user ${fileusers[j]} does not exist" + fi + done + + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + for j in "${!fileapiusers[@]}"; do + supported=false + for i in "${!api_users[@]}"; do + if [[ "${api_users[i]}" == "${fileapiusers[j]}" ]]; then + passwords_checkPassword "${fileapipasswords[j]}" + finalapiusers+=("${fileapiusers[j]}") + finalapipasswords+=("${fileapipasswords[j]}") + supported=true + fi + done + if [ ${supported} = false ] && [ -n "${indexer_installed}" ]; then + common_logger -e "The Wazuh API user ${fileapiusers[j]} does not exist" + fi + done + fi + + users=() + passwords=() + mapfile -t users < <(printf "%s\n" "${finalusers[@]}") + mapfile -t passwords < <(printf "%s\n" "${finalpasswords[@]}") + mapfile -t api_users < <(printf "%s\n" "${finalapiusers[@]}") + mapfile -t api_passwords < <(printf "%s\n" "${finalapipasswords[@]}") + + changeall=1 + fi + +} + +function passwords_readUsers() { + + passwords_updateInternalUsers + susers=$(grep -B 1 hash: /etc/wazuh-indexer/opensearch-security/internal_users.yml | grep -v hash: | grep -v "-" | awk '{ print substr( $0, 1, length($0)-1 ) }') + mapfile -t users <<< "${susers[@]}" + +} + +function passwords_restartService() { + + common_logger -d "Restarting ${1} service..." + if [ "$#" -ne 1 ]; then + common_logger -e "passwords_restartService must be called with 1 argument." + exit 1 + fi + + if [[ -d /run/systemd/system ]]; then + eval "systemctl daemon-reload ${debug}" + eval "systemctl restart ${1}.service ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + else + common_logger -d "${1} started." + fi + elif ps -p 1 -o comm= | grep "init"; then + eval "/etc/init.d/${1} restart ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + else + common_logger -d "${1} started." + fi + elif [ -x "/etc/rc.d/init.d/${1}" ] ; then + eval "/etc/rc.d/init.d/${1} restart ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "${1} could not be started." + if [ -n "$(command -v journalctl)" ]; then + eval "journalctl -u ${1} >> ${logfile}" + fi + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + exit 1; + else + common_logger -d "${1} started." + fi + else + if [[ $(type -t installCommon_rollBack) == "function" ]]; then + installCommon_rollBack + fi + common_logger -e "${1} could not start. No service manager found on the system." + exit 1; + fi + +} + +function passwords_runSecurityAdmin() { + + common_logger -d "Running security admin tool." + if [ -z "${indexer_installed}" ] && [ -z "${dashboard_installed}" ] && [ -z "${filebeat_installed}" ]; then + common_logger -e "Cannot find Wazuh indexer, Wazuh dashboard or Filebeat on the system." + exit 1; + else + if [ -n "${indexer_installed}" ]; then + capem=$(grep "plugins.security.ssl.transport.pemtrustedcas_filepath: " /etc/wazuh-indexer/opensearch.yml ) + rcapem="plugins.security.ssl.transport.pemtrustedcas_filepath: " + capem="${capem//$rcapem}" + fi + fi + + common_logger -d "Loading new passwords changes." + eval "OPENSEARCH_CONF_DIR=/etc/wazuh-indexer /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -f /etc/wazuh-indexer/backup/internal_users.yml -t internalusers -p 9200 -nhnv -cacert ${capem} -cert ${adminpem} -key ${adminkey} -icl -h ${IP} ${debug}" + if [ "${PIPESTATUS[0]}" != 0 ]; then + common_logger -e "Could not load the changes." + exit 1; + fi + eval "cp /etc/wazuh-indexer/backup/internal_users.yml /etc/wazuh-indexer/opensearch-security/internal_users.yml" + eval "rm -rf /etc/wazuh-indexer/backup/ ${debug}" + + if [[ -n "${nuser}" ]] && [[ -n ${autopass} ]]; then + common_logger -nl "The password for user ${nuser} is ${password}" + common_logger -w "Password changed. Remember to update the password in the Wazuh dashboard, Wazuh server, and Filebeat nodes if necessary, and restart the services." + fi + + if [[ -n "${nuser}" ]] && [[ -z ${autopass} ]]; then + common_logger -w "Password changed. Remember to update the password in the Wazuh dashboard, Wazuh server, and Filebeat nodes if necessary, and restart the services." + fi + + if [ -n "${changeall}" ]; then + if [ -z "${AIO}" ] && [ -z "${indexer}" ] && [ -z "${dashboard}" ] && [ -z "${wazuh}" ] && [ -z "${start_indexer_cluster}" ]; then + for i in "${!users[@]}"; do + common_logger -nl "The password for user ${users[i]} is ${passwords[i]}" + done + common_logger -w "Wazuh indexer passwords changed. Remember to update the password in the Wazuh dashboard, Wazuh server, and Filebeat nodes if necessary, and restart the services." + else + common_logger -d "Passwords changed." + fi + fi + +} + +function passwords_updateInternalUsers() { + + common_logger "Updating the internal users." + backup_datetime=$(date +"%Y%m%d_%H%M%S") + internal_users_backup_path="/etc/wazuh-indexer/internalusers-backup" + passwords_getNetworkHost + passwords_createBackUp + + eval "mkdir -p ${internal_users_backup_path} ${debug}" + eval "cp /etc/wazuh-indexer/backup/internal_users.yml ${internal_users_backup_path}/internal_users_${backup_datetime}.yml.bkp ${debug}" + eval "chmod 750 ${internal_users_backup_path} ${debug}" + eval "chmod 640 ${internal_users_backup_path}/internal_users_${backup_datetime}.yml.bkp" + eval "chown -R wazuh-indexer:wazuh-indexer ${internal_users_backup_path} ${debug}" + common_logger "A backup of the internal users has been saved in the /etc/wazuh-indexer/internalusers-backup folder." + + eval "cp /etc/wazuh-indexer/backup/internal_users.yml /etc/wazuh-indexer/opensearch-security/internal_users.yml ${debug}" + eval "rm -rf /etc/wazuh-indexer/backup/ ${debug}" + common_logger -d "The internal users have been updated before changing the passwords." + +} diff --git a/passwords_tool/passwordsMain.sh b/passwords_tool/passwordsMain.sh new file mode 100644 index 0000000..52691fa --- /dev/null +++ b/passwords_tool/passwordsMain.sh @@ -0,0 +1,285 @@ +# Passwords tool - main functions +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +function getHelp() { + + echo -e "" + echo -e "NAME" + echo -e " $(basename "${0}") - Manage passwords for Wazuh indexer users." + echo -e "" + echo -e "SYNOPSIS" + echo -e " $(basename "${0}") [OPTIONS]" + echo -e "" + echo -e "DESCRIPTION" + echo -e " -a, --change-all" + echo -e " Changes all the Wazuh indexer and Wazuh API user passwords and prints them on screen." + echo -e " To change API passwords -au|--admin-user and -ap|--admin-password are required." + echo -e "" + echo -e " -A, --api" + echo -e " Change the Wazuh API password." + echo -e " Requires -u|--user, and -p|--password, -au|--admin-user and -ap|--admin-password." + echo -e "" + echo -e " -au, --admin-user " + echo -e " Admin user for Wazuh API, Required to change Wazuh API passwords." + echo -e " Requires -A|--api." + echo -e "" + echo -e " -ap, --admin-password " + echo -e " Password for Wazuh API admin user, Required to change Wazuh API passwords." + echo -e " Requires -A|--api." + echo -e "" + echo -e " -u, --user " + echo -e " Indicates the name of the user whose password will be changed." + echo -e " If no password specified it will generate a random one." + echo -e "" + echo -e " -p, --password " + echo -e " Indicates the new password, must be used with option -u." + echo -e "" + echo -e " -c, --cert " + echo -e " Indicates route to the admin certificate." + echo -e "" + echo -e " -k, --certkey " + echo -e " Indicates route to the admin certificate key." + echo -e "" + echo -e " -v, --verbose" + echo -e " Shows the complete script execution output." + echo -e "" + echo -e " -f, --file " + echo -e " Changes the passwords for the ones given in the file." + echo -e "" + echo -e " Wazuh indexer users must have this format:" + echo -e "" + echo -e " # Description" + echo -e " indexer_username: " + echo -e " indexer_password: " + echo -e "" + echo -e " Wazuh API users must have this format:" + echo -e "" + echo -e " # Description" + echo -e " api_username: " + echo -e " api_password: " + echo -e "" + echo -e " -gf, --generate-file " + echo -e " Generate password file with random passwords for standard users." + echo -e "" + echo -e " -h, --help" + echo -e " Shows help." + echo -e "" + exit 1 + +} + +function main() { + + umask 177 + + common_checkRoot + + if [ -n "${1}" ]; then + while [ -n "${1}" ] + do + case "${1}" in + "-v"|"--verbose") + verboseenabled=1 + shift 1 + ;; + "-a"|"--change-all") + changeall=1 + shift 1 + ;; + "-A"|"--api") + api=1 + shift 1 + ;; + "-au"|"--admin-user") + if [ -z "${2}" ]; then + echo "Argument au|--admin-user needs a second argument" + getHelp + exit 1 + fi + adminUser=${2} + shift 2 + ;; + "-ap"|"--admin-password") + if [ -z "${2}" ]; then + echo "Argument -ap|--admin-password needs a second argument" + getHelp + exit 1 + fi + adminPassword=${2} + shift 2 + ;; + "-u"|"--user") + if [ -z "${2}" ]; then + echo "Argument --user needs a second argument" + getHelp + exit 1 + fi + nuser=${2} + shift 2 + ;; + "-p"|"--password") + if [ -z "${2}" ]; then + echo "Argument --password needs a second argument" + getHelp + exit 1 + fi + password=${2} + shift 2 + ;; + "-c"|"--cert") + if [ -z "${2}" ]; then + echo "Argument --cert needs a second argument" + getHelp + exit 1 + fi + adminpem=${2} + shift 2 + ;; + "-k"|"--certkey") + if [ -z "${2}" ]; then + echo "Argument --certkey needs a second argument" + getHelp + exit 1 + fi + adminkey=${2} + shift 2 + ;; + "-f"|"--file") + if [ -z "${2}" ]; then + echo "Argument --file needs a second argument" + getHelp + exit 1 + fi + p_file=${2} + shift 2 + ;; + "-gf"|"--generate-file") + if [ -z "${2}" ]; then + echo "Argument --generate-file needs a second argument" + getHelp + exit 1 + fi + gen_file=${2} + shift 2 + ;; + "-h"|"--help") + getHelp + ;; + *) + getHelp + esac + done + + export JAVA_HOME=/usr/share/wazuh-indexer/jdk/ + + if [ -n "${verboseenabled}" ]; then + debug="2>&1 | tee -a ${logfile}" + fi + + if [ -n "${gen_file}" ]; then + passwords_generatePasswordFile + if [ -z "${p_file}" ] && [ -z "${nuser}" ] && [ -z "${changeall}" ]; then + exit 0 + fi + fi + + common_checkSystem + common_checkInstalled + + if [ -n "${p_file}" ] && [ ! -f "${p_file}" ]; then + getHelp + fi + + if [ -n "${nuser}" ] && [ -n "${changeall}" ]; then + getHelp + fi + + if [ -n "${password}" ] && [ -n "${changeall}" ]; then + getHelp + fi + + if [ -n "${nuser}" ] && [ -n "${p_file}" ]; then + getHelp + fi + + if [ -n "${password}" ] && [ -n "${p_file}" ]; then + getHelp + fi + + if [ -z "${nuser}" ] && [ -n "${password}" ]; then + getHelp + fi + + if [ -z "${nuser}" ] && [ -z "${password}" ] && [ -z "${changeall}" ] && [ -z "${p_file}" ]; then + getHelp + fi + + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ] && [ -z "${api}" ]; then + getHelp + fi + + if [ -n "${nuser}" ]; then + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + passwords_getApiToken + passwords_getApiUsers + passwords_getApiIds + elif [ -n "${indexer_installed}" ]; then + passwords_readUsers + fi + passwords_checkUser + fi + + if [ -n "${nuser}" ] && [ -z "${password}" ]; then + autopass=1 + passwords_generatePassword + fi + + if [ -n "${nuser}" ] && [ -n "${password}" ]; then + passwords_checkPassword "${password}" + fi + + + if [ -n "${changeall}" ] || [ -n "${p_file}" ]; then + if [ -n "${indexer_installed}" ]; then + passwords_readUsers + fi + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + passwords_getApiToken + passwords_getApiUsers + passwords_getApiIds + else + common_logger "Wazuh API admin credentials not provided, Wazuh API passwords not changed." + fi + if [ -n "${changeall}" ]; then + passwords_generatePassword + fi + fi + + + if [ -n "${p_file}" ]; then + passwords_readFileUsers + fi + + if { [ -z "${api}" ] || [ -n "${changeall}" ]; } && [ -n "${indexer_installed}" ]; then + passwords_getNetworkHost + passwords_generateHash + passwords_changePassword + passwords_runSecurityAdmin + fi + + if [ -n "${api}" ] || [ -n "${changeall}" ]; then + if [ -n "${adminUser}" ] && [ -n "${adminPassword}" ]; then + passwords_changePasswordApi + fi + fi + + else + getHelp + fi + +} \ No newline at end of file diff --git a/passwords_tool/passwordsVariables.sh b/passwords_tool/passwordsVariables.sh new file mode 100644 index 0000000..201c6f6 --- /dev/null +++ b/passwords_tool/passwordsVariables.sh @@ -0,0 +1,10 @@ +# Passwords tool - variables +# Copyright (C) 2015, Wazuh Inc. +# +# This program is a free software; you can redistribute it +# and/or modify it under the terms of the GNU General Public +# License (version 2) as published by the FSF - Free Software +# Foundation. + +readonly logfile="/var/log/wazuh-passwords-tool.log" +debug=">> ${logfile} 2>&1" diff --git a/tests/install/pytest.ini b/tests/install/pytest.ini new file mode 100644 index 0000000..3827ff1 --- /dev/null +++ b/tests/install/pytest.ini @@ -0,0 +1,10 @@ +[pytest] +filterwarnings = + ignore:Unverified HTTPS request is being made.* +markers = + wazuh: tests to be executed on Wazuh hosts (does not include wazuh-clusterd test). + wazuh_cluster: test for wazuh-clusterd it is meant to be executed on the master node if a wazuh cluster is configured. + wazuh_worker: test for wazuh cluster worker nodes. It is meant to be executed on the worker nodes. + indexer: tests to be executed on Wazuh Indexer hosts. + indexer_cluster: tests to be executed on Wazuh Indexer hosts on distributed installations. + dashboard: tests to be executed on Wazuh dashboard hosts. \ No newline at end of file diff --git a/tests/install/test_installation_assistant.py b/tests/install/test_installation_assistant.py new file mode 100644 index 0000000..0cdd7df --- /dev/null +++ b/tests/install/test_installation_assistant.py @@ -0,0 +1,280 @@ +from datetime import datetime +import pytest +import json +import sys +import tarfile +from subprocess import Popen, PIPE, check_output +import yaml +import requests +import socket +from base64 import b64encode +import warnings +import subprocess +from subprocess import check_call + +warnings.filterwarnings('ignore', message='Unverified HTTPS request') + +# ----------------------------- Aux functions ----------------------------- + +def read_services(): + services = None + p = Popen(['/var/ossec/bin/wazuh-control', 'status'], stdin=PIPE, stdout=PIPE, stderr=PIPE) + if sys.version_info[0] < 3: + services = p.stdout.read() + else: + services = p.stdout + p.kill() + +def get_password(username): + pass_dict={'username': 'tmp_user', 'password': 'tmp_pass'} + tmp_yaml="" + + with tarfile.open("../../wazuh-install-files.tar") as configurations: + configurations.extract("wazuh-install-files/wazuh-passwords.txt") + + with open("wazuh-install-files/wazuh-passwords.txt", 'r') as pass_file: + while pass_dict["username"] != username: + for i in range(4): + tmp_yaml+=pass_file.readline() + tmp_dict=yaml.safe_load(tmp_yaml) + if 'indexer_username' in tmp_dict: + pass_dict["username"]=tmp_dict["indexer_username"] + pass_dict["password"]=tmp_dict["indexer_password"] + if 'api_username' in tmp_dict: + pass_dict["username"]=tmp_dict["api_username"] + pass_dict["password"]=tmp_dict["api_password"] + return pass_dict["password"] + +def get_wazuh_version(): + wazuh_version = None + wazuh_version = subprocess.getoutput('/var/ossec/bin/wazuh-control info | grep VERSION | cut -d "=" -f2 | sed s/\\"//g') + return wazuh_version + +def get_indexer_ip(): + + with open("/etc/wazuh-indexer/opensearch.yml", 'r') as stream: + dictionary = yaml.safe_load(stream) + return (dictionary.get('network.host')) + +def get_dashboard_ip(): + + with open("/etc/wazuh-dashboard/opensearch_dashboards.yml", 'r') as stream: + dictionary = yaml.safe_load(stream) + return (dictionary.get('server.host')) + +def get_api_ip(): + + with open("/var/ossec/api/configuration/api.yaml", 'r') as stream: + dictionary = yaml.safe_load(stream) + try: + ip = dictionary.get('host') + except: + ip = '127.0.0.1' + return ip + +def api_call_indexer(host,query,address,api_protocol,api_user,api_pass,api_port): + + if (query == ""): # Calling ES API without query + if (api_user != "" and api_pass != ""): # If credentials provided + resp = requests.get(api_protocol + '://' + address + ':' + api_port, + auth=(api_user, + api_pass), + verify=False) + else: + resp = requests.get(api_protocol + '://' + address + ':' + api_port, verify=False) + + else: # Executing query search + if (api_pass != "" and api_pass != ""): + resp = requests.post(api_protocol + '://' + address + ':' + api_port + "/wazuh-alerts-4.x-*/_search", + json=query, + auth=(api_user, + api_pass), + verify=False) + else: + resp = requests.get(api_protocol + "://" + address + ":" + api_port) + response = resp.json() + return response + +def get_indexer_cluster_status(): + ip = get_indexer_ip() + resp = requests.get('https://'+ip+':9200/_cluster/health', + auth=("admin", + get_password("admin")), + verify=False) + return (resp.json()['status']) + +def get_dashboard_status(): + ip = get_dashboard_ip() + resp = requests.get('https://'+ip, + auth=("kibanaserver", + get_password("kibanaserver")), + verify=False) + return (resp.status_code) + +def get_wazuh_api_status(): + + protocol = 'https' + host = get_api_ip() + port = 55000 + user = 'wazuh' + password = get_password('wazuh') + login_endpoint = 'security/user/authenticate' + + login_url = f"{protocol}://{host}:{port}/{login_endpoint}" + basic_auth = f"{user}:{password}".encode() + login_headers = {'Content-Type': 'application/json', + 'Authorization': f'Basic {b64encode(basic_auth).decode()}'} + response = requests.post(login_url, headers=login_headers, verify=False) + token = json.loads(response.content.decode())['data']['token'] + requests_headers = {'Content-Type': 'application/json', + 'Authorization': f'Bearer {token}'} + response = requests.get(f"{protocol}://{host}:{port}/?pretty=true", headers=requests_headers, verify=False) + return response.json()['data']['title'] + +# ----------------------------- Tests ----------------------------- + +@pytest.mark.wazuh +def test_check_wazuh_manager_authd(): + assert check_call("ps -xa | grep wazuh-authd | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_db(): + assert check_call("ps -xa | grep wazuh-db | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_execd(): + assert check_call("ps -xa | grep wazuh-execd | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_analysisd(): + assert check_call("ps -xa | grep wazuh-analysisd | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_syscheckd(): + assert check_call("ps -xa | grep wazuh-syscheckd | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_remoted(): + assert check_call("ps -xa | grep wazuh-remoted | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_logcollec(): + assert check_call("ps -xa | grep wazuh-logcollec | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_monitord(): + assert check_call("ps -xa | grep wazuh-monitord | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_modulesd(): + assert check_call("ps -xa | grep wazuh-modulesd | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_wazuh_manager_apid(): + assert check_call("ps -xa | grep wazuh_apid | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh_cluster +def test_check_wazuh_manager_clusterd(): + assert check_call("ps -xa | grep clusterd.py | grep -v grep", shell=True) != "" + +@pytest.mark.wazuh +def test_check_filebeat_process(): + assert check_call("ps -xa | grep \"/usr/share/filebeat/bin/filebeat\" | grep -v grep", shell=True) != "" + +@pytest.mark.indexer +def test_check_indexer_process(): + assert check_call("ps -xa | grep wazuh-indexer | grep -v grep | cut -d \" \" -f15", shell=True) != "" + +@pytest.mark.dashboard +def test_check_dashboard_process(): + assert check_call("ps -xa | grep wazuh-dashboard | grep -v grep", shell=True) != "" + +@pytest.mark.indexer +def test_check_indexer_cluster_status_not_red(): + assert get_indexer_cluster_status() != "red" + +@pytest.mark.indexer_cluster +def test_check_indexer_cluster_status_not_yellow(): + assert get_indexer_cluster_status() != "yellow" + +@pytest.mark.dashboard +def test_check_dashboard_status(): + assert get_dashboard_status() == 200 + +@pytest.mark.wazuh +def test_check_wazuh_api_status(): + assert get_wazuh_api_status() == "Wazuh API REST" + +@pytest.mark.wazuh +def test_check_log_errors(): + found_error = False + exceptions = [ + 'WARNING: Cluster error detected', + 'agent-upgrade: ERROR: (8123): There has been an error executing the request in the tasks manager.', + "ERROR: Could not send message through the cluster after '10' attempts" + + ] + + with open('/var/ossec/logs/ossec.log', 'r') as f: + for line in f.readlines(): + if 'ERROR' in line: + if not any(exception in line for exception in exceptions): + found_error = True + break + assert found_error == False, line + +@pytest.mark.wazuh_cluster +def test_check_cluster_log_errors(): + found_error = False + with open('/var/ossec/logs/cluster.log', 'r') as f: + for line in f.readlines(): + if 'ERROR' in line: + found_error = True + break + assert found_error == False, line + +@pytest.mark.wazuh_worker +def test_check_cluster_log_errors(): + found_error = False + with open('/var/ossec/logs/cluster.log', 'r') as f: + for line in f.readlines(): + if 'ERROR' in line: + if 'Could not connect to master' not in line and 'Worker node is not connected to master' not in line and 'Connection reset by peer' not in line and "Error sending sendsync response to local client: Error 3020 - Timeout sending" not in line: + found_error = True + break + assert found_error == False, line + +@pytest.mark.wazuh_cluster +def test_check_api_log_errors(): + found_error = False + with open('/var/ossec/logs/api.log', 'r') as f: + for line in f.readlines(): + if 'ERROR' in line: + found_error = True + break + assert found_error == False, line + +@pytest.mark.indexer +def test_check_alerts(): + node_name = socket.gethostname() + query = { + "query": { + "bool": { + "must": [ + { + "wildcard": { + "agent.name": { + "value": '*' + } + } + } + ] + } + } + } + + response = api_call_indexer(get_indexer_ip(),query,get_indexer_ip(),'https',"admin",get_password("admin"),'9200') + + print(response) + + assert (response["hits"]["total"]["value"] > 0) diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 0000000..a41be7b --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,42 @@ +# Unit Test Instructions for Wazuh installation assistant + +This document provides instructions on how to run unit tests for the Wazuh installation assistant using Docker. + +## Overview + +- **Test Naming Convention**: All test files follow the naming pattern `tests-{script_name}.sh`, where `{script_name}.sh` corresponds to the script being tested. +- **Testing Environment**: The `unit-tests.sh` script is used to run these tests. It creates a clean Docker environment for each test run to ensure consistency. +- **Docker Requirement**: Docker must be installed, running, and accessible by the user. The Docker image used for testing is retained after the script execution to save time on subsequent runs. If the Dockerfile is modified, use the `-r` option to rebuild the image. + +## Usage + +``` +unit-tests.sh - Unit test for the Wazuh installation assistant. +``` + +### Synopsis + +``` +bash unit-tests.sh [OPTIONS] -a | -d | -f +``` + +### Options + +| Option | Description | +|-------------------------------------|-------------------------------------------------------------------| +| `-a`, `--test-all` | Runs tests on all available scripts. | +| `-d`, `--debug` | Displays the complete installation output for debugging purposes. | +| `-f`, `--files ` | Specifies a list of files to test. Example: `-f common checks`. | +| `-h`, `--help` | Displays the help message with usage details. | +| `-r`, `--rebuild-image` | Forces the Docker image to be rebuilt before running tests. | + +## Tips for Debugging + +When multiple tests fail after a merge, it can be challenging to isolate and fix them. Here's a method to streamline this process: + +> [!TIP] +> **1. Sequential Testing**: Since a bash script exits on an unknown character, you can insert a `Ç` character after the first test you want to run. Only the tests before the `Ç` character will be executed. +> +> **2. Incremental Fixing**: As you fix each test, move the `Ç` character down to include the next test or group of tests. This approach prevents you from having to scroll through all tests to identify which ones are failing. + +This technique allows for a more manageable and systematic approach to resolving issues, especially when dealing with a large number of tests. \ No newline at end of file diff --git a/tests/unit/docker-unit-testing-tool/Dockerfile b/tests/unit/docker-unit-testing-tool/Dockerfile new file mode 100644 index 0000000..03eeabd --- /dev/null +++ b/tests/unit/docker-unit-testing-tool/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:latest +RUN apk add --no-cache bash coreutils diffutils +RUN mkdir -p /tests/ + +COPY entrypoint.sh /usr/local/bin/test_file +RUN chmod +x /usr/local/bin/test_file + +# Set the entrypoint +ENTRYPOINT ["/usr/local/bin/test_file"] \ No newline at end of file diff --git a/tests/unit/docker-unit-testing-tool/entrypoint.sh b/tests/unit/docker-unit-testing-tool/entrypoint.sh new file mode 100644 index 0000000..9659b58 --- /dev/null +++ b/tests/unit/docker-unit-testing-tool/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +FILE_NAME=${1} +cd /tests/ +if [ -f test-${FILE_NAME}.sh ]; then + bash test-${FILE_NAME}.sh +else + echo "Couldn't find test-${FILE_NAME}.sh" +fi \ No newline at end of file diff --git a/tests/unit/framework/bach.sh b/tests/unit/framework/bach.sh new file mode 100644 index 0000000..6462d1e --- /dev/null +++ b/tests/unit/framework/bach.sh @@ -0,0 +1,642 @@ +# -*- mode: sh -*- +# Bach Testing Framework, https://bach.sh +# Copyright (C) 2019 Chai Feng +# +# Bach Testing Framework is dual licensed under: +# - GNU General Public License v3.0 +# - Mozilla Public License 2.0 +set -euo pipefail +shopt -s expand_aliases + +export BACH_COLOR="${BACH_COLOR:-auto}" +export PS4='+(${BASH_SOURCE##*/}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + +BACH_OS_NAME="$(uname)" +declare -gxr BACH_OS_NAME + +declare -gxa bach_origin_paths=() +while builtin read -r -d: folder; do + bach_origin_paths+=("$folder") +done <<< "${PATH}:" + +function @out() { + if [[ "${1:-}" == "-" || ! -t 0 ]]; then + [[ "${1:-}" == "-" ]] && shift + while IFS=$'\n' read -r line; do + builtin printf "%s\n" "${*}$line" + done + elif [[ "$#" -gt 0 ]]; then + builtin printf "%s\n" "$*" + else + builtin printf "\n" + fi +} 8>/dev/null +export -f @out + +function @err() { + @out "$@" +} >&2 +export -f @err + +function @die() { + @out "$@" + exit 1 +} >&2 +export -f @die + +if [[ -z "${BASH_VERSION:-}" ]] || [[ "${BASH_VERSINFO[0]}" -lt 4 ]]; then + @die "Bach Testing Framework only support Bash v4+!" +fi + +if [[ "${BACH_DEBUG:-}" != true ]]; then + function @debug() { + : + } +else + exec 8>&2 + function @debug() { + builtin printf '[DEBUG] %s\n' "$*" + } >&8 +fi +export -f @debug + +function bach-real-path() { + declare folder name="$1" + declare altname="${name#*|}" + name="${name%|*}" + for folder in "${bach_origin_paths[@]}"; do + if [[ -x "$folder/$name" ]]; then + builtin echo "$folder/$name" + return 0 + elif [[ "$name" != "$altname" && -x "$folder/$altname" ]]; then + builtin echo "$folder/$altname" + return 0 + fi + done + return 1 +} +export -f bach-real-path + +export BACH_DEV_STDIN="" + +function bach_restore_stdin() { + if [[ ! -t 0 ]]; then + declare name + [[ -n "$BACH_DEV_STDIN" ]] || for name in /dev/ptmx /dev/pts/ptmx /dev/ttyv[0-9a-f]; do + if [[ -r "$name" && -c "$name" ]]; then + ls -l "$name" >&2 + BACH_DEV_STDIN="$name" + break + fi + done + exec 0<&- + exec 0<"$BACH_DEV_STDIN" + fi +} + +function bach_initialize(){ + enable -n alias bg bind dirs disown fc fg hash help history jobs kill suspend times ulimit umask unalias wait + + declare util name util_path + + declare -a bash_builtin_cmds=(cd echo enable popd pushd pwd shopt test trap type) + + for name in . command exec false set true unset "${bash_builtin_cmds[@]}"; do + eval "function @${name}() { builtin $name \"\$@\"; } 8>/dev/null; export -f @${name}" + done + + for name in eval; do + eval "function @${name}() { builtin $name \"\$@\"; }; export -f @${name}" + done + + function @source() { + declare script="$1" + shift + builtin source "$script" "$@" + } + + declare -a bach_core_utils=(cat chmod cut diff find env grep ls "shasum|sha1sum" mkdir mktemp rm rmdir sed sort tee touch which xargs) + + for util in "${bach_core_utils[@]}"; do + if [[ "$util" == "shasum|"* && "$BACH_OS_NAME" == FreeBSD ]]; then + util="shasum|sha1" + fi + name="${util%|*}" + util_path="$(bach-real-path "$util")" + eval "[[ -n \"${util_path}\" ]] || @die \"Fatal, CAN NOT find '$name' in \\\$PATH\"; function @${name}() { \"${util_path}\" \"\$@\"; } 8>/dev/null; export -f @${name}" + done + + bach_restore_stdin + @mockall "${bash_builtin_cmds[@]}" source . + + eval "$(builtin export | while read -rs name; do + name="${name%%=*}" + name="${name##* }" + [[ "${name^^}" != BACH_* ]] || continue + builtin echo "unset '$name' || builtin true" + done)" + builtin export LANG=C TERM=vt100 + unset name util_path +} + +function @real() { + declare name="$1" real_cmd + if [[ "$name" == */* ]]; then + @echo "$@" + return + fi + real_cmd="$(bach-real-path "$1" 7>&1)" + if [[ -z "${real_cmd}" ]]; then + real_cmd="${name}_not_found" + fi + declare -a cmd=("${real_cmd}" "${@:2}") + @debug "[REAL-CMD]" "${cmd[@]}" + "${cmd[@]}" +} +export -f @real + +function bach-get-all-functions() { + declare -F +} +export -f bach-get-all-functions + +function bach--skip-the-test() { + declare test="$1" test_filter + while read -d, test_filter; do + [[ -n "$test_filter" ]] || continue + [[ "$test" == $test_filter ]] && return 0 + [[ "$test" == test-$test_filter ]] && return 0 + done <<< "${BACH_TESTS:-}," +} +export -f bach--skip-the-test + +function bach-run-tests--get-all-tests() { + bach-get-all-functions | @sort -R | while read -r _ _ name; do + [[ "$name" == test?* ]] || continue + [[ "$name" == *-assert ]] && continue + bach--skip-the-test "$name" || continue + builtin printf "%s\n" "$name" + done +} + +for donotpanic in donotpanic dontpanic do-not-panic dont-panic do_not_panic dont_panic; do + eval "function @${donotpanic}() { builtin printf '\n%s\n line number: %s\n script stack: %s\n\n' 'DO NOT PANIC!' \"\${BASH_LINENO}\" \"\${BASH_SOURCE[*]}\"; builtin exit 1; } >&2; export -f @${donotpanic};" +done + +function bach--is-function() { + [[ "$(@type -t "$1")" == function ]] +} +export -f bach--is-function + +declare -gr __bach_run_test__ignore_prefix="## BACH:" +function @comment() { + @out "${__bach_run_test__ignore_prefix}" "$@" +} +export -f @comment + +function bach-run-tests() { + set -euo pipefail + + bach_initialize + + for donotpanic in donotpanic dontpanic do-not-panic dont-panic do_not_panic dont_panic; do + eval "function @${donotpanic}() { builtin true; }; export -f @${donotpanic}" + done + + function command() { + if [[ "$1" != -* ]] && bach--is-function "$1"; then + "$@" + else + mockfunc="$(@generate_mock_function_name command "$@")" + if bach--is-function "${mockfunc}"; then + @debug "[BC-func]" "${mockfunc}" "$@" + "${mockfunc}" "$@" + else + command_not_found_handle command "$@" + fi + fi + } + export -f command + + function xargs() { + declare param + declare -a xargs_opts + while param="${1:-}"; [[ -n "${param:-}" ]]; do + shift || true + if [[ "$param" == "--" ]]; then + xargs_opts+=("${BASH:-bash}" "-c" "$(builtin printf "'%s' " "$@") \$@" "-s") + break + else + xargs_opts+=("$param") + fi + done + @debug "@mock-xargs" "${xargs_opts[@]}" + if [[ "$#" -gt 0 ]]; then + @xargs "${xargs_opts[@]}" + else + [[ -t 0 ]] || @cat &>/dev/null + @dryrun xargs "${xargs_opts[@]}" + fi + } + export -f xargs + + if [[ "${BACH_ASSERT_IGNORE_COMMENT}" == true ]]; then + BACH_ASSERT_DIFF_OPTS+=(-I "^${__bach_run_test__ignore_prefix}") + fi + + declare color_ok color_err color_end + if [[ "$BACH_COLOR" == "always" ]] || [[ "$BACH_COLOR" != "no" && -t 1 && -t 2 ]]; then + color_ok="\e[1;32m" + color_err="\e[1;31m" + color_end="\e[0;m" + else + color_ok="" + color_err="" + color_end="" + fi + declare name friendly_name testresult test_name_assert_fail + declare -i total=0 error=0 + declare -a all_tests + mapfile -t all_tests < <(bach-run-tests--get-all-tests) + @echo "1..${#all_tests[@]}" + for name in "${all_tests[@]}"; do + # @debug "Running test: $name" + friendly_name="${name/#test-/}" + friendly_name="${friendly_name//-/ }" + friendly_name="${friendly_name// / -}" + : $(( ++total )) + testresult="$(@mktemp)" + @set +e + assert-execution "$name" &>"$testresult"; test_retval="$?" + @set -e + if [[ "$name" == test-ASSERT-FAIL-* ]]; then + test_retval="$(( test_retval == 0?1:0 ))" + test_name_assert_fail="${color_err}ASSERT FAIL${color_end}" + friendly_name="${friendly_name/#ASSERT FAIL/}" + else + test_name_assert_fail="" + fi + if [[ "$test_retval" -eq 0 ]]; then + builtin printf "${color_ok}ok %d - ${test_name_assert_fail}${color_ok}%s${color_end}\n" "$total" "$friendly_name" + else + : $(( ++error )) + builtin printf "${color_err}not ok %d - ${test_name_assert_fail}${color_err}%s${color_end}\n" "$total" "$friendly_name" + { + builtin printf "\n" + @cat "$testresult" >&2 + builtin printf "\n" + } >&2 + fi + @rm "$testresult" &>/dev/null + done + + declare color_result="$color_ok" + if (( error > 0 )); then + color_result="$color_err" + fi + builtin printf -- "# -----\n#${color_result} All tests: %s, failed: %d, skipped: %d${color_end}\n" \ + "${#all_tests[@]}" "$error" "$(( ${#all_tests[@]} - total ))">&2 + [[ "$error" == 0 ]] && [[ "${#all_tests[@]}" -eq "$total" ]] +} + +function bach-on-exit() { + if [[ -o xtrace ]]; then + exec 8>&2 + BASH_XTRACEFD=8 + fi + if [[ "$?" -eq 0 ]]; then + [[ "${BACH_DISABLED:-false}" == true ]] || bach-run-tests + else + builtin printf "Bail out! %s\n" "Couldn't initlize tests." + fi +} + +trap bach-on-exit EXIT + +function @generate_mock_function_name() { + declare name="$1" + @echo "mock_exec_${name}_$(@dryrun "${@}" | @shasum | @cut -b1-7)" +} +export -f @generate_mock_function_name + +function @mock() { + declare -a param name cmd func body desttype + name="$1" + if [[ "$name" == @(builtin|declare|eval|set|unset|true|false|read) ]]; then + @die "Cannot mock the builtin command: $name" + fi + if [[ command == "$name" && "$2" != -* ]]; then + shift + name="$1" + fi + desttype="$(@type -t "$name" || true)" + for param; do + if [[ "$param" == '===' ]]; then + shift + break + fi + cmd+=("$param") + done + shift "${#cmd[@]}" + if [[ "$name" == /* ]]; then + @die "Cannot mock an absolute path: $name" + elif [[ "$name" == */* ]] && [[ -e "$name" ]]; then + @die "Cannot mock an existed path: $name" + fi + @debug "@mock $name" + if [[ "$#" -gt 0 ]]; then + @debug "@mock $name $*" + declare -a params=("$@") + func="$(declare -p params); \"\${params[@]}\"" + #func="$*" + elif [[ ! -t 0 ]]; then + @debug "@mock $name @cat" + func="$(@cat)" + fi + if [[ -z "${func:-}" ]]; then + @debug "@mock default $name" + func="if [[ -t 0 ]]; then @dryrun \"${name}\" \"\$@\" >&7; else @cat; fi" + fi + if [[ "$name" == */* ]]; then + [[ -d "${name%/*}" ]] || @mkdir -p "${name%/*}" + @cat > "$name" <