#!/bin/bash
################################################################################
# Script Name: install-ossec.sh
# Version: 1.0
# Description: Automated OSSEC HIDS installation — server or agent mode,
#              file integrity monitoring, rootkit detection, log analysis,
#              active response, email alerts, and syscheck configuration
#              on Debian/Ubuntu and RHEL/Rocky/AlmaLinux
#
# Author:  Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Usage:
#   sudo ./install-ossec.sh --mode server
#   sudo ./install-ossec.sh --mode agent --server-ip 10.0.1.10
#   sudo ./install-ossec.sh --mode local
#   sudo ./install-ossec.sh --mode server --email admin@example.com
#   sudo ./install-ossec.sh --dry-run --mode server
#   sudo ./install-ossec.sh --uninstall
#
################################################################################

set -euo pipefail

# ============================================================================
# DEFAULTS
# ============================================================================

readonly VERSION="1.0"
readonly SCRIPT_NAME="${0##*/}"
readonly LOG_FILE="/var/log/ossec-install.log"

INSTALL_MODE=""
SERVER_IP=""
EMAIL_ADDR=""
EMAIL_SMTP="localhost"
ACTIVE_RESPONSE=true
SYSCHECK_FREQUENCY="21600"
SYSCHECK_DIRS="/etc,/usr/bin,/usr/sbin,/bin,/sbin"
ROOTCHECK=true
LOG_ANALYSIS=true
DRY_RUN=false
UNINSTALL=false
OSSEC_VERSION="3.7.0"
OSSEC_DIR="/var/ossec"

# OS detection
OS_ID=""
OS_VERSION=""
PKG_MGR=""

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

log_info()  { echo -e "${GREEN}[INFO]${NC} $*"; echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC} $*"; echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true; }
log_step()  { echo -e "${CYAN}[STEP]${NC} $*"; echo "[STEP] $(date '+%Y-%m-%d %H:%M:%S') $*" >> "$LOG_FILE" 2>/dev/null || true; }

show_usage() {
    cat <<EOF
Usage: sudo $SCRIPT_NAME [OPTIONS]

Installs and configures OSSEC HIDS (Host-based Intrusion Detection System).

REQUIRED:
    --mode TYPE         Installation type: server, agent, or local

OPTIONS:
    --server-ip IP      Manager IP (required for agent mode)
    --email ADDRESS     Email address for alerts
    --smtp SERVER       SMTP server for email alerts (default: localhost)
    --no-active-response  Disable active response (firewall blocking)
    --syscheck-freq SECS  Syscheck frequency in seconds (default: 21600 = 6h)
    --syscheck-dirs DIRS  Comma-separated directories to monitor
    --no-rootcheck      Disable rootkit detection
    --no-log-analysis   Disable log analysis
    --version VER       OSSEC version to install (default: $OSSEC_VERSION)
    --dry-run           Show what would be done
    --uninstall         Remove OSSEC
    -h, --help          Show this help message

MODES:
    server    OSSEC manager — receives alerts from agents, runs analysis
    agent     OSSEC agent — connects to a server, sends events
    local     Standalone — all features, no agent/server communication

EXAMPLES:
    # Install OSSEC server with email alerts
    sudo $SCRIPT_NAME --mode server --email admin@example.com

    # Install OSSEC agent pointing to server
    sudo $SCRIPT_NAME --mode agent --server-ip 10.0.1.10

    # Standalone installation
    sudo $SCRIPT_NAME --mode local

EOF
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --mode)               INSTALL_MODE="$2"; shift 2 ;;
            --server-ip)          SERVER_IP="$2"; shift 2 ;;
            --email)              EMAIL_ADDR="$2"; shift 2 ;;
            --smtp)               EMAIL_SMTP="$2"; shift 2 ;;
            --no-active-response) ACTIVE_RESPONSE=false; shift ;;
            --syscheck-freq)      SYSCHECK_FREQUENCY="$2"; shift 2 ;;
            --syscheck-dirs)      SYSCHECK_DIRS="$2"; shift 2 ;;
            --no-rootcheck)       ROOTCHECK=false; shift ;;
            --no-log-analysis)    LOG_ANALYSIS=false; shift ;;
            --version)            OSSEC_VERSION="$2"; shift 2 ;;
            --dry-run)            DRY_RUN=true; shift ;;
            --uninstall)          UNINSTALL=true; shift ;;
            -h|--help)            show_usage ;;
            *)                    log_error "Unknown option: $1"; show_usage ;;
        esac
    done

    if ! $UNINSTALL && [ -z "$INSTALL_MODE" ]; then
        log_error "--mode is required (server, agent, or local)"
        show_usage
    fi

    if [ "$INSTALL_MODE" = "agent" ] && [ -z "$SERVER_IP" ]; then
        log_error "Agent mode requires --server-ip"
        exit 1
    fi
}

