Files
linux-scripts/wazuh-api-backup.sh
T
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

345 lines
9.5 KiB
Bash

#!/usr/bin/env bash
#
# Wazuh API Backup
#
# Backs up Wazuh configuration objects via the REST API.
# Exports agents, groups, rules, decoders, CDB lists, manager
# configuration, API users, roles, policies, and cluster status
# to individual JSON files in a dated directory.
#
# Usage:
# WAZUH_USER="wazuh-wui" WAZUH_PASS="wazuh" ./wazuh-api-backup.sh
# WAZUH_USER="wazuh-wui" WAZUH_PASS="wazuh" ./wazuh-api-backup.sh --dry-run
# WAZUH_USER="wazuh-wui" WAZUH_PASS="wazuh" ./wazuh-api-backup.sh --install
#
# Parameters:
# --dry-run Show what would be backed up without writing files
# --install Create cron job for daily backup at 3am
# --help Show usage
#
# Environment:
# WAZUH_API Wazuh API base URL (default: https://localhost:55000)
# WAZUH_USER API username (default: wazuh-wui)
# WAZUH_PASS API password (default: wazuh)
# BACKUP_DIR Base backup directory (default: /backup/wazuh-api)
# RETENTION_DAYS Delete backups older than this many days (default: 30)
# CURL_TIMEOUT API request timeout in seconds (default: 15)
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
# Version: 1.01
set -euo pipefail
# --- Configuration ---
readonly VERSION="1.0"
readonly SCRIPT_NAME="$(basename "$0")"
WAZUH_API="${WAZUH_API:-https://localhost:55000}"
WAZUH_USER="${WAZUH_USER:-wazuh-wui}"
WAZUH_PASS="${WAZUH_PASS:-wazuh}"
BACKUP_DIR="${BACKUP_DIR:-/backup/wazuh-api}"
RETENTION_DAYS="${RETENTION_DAYS:-30}"
CURL_TIMEOUT="${CURL_TIMEOUT:-15}"
DRY_RUN=false
TOKEN=""
# Backup endpoints: "api_path output_filename"
readonly ENDPOINTS=(
"agents?limit=500 agents.json"
"groups groups.json"
"rules?limit=500 rules.json"
"decoders?limit=500 decoders.json"
"lists cdb-lists.json"
"manager/configuration manager-config.json"
"manager/status manager-status.json"
"security/users users.json"
"security/roles roles.json"
"security/policies policies.json"
"security/rules security-rules.json"
"cluster/status cluster-status.json"
"syscheck syscheck-config.json"
"active-response active-response.json"
)
# Custom rule/decoder files to export (raw XML)
readonly CUSTOM_FILES=(
"rules/files/local_rules.xml local_rules.xml"
"decoders/files/local_decoder.xml local_decoder.xml"
)
# --- Functions ---
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]
Wazuh API Backup
Options:
--dry-run Show what would be backed up without writing files
--install Create cron job for daily backup at 3am
--help Show this help message
Environment Variables:
WAZUH_API Wazuh API base URL (default: https://localhost:55000)
WAZUH_USER API username (default: wazuh-wui)
WAZUH_PASS API password (default: wazuh)
BACKUP_DIR Base backup directory (default: /backup/wazuh-api)
RETENTION_DAYS Delete backups older than N days (default: 30)
CURL_TIMEOUT Request timeout in seconds (default: 15)
Examples:
WAZUH_USER="wazuh-wui" WAZUH_PASS="secret" $SCRIPT_NAME
WAZUH_API="https://wazuh.example.com:55000" WAZUH_USER="automation" WAZUH_PASS="secret" $SCRIPT_NAME
WAZUH_USER="wazuh-wui" WAZUH_PASS="secret" $SCRIPT_NAME --dry-run
WAZUH_USER="wazuh-wui" WAZUH_PASS="secret" $SCRIPT_NAME --install
EOF
exit 0
}
check_dependencies() {
local missing=()
for cmd in curl jq; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
echo "ERROR: Missing required commands: ${missing[*]}" >&2
echo "Install with: apt install ${missing[*]} OR dnf install ${missing[*]}" >&2
exit 1
fi
}
validate_config() {
# Strip trailing slash
WAZUH_API="${WAZUH_API%/}"
}
get_token() {
local response
response=$(curl -sf -k --max-time "$CURL_TIMEOUT" \
-u "${WAZUH_USER}:${WAZUH_PASS}" \
-X POST "${WAZUH_API}/security/user/authenticate" 2>/dev/null) || {
echo "ERROR: Failed to authenticate to Wazuh API at ${WAZUH_API}" >&2
echo "Check WAZUH_USER, WAZUH_PASS, and API connectivity" >&2
exit 1
}
TOKEN=$(echo "$response" | jq -r '.data.token // empty')
if [[ -z "$TOKEN" ]]; then
echo "ERROR: Authentication succeeded but no token returned" >&2
exit 1
fi
}
api_get() {
local endpoint="$1"
curl -sf -k --max-time "$CURL_TIMEOUT" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"${WAZUH_API}/${endpoint}" 2>/dev/null || echo ""
}
backup_endpoint() {
local endpoint="$1"
local filename="$2"
local output_dir="$3"
local response
if [[ "$DRY_RUN" == true ]]; then
printf " %-40s → %s (dry-run)\n" "$endpoint" "$filename"
return 0
fi
response=$(api_get "$endpoint")
if [[ -z "$response" ]]; then
printf " %-40s → %-30s FAIL\n" "$endpoint" "$filename"
return 1
fi
echo "$response" | jq '.' > "${output_dir}/${filename}" 2>/dev/null || {
# Not JSON (raw XML file) — write as-is
echo "$response" > "${output_dir}/${filename}"
}
local size
size=$(du -h "${output_dir}/${filename}" | cut -f1)
printf " %-40s → %-30s OK %s\n" "$endpoint" "$filename" "$size"
return 0
}
backup_group_configs() {
local output_dir="$1"
local groups_response
if [[ "$DRY_RUN" == true ]]; then
printf " %-40s → %s (dry-run)\n" "groups/*/configuration" "group-*-config.json"
return 0
fi
groups_response=$(api_get "groups")
if [[ -z "$groups_response" ]]; then
printf " %-40s → %-30s FAIL\n" "groups/*/configuration" "group configs"
return 1
fi
local group_names
group_names=$(echo "$groups_response" | jq -r '.data.affected_items[].name' 2>/dev/null)
if [[ -z "$group_names" ]]; then
printf " %-40s → %-30s SKIP (no groups)\n" "groups/*/configuration" "group configs"
return 0
fi
local count=0
while IFS= read -r group; do
local config
config=$(api_get "groups/${group}/configuration")
if [[ -n "$config" ]]; then
echo "$config" | jq '.' > "${output_dir}/group-${group}-config.json" 2>/dev/null
((count++)) || true
fi
done <<< "$group_names"
printf " %-40s → %-30s OK %d groups\n" "groups/*/configuration" "group-*-config.json" "$count"
return 0
}
cleanup_old_backups() {
if [[ ! -d "$BACKUP_DIR" ]]; then
return
fi
local removed=0
while IFS= read -r dir; do
rm -rf "$dir"
((removed++)) || true
done < <(find "$BACKUP_DIR" -maxdepth 1 -mindepth 1 -type d -mtime +"$RETENTION_DAYS" 2>/dev/null)
if [[ $removed -gt 0 ]]; then
echo "Retention: removed $removed backup(s) older than ${RETENTION_DAYS} days"
fi
}
install_cron() {
if [[ $EUID -ne 0 ]]; then
echo "ERROR: --install requires root" >&2
exit 1
fi
local script_path
script_path=$(readlink -f "$0")
cat > /etc/cron.d/wazuh-api-backup <<EOF
# Wazuh API Backup — runs daily at 3am
WAZUH_API=${WAZUH_API}
WAZUH_USER=${WAZUH_USER}
WAZUH_PASS=${WAZUH_PASS}
BACKUP_DIR=${BACKUP_DIR}
RETENTION_DAYS=${RETENTION_DAYS}
0 3 * * * root ${script_path} 2>/dev/null
EOF
chmod 644 /etc/cron.d/wazuh-api-backup
echo "Installed cron job: /etc/cron.d/wazuh-api-backup"
echo "Backups will be written to: ${BACKUP_DIR}/<date>"
}
# --- Main ---
main() {
# Parse arguments
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=true ;;
--install)
check_dependencies
validate_config
install_cron
exit 0
;;
--help|-h) usage ;;
*) echo "Unknown option: $arg" >&2; usage ;;
esac
done
check_dependencies
validate_config
local today
today=$(date +%Y%m%d)
local output_dir="${BACKUP_DIR}/${today}"
if [[ "$DRY_RUN" == true ]]; then
echo "Wazuh API Backup v${VERSION} (dry-run)"
echo "Target: ${output_dir}"
echo ""
else
mkdir -p "$output_dir"
echo "Wazuh API Backup v${VERSION}"
echo "Target: ${output_dir}"
echo ""
fi
# Authenticate
if [[ "$DRY_RUN" != true ]]; then
get_token
fi
local ok=0
local fail=0
# Back up standard endpoints
for entry in "${ENDPOINTS[@]}"; do
local endpoint filename
endpoint="${entry%% *}"
filename="${entry##* }"
if backup_endpoint "$endpoint" "$filename" "$output_dir"; then
((ok++)) || true
else
((fail++)) || true
fi
done
# Back up custom rule/decoder files (raw XML)
for entry in "${CUSTOM_FILES[@]}"; do
local endpoint filename
endpoint="${entry%% *}"
filename="${entry##* }"
if backup_endpoint "$endpoint" "$filename" "$output_dir"; then
((ok++)) || true
else
((fail++)) || true
fi
done
# Back up per-group configurations
if backup_group_configs "$output_dir"; then
((ok++)) || true
else
((fail++)) || true
fi
echo ""
if [[ "$DRY_RUN" == true ]]; then
local total=$((${#ENDPOINTS[@]} + ${#CUSTOM_FILES[@]} + 1))
echo "Dry-run complete: ${total} backup targets"
else
local total_size
total_size=$(du -sh "$output_dir" | cut -f1)
echo "Complete: ${ok} OK, ${fail} failed, ${total_size} total"
cleanup_old_backups
fi
}
main "$@"