#!/bin/sh
# zabezpieczenie-posix.sh
# Skrypt działa pod dowolnym POSIX-sh (dash, ash/busybox, ksh, zsh, bash)
#
# Skrypt:
#   1. pobiera punkt montowania nośnika zewnętrznego (argument $1),
#   2. weryfikuje uprawnienia root oraz poprawność punktu montowania,
#   3. weryfikuje dostępne miejsce na nośniku i dobiera zakres /var/log,
#   4. wykonuje wszystkie kroki procedury zapisując artefakty na nośnik,
#   5. prowadzi rejestr przebiegu,
#   6. oblicza sumy kontrolne SHA-256 wszystkich zebranych plików,
#   7. pakuje wszystkie artefakty (wraz z logami i sumami kontrolnymi) do
#      archiwum ${HOSTNAME}_zabezpieczenie.tar.gz na nośniku, a następnie
#      usuwa pojedyncze pliki artefaktów – na nośniku pozostaje tylko archiwum,
#   8. bezpiecznie odmontowuje nośnik.
#
# Użycie:
#   sudo sh zabezpieczenie-posix.sh <punkt_montowania_nosnika>
#
# Stosować wyłącznie na badanym systemie, z uprawnieniami root, z czystym nośnikiem
# zewnętrznym podmontowanym we wskazanym punkcie montowania.
#
set -u

nosnik=""              # punkt montowania nośnika zewnętrznego (argument $1).
SESSION=""             # ścieżka logu przebiegu sesji (session.log).
ERR=""                 # ścieżka logu błędów (error_${HOSTNAME}.log).
VARLOG_MODE=""         # tryb zabezpieczania /var/log: "full" (cały katalog) lub "files" (tylko pliki).


HOSTNAME="${HOSTNAME:-$(hostname 2>/dev/null || uname -n)}"
export HOSTNAME

# ---------------------------------------------------------------------------
# Funkcje pomocnicze (składnia POSIX: name() { ... })
# ---------------------------------------------------------------------------

# log: Rejestr przebiegu
log() {
    msg="[$(date -u +%Y%m%dT%H%M%S-UTC)] $*"
    printf '%s\n' "$msg"
    [ -n "$SESSION" ] && printf '%s\n' "$msg" >> "$SESSION"
}

# err: dopisz komunikat do logu błędów (error_${HOSTNAME}.log) z znacznikiem czasu.

errlog() {
    [ -n "$ERR" ] && printf '[%s] %s\n' "$(date -u +%Y%m%dT%H%M%S-UTC)" "$*" >> "$ERR"
}

# die: wypisz komunikat błędu (terminal + session.log + error log) i zakończ z kodem 1.
die() {
    log "BŁĄD: $*"
    errlog "BŁĄD: $*"
    exit 1
}


is_mountpoint() {
    if command -v mountpoint >/dev/null 2>&1; then
        mountpoint -q "$1"
    else
        [ "$1" = "$(findmnt -M -n -o TARGET "$1" 2>/dev/null)" ]
    fi
}

usage() {
    cat <<USAGE
Użycie: sudo sh $0 <punkt_montowania_nosnika>

Skrypt wykonuje procedurę zabezpieczania danych z działającego systemu Linux,
zapisuje artefakty na podmontowanym nośniku zewnętrznym, a na koniec pakuje je
do archiwum ${HOSTNAME}_zabezpieczenie.tar.gz (na nośniku pozostaje tylko archiwum).
USAGE
}