check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "This script must be run as root (sudo)"
        exit 1
    fi
}

# ============================================================================
# OS DETECTION
# ============================================================================

detect_os() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS_ID="${ID}"
        OS_VERSION="${VERSION_ID}"
    else
        log_error "Cannot detect OS"
        exit 1
    fi

    case "$OS_ID" in
        ubuntu|debian)       PKG_MGR="apt" ;;
        rhel|rocky|almalinux|centos|fedora) PKG_MGR="dnf"; command -v dnf &>/dev/null || PKG_MGR="yum" ;;
        *)                   log_error "Unsupported OS: $OS_ID"; exit 1 ;;
    esac

    log_info "Detected OS: $OS_ID $OS_VERSION (package manager: $PKG_MGR)"
}

# ============================================================================
# DEPENDENCIES
# ============================================================================

install_dependencies() {
    log_step "Installing build dependencies..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would install build dependencies"
        return
    fi

    case "$PKG_MGR" in
        apt)
            apt-get update -qq
            apt-get install -y -qq build-essential make gcc libevent-dev libpcre2-dev \
                libssl-dev libsystemd-dev zlib1g-dev wget tar
            ;;
        dnf|yum)
            $PKG_MGR groupinstall -y "Development Tools" 2>/dev/null || true
            $PKG_MGR install -y -q gcc make libevent-devel pcre2-devel openssl-devel \
                systemd-devel zlib-devel wget tar
            ;;
    esac

    log_info "Dependencies installed"
}

# ============================================================================
# DOWNLOAD AND COMPILE
# ============================================================================

download_ossec() {
    log_step "Downloading OSSEC ${OSSEC_VERSION}..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would download OSSEC ${OSSEC_VERSION}"
        return
    fi

    local url="https://github.com/ossec/ossec-hids/archive/refs/tags/${OSSEC_VERSION}.tar.gz"
    local tmpdir="/tmp/ossec-build"

    rm -rf "$tmpdir"
    mkdir -p "$tmpdir"
    cd "$tmpdir"

    wget -q "$url" -O "ossec-${OSSEC_VERSION}.tar.gz"
    tar xzf "ossec-${OSSEC_VERSION}.tar.gz"

    log_info "Downloaded and extracted OSSEC ${OSSEC_VERSION}"
}

