#!/usr/bin/env bash
# =============================================================================
# VCS Akadémia — Level 1 / Chapter 1: First Survey & System Update
# -----------------------------------------------------------------------------
# Web:    https://vcs-akademia.net/kurz/level-1/system-update
# GitHub: github.com/virtucybersecurity/vcs-akademia
#
# Usage (run on the VPS, as root or via sudo):
#   curl -O https://vcs-akademia.net/script/level-1/system-update/setup-first-survey.sh
#   bash setup-first-survey.sh
#
# =============================================================================
# PREČO TENTO SKRIPT EXISTUJE
# =============================================================================
#
# Nový server od poskytovateľa NIKDY nie je v takom stave, v akom myslíš.
# Tri konkrétne bezpečnostné problémy:
#
#   1. Staré balíky so známymi zraniteľnosťami (CVE).
#      Botnety skenujú servery a vedia presne, ktoré verzie balíkov sú
#      napadnuteľné. Server po týždňoch bez updateov je doslova červený
#      koberec.
#
#   2. Predinštalované služby, ktoré počúvajú na verejných portoch.
#      Niektorí poskytovatelia majú v default image webhosting panely,
#      monitoring agentov alebo databázy s defaultnými heslami. Ak
#      nevidíš, čo počúva, nemôžeš to chrániť.
#
#   3. Žiadny referenčný bod pre budúce zmeny.
#      O mesiac sa niečo pokazí. Bez baseline dokumentu nevieš, čo bolo
#      normálne a čo je nové (= podozrivé).
#
# Tento skript rieši všetky tri:
#   - Aplikuje bezpečnostné updaty
#   - Audituje, čo počúva (a varuje, ak vidí veci na verejnej IP)
#   - Vytvorí baseline.txt ako referenciu na disk
#
# =============================================================================
# AKO TO TENTO SKRIPT ROBÍ
# =============================================================================
#
# Skript beží PRIAMO NA VPS (nie lokálne). Predpokladá root alebo sudo.
#
# V 5 krokoch:
#   1. Audit "pred" — zachytí súčasný stav servera (OS, kernel, služby,
#      porty, disk, RAM) do dočasného súboru.
#   2. Apt update + upgrade — aplikuje všetky dostupné updaty.
#   3. Housekeeping — pýta sa na hostname a timezone, nastavuje ich.
#   4. Audit "po" — zachytí nový stav po aktualizácii.
#   5. Vytvorí /root/server-baseline.txt s celým auditom.
#
# Skript NEINŠTALUJE žiadne aplikácie ani služby. Len upgraduje existujúce
# balíky a zbiera informácie.
#
# Bezpečnostné varovanie počas auditu:
#   Ak skript zistí, že na 0.0.0.0 počúva niečo iné než SSH (port 22),
#   upozorní ťa. To je riziko, ktoré treba riešiť — buď zastavením služby,
#   alebo zmenou na 127.0.0.1, alebo firewallom (CH 5).
#
# =============================================================================

set -u
set -o pipefail

# -----------------------------------------------------------------------------
# Colors (ANSI escape codes)
# -----------------------------------------------------------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

# -----------------------------------------------------------------------------
# Output helpers
# -----------------------------------------------------------------------------
print_info()    { printf "${BLUE}[INFO]${NC} %s\n" "$*"; }
print_success() { printf "${GREEN}[OK]${NC} %s\n" "$*"; }
print_warning() { printf "${YELLOW}[VAROVANIE]${NC} %s\n" "$*" >&2; }
print_error()   { printf "${RED}[CHYBA]${NC} %s\n" "$*" >&2; }
print_fatal()   { print_error "$*"; cleanup_on_exit; exit 1; }

# -----------------------------------------------------------------------------
# Cleanup — remove the script file when we're done (success or failure).
# -----------------------------------------------------------------------------
SCRIPT_SOURCE="${BASH_SOURCE[0]:-$0}"