# ---------------------------------------------------------------------------
# Procedura zabezpieczania – wykonuje wszystkie kroki z artykułu.
# ---------------------------------------------------------------------------
run_procedure() {
    # Nagłówek w session.log i error log 
    log "============================================================"
    log "  ZABEZPIECZANIE DANYCH  |  host: $HOSTNAME  |  nośnik: $nosnik"
    log "  start: $(date -u +%Y%m%dT%H%M%S-UTC)"
    log "============================================================"
    errlog "============================================================"
    errlog "  ZABEZPIECZANIE DANYCH  |  host: $HOSTNAME  |  nośnik: $nosnik"
    errlog "  start: $(date -u +%Y%m%dT%H%M%S-UTC)"
    errlog "============================================================"

    # Informacje początkowe
    log "### Informacje początkowe"
    log "date: $(date -u)"
    log "uptime: $(uptime 2>>"$ERR" || echo '(brak uptime)')"
    log "who -a: $(who -a 2>>"$ERR" || echo '(brak who)')"
    log "nosnik: $nosnik"

    # 1. Procesy i usługi -----------------------------------------------------
    log "### [1/13] Procesy i usługi"
    errlog "Rozpoczęcie zabezpieczania informacji o procesach i usługach"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    ps axjfww > "$nosnik/processes_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    systemctl list-units --type=service > "$nosnik/services_${HOSTNAME}_${TS}.txt"   2>>"$ERR"
    pstree -p > "$nosnik/pstree_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o procesach i usługach"

    # 2. Sieć ----------------------------------------------------------------
    log "### [2/13] Sieć"
    # Aktywne połączenia i nasłuchujące porty 
    errlog "Rozpoczęcie zabezpieczania informacji o połączeniach sieciowych"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    if command -v ss >/dev/null 2>&1; then
        ss -tunap > "$nosnik/connections_ss_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    elif command -v netstat >/dev/null 2>&1; then
        netstat -peanut > "$nosnik/connections_netstat_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    elif command -v lsof >/dev/null 2>&1; then
        lsof -i -P -n -R +c 0 > "$nosnik/connections_lsof_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    fi
    errlog "Zakończenie zabezpieczania informacji o połączeniach sieciowych"

    # Tablica ARP.
    errlog "Rozpoczęcie zabezpieczania informacji o tablicy ARP"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    ip neigh > "$nosnik/arp_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o tablicy ARP"

    # Adresy interfejsów sieciowych.
    errlog "Rozpoczęcie zabezpieczania informacji o interfejsach sieciowych"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    if command -v ip >/dev/null 2>&1; then
        ip addr > "$nosnik/ip_addr_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    elif command -v ifconfig >/dev/null 2>&1; then
        ifconfig -a > "$nosnik/ifconfig_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    fi
    errlog "Zakończenie zabezpieczania informacji o interfejsach sieciowych"

    # Reguły zapory sieciowej.
    errlog "Rozpoczęcie zabezpieczania informacji o zaporze sieciowej"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    iptables-save   > "$nosnik/iptables_${HOSTNAME}_${TS}.txt"    2>>"$ERR"
    ip6tables-save  > "$nosnik/ip6tables_${HOSTNAME}_${TS}.txt"  2>>"$ERR"
    nft list ruleset > "$nosnik/nftables_${HOSTNAME}_${TS}.txt"  2>>"$ERR"
    ufw status verbose > "$nosnik/ufw_${HOSTNAME}_${TS}.txt"      2>>"$ERR"
    firewall-cmd --list-all-zones > "$nosnik/firewalld_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o zaporze sieciowej"

    # Tablica routingu i reguły routingu (IPv4 oraz IPv6).
    errlog "Rozpoczęcie zabezpieczania informacji o routingu"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    ip route    > "$nosnik/routes_${HOSTNAME}_${TS}.txt"    2>>"$ERR"
    ip -6 route > "$nosnik/routes6_${HOSTNAME}_${TS}.txt"   2>>"$ERR"
    ip rule     > "$nosnik/ip_rules_${HOSTNAME}_${TS}.txt"  2>>"$ERR"
    ip -6 rule  > "$nosnik/ip6_rules_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o routingu"

    # 3. Podstawowe informacje o urządzeniu ----------------------------------
    log "### [3/13] Podstawowe informacje o urządzeniu"
    errlog "Rozpoczęcie zabezpieczania podstawowych informacji o urządzeniu"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    timedatectl          >  "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    hostnamectl          >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    uname -a             >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    systemd-detect-virt  >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    cat /etc/timezone    >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    cat /etc/os-release  >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    lsblk                >> "$nosnik/info_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania podstawowych informacji o urządzeniu"

    # 4. Konta i grupy -------------------------------------------------------
    log "### [4/13] Konta i grupy"
    errlog "Rozpoczęcie zabezpieczania informacji o kontach i grupach"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    cp -a /etc/passwd "$nosnik/${HOSTNAME}_passwd_${TS}.txt" 2>>"$ERR"
    cp -a /etc/group  "$nosnik/${HOSTNAME}_group_${TS}.txt"   2>>"$ERR"
    cp -a /etc/sudoers   "$nosnik/${HOSTNAME}_sudoers_${TS}.txt"   2>>"$ERR"
    cp -a /etc/sudoers.d "$nosnik/${HOSTNAME}_sudoers.d_${TS}"      2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o kontach i grupach"

    # 5. Historia logowań ---------------------------------------------------
    log "### [5/13] Historia logowań"
    errlog "Rozpoczęcie zabezpieczania informacji o historii logowań"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    last -aF > "$nosnik/last_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o historii logowań"

    # 6. Klucze SSH ---------------------------------------------------------
    log "### [6/13] Klucze SSH (authorized_keys, known_hosts)"
    errlog "Rozpoczęcie zabezpieczania informacji o kluczach SSH"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    find /root /home -maxdepth 3 -type f \( -name authorized_keys -o -name known_hosts \) 2>>"$ERR" | while read -r f; do
        USER_OWNER="$(stat -c '%U' "$f")"
        BASE="$(basename "$f")"
        cp -a "$f" "$nosnik/${USER_OWNER}_${BASE}_${TS}" 2>>"$ERR"
    done
    errlog "Zakończenie zabezpieczania informacji o kluczach SSH"

    # 7. Zadania cykliczne --------------------------------------------------
    log "### [7/13] Zadania cykliczne (cron + systemd timers)"
    errlog "Rozpoczęcie zabezpieczania informacji o zadaniach cyklicznych"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    for u in $(cut -d: -f1 /etc/passwd); do
        printf '===== %s =====\n' "$u"
        crontab -l -u "$u" 2>&1
    done > "$nosnik/crontabs_all_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    tar --xattrs --acls --selinux --ignore-failed-read \
        -czf "$nosnik/cron_${HOSTNAME}_${TS}.tar.gz" \
        /etc/crontab /etc/cron.d /etc/cron.hourly /etc/cron.daily \
        /etc/cron.weekly /etc/cron.monthly /etc/anacrontab \
        /etc/cron.allow /etc/cron.deny \
        /var/spool/cron /var/spool/anacron /var/spool/at 2>>"$ERR"

    systemctl --no-pager --full list-timers --all              > "$nosnik/timers_${HOSTNAME}_${TS}.txt"        2>>"$ERR"
    systemctl --no-pager --full list-unit-files --state=enabled > "$nosnik/units_enabled_${HOSTNAME}_${TS}.txt" 2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o zadaniach cyklicznych"

    # 8. Historia poleceń użytkowników --------------------------------------
    log "### [8/13] Historia poleceń"
    errlog "Rozpoczęcie zabezpieczania informacji o historii poleceń"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    while IFS=: read -r username _ _ _ _ home _; do
        case "$home" in ""|/|/nonexistent|/dev/null|/var/empty) continue ;; esac
        [ -d "$home" ] || continue
        for hf in .bash_history .zsh_history .sh_history .history .tcsh_history \
                   .local/share/fish/fish_history .config/fish/fish_history; do
            if [ -f "$home/$hf" ]; then
                hf_safe="$(printf '%s' "$hf" | tr '/' '_')"
                cp -a "$home/$hf" "$nosnik/history_${username}_${hf_safe}_${TS}" 2>>"$ERR"
            fi
        done
    done < /etc/passwd
    errlog "Zakończenie zabezpieczania informacji o historii poleceń"

    # 9. Pliki konfiguracyjne powłoki ---------------------------------------
    log "### [9/13] Pliki konfiguracyjne powłoki (.bash_profile, .bashrc, .cshrc, .tcshrc, config.fish)"
    errlog "Rozpoczęcie zabezpieczania plików konfiguracyjnych powłoki"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    while IFS=: read -r username _ _ _ _ home _; do
        case "$home" in ""|/|/nonexistent|/dev/null|/var/empty) continue ;; esac
        [ -d "$home" ] || continue
        for sf in .bash_profile .bashrc .cshrc .tcshrc .config/fish/config.fish; do
            if [ -f "$home/$sf" ]; then
                sf_safe="$(printf '%s' "$sf" | tr '/' '_')"
                cp -a "$home/$sf" "$nosnik/shell_${username}_${sf_safe}_${TS}" 2>>"$ERR"
            fi
        done
    done < /etc/passwd
    errlog "Zakończenie zabezpieczania plików konfiguracyjnych powłoki"

    # 10. Dziennik systemowy journald ---------------------------------------
    log "### [10/13] Dziennik systemowy (journald)"
    errlog "Rozpoczęcie zabezpieczania informacji o dzienniku systemowym"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    journalctl --merge --all --output export > "$nosnik/journal_export_${HOSTNAME}_${TS}.export" 2>>"$ERR"
    journalctl --merge --all --output json   > "$nosnik/journal_all_${HOSTNAME}_${TS}.json"       2>>"$ERR"
    errlog "Zakończenie zabezpieczania informacji o dzienniku systemowym"

    # 11. Listing plików ----------------------------------------
    log "### [11/13] Listing plików"
    errlog "Rozpoczęcie zabezpieczania listingu plików"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    find /root /home /var /tmp -printf '%D\t%i\t%s\t%M\t%u\t%g\t%A@\t%T@\t%C@\t%y\t%p\n' 2>>"$ERR" > "$nosnik/filelist_${HOSTNAME}_${TS}.tsv"
    errlog "Zakończenie zabezpieczania listingu plików"

    # 12. Dzienniki zdarzeń /var/log -----------------------------------------
    log "### [12/13] Dzienniki zdarzeń /var/log"
    errlog "Rozpoczęcie zabezpieczania dzienników zdarzeń"
    TS=$(date -u +%Y%m%dT%H%M%S-UTC)
    if [ "$VARLOG_MODE" = "full" ]; then
        tar --xattrs --acls --selinux -czpf "$nosnik/varlog_${HOSTNAME}_${TS}.tar.gz" -C / var/log 2>>"$ERR"
        log "    Tryb: cały katalog /var/log (rekursywnie)."
    else
        ( cd / && find var/log -maxdepth 1 -type f -print0 ) 2>>"$ERR" \
            | tar --xattrs --acls --selinux -C / --null -T - -czpf "$nosnik/varlog_${HOSTNAME}_${TS}.tar.gz" 2>>"$ERR"
        log "    Tryb: tylko pliki /var/log/* (bez podkatalogów)."
    fi
    errlog "Zakończenie zabezpieczania dzienników zdarzeń"

    # Zakończenie procedury.
    log "### Zakończenie procedury: $(date -u +%Y%m%dT%H%M%S-UTC)"
    errlog "============================================================"
    errlog "  ZABEZPIECZANIE ZAKOŃCZONE  |  host: $HOSTNAME"
    errlog "  koniec: $(date -u +%Y%m%dT%H%M%S-UTC)"
    errlog "============================================================"
}