install_ossec() {
    log_step "Installing OSSEC in ${INSTALL_MODE} mode..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would install OSSEC as ${INSTALL_MODE}"
        return
    fi

    local tmpdir="/tmp/ossec-build/ossec-hids-${OSSEC_VERSION}"
    cd "$tmpdir"

    # Generate preloaded-vars.conf for non-interactive install
    cat > etc/preloaded-vars.conf <<PRELOAD
USER_LANGUAGE="en"
USER_NO_STOP="y"
USER_INSTALL_TYPE="${INSTALL_MODE}"
USER_DIR="${OSSEC_DIR}"
USER_ENABLE_EMAIL="$([ -n "$EMAIL_ADDR" ] && echo y || echo n)"
USER_EMAIL_ADDRESS="${EMAIL_ADDR:-admin@example.com}"
USER_EMAIL_SMTP="${EMAIL_SMTP}"
USER_ENABLE_SYSCHECK="y"
USER_ENABLE_ROOTCHECK="$(${ROOTCHECK} && echo y || echo n)"
USER_ENABLE_ACTIVE_RESPONSE="$(${ACTIVE_RESPONSE} && echo y || echo n)"
USER_ENABLE_FIREWALL_RESPONSE="$(${ACTIVE_RESPONSE} && echo y || echo n)"
USER_UPDATE="n"
PRELOAD

    if [ "$INSTALL_MODE" = "agent" ]; then
        echo "USER_AGENT_SERVER_IP=\"${SERVER_IP}\"" >> etc/preloaded-vars.conf
    fi

    # Run the install
    ./install.sh

    log_info "OSSEC installed to ${OSSEC_DIR}"
}

# ============================================================================
# CONFIGURATION
# ============================================================================

configure_ossec() {
    log_step "Configuring OSSEC..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would configure syscheck directories and rules"
        return
    fi

    local ossec_conf="${OSSEC_DIR}/etc/ossec.conf"

    if [ ! -f "$ossec_conf" ]; then
        log_warn "ossec.conf not found — skipping custom configuration"
        return
    fi

    # Update syscheck frequency
    if grep -q "<frequency>" "$ossec_conf"; then
        sed -i "s|<frequency>[0-9]*</frequency>|<frequency>${SYSCHECK_FREQUENCY}</frequency>|g" "$ossec_conf"
        log_info "Set syscheck frequency to ${SYSCHECK_FREQUENCY} seconds"
    fi

    # Add additional syscheck directories
    IFS=',' read -ra DIRS <<< "$SYSCHECK_DIRS"
    for dir in "${DIRS[@]}"; do
        dir=$(echo "$dir" | xargs)
        if ! grep -q "<directories.*>${dir}<" "$ossec_conf" 2>/dev/null; then
            sed -i "/<\/syscheck>/i\\    <directories check_all=\"yes\" realtime=\"yes\">${dir}</directories>" "$ossec_conf"
            log_info "Added syscheck directory: ${dir}"
        fi
    done

    # Add common ignore paths to reduce noise
    local ignores=(
        "/etc/mtab"
        "/etc/resolv.conf"
        "/etc/adjtime"
        "/etc/mail/statistics"
        "/etc/random-seed"
        "/etc/sysstat"
    )
    for ign in "${ignores[@]}"; do
        if ! grep -q "<ignore>${ign}</ignore>" "$ossec_conf" 2>/dev/null; then
            sed -i "/<\/syscheck>/i\\    <ignore>${ign}</ignore>" "$ossec_conf"
        fi
    done

    log_info "OSSEC configuration updated"
}

# ============================================================================
# FIREWALL
# ============================================================================

configure_firewall() {
    if [ "$INSTALL_MODE" != "server" ]; then
        return
    fi

    log_step "Opening OSSEC server port (1514/udp)..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would open port 1514/udp"
        return
    fi

    if command -v ufw &>/dev/null && ufw status | grep -q "active"; then
        ufw allow 1514/udp
        log_info "Opened port 1514/udp via ufw"
    elif command -v firewall-cmd &>/dev/null; then
        firewall-cmd --permanent --add-port=1514/udp
        firewall-cmd --reload
        log_info "Opened port 1514/udp via firewalld"
    elif command -v nft &>/dev/null; then
        nft add rule inet filter input udp dport 1514 accept 2>/dev/null || true
        log_info "Opened port 1514/udp via nftables"
    fi
}

# ============================================================================
# SERVICE
# ============================================================================

