From 9452c70712abb8a94c1461605cf62788f4d1c5ea Mon Sep 17 00:00:00 2001 From: Christopher Berger Date: Fri, 29 May 2026 19:16:30 +0000 Subject: [PATCH] Add configs/wazuh-passwords-tool.sh --- configs/wazuh-passwords-tool.sh | 1120 +++++++++++++++++++++++++++++++ 1 file changed, 1120 insertions(+) create mode 100644 configs/wazuh-passwords-tool.sh diff --git a/configs/wazuh-passwords-tool.sh b/configs/wazuh-passwords-tool.sh new file mode 100644 index 0000000..1921ef6 --- /dev/null +++ b/configs/wazuh-passwords-tool.sh @@ -0,0 +1,1120 @@ +#!/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. +adminpem="/etc/wazuh-indexer/certs/admin.pem" +adminkey="/etc/wazuh-indexer/certs/admin-key.pem" +readonly logfile="/var/log/wazuh-passwords-tool.log" +debug=">> ${logfile} 2>&1" + +# ------------ passwordsVariables.sh ------------ + +# ------------ passwordsMain.sh ------------ +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 + +} +# ------------ passwordsFunctions.sh ------------ +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}" ] && [ -z "${dashboard}" ]; 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) + 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) + 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." + +} + +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 + +} +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 + +} + +main "$@" \ No newline at end of file