# ---------------------------------------------------------------------------
# Kontroler – walidacja wejścia, wykonanie procedury, sumy kontrolne,
# utworzenie archiwum i odmontowanie nośnika.
# ---------------------------------------------------------------------------

# --help / -h – wypisz pomoc i zakończ.
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
    usage
    exit 0
fi

# 1. Punkt montowania nośnika 
if [ "$#" -ge 1 ]; then
    nosnik="$1"
else
    usage >&2
    exit 2
fi

# 2. Uprawnienia root 
[ "$(id -u)" -eq 0 ] || { echo "BŁĄD: uruchom z uprawnieniami root (np. sudo -i; albo: sudo sh $0 $nosnik)." >&2; exit 1; }

# 3. Poprawność punktu montowania
[ -d "$nosnik" ] || { echo "BŁĄD: punkt montowania nie istnieje lub nie jest katalogiem: $nosnik" >&2; exit 1; }
is_mountpoint "$nosnik" || { echo "BŁĄD: $nosnik nie jest punktem montowania – podmontuj nośnik zewnętrzny przed uruchomieniem." >&2; exit 1; }

# RW nośnika
touch "$nosnik/.zabezpieczenie_writetest" 2>/dev/null || { echo "BŁĄD: brak uprawnień do zapisu w $nosnik." >&2; exit 1; }
rm -f "$nosnik/.zabezpieczenie_writetest"

