Files
ssh-monitor/update_ssh_monitor.sh
T
PapaTramp 4cb158f30a fix: always sync sac-client.sh in update_ssh_monitor
Copy sac-client.sh on every git pull by checksum, not only when
ssh-monitor binary changes. Restart service when either file updates.
2026-05-27 12:20:14 +10:00

436 lines
20 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# === Конфигурация скрипта ===
UPDATE_DIR="/opt/scripts/update"
SCRIPT_NAME="ssh-monitor"
REPO_URL="https://git.kalinamall.ru/PapaTramp/ssh-monitor.git"
LOCAL_SCRIPT_PATH="/usr/local/bin/ssh-monitor"
LOG_FILE="/var/log/update_script.log"
# Режим отладки: 1 = включён, 0 = выключен
DEBUG=0
# === Список переменных, объявленных в скрипте (для логирования при DEBUG=1) ===
SCRIPT_VARS=("UPDATE_DIR" "SCRIPT_NAME" "REPO_URL" "LOCAL_SCRIPT_PATH" "LOG_FILE" "DEBUG" "REMOTE_SCRIPT_PATH" "LOCAL_CHECKSUM" "REMOTE_CHECKSUM")
# === Функции ===
# Основная функция логирования
log_message() {
local level="$1"
local message="$2"
local timestamp
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
# Создаём лог-файл и директорию при необходимости
if [ ! -f "$LOG_FILE" ]; then
mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
touch "$LOG_FILE" 2>/dev/null
fi
if [ "$level" = "INFO" ]; then
echo "$timestamp INFO: $message" >> "$LOG_FILE"
elif [ "$level" = "DEBUG" ] && [ "$DEBUG" -eq 1 ]; then
echo "$timestamp DEBUG: $message" >> "$LOG_FILE"
elif [ "$level" = "ERROR" ]; then
echo "$timestamp ERROR: $message" >> "$LOG_FILE"
fi
}
# Функция для логирования шагов с комментариями (работает только при DEBUG=1)
debug_step() {
if [ "$DEBUG" -eq 1 ]; then
log_message "DEBUG" "# $1"
fi
}
# Функция для логирования только объявленных в скрипте переменных
log_script_vars() {
if [ "$DEBUG" -eq 1 ]; then
log_message "DEBUG" "=== Значения переменных скрипта ==="
for var_name in "${SCRIPT_VARS[@]}"; do
local var_value="${!var_name}"
log_message "DEBUG" "$var_name='$var_value'"
done
log_message "DEBUG" "=== Конец списка переменных ==="
fi
}
# === КРИТИЧЕСКАЯ ФУНКЦИЯ: Проверка и установка ipset ===
ensure_ipset_installed() {
debug_step "Проверяем наличие ipset в системе"
# Проверяем, установлен ли ipset
if ! command -v ipset &>/dev/null; then
log_message "INFO" "ipset not found. Attempting silent installation..."
debug_step "ipset не обнаружен, начинаем установку в silent режиме"
# Определяем пакетный менеджер и устанавливаем ipset
if command -v apt-get &>/dev/null || command -v apt &>/dev/null; then
debug_step "Обнаружен Debian/Ubuntu, используем apt"
log_message "INFO" "Detected apt package manager, installing ipset..."
# Тихая установка через apt
if apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq ipset >/dev/null 2>&1; then
log_message "INFO" "ipset installed successfully via apt"
debug_step "ipset успешно установлен через apt"
else
log_message "ERROR" "Failed to install ipset via apt"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить ipset через apt"
return 1
fi
elif command -v yum &>/dev/null; then
debug_step "Обнаружен RHEL/CentOS, используем yum"
log_message "INFO" "Detected yum package manager, installing ipset..."
if yum install -y -q ipset >/dev/null 2>&1; then
log_message "INFO" "ipset installed successfully via yum"
debug_step "ipset успешно установлен через yum"
else
log_message "ERROR" "Failed to install ipset via yum"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить ipset через yum"
return 1
fi
elif command -v dnf &>/dev/null; then
debug_step "Обнаружен Fedora/RHEL 8+, используем dnf"
log_message "INFO" "Detected dnf package manager, installing ipset..."
if dnf install -y -q ipset >/dev/null 2>&1; then
log_message "INFO" "ipset installed successfully via dnf"
debug_step "ipset успешно установлен через dnf"
else
log_message "ERROR" "Failed to install ipset via dnf"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить ipset через dnf"
return 1
fi
elif command -v pacman &>/dev/null; then
debug_step "Обнаружен Arch Linux, используем pacman"
log_message "INFO" "Detected pacman package manager, installing ipset..."
if pacman -S --noconfirm --quiet ipset >/dev/null 2>&1; then
log_message "INFO" "ipset installed successfully via pacman"
debug_step "ipset успешно установлен через pacman"
else
log_message "ERROR" "Failed to install ipset via pacman"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить ipset через pacman"
return 1
fi
elif command -v zypper &>/dev/null; then
debug_step "Обнаружен openSUSE, используем zypper"
log_message "INFO" "Detected zypper package manager, installing ipset..."
if zypper --quiet --non-interactive install ipset >/dev/null 2>&1; then
log_message "INFO" "ipset installed successfully via zypper"
debug_step "ipset успешно установлен через zypper"
else
log_message "ERROR" "Failed to install ipset via zypper"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить ipset через zypper"
return 1
fi
else
log_message "ERROR" "Unsupported package manager. Cannot install ipset"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: Пакетный менеджер не распознан"
return 1
fi
# Проверяем, что ipset действительно установился
if ! command -v ipset &>/dev/null; then
log_message "ERROR" "ipset installation verification failed"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
debug_step "КРИТИЧЕСКАЯ ОШИБКА: ipset не обнаружен после установки"
return 1
fi
else
debug_step "ipset уже установлен в системе"
log_message "INFO" "ipset is already installed"
# Проверяем версию ipset (информационно)
if [ "$DEBUG" -eq 1 ]; then
local ipset_version=$(ipset --version 2>&1)
log_message "DEBUG" "ipset version: $ipset_version"
fi
fi
log_message "INFO" "ipset check/installation completed successfully"
return 0
}
# === Гарантированное создание директорий ===
ensure_directories() {
debug_step "Проверяем и создаём необходимые директории"
# Создаём UPDATE_DIR если не существует
if [ ! -d "$UPDATE_DIR" ]; then
log_message "INFO" "Creating directory: $UPDATE_DIR"
debug_step "Директория $UPDATE_DIR не существует, создаём"
if mkdir -p "$UPDATE_DIR" 2>/dev/null; then
log_message "INFO" "Directory created successfully"
else
log_message "INFO" "ERROR: Cannot create directory $UPDATE_DIR"
return 1
fi
else
debug_step "Директория $UPDATE_DIR уже существует"
fi
# Проверяем права на запись
if [ ! -w "$UPDATE_DIR" ]; then
log_message "INFO" "WARNING: Directory $UPDATE_DIR is not writable"
debug_step "ПРЕДУПРЕЖДЕНИЕ: Нет прав на запись в $UPDATE_DIR"
return 1
fi
return 0
}
# Путь к sac-client.sh в клоне репозитория (корень git)
find_sac_client_remote() {
local candidates=(
"$UPDATE_DIR/$SCRIPT_NAME/sac-client.sh"
)
local p
for p in "${candidates[@]}"; do
if [ -f "$p" ]; then
printf '%s\n' "$p"
return 0
fi
done
return 1
}
# Копирует sac-client.sh рядом с LOCAL_SCRIPT_PATH (всегда после git pull, по checksum).
# Устанавливает SAC_CLIENT_CHANGED=1 при фактическом обновлении файла.
sync_sac_client() {
SAC_CLIENT_CHANGED=0
local remote local_dst local_sum remote_sum
remote=$(find_sac_client_remote) || {
log_message "INFO" "WARNING: sac-client.sh not found in repository clone"
debug_step "sac-client.sh не найден в клоне репозитория"
return 0
}
local_dst="${LOCAL_SCRIPT_PATH%/*}/sac-client.sh"
remote_sum=$(sha256sum "$remote" 2>/dev/null | awk '{print $1}')
if [ -z "$remote_sum" ]; then
log_message "INFO" "ERROR: Failed to checksum remote sac-client.sh"
return 1
fi
if [ -f "$local_dst" ]; then
local_sum=$(sha256sum "$local_dst" 2>/dev/null | awk '{print $1}')
if [ "$local_sum" = "$remote_sum" ]; then
log_message "INFO" "sac-client.sh checksums match. No update needed."
debug_step "sac-client.sh без изменений"
return 0
fi
else
log_message "INFO" "Local sac-client.sh not found. Will install."
debug_step "Локальный sac-client.sh отсутствует, будет установлен"
fi
debug_step "Копируем sac-client.sh в $local_dst"
if ! cp "$remote" "$local_dst"; then
log_message "INFO" "ERROR: Failed to copy sac-client.sh"
return 1
fi
chmod 644 "$local_dst"
SAC_CLIENT_CHANGED=1
log_message "INFO" "sac-client.sh updated."
return 0
}
restart_ssh_monitor_service() {
debug_step "Перезапускаем сервис ssh-monitor.service"
if command -v systemctl &>/dev/null; then
if systemctl restart ssh-monitor.service 2>/dev/null; then
log_message "INFO" "Service restarted successfully."
debug_step "Сервис успешно перезапущен"
else
log_message "INFO" "WARNING: Failed to restart service (may not be running)"
debug_step "ПРЕДУПРЕЖДЕНИЕ: Не удалось перезапустить сервис"
fi
else
log_message "INFO" "systemctl not found, service not restarted"
debug_step "systemctl не найден, сервис не перезапущен"
fi
}
# Функция для обновления скрипта
update_script() {
log_message "INFO" "Starting script update..."
debug_step "Запуск функции обновления скрипта"
# Гарантируем наличие директории
debug_step "Гарантируем наличие директории для работы"
if ! ensure_directories; then
return 1
fi
# Переходим в директорию для клона репозитория
debug_step "Переходим в директорию для клона репозитория: $UPDATE_DIR"
if ! cd "$UPDATE_DIR" 2>/dev/null; then
log_message "INFO" "ERROR: Cannot cd to $UPDATE_DIR"
return 1
fi
# Клонируем или обновляем репозиторий
debug_step "Проверяем наличие репозитория и обновляем/клонируем"
if [ -d "$SCRIPT_NAME" ]; then
log_message "INFO" "Repository exists, pulling updates..."
debug_step "Выполняем git pull origin main"
if ! git -C "$SCRIPT_NAME" pull origin main >> "$LOG_FILE" 2>&1; then
log_message "INFO" "WARNING: git pull failed, continuing with existing local copy..."
fi
else
log_message "INFO" "Cloning repository from $REPO_URL"
debug_step "Выполняем git clone $REPO_URL $SCRIPT_NAME"
if ! git clone "$REPO_URL" "$SCRIPT_NAME" >> "$LOG_FILE" 2>&1; then
log_message "INFO" "ERROR: git clone failed"
return 1
fi
fi
# Формируем путь к скрипту в репозитории (основной и альтернативный)
REMOTE_SCRIPT_PATH="$UPDATE_DIR/$SCRIPT_NAME/src/branch/main/$SCRIPT_NAME"
ALT_REMOTE_PATH="$UPDATE_DIR/$SCRIPT_NAME/$SCRIPT_NAME"
debug_step "Основной путь к скрипту: $REMOTE_SCRIPT_PATH"
debug_step "Альтернативный путь к скрипту: $ALT_REMOTE_PATH"
# Определяем реальный путь к скрипту
local actual_script_path=""
if [ -f "$REMOTE_SCRIPT_PATH" ]; then
actual_script_path="$REMOTE_SCRIPT_PATH"
debug_step "Скрипт найден по основному пути"
elif [ -f "$ALT_REMOTE_PATH" ]; then
actual_script_path="$ALT_REMOTE_PATH"
log_message "INFO" "Script found at alternative path"
debug_step "Скрипт найден по альтернативному пути"
fi
# sac-client.sh — отдельно от ssh-monitor: синхронизируем всегда (нужен для UseSAC/--check-sac)
SAC_CLIENT_CHANGED=0
if ! sync_sac_client; then
return 1
fi
local script_changed=0
# Проверяем наличие файла скрипта в репозитории
if [ -n "$actual_script_path" ] && [ -f "$actual_script_path" ]; then
log_message "INFO" "Remote script found. Comparing checksums..."
debug_step "Проверяем наличие файла скрипта в репозитории"
# Проверяем существование локального скрипта
if [ ! -f "$LOCAL_SCRIPT_PATH" ]; then
log_message "INFO" "Local script not found. Will install new one."
LOCAL_CHECKSUM=""
debug_step "Локальный скрипт отсутствует, будет установлен новый"
else
debug_step "Вычисляем контрольную сумму локального скрипта"
LOCAL_CHECKSUM=$(sha256sum "$LOCAL_SCRIPT_PATH" 2>/dev/null | awk '{print $1}')
fi
debug_step "Вычисляем контрольную сумму удалённого скрипта"
REMOTE_CHECKSUM=$(sha256sum "$actual_script_path" 2>/dev/null | awk '{print $1}')
# Сравниваем контрольные суммы
debug_step "Сравниваем контрольные суммы"
if [ "$LOCAL_CHECKSUM" != "$REMOTE_CHECKSUM" ]; then
log_message "INFO" "Checksums differ. Updating $LOCAL_SCRIPT_PATH..."
debug_step "Контрольные суммы различаются, начинаем обновление"
# Создаем бекап текущего скрипта если он существует
if [ -f "$LOCAL_SCRIPT_PATH" ]; then
debug_step "Создаем резервную копию текущего скрипта"
cp "$LOCAL_SCRIPT_PATH" "${LOCAL_SCRIPT_PATH}.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null
fi
debug_step "Копируем новый скрипт в $LOCAL_SCRIPT_PATH"
if ! cp "$actual_script_path" "$LOCAL_SCRIPT_PATH"; then
log_message "INFO" "ERROR: Failed to copy script"
return 1
fi
debug_step "Делаем скрипт исполняемым"
chmod +x "$LOCAL_SCRIPT_PATH"
script_changed=1
log_message "INFO" "Script updated successfully."
debug_step "Скрипт успешно обновлен"
else
log_message "INFO" "Checksums match. No update needed."
debug_step "Контрольные суммы совпадают, обновление не требуется"
fi
else
log_message "INFO" "No remote script found. No update needed."
debug_step "Удаленный скрипт не найден ни по одному из путей"
# Информация для отладки
if [ "$DEBUG" -eq 1 ]; then
debug_step "Содержимое директории репозитория:"
if [ -d "$UPDATE_DIR/$SCRIPT_NAME" ]; then
find "$UPDATE_DIR/$SCRIPT_NAME" -type f -name "$SCRIPT_NAME" 2>/dev/null | while read -r found_file; do
log_message "DEBUG" "Найден файл: $found_file"
done
fi
fi
fi
if [ "$script_changed" -eq 1 ] || [ "${SAC_CLIENT_CHANGED:-0}" -eq 1 ]; then
restart_ssh_monitor_service
fi
# Логируем значения переменных скрипта при DEBUG=1
log_script_vars
return 0
}
# === Основной блок выполнения ===
log_message "INFO" "=== Script update started ==="
debug_step "Инициализация скрипта обновления"
# 1. Гарантируем создание необходимых директорий
if ! ensure_directories; then
log_message "INFO" "ERROR: Failed to ensure directories. Exiting."
exit 1
fi
# 2. КРИТИЧЕСКИ ВАЖНО: Проверяем и устанавливаем ipset
debug_step "Начинаем проверку и установку ipset (КРИТИЧЕСКИЙ ШАГ)"
if ! ensure_ipset_installed; then
log_message "ERROR" "CRITICAL: ipset installation or verification failed"
log_message "ERROR" "Работа обновленного скрипта невозможна без установленного ipset"
echo "ERROR: Работа обновленного скрипта невозможна без установленного ipset" >&2
exit 1
fi
# 3. Выполняем функцию обновления
update_script
exit_code=$?
if [ $exit_code -eq 0 ]; then
log_message "INFO" "=== Script update completed successfully ==="
debug_step "Скрипт успешно завершил работу"
else
log_message "INFO" "=== Script update completed with errors (exit code: $exit_code) ==="
debug_step "Скрипт завершил работу с ошибками"
fi
# Выход с кодом завершения
exit $exit_code