diff --git a/.github/workflows/bash-syntax.yml b/.github/workflows/bash-syntax.yml index fcfea4b..a3d8776 100644 --- a/.github/workflows/bash-syntax.yml +++ b/.github/workflows/bash-syntax.yml @@ -62,3 +62,6 @@ jobs: - name: Test step 3 socket activation runtime directory run: bash tests/test-step3-runtime-directory.sh + + - name: Test step 2 UFW boot readiness + run: bash tests/test-step2-ufw-boot.sh diff --git a/ochenstarik-server-2.sh b/ochenstarik-server-2.sh index b58492a..aecdb19 100755 --- a/ochenstarik-server-2.sh +++ b/ochenstarik-server-2.sh @@ -17,6 +17,20 @@ require_command() { command -v "$1" >/dev/null 2>&1 || die "Не найдена команда: $1" } +ensure_ufw_boot_service() { + local state + state="$(systemctl is-enabled ufw.service 2>/dev/null || true)" + case "$state" in + masked|masked-runtime) + die "ufw.service замаскирована: сначала проверьте причину маски и включите службу вручную. Правила не изменены." + ;; + esac + systemctl enable ufw.service \ + || die "Не удалось включить автозапуск ufw.service; правила не изменены" + systemctl is-enabled --quiet ufw.service \ + || die "Автозапуск ufw.service не подтверждён; правила не изменены" +} + is_valid_port() { local port="$1" [[ "$port" =~ ^[0-9]{1,5}$ ]] || return 1 @@ -403,9 +417,10 @@ if [[ "$ACTION" == install ]]; then apt-get install -y \ sudo ufw fail2ban curl ca-certificates openssl openssh-server logrotate iproute2 - for command_name in ip sed sysctl ufw; do + for command_name in ip sed sysctl ufw systemctl; do require_command "$command_name" done + ensure_ufw_boot_service install -d -m 700 -o root -g root "$CONFIG_DIR" load_managed_rules PREVIOUS_MANAGED_RULES=("${MANAGED_RULES[@]}") @@ -415,9 +430,10 @@ if [[ "$ACTION" == install ]]; then save_configuration log "SSH-порт ${SSH_PORT} сохранён для этапа 3" else - for command_name in ip sed sysctl ufw; do + for command_name in ip sed sysctl ufw systemctl; do require_command "$command_name" done + ensure_ufw_boot_service install -d -m 700 -o root -g root "$CONFIG_DIR" SSH_PORT="$(read_saved_value "$SSH_PORT_CONFIG" SSH_PORT)" is_valid_port "$SSH_PORT" || SSH_PORT="$DEFAULT_SSH_PORT" diff --git a/tests/test-step2-ufw-boot.sh b/tests/test-step2-ufw-boot.sh new file mode 100644 index 0000000..32005b2 --- /dev/null +++ b/tests/test-step2-ufw-boot.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -Eeuo pipefail +cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." +source <(awk '/^\[\[ "\$EUID"/{exit} {print}' ochenstarik-server-2.sh) +test_dir="$(mktemp -d)" +trap 'rm -rf -- "$test_dir"' EXIT + +systemctl() { + printf '%s\n' "$*" >> "$test_dir/calls" + case "$*" in + 'is-enabled ufw.service') cat "$test_dir/state"; return 1 ;; + 'enable ufw.service') + [[ "$scenario" != enable-failed ]] || return 1 + [[ "$scenario" == verify-failed ]] || printf enabled > "$test_dir/state" + ;; + 'is-enabled --quiet ufw.service') [[ "$(cat "$test_dir/state")" == enabled ]] ;; + *) return 99 ;; + esac +} + +for state in masked masked-runtime; do + printf '%s' "$state" > "$test_dir/state" + : > "$test_dir/calls" + scenario=masked + if (ensure_ufw_boot_service) > "$test_dir/output" 2>&1; then exit 1; fi + ! grep -q '^enable ' "$test_dir/calls" + [[ "$(cat "$test_dir/state")" == "$state" ]] +done + +for scenario in disabled enabled enable-failed verify-failed; do + printf '%s' "$scenario" > "$test_dir/state" + : > "$test_dir/calls" + if [[ "$scenario" == *-failed ]]; then + if (ensure_ufw_boot_service) > "$test_dir/output" 2>&1; then exit 1; fi + else + ensure_ufw_boot_service + [[ "$(cat "$test_dir/state")" == enabled ]] + grep -qx 'is-enabled --quiet ufw.service' "$test_dir/calls" + fi +done +printf 'Step 2 UFW boot readiness tests passed.\n'