cleanup_on_exit() {
    [ -f "$SCRIPT_SOURCE" ] || return 0

    local script_dir script_name script_path
    script_dir="$(cd "$(dirname "$SCRIPT_SOURCE")" 2>/dev/null && pwd)" || return 0
    script_name="$(basename "$SCRIPT_SOURCE")"
    script_path="${script_dir}/${script_name}"

    case "$script_name" in
        setup-first-survey.sh) ;;
        *) return 0 ;;
    esac

    case "$script_path" in
        /|/bin/*|/sbin/*|/usr/*|/etc/*|/var/*|/lib/*|/lib64/*|/boot/*)
            return 0 ;;
    esac

    [ -f "$script_path" ] && rm -f "$script_path" 2>/dev/null && \
        print_info "Skript bol odstránený: $script_path"
    return 0
}

trap cleanup_on_exit EXIT

# -----------------------------------------------------------------------------
# Pre-flight checks
# -----------------------------------------------------------------------------
preflight() {
    # Must be Linux (this script runs ON the server, not locally)
    if [ "$(uname -s)" != "Linux" ]; then
        print_fatal "Tento skript sa spúšťa NA serveri (Linux), nie lokálne."
    fi

    # Must be root or have sudo
    if [ "$(id -u)" -ne 0 ]; then
        if ! command -v sudo >/dev/null 2>&1; then
            print_fatal "Skript potrebuje root prístup. Spusti ako root alebo nainštaluj sudo."
        fi
        print_warning "Skript nie je spustený ako root."
        print_info "Mnoho operácií bude vyžadovať sudo heslo."
    fi

    # Must be Debian/Ubuntu based (uses apt)
    if ! command -v apt >/dev/null 2>&1; then
        print_fatal "Tento skript podporuje len Debian/Ubuntu (apt). Pre iné distribúcie použi ručný postup z kapitoly."
    fi
}

# -----------------------------------------------------------------------------
# Confirmation prompt
# -----------------------------------------------------------------------------
confirm() {
    local prompt="${1:-Pokračovať?}"
    local reply
    printf "${YELLOW}%s [y/N]:${NC} " "$prompt"
    read -r reply </dev/tty
    case "$reply" in
        y|Y|yes|YES|ano|ANO) return 0 ;;
        *) printf "${BLUE}[INFO]${NC} Zrušené.\n"; exit 0 ;;
    esac
}

# -----------------------------------------------------------------------------
# Ask for user input with optional default
# -----------------------------------------------------------------------------
ask() {
    local prompt="$1"
    local default="${2:-}"
    local reply
    if [ -n "$default" ]; then
        printf "%s [%s]: " "$prompt" "$default" >&2
    else
        printf "%s: " "$prompt" >&2
    fi
    read -r reply </dev/tty
    if [ -z "$reply" ]; then
        echo "$default"
    else
        echo "$reply"
    fi
}

# -----------------------------------------------------------------------------
# Run command with sudo if not root
# -----------------------------------------------------------------------------
run_priv() {
    if [ "$(id -u)" -eq 0 ]; then
        "$@"
    else
        sudo "$@"
    fi
}

# -----------------------------------------------------------------------------
# Audit: collect server state into a file
# -----------------------------------------------------------------------------
audit_to_file() {
    local out_file="$1"
    local title="$2"

    {
        echo "================================================"
        echo "$title"
        echo "Dátum: $(date +'%Y-%m-%d %H:%M:%S %Z')"
        echo "================================================"
        echo

        echo "=== Operačný systém ==="
        if command -v lsb_release >/dev/null 2>&1; then
            lsb_release -a 2>/dev/null
        else
            cat /etc/os-release 2>/dev/null
        fi
        echo

        echo "=== Kernel a architektúra ==="
        uname -a
        echo

        echo "=== Hostname ==="
        hostnamectl 2>/dev/null || hostname
        echo

        echo "=== Aktívne služby (systemd) ==="
        if command -v systemctl >/dev/null 2>&1; then
            systemctl list-units --type=service --state=running --no-pager 2>/dev/null
        else
            echo "systemctl nie je dostupný"
        fi
        echo

        echo "=== Otvorené TCP porty ==="
        if command -v ss >/dev/null 2>&1; then
            ss -tlnp 2>/dev/null
        elif command -v netstat >/dev/null 2>&1; then
            netstat -tlnp 2>/dev/null
        else
            echo "ss ani netstat nie sú dostupné"
        fi
        echo

        echo "=== Disk ==="
        df -h
        echo

        echo "=== RAM a Swap ==="
        free -h
        echo

        echo "=== CPU ==="
        if command -v lscpu >/dev/null 2>&1; then
            lscpu | head -10
        else
            cat /proc/cpuinfo | head -20
        fi
        echo
    } > "$out_file"
}

# -----------------------------------------------------------------------------
# Security check: warn about services listening on 0.0.0.0 (other than SSH)
# -----------------------------------------------------------------------------
check_exposed_services() {
    if ! command -v ss >/dev/null 2>&1; then
        print_warning "ss nie je dostupné, preskakujem kontrolu vystavených služieb."
        return 0
    fi

    # Get services listening on 0.0.0.0 (excluding SSH on 22)
    local exposed
    exposed=$(ss -tlnp 2>/dev/null | awk '
        NR > 1 && $4 ~ /^0\.0\.0\.0:/ {
            split($4, a, ":")
            port = a[length(a)]
            if (port != "22") {
                print $4 " " $NF
            }
        }
    ')

    if [ -z "$exposed" ]; then
        print_success "Žiadne neočakávané služby na verejnej IP (0.0.0.0)."
        return 0
    fi

    echo
    print_warning "VAROVANIE — Tieto služby počúvajú na verejnej IP (0.0.0.0):"
    echo
    echo "$exposed" | while IFS= read -r line; do
        printf "  ${RED}%s${NC}\n" "$line"
    done
    echo
    print_warning "Toto je BEZPEČNOSTNÉ RIZIKO:"
    print_warning "  - Botnety vidia tieto porty na celom internete."
    print_warning "  - Skenujú ich, hľadajú default heslá a známe zraniteľnosti."
    print_warning ""
    print_warning "Možnosti riešenia:"
    print_warning "  a) Ak službu nepotrebuješ — zastav ju a vypni boot:"
    print_warning "     systemctl stop <nazov>"
    print_warning "     systemctl disable <nazov>"
    print_warning "  b) Ak ju potrebuješ ale len lokálne — zmeň ju na 127.0.0.1"
    print_warning "     (úprava jej konfigurácie)"
    print_warning "  c) Vytvor firewall pravidlo (preberáme v CH 5)"
    echo
    print_info "Tento skript NIČ NEVYPÍNA. Rozhodnutie je na tebe."
    echo
}

# -----------------------------------------------------------------------------
# Apply system updates
# -----------------------------------------------------------------------------
apply_updates() {
    print_info "Aktualizujem databázu balíkov (apt update)..."
    if ! run_priv apt update; then
        print_fatal "apt update zlyhal. Skontroluj sieť alebo apt repozitáre."
    fi

    # Count upgradable packages
    local upgradable
    upgradable=$(apt list --upgradable 2>/dev/null | grep -c upgradable)
    upgradable=$((upgradable > 0 ? upgradable - 1 : 0))  # subtract header line

    if [ "$upgradable" -eq 0 ]; then
        print_success "Všetky balíky sú aktuálne — žiadne updaty."
        return 0
    fi

    print_info "Dostupných $upgradable updateov."
    echo
    confirm "Spustiť apt upgrade?"

    print_info "Inštalujem updaty (môže trvať pár minút)..."
    if ! run_priv apt upgrade -y; then
        print_warning "apt upgrade skončil s chybou. Skontroluj výstup vyššie."
        return 1
    fi

    print_success "Updaty nainštalované."

    # Check if reboot is required
    if [ -f /var/run/reboot-required ]; then
        echo
        print_warning "Niektoré updaty (kernel, libc) vyžadujú REŠTART servera."
        print_warning "Bez reštartu bežia staré verzie napriek tomu, že apt ich má za nainštalované."
        print_info "Reštart neskôr cez: reboot"
    fi
}

# -----------------------------------------------------------------------------
# Set hostname interactively
# -----------------------------------------------------------------------------
set_hostname_interactive() {
    local current_hostname
    current_hostname=$(hostname)

    print_info "Aktuálny hostname: $current_hostname"
    print_info "Hostname je meno servera, ktoré uvidíš v termináli ('user@nazov')."
    print_info "Daj mu zmysluplné meno (napr. 'moj-web', 'db-prod', 'lab-test')."
    echo

    local new_hostname
    new_hostname=$(ask "Nový hostname (Enter pre ponechanie)" "$current_hostname")

    if [ "$new_hostname" = "$current_hostname" ]; then
        print_info "Hostname zostáva: $current_hostname"
        return 0
    fi

    # Validate: only alphanumeric, hyphens, no spaces
    if ! echo "$new_hostname" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$'; then
        print_warning "Neplatný hostname (povolené: písmená, čísla, pomlčky)."
        print_info "Hostname nezmenený."
        return 0
    fi

    if run_priv hostnamectl set-hostname "$new_hostname"; then
        print_success "Hostname zmenený na: $new_hostname"
        print_info "Pre prejavenie v aktuálnej session: exec bash (alebo nové prihlásenie)"
    else
        print_warning "Nepodarilo sa zmeniť hostname."
    fi
}

# -----------------------------------------------------------------------------
# Set timezone interactively
# -----------------------------------------------------------------------------
set_timezone_interactive() {
    local current_tz
    current_tz=$(timedatectl show -p Timezone --value 2>/dev/null || echo "unknown")

    print_info "Aktuálny timezone: $current_tz"
    print_info "Možnosti:"
    print_info "  - UTC (odporúčané pre produkčné servery)"
    print_info "  - Europe/Bratislava (slovenský čas)"
    print_info "  - Europe/Prague (český čas)"
    print_info "  - Iný (zadaj manuálne)"
    echo

    local new_tz
    new_tz=$(ask "Nový timezone (Enter pre ponechanie)" "$current_tz")

    if [ "$new_tz" = "$current_tz" ]; then
        print_info "Timezone zostáva: $current_tz"
    elif run_priv timedatectl set-timezone "$new_tz" 2>/dev/null; then
        print_success "Timezone zmenený na: $new_tz"
    else
        print_warning "Neplatný timezone alebo chyba. Timezone nezmenený."
        print_info "Zoznam timezone: timedatectl list-timezones"
    fi

    # Check NTP sync
    local ntp_active
    ntp_active=$(timedatectl show -p NTP --value 2>/dev/null || echo "unknown")
    local sync_status
    sync_status=$(timedatectl show -p NTPSynchronized --value 2>/dev/null || echo "unknown")

    if [ "$sync_status" = "yes" ]; then
        print_success "NTP synchronizácia funguje."
    else
        print_warning "NTP synchronizácia nie je aktívna."
        print_info "Zapínam ju..."
        if run_priv timedatectl set-ntp true 2>/dev/null; then
            print_success "NTP zapnuté. Synchronizácia môže trvať minútu."
        else
            print_warning "Nepodarilo sa zapnúť NTP automaticky."
        fi
    fi
}

# -----------------------------------------------------------------------------
# Final summary
# -----------------------------------------------------------------------------
print_summary() {
    local baseline_path="$1"

    echo
    printf "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
    printf "${GREEN}  Audit a housekeeping: HOTOVO${NC}\n"
    printf "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
    echo
    printf "  Baseline dokument:  ${BLUE}%s${NC}\n" "$baseline_path"
    printf "  Pozri si ho:        ${BLUE}cat %s${NC}\n" "$baseline_path"
    echo
    printf "${YELLOW}  Tento súbor zachováva stav servera v jednom konkrétnom${NC}\n"
    printf "${YELLOW}  momente. V budúcnosti, keď sa niečo pokazí alebo objavíš${NC}\n"
    printf "${YELLOW}  podozrivú vec, porovnaj aktuálny stav s týmto baseline.${NC}\n"
    echo
    printf "${YELLOW}  Ďalej:${NC} CH 2 — Sudo user (non-root)\n"
    printf "  Vytvoríme používateľa s sudo právami, aby si sa odpútal\n"
    printf "  od priameho prihlasovania ako root.\n"
    printf "  https://vcs-akademia.net/kurz/level-1/sudo-user\n"
    echo

    # Reboot reminder
    if [ -f /var/run/reboot-required ]; then
        print_warning "Server vyžaduje REŠTART pred ďalšou kapitolou (kernel/libc update)."
        print_info "Spusti: reboot"
        echo
    fi
}

# -----------------------------------------------------------------------------
# MAIN
# -----------------------------------------------------------------------------
main() {
    printf "\n${BLUE}=== VCS Akadémia — CH 1: Prvý kontakt ===${NC}\n\n"

    cat <<EOF
Tento skript urobí 5 krokov priamo na serveri:
  1. Audit "pred" — zachytí súčasný stav servera.
  2. Apt update + upgrade — bezpečnostné a feature updaty.
  3. Housekeeping — nastaví hostname a timezone (interaktívne).
  4. Audit "po" — zachytí nový stav po aktualizácii.
  5. Vytvorí /root/server-baseline.txt ako referenciu.

BEZPEČNOSTNÁ POZNÁMKA:
  Počas auditu skript skontroluje, či na verejnej IP (0.0.0.0)
  počúva niečo iné než SSH. Ak áno, upozorní ťa — toto je
  potenciálne riziko, ktoré treba riešiť.

EOF

    confirm "Chceš pokračovať?"

    printf "\n${BLUE}-- Pre-flight kontrola --${NC}\n"
    preflight
    print_success "Server je pripravený."

    # Determine baseline path (root vs sudo)
    local baseline_path="/root/server-baseline.txt"
    if [ "$(id -u)" -ne 0 ]; then
        baseline_path="$HOME/server-baseline.txt"
        print_info "Beží ako non-root, baseline uložím do: $baseline_path"
    fi

    # Temp file for pre-update audit
    local audit_before
    audit_before=$(mktemp /tmp/audit-before-XXXXXX.txt)

    printf "\n${BLUE}-- Krok 1/5 — Audit pred zmenami --${NC}\n"
    audit_to_file "$audit_before" "AUDIT PRED APLIKÁCIOU UPDATEOV"
    print_success "Pred-audit zachytený."

    # Check for exposed services BEFORE updates (might change after)
    check_exposed_services

    printf "\n${BLUE}-- Krok 2/5 — System update --${NC}\n"
    apply_updates

    printf "\n${BLUE}-- Krok 3/5 — Hostname --${NC}\n"
    set_hostname_interactive

    printf "\n${BLUE}-- Krok 4/5 — Timezone a NTP --${NC}\n"
    set_timezone_interactive

    printf "\n${BLUE}-- Krok 5/5 — Vytváram baseline --${NC}\n"
    audit_to_file "$baseline_path" "SERVER BASELINE (po prvom audite a update)"

    # Append the "before" audit as appendix for reference
    {
        echo
        echo "================================================"
        echo "APPENDIX: Stav PRED aplikovaním updateov"
        echo "(pre porovnanie)"
        echo "================================================"
        echo
        cat "$audit_before"
    } >> "$baseline_path"

    rm -f "$audit_before"
    print_success "Baseline vytvorený: $baseline_path"

    print_summary "$baseline_path"
}

main "$@"