# Ustawienie ścieżki logów na nośniku.
SESSION="$nosnik/session.log"
ERR="$nosnik/error_${HOSTNAME}.log"

export nosnik

# 4. Weryfikacja miejsca na nośniku – wybór zakresu /var/log.
log "[*] Weryfikacja dostępnego miejsca na nośniku..."
MEDIA_AVAIL=$(df -k "$nosnik" 2>/dev/null | awk 'NR==2{print $4}')
VARLOG_SIZE=$(du -sk /var/log 2>/dev/null | awk '{print $1}')
log "    Dostępne miejsce na nośniku: ${MEDIA_AVAIL:-?} KB"
log "    Rozmiar /var/log: ${VARLOG_SIZE:-?} KB"
if [ -n "$MEDIA_AVAIL" ] && [ -n "$VARLOG_SIZE" ] && [ "$VARLOG_SIZE" -le "$MEDIA_AVAIL" ]; then
    VARLOG_MODE="full"
    log "    /var/log zabezpieczany cały katalog /var/log."
else
    VARLOG_MODE="files"
    log "    /var/log nie zmieści się w całości, zabezpieczane tylko pliki /var/log/*."
fi
errlog "Tryb zabezpieczania /var/log: $VARLOG_MODE (rozmiar: ${VARLOG_SIZE:-?} KB, dostępne: ${MEDIA_AVAIL:-?} KB)"

