From 2da7465d84714b11b989475527ce3d01880a7c19 Mon Sep 17 00:00:00 2001 From: Testtesttestestes <118276433+Testtesttestestes@users.noreply.github.com> Date: Wed, 20 May 2026 07:16:07 +0400 Subject: [PATCH 1/2] Add automated report script for tasks 5-8 --- scripts/task5_8_admin_report.sh | 90 +++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 scripts/task5_8_admin_report.sh diff --git a/scripts/task5_8_admin_report.sh b/scripts/task5_8_admin_report.sh new file mode 100755 index 0000000..c1162a6 --- /dev/null +++ b/scripts/task5_8_admin_report.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPORT_AUTHOR="${REPORT_AUTHOR:-Миленькова Ивана}" +ROOT="reports/task5_8" +LOG_DIR="$ROOT/logs" +IMG_DIR="$ROOT/images" +mkdir -p "$LOG_DIR" "$IMG_DIR" + +NEW_USER="${NEW_USER:-studentnew}" +OLD_USER="${OLD_USER:-$(id -un)}" +NEW_GROUP="${NEW_GROUP:-pktcap}" + +run_capture() { + local key="$1"; shift + { + echo "$ $*" + bash -lc "$*" + } >"$LOG_DIR/${key}.txt" 2>&1 || true + python3 scripts/text_to_png.py "$LOG_DIR/${key}.txt" "$IMG_DIR/${key}.png" --title "$key" +} + +note_capture() { + local key="$1" title="$2" body="$3" + printf '%s\n' "$body" >"$LOG_DIR/${key}.txt" + python3 scripts/text_to_png.py "$LOG_DIR/${key}.txt" "$IMG_DIR/${key}.png" --title "$title" +} + +# Task 5 +run_capture 01_useradd "sudo useradd -m -s /bin/bash '${NEW_USER}' && id '${NEW_USER}' && getent passwd '${NEW_USER}'" +run_capture 02_first_file "sudo -u '${NEW_USER}' bash -lc 'echo first_owned_by_new_user > ~/first' && sudo install -o '${NEW_USER}' -g '${NEW_USER}' -m 600 ~'${NEW_USER}'/first /tmp/first && ls -l /tmp/first" +run_capture 03_second_file "echo second_owned_by_old_user > /tmp/second && sudo chown '${OLD_USER}:${OLD_USER}' /tmp/second && sudo chmod 600 /tmp/second && ls -l /tmp/second" +run_capture 04_sudoers_pkgmgr "sudo sh -c \"printf '%s\\n' '${OLD_USER} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dnf, /usr/bin/yum, /usr/bin/pacman' > /etc/sudoers.d/90-${OLD_USER}-pkgmgr\" && sudo chmod 440 /etc/sudoers.d/90-${OLD_USER}-pkgmgr && sudo visudo -cf /etc/sudoers.d/90-${OLD_USER}-pkgmgr" +run_capture 05_pkgmgr_vs_tcpdump "sudo -n apt-get -v || sudo -n dnf --version || sudo -n yum --version || sudo -n pacman --version; sudo -n tcpdump -D" +run_capture 06_new_user_denied_pkgmgr "sudo -u '${NEW_USER}' sudo -n apt-get -v || sudo -u '${NEW_USER}' sudo -n dnf --version || sudo -u '${NEW_USER}' sudo -n yum --version || sudo -u '${NEW_USER}' sudo -n pacman --version" +run_capture 07_groupadd_and_tcpdump "sudo groupadd -f '${NEW_GROUP}' && sudo usermod -aG '${NEW_GROUP}' '${OLD_USER}' && sudo usermod -aG '${NEW_GROUP}' '${NEW_USER}' && sudo sh -c \"printf '%%%s ALL=(ALL) NOPASSWD: /usr/sbin/tcpdump\\n' '${NEW_GROUP}' > /etc/sudoers.d/91-${NEW_GROUP}-tcpdump\" && sudo chmod 440 /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && sudo visudo -cf /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && getent group '${NEW_GROUP}'" +run_capture 08_tcpdump_both_users "sudo -n tcpdump -D ; sudo -u '${NEW_USER}' bash -lc 'id && sudo -n tcpdump -D'" + +# Task 6 +note_capture 09_crontab_script "cron_script" "#!/usr/bin/env bash\nset -euo pipefail\nTS=\$(date +%F_%H-%M-%S)\nmkdir -p /var/backups/home\ntar -czf /var/backups/home/home_backup_\${TS}.tar.gz \"$HOME\"" +run_capture 10_crontab_install "echo '0 0 * * 0 root /usr/local/bin/weekly_home_backup.sh' | sudo tee -a /etc/crontab && tail -n 5 /etc/crontab" + +# Task 7 +run_capture 11_start_stress "nohup bash -lc 'while :; do :; done' >/tmp/cpu_stress.log 2>&1 & echo $! > /tmp/stress.pid; cat /tmp/stress.pid" +run_capture 12_find_pid "ps -eo pid,ppid,user,cmd | grep -E 'while :; do :; done|cpu_stress' | grep -v grep" +run_capture 13_top_usage "PID=\$(cat /tmp/stress.pid); top -b -n 1 -p \"$PID\"" +run_capture 14_ps_before_kill "ps -fp \"$(cat /tmp/stress.pid)\"" +run_capture 15_sigterm_then_kill "PID=\$(cat /tmp/stress.pid); kill -TERM \"$PID\"; sleep 1; ps -p \"$PID\" || true; if ps -p \"$PID\" >/dev/null; then kill -KILL \"$PID\"; fi" +run_capture 16_ps_after_kill "ps -p \"$(cat /tmp/stress.pid)\" || true" + +# Task 8 +run_capture 17_journal_last20 "journalctl -n 20 --no-pager" +run_capture 18_ssh_logs "journalctl -u ssh -u sshd --no-pager | tail -n 50" +run_capture 19_enable_ssh_service "sudo systemctl enable ssh || sudo systemctl enable sshd; sudo systemctl restart ssh || sudo systemctl restart sshd; systemctl is-enabled ssh || systemctl is-enabled sshd" +run_capture 20_last_hour "journalctl --since '1 hour ago' --no-pager | tail -n 80" +run_capture 21_last_boot_to_file "journalctl -b --no-pager > '$ROOT/last_boot.log' && wc -l '$ROOT/last_boot.log' && tail -n 20 '$ROOT/last_boot.log'" +run_capture 22_errors_in_boot "grep -i 'error' '$ROOT/last_boot.log' | tail -n 50 || true" + +cat > "$ROOT/report.json" < Date: Wed, 20 May 2026 07:20:38 +0400 Subject: [PATCH 2/2] Split tasks 5-8 into separate scripts and workflows --- .../task5-users-privileges-report.yml | 24 +++++ .../workflows/task6-crontab-backup-report.yml | 24 +++++ .../task7-process-signals-report.yml | 24 +++++ .github/workflows/task8-journal-report.yml | 24 +++++ README.md | 6 +- scripts/task5_8_admin_report.sh | 90 ------------------- scripts/task5_users_privileges_report.sh | 41 +++++++++ scripts/task6_crontab_backup_report.sh | 24 +++++ scripts/task7_process_signals_report.sh | 16 ++++ scripts/task8_journal_report.sh | 16 ++++ 10 files changed, 198 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/task5-users-privileges-report.yml create mode 100644 .github/workflows/task6-crontab-backup-report.yml create mode 100644 .github/workflows/task7-process-signals-report.yml create mode 100644 .github/workflows/task8-journal-report.yml delete mode 100755 scripts/task5_8_admin_report.sh create mode 100755 scripts/task5_users_privileges_report.sh create mode 100755 scripts/task6_crontab_backup_report.sh create mode 100755 scripts/task7_process_signals_report.sh create mode 100755 scripts/task8_journal_report.sh diff --git a/.github/workflows/task5-users-privileges-report.yml b/.github/workflows/task5-users-privileges-report.yml new file mode 100644 index 0000000..511ca5e --- /dev/null +++ b/.github/workflows/task5-users-privileges-report.yml @@ -0,0 +1,24 @@ +name: Task 5 - Users, permissions and sudo report +on: + workflow_dispatch: + push: + paths: + - '.github/workflows/task5-users-privileges-report.yml' + - 'scripts/task5_users_privileges_report.sh' + - 'scripts/build_pdf_report.py' + - 'scripts/text_to_png.py' +jobs: + build-report-task5: + runs-on: ubuntu-latest + env: + REPORT_AUTHOR: Миленькова Ивана + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt-get update + sudo apt-get install -y python3-pil sudo tcpdump + - run: bash scripts/task5_users_privileges_report.sh + - uses: actions/upload-artifact@v4 + with: + name: task5-report-pdf + path: reports/task5/report.pdf diff --git a/.github/workflows/task6-crontab-backup-report.yml b/.github/workflows/task6-crontab-backup-report.yml new file mode 100644 index 0000000..6a3e17d --- /dev/null +++ b/.github/workflows/task6-crontab-backup-report.yml @@ -0,0 +1,24 @@ +name: Task 6 - Crontab backup report +on: + workflow_dispatch: + push: + paths: + - '.github/workflows/task6-crontab-backup-report.yml' + - 'scripts/task6_crontab_backup_report.sh' + - 'scripts/build_pdf_report.py' + - 'scripts/text_to_png.py' +jobs: + build-report-task6: + runs-on: ubuntu-latest + env: + REPORT_AUTHOR: Миленькова Ивана + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt-get update + sudo apt-get install -y python3-pil + - run: bash scripts/task6_crontab_backup_report.sh + - uses: actions/upload-artifact@v4 + with: + name: task6-report-pdf + path: reports/task6/report.pdf diff --git a/.github/workflows/task7-process-signals-report.yml b/.github/workflows/task7-process-signals-report.yml new file mode 100644 index 0000000..b5f67fc --- /dev/null +++ b/.github/workflows/task7-process-signals-report.yml @@ -0,0 +1,24 @@ +name: Task 7 - Process signals report +on: + workflow_dispatch: + push: + paths: + - '.github/workflows/task7-process-signals-report.yml' + - 'scripts/task7_process_signals_report.sh' + - 'scripts/build_pdf_report.py' + - 'scripts/text_to_png.py' +jobs: + build-report-task7: + runs-on: ubuntu-latest + env: + REPORT_AUTHOR: Миленькова Ивана + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt-get update + sudo apt-get install -y python3-pil procps + - run: bash scripts/task7_process_signals_report.sh + - uses: actions/upload-artifact@v4 + with: + name: task7-report-pdf + path: reports/task7/report.pdf diff --git a/.github/workflows/task8-journal-report.yml b/.github/workflows/task8-journal-report.yml new file mode 100644 index 0000000..19a2a15 --- /dev/null +++ b/.github/workflows/task8-journal-report.yml @@ -0,0 +1,24 @@ +name: Task 8 - Journalctl report +on: + workflow_dispatch: + push: + paths: + - '.github/workflows/task8-journal-report.yml' + - 'scripts/task8_journal_report.sh' + - 'scripts/build_pdf_report.py' + - 'scripts/text_to_png.py' +jobs: + build-report-task8: + runs-on: ubuntu-latest + env: + REPORT_AUTHOR: Миленькова Ивана + steps: + - uses: actions/checkout@v4 + - run: | + sudo apt-get update + sudo apt-get install -y python3-pil systemd + - run: bash scripts/task8_journal_report.sh + - uses: actions/upload-artifact@v4 + with: + name: task8-report-pdf + path: reports/task8/report.pdf diff --git a/README.md b/README.md index 2eb6806..3748ce2 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # Автоматические отчёты по заданиям (GitHub Actions) -В репозитории настроены **4 отдельных workflow** (по одному на каждую практическую работу): +В репозитории настроены **8 отдельных workflow** (по одному на каждую практическую работу): 1. `Task 1 - VM and Linux install report` 2. `Task 2 - Terminal commands report` 3. `Task 3 - Filesystem image report` 4. `Task 4 - SSH and RDP remote access report` +5. `Task 5 - Users, permissions and sudo report` +6. `Task 6 - Crontab backup report` +7. `Task 7 - Process signals report` +8. `Task 8 - Journalctl report` Каждый workflow: - запускается вручную (`workflow_dispatch`), diff --git a/scripts/task5_8_admin_report.sh b/scripts/task5_8_admin_report.sh deleted file mode 100755 index c1162a6..0000000 --- a/scripts/task5_8_admin_report.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -REPORT_AUTHOR="${REPORT_AUTHOR:-Миленькова Ивана}" -ROOT="reports/task5_8" -LOG_DIR="$ROOT/logs" -IMG_DIR="$ROOT/images" -mkdir -p "$LOG_DIR" "$IMG_DIR" - -NEW_USER="${NEW_USER:-studentnew}" -OLD_USER="${OLD_USER:-$(id -un)}" -NEW_GROUP="${NEW_GROUP:-pktcap}" - -run_capture() { - local key="$1"; shift - { - echo "$ $*" - bash -lc "$*" - } >"$LOG_DIR/${key}.txt" 2>&1 || true - python3 scripts/text_to_png.py "$LOG_DIR/${key}.txt" "$IMG_DIR/${key}.png" --title "$key" -} - -note_capture() { - local key="$1" title="$2" body="$3" - printf '%s\n' "$body" >"$LOG_DIR/${key}.txt" - python3 scripts/text_to_png.py "$LOG_DIR/${key}.txt" "$IMG_DIR/${key}.png" --title "$title" -} - -# Task 5 -run_capture 01_useradd "sudo useradd -m -s /bin/bash '${NEW_USER}' && id '${NEW_USER}' && getent passwd '${NEW_USER}'" -run_capture 02_first_file "sudo -u '${NEW_USER}' bash -lc 'echo first_owned_by_new_user > ~/first' && sudo install -o '${NEW_USER}' -g '${NEW_USER}' -m 600 ~'${NEW_USER}'/first /tmp/first && ls -l /tmp/first" -run_capture 03_second_file "echo second_owned_by_old_user > /tmp/second && sudo chown '${OLD_USER}:${OLD_USER}' /tmp/second && sudo chmod 600 /tmp/second && ls -l /tmp/second" -run_capture 04_sudoers_pkgmgr "sudo sh -c \"printf '%s\\n' '${OLD_USER} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dnf, /usr/bin/yum, /usr/bin/pacman' > /etc/sudoers.d/90-${OLD_USER}-pkgmgr\" && sudo chmod 440 /etc/sudoers.d/90-${OLD_USER}-pkgmgr && sudo visudo -cf /etc/sudoers.d/90-${OLD_USER}-pkgmgr" -run_capture 05_pkgmgr_vs_tcpdump "sudo -n apt-get -v || sudo -n dnf --version || sudo -n yum --version || sudo -n pacman --version; sudo -n tcpdump -D" -run_capture 06_new_user_denied_pkgmgr "sudo -u '${NEW_USER}' sudo -n apt-get -v || sudo -u '${NEW_USER}' sudo -n dnf --version || sudo -u '${NEW_USER}' sudo -n yum --version || sudo -u '${NEW_USER}' sudo -n pacman --version" -run_capture 07_groupadd_and_tcpdump "sudo groupadd -f '${NEW_GROUP}' && sudo usermod -aG '${NEW_GROUP}' '${OLD_USER}' && sudo usermod -aG '${NEW_GROUP}' '${NEW_USER}' && sudo sh -c \"printf '%%%s ALL=(ALL) NOPASSWD: /usr/sbin/tcpdump\\n' '${NEW_GROUP}' > /etc/sudoers.d/91-${NEW_GROUP}-tcpdump\" && sudo chmod 440 /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && sudo visudo -cf /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && getent group '${NEW_GROUP}'" -run_capture 08_tcpdump_both_users "sudo -n tcpdump -D ; sudo -u '${NEW_USER}' bash -lc 'id && sudo -n tcpdump -D'" - -# Task 6 -note_capture 09_crontab_script "cron_script" "#!/usr/bin/env bash\nset -euo pipefail\nTS=\$(date +%F_%H-%M-%S)\nmkdir -p /var/backups/home\ntar -czf /var/backups/home/home_backup_\${TS}.tar.gz \"$HOME\"" -run_capture 10_crontab_install "echo '0 0 * * 0 root /usr/local/bin/weekly_home_backup.sh' | sudo tee -a /etc/crontab && tail -n 5 /etc/crontab" - -# Task 7 -run_capture 11_start_stress "nohup bash -lc 'while :; do :; done' >/tmp/cpu_stress.log 2>&1 & echo $! > /tmp/stress.pid; cat /tmp/stress.pid" -run_capture 12_find_pid "ps -eo pid,ppid,user,cmd | grep -E 'while :; do :; done|cpu_stress' | grep -v grep" -run_capture 13_top_usage "PID=\$(cat /tmp/stress.pid); top -b -n 1 -p \"$PID\"" -run_capture 14_ps_before_kill "ps -fp \"$(cat /tmp/stress.pid)\"" -run_capture 15_sigterm_then_kill "PID=\$(cat /tmp/stress.pid); kill -TERM \"$PID\"; sleep 1; ps -p \"$PID\" || true; if ps -p \"$PID\" >/dev/null; then kill -KILL \"$PID\"; fi" -run_capture 16_ps_after_kill "ps -p \"$(cat /tmp/stress.pid)\" || true" - -# Task 8 -run_capture 17_journal_last20 "journalctl -n 20 --no-pager" -run_capture 18_ssh_logs "journalctl -u ssh -u sshd --no-pager | tail -n 50" -run_capture 19_enable_ssh_service "sudo systemctl enable ssh || sudo systemctl enable sshd; sudo systemctl restart ssh || sudo systemctl restart sshd; systemctl is-enabled ssh || systemctl is-enabled sshd" -run_capture 20_last_hour "journalctl --since '1 hour ago' --no-pager | tail -n 80" -run_capture 21_last_boot_to_file "journalctl -b --no-pager > '$ROOT/last_boot.log' && wc -l '$ROOT/last_boot.log' && tail -n 20 '$ROOT/last_boot.log'" -run_capture 22_errors_in_boot "grep -i 'error' '$ROOT/last_boot.log' | tail -n 50 || true" - -cat > "$ROOT/report.json" <"$LOG_DIR/${key}.txt" 2>&1 || true + python3 scripts/text_to_png.py "$LOG_DIR/${key}.txt" "$IMG_DIR/${key}.png" --title "$key" +} + +run_capture 01_useradd "sudo useradd -m -s /bin/bash '${NEW_USER}' && id '${NEW_USER}' && getent passwd '${NEW_USER}'" +run_capture 02_first_file "sudo -u '${NEW_USER}' bash -lc 'echo first_owned_by_new_user > ~/first' && sudo install -o '${NEW_USER}' -g '${NEW_USER}' -m 600 ~'${NEW_USER}'/first /tmp/first && ls -l /tmp/first" +run_capture 03_second_file "echo second_owned_by_old_user > /tmp/second && sudo chown '${OLD_USER}:${OLD_USER}' /tmp/second && sudo chmod 600 /tmp/second && ls -l /tmp/second" +run_capture 04_sudoers_pkgmgr "sudo sh -c \"printf '%s\\n' '${OLD_USER} ALL=(ALL) NOPASSWD: /usr/bin/apt, /usr/bin/apt-get, /usr/bin/dnf, /usr/bin/yum, /usr/bin/pacman' > /etc/sudoers.d/90-${OLD_USER}-pkgmgr\" && sudo chmod 440 /etc/sudoers.d/90-${OLD_USER}-pkgmgr && sudo visudo -cf /etc/sudoers.d/90-${OLD_USER}-pkgmgr" +run_capture 05_pkgmgr_vs_tcpdump "sudo -n apt-get -v || sudo -n dnf --version || sudo -n yum --version || sudo -n pacman --version; sudo -n tcpdump -D" +run_capture 06_new_user_denied_pkgmgr "sudo -u '${NEW_USER}' sudo -n apt-get -v || sudo -u '${NEW_USER}' sudo -n dnf --version || sudo -u '${NEW_USER}' sudo -n yum --version || sudo -u '${NEW_USER}' sudo -n pacman --version" +run_capture 07_groupadd_and_tcpdump "sudo groupadd -f '${NEW_GROUP}' && sudo usermod -aG '${NEW_GROUP}' '${OLD_USER}' && sudo usermod -aG '${NEW_GROUP}' '${NEW_USER}' && sudo sh -c \"printf '%%%s ALL=(ALL) NOPASSWD: /usr/sbin/tcpdump\\n' '${NEW_GROUP}' > /etc/sudoers.d/91-${NEW_GROUP}-tcpdump\" && sudo chmod 440 /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && sudo visudo -cf /etc/sudoers.d/91-${NEW_GROUP}-tcpdump && getent group '${NEW_GROUP}'" +run_capture 08_tcpdump_both_users "sudo -n tcpdump -D ; sudo -u '${NEW_USER}' bash -lc 'id && sudo -n tcpdump -D'" + +cat > "$ROOT/report.json" <"$LOG_DIR/$k.txt" 2>&1 || true; python3 scripts/text_to_png.py "$LOG_DIR/$k.txt" "$IMG_DIR/$k.png" --title "$k"; } +note_capture(){ local k="$1" t="$2" b="$3"; printf '%s\n' "$b" >"$LOG_DIR/$k.txt"; python3 scripts/text_to_png.py "$LOG_DIR/$k.txt" "$IMG_DIR/$k.png" --title "$t"; } +note_capture 01_script cron_script "#!/usr/bin/env bash +set -euo pipefail +TS=\$(date +%F_%H-%M-%S) +mkdir -p /var/backups/home +tar -czf /var/backups/home/home_backup_\${TS}.tar.gz \"$HOME\"" +run_capture 02_install "sudo install -m 755 /dev/stdin /usr/local/bin/weekly_home_backup.sh <<'EOS' +#!/usr/bin/env bash +set -euo pipefail +TS=\$(date +%F_%H-%M-%S) +mkdir -p /var/backups/home +tar -czf /var/backups/home/home_backup_\${TS}.tar.gz /home +EOS +sudo sed -i '/weekly_home_backup.sh/d' /etc/crontab; echo '0 0 * * 0 root /usr/local/bin/weekly_home_backup.sh' | sudo tee -a /etc/crontab; tail -n 8 /etc/crontab" +cat > "$ROOT/report.json" <"$LOG_DIR/$k.txt" 2>&1 || true; python3 scripts/text_to_png.py "$LOG_DIR/$k.txt" "$IMG_DIR/$k.png" --title "$k"; } +run_capture 01_start "nohup bash -lc 'while :; do :; done' >/tmp/cpu_stress.log 2>&1 & echo \$! > /tmp/stress.pid; cat /tmp/stress.pid" +run_capture 02_find_pid "ps -eo pid,ppid,user,cmd | grep -E 'while :; do :; done|cpu_stress' | grep -v grep" +run_capture 03_top "PID=\$(cat /tmp/stress.pid); top -b -n 1 -p \"\$PID\"" +run_capture 04_ps_before "ps -fp \"\$(cat /tmp/stress.pid)\"" +run_capture 05_term_kill "PID=\$(cat /tmp/stress.pid); kill -TERM \"\$PID\"; sleep 1; ps -p \"\$PID\" || true; if ps -p \"\$PID\" >/dev/null; then kill -KILL \"\$PID\"; fi" +run_capture 06_ps_after "ps -p \"\$(cat /tmp/stress.pid)\" || true" +cat > "$ROOT/report.json" <"$LOG_DIR/$k.txt" 2>&1 || true; python3 scripts/text_to_png.py "$LOG_DIR/$k.txt" "$IMG_DIR/$k.png" --title "$k"; } +run_capture 01_last20 "journalctl -n 20 --no-pager" +run_capture 02_ssh "journalctl -u ssh -u sshd --no-pager | tail -n 50" +run_capture 03_enable_ssh "sudo systemctl enable ssh || sudo systemctl enable sshd; sudo systemctl restart ssh || sudo systemctl restart sshd; systemctl is-enabled ssh || systemctl is-enabled sshd" +run_capture 04_hour "journalctl --since '1 hour ago' --no-pager | tail -n 80" +run_capture 05_boot_file "journalctl -b --no-pager > '$ROOT/last_boot.log' && wc -l '$ROOT/last_boot.log' && tail -n 20 '$ROOT/last_boot.log'" +run_capture 06_errors "grep -i 'error' '$ROOT/last_boot.log' | tail -n 50 || true" +cat > "$ROOT/report.json" <