start_ossec() {
    log_step "Starting OSSEC..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would start OSSEC"
        return
    fi

    "${OSSEC_DIR}/bin/ossec-control" start

    sleep 3

    if "${OSSEC_DIR}/bin/ossec-control" status | grep -q "running"; then
        log_info "OSSEC is running"
    else
        log_warn "OSSEC may not have started — check ${OSSEC_DIR}/logs/ossec.log"
    fi
}

# ============================================================================
# VERIFICATION
# ============================================================================

verify_installation() {
    log_step "Verifying installation..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Installation summary:"
        echo "  Mode:            ${INSTALL_MODE}"
        echo "  Directory:       ${OSSEC_DIR}"
        echo "  Syscheck freq:   ${SYSCHECK_FREQUENCY}s"
        echo "  Active response: ${ACTIVE_RESPONSE}"
        echo "  Rootcheck:       ${ROOTCHECK}"
        echo "  Email:           ${EMAIL_ADDR:-disabled}"
        [ "$INSTALL_MODE" = "agent" ] && echo "  Server IP:       ${SERVER_IP}"
        return
    fi

    echo ""
    echo "=== OSSEC Installation Summary ==="
    echo "Mode:            ${INSTALL_MODE}"
    echo "Version:         ${OSSEC_VERSION}"
    echo "Install dir:     ${OSSEC_DIR}"
    echo "Active response: ${ACTIVE_RESPONSE}"
    echo "Rootcheck:       ${ROOTCHECK}"
    echo "Syscheck freq:   ${SYSCHECK_FREQUENCY}s ($(( SYSCHECK_FREQUENCY / 3600 ))h)"
    echo ""

    echo "=== Process Status ==="
    "${OSSEC_DIR}/bin/ossec-control" status
    echo ""

    echo "=== Syscheck Status ==="
    "${OSSEC_DIR}/bin/syscheck_control" -l 2>/dev/null | head -5 || echo "(syscheck not yet run)"
    echo ""

    if [ "$INSTALL_MODE" = "server" ]; then
        echo "=== Agent Management ==="
        echo "To add agents:"
        echo "  ${OSSEC_DIR}/bin/manage_agents"
        echo ""
    fi

    echo "=== Useful Commands ==="
    echo "  Status:    ${OSSEC_DIR}/bin/ossec-control status"
    echo "  Restart:   ${OSSEC_DIR}/bin/ossec-control restart"
    echo "  Logs:      tail -f ${OSSEC_DIR}/logs/ossec.log"
    echo "  Alerts:    tail -f ${OSSEC_DIR}/logs/alerts/alerts.log"
    echo ""
    log_info "Installation complete. Logs: $LOG_FILE"
}

# ============================================================================
# UNINSTALL
# ============================================================================

uninstall_ossec() {
    log_step "Uninstalling OSSEC..."

    if $DRY_RUN; then
        log_info "[DRY RUN] Would stop and remove OSSEC"
        return
    fi

    if [ -x "${OSSEC_DIR}/bin/ossec-control" ]; then
        "${OSSEC_DIR}/bin/ossec-control" stop 2>/dev/null || true
    fi

    if [ -d "$OSSEC_DIR" ]; then
        rm -rf "$OSSEC_DIR"
        log_info "Removed ${OSSEC_DIR}"
    fi

    # Remove ossec user/group
    userdel ossec 2>/dev/null || true
    userdel ossecm 2>/dev/null || true
    userdel ossecr 2>/dev/null || true
    groupdel ossec 2>/dev/null || true

    log_info "OSSEC uninstalled"
    exit 0
}

# ============================================================================
# MAIN
# ============================================================================

main() {
    parse_args "$@"

    echo ""
    echo "============================================"
    echo "  OSSEC Install Script v${VERSION}"
    echo "  https://mylinux.work"
    echo "============================================"
    echo ""

    check_root
    detect_os

    if $UNINSTALL; then
        uninstall_ossec
    fi

    install_dependencies
    download_ossec
    install_ossec
    configure_ossec
    configure_firewall
    start_ossec
    verify_installation
}

main "$@"