# 5. Wykonanie procedury. 
run_procedure

# 6. Sumy kontrolne SHA-256 
log "[*] Obliczanie sum kontrolnych SHA-256..."
TS=$(date -u +%Y%m%dT%H%M%S-UTC)
HASHES="$nosnik/hashes_${HOSTNAME}_${TS}.txt"
HASHES_BASE="$(basename "$HASHES")"
find "$nosnik" -type f \
    ! -name "$HASHES_BASE" \
    ! -name "session.log" \
    ! -path "$ERR" \
    -exec sha256sum {} + > "$HASHES" 2>>"$ERR"
if [ -s "$HASHES" ]; then
    log "[*] Sumy kontrolne zapisane: $HASHES ($(wc -l < "$HASHES") plików)"
else
    log "[!] OSTRZEŻENIE: plik sum kontrolnych jest pusty."
fi

# 7. Utworzenie archiwum ze wszystkich artefaktów (wraz z logami i sumami).
log "[*] Tworzenie archiwum ${HOSTNAME}_zabezpieczenie.tar.gz..."
errlog "Tworzenie archiwum ${HOSTNAME}_zabezpieczenie.tar.gz"
ARCHIVE="$nosnik/${HOSTNAME}_zabezpieczenie.tar.gz"
ARCHIVE_BASE="$(basename "$ARCHIVE")"
( cd "$nosnik" && find . -mindepth 1 -maxdepth 1 ! -name "$ARCHIVE_BASE" -print0 ) 2>>"$ERR" \
    | tar --xattrs --acls --selinux -C "$nosnik" --null -T - -czpf "$ARCHIVE" 2>>"$ERR"
[ -s "$ARCHIVE" ] || die "nie udało się utworzyć archiwum $ARCHIVE"
log "[*] Archiwum utworzone: $ARCHIVE"
errlog "Archiwum utworzone: $ARCHIVE"

# Od tego momentu logi są zamykane – kolejne komunikaty trafiają tylko na terminal,
# ponieważ pojedyncze pliki (session.log, error.log, hashes.txt) zostaną usunięte.
SESSION=""
ERR=""

# 8. Usunięcie pojedynczych plików artefaktów – na nośniku zostaje tylko archiwum.
log "[*] Usuwanie pojedynczych plików artefaktów z nośnika..."
( cd "$nosnik" && find . -mindepth 1 -maxdepth 1 ! -name "$ARCHIVE_BASE" -exec rm -rf {} + ) 2>/dev/null
# 9. Bezpieczne odmontowanie nośnika.
log "[*] Synchronizacja buforów nośnika $nosnik (sync)..."
sync "$nosnik"
log "[*] Zakończono synchronizację. Próba odmontowania nośnika $nosnik..."
if umount "$nosnik" && ! is_mountpoint "$nosnik"; then
    echo "[OK] Nośnik odmontowany. Można go bezpiecznie odłączyć."
    echo "    Archiwum zabezpieczonych danych: ${HOSTNAME}_zabezpieczenie.tar.gz"
else
    log "[!] OSTRZEŻENIE: nie udało się odmontować $nosnik (prawdopodobnie zajęty)."
    log "    Dane zostały zapisane i zsynchronizowane (sync). Odmontuj nośnik ręcznie: sudo umount $nosnik"
    log "    Archiwum zabezpieczonych danych: ${HOSTNAME}_zabezpieczenie.tar.gz"
fi

# 10. Komunikat końcowy dla trybu "files".
if [ "$VARLOG_MODE" = "files" ]; then
    VARLOG_MB=$(( (VARLOG_SIZE + 1023) / 1024 ))
    echo ""
    echo "[!] UWAGA: /var/log zabezpieczony częściowo (tylko pliki /var/log/*)."
    echo "    Minimalna pojemność nośnika dla całego /var/log: ${VARLOG_MB} MB."
    echo "    1. Przekaż archiwum ${HOSTNAME}_zabezpieczenie.tar.gz do analizy."
    echo "    2. Przygotuj nośnik o pojemności co najmniej ${VARLOG_MB} MB"
    echo "       (plus zapas na pozostałe artefakty)."
    echo "    3. Powtórz procedurę zabezpieczania, aby zabezpieczyć cały katalog /var/log."
fi

exit 0
