feat: first deploy via update_ssh_monitor.sh --deploy

Add first_deploy.sh wrapper and --deploy mode: git clone, install
ssh-monitor and sac-client, optional config/systemd/updater copy,
console summary and config variable checklist.
This commit is contained in:
PTah
2026-05-27 12:36:02 +10:00
parent 1928e68088
commit f1c4adcc13
5 changed files with 327 additions and 85 deletions
+13 -1
View File
@@ -165,12 +165,24 @@ make dist
## Установка скрипта, конфигурации и systemd
### Автоматическая установка (рекомендуется)
```bash
sudo ./first_deploy.sh
# или: sudo ./update_ssh_monitor.sh --deploy
```
См. [docs/auto-update.ru.md](docs/auto-update.ru.md) — клон из git, копирование в `/usr/local/bin/`, `sac-client.sh`, пример `/etc/ssh-monitor.conf`, unit systemd.
### Ручная установка
1. Скопируйте скрипт в постоянное место:
- `sudo install -m 750 ./ssh-monitor /usr/local/bin/ssh-monitor`
- `sudo install -m 644 ./sac-client.sh /usr/local/bin/sac-client.sh`
2. Убедитесь, что конфиг создан (см. раздел **«Конфигурация»** выше):
- `sudo cp ./ssh-monitor.conf.example /etc/ssh-monitor.conf`
- `sudo chmod 600 /etc/ssh-monitor.conf`
3. Создайте unit-файл `/etc/systemd/system/ssh-monitor.service`:
3. Создайте unit-файл `/etc/systemd/system/ssh-monitor.service` (или `sudo cp ssh-monitor.service.example ...`):
```ini
[Unit]
+23 -1
View File
@@ -52,7 +52,29 @@ flowchart LR
- **git** и сетевой доступ к репозиторию (SSH-ключ или HTTPS);
- ветка в скрипте жёстко **`main`** — при другой ветке измените `git pull` в файле.
## Установка
## Первичная установка (--deploy)
Один сценарий для нового сервера (клон git, `ssh-monitor`, `sac-client.sh`, пример конфига, systemd unit):
```bash
cd /path/to/ssh-monitor # или curl/clone репозитория
sudo ./first_deploy.sh
# то же самое:
sudo ./update_ssh_monitor.sh --deploy
```
Скрипт выводит в консоль, **куда что скопировано**, и список переменных для **`/etc/ssh-monitor.conf`**. Существующий конфиг **не перезаписывается**.
После установки отредактируйте конфиг и проверьте:
```bash
sudo nano /etc/ssh-monitor.conf
sudo /usr/local/bin/ssh-monitor --check-config
sudo /usr/local/bin/ssh-monitor --check-sac
sudo systemctl restart ssh-monitor.service
```
## Установка updater (для последующих обновлений)
```bash
sudo mkdir -p /opt/scripts
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
# Первичная установка ssh-monitor — обёртка над update_ssh_monitor.sh --deploy
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "$SCRIPT_DIR/update_ssh_monitor.sh" --deploy "$@"
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=SSH Monitor with Telegram alerts and SAC integration
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/ssh-monitor
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
Regular → Executable
+264 -75
View File
@@ -9,12 +9,89 @@ LOG_FILE="/var/log/update_script.log"
# Режим отладки: 1 = включён, 0 = выключен
DEBUG=0
DEPLOY_MODE=0
CONFIG_FILE="/etc/ssh-monitor.conf"
DEPLOY_INSTALL_PATH="/opt/scripts/update_ssh_monitor.sh"
SAC_SPOOL_DIR_DEFAULT="/var/lib/ssh-monitor/sac-spool"
SYSTEMD_UNIT_PATH="/etc/systemd/system/ssh-monitor.service"
# === Список переменных, объявленных в скрипте (для логирования при DEBUG=1) ===
SCRIPT_VARS=("UPDATE_DIR" "SCRIPT_NAME" "REPO_URL" "LOCAL_SCRIPT_PATH" "LOG_FILE" "DEBUG" "REMOTE_SCRIPT_PATH" "LOCAL_CHECKSUM" "REMOTE_CHECKSUM")
# === Функции ===
say_step() {
printf '\n==> %s\n' "$1"
}
say_ok() {
printf '[OK] %s\n' "$1"
log_message "INFO" "$1"
}
say_warn() {
printf '[WARN] %s\n' "$1"
log_message "INFO" "WARNING: $1"
}
say_info() {
printf ' %s\n' "$1"
}
show_help() {
cat <<'EOF'
update_ssh_monitor.sh — обновление или первичная установка ssh-monitor
Использование:
sudo ./update_ssh_monitor.sh обновление (git pull + checksum)
sudo ./update_ssh_monitor.sh --deploy первичная установка / полный deploy
sudo ./first_deploy.sh то же, что --deploy
Опции:
--deploy установить ssh-monitor, sac-client, пример конфига, systemd unit
--debug подробный лог в /var/log/update_script.log
-h, --help эта справка
После --deploy отредактируйте /etc/ssh-monitor.conf (Telegram и/или SAC).
EOF
}
parse_command_line() {
DEPLOY_MODE=0
while [ $# -gt 0 ]; do
case "$1" in
--deploy)
DEPLOY_MODE=1
shift
;;
--debug)
DEBUG=1
shift
;;
-h | --help)
show_help
exit 0
;;
*)
printf 'Неизвестная опция: %s\n' "$1" >&2
show_help >&2
exit 2
;;
esac
done
}
deploy_require_root() {
if [ "$DEPLOY_MODE" -ne 1 ]; then
return 0
fi
if [ "$(id -u)" -ne 0 ]; then
printf 'ERROR: режим --deploy нужно запускать от root (sudo)\n' >&2
exit 1
fi
}
# Основная функция логирования
log_message() {
local level="$1"
@@ -228,14 +305,14 @@ sync_sac_client() {
return 1
fi
if [ -f "$local_dst" ]; then
if [ "$DEPLOY_MODE" -ne 1 ] && [ -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
elif [ ! -f "$local_dst" ]; then
log_message "INFO" "Local sac-client.sh not found. Will install."
debug_step "Локальный sac-client.sh отсутствует, будет установлен"
fi
@@ -248,9 +325,139 @@ sync_sac_client() {
chmod 644 "$local_dst"
SAC_CLIENT_CHANGED=1
log_message "INFO" "sac-client.sh updated."
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_ok "sac-client.sh → $local_dst"
fi
return 0
}
find_remote_ssh_monitor_path() {
REMOTE_SCRIPT_PATH="$UPDATE_DIR/$SCRIPT_NAME/src/branch/main/$SCRIPT_NAME"
ALT_REMOTE_PATH="$UPDATE_DIR/$SCRIPT_NAME/$SCRIPT_NAME"
if [ -f "$REMOTE_SCRIPT_PATH" ]; then
printf '%s\n' "$REMOTE_SCRIPT_PATH"
return 0
fi
if [ -f "$ALT_REMOTE_PATH" ]; then
log_message "INFO" "Script found at alternative path"
printf '%s\n' "$ALT_REMOTE_PATH"
return 0
fi
return 1
}
install_ssh_monitor_from() {
local actual_script_path="$1"
if [ -f "$LOCAL_SCRIPT_PATH" ]; then
cp "$LOCAL_SCRIPT_PATH" "${LOCAL_SCRIPT_PATH}.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
fi
if ! cp "$actual_script_path" "$LOCAL_SCRIPT_PATH"; then
log_message "INFO" "ERROR: Failed to copy script"
return 1
fi
chmod +x "$LOCAL_SCRIPT_PATH"
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_ok "ssh-monitor → $LOCAL_SCRIPT_PATH"
fi
return 0
}
deploy_prepare_dirs() {
say_step "Каталоги"
mkdir -p "$UPDATE_DIR" /opt/scripts /var/log "$SAC_SPOOL_DIR_DEFAULT" /var/lib/ssh-monitor 2>/dev/null || true
say_ok "UPDATE_DIR=$UPDATE_DIR, spool=$SAC_SPOOL_DIR_DEFAULT"
}
deploy_install_config() {
local example="$UPDATE_DIR/$SCRIPT_NAME/ssh-monitor.conf.example"
say_step "Конфигурация"
if [ -f "$CONFIG_FILE" ]; then
say_info "Уже существует: $CONFIG_FILE (не перезаписываем)"
return 0
fi
if [ ! -f "$example" ]; then
say_warn "Пример конфига не найден в клоне: $example"
return 0
fi
cp "$example" "$CONFIG_FILE"
chmod 600 "$CONFIG_FILE"
say_ok "Создан $CONFIG_FILE из ssh-monitor.conf.example"
}
deploy_install_systemd() {
local example="$UPDATE_DIR/$SCRIPT_NAME/ssh-monitor.service.example"
say_step "systemd"
if [ -f "$SYSTEMD_UNIT_PATH" ]; then
say_info "Уже существует: $SYSTEMD_UNIT_PATH"
return 0
fi
if [ ! -f "$example" ]; then
say_warn "Unit example не найден: $example"
return 0
fi
cp "$example" "$SYSTEMD_UNIT_PATH"
if command -v systemctl &>/dev/null; then
systemctl daemon-reload 2>/dev/null || true
systemctl enable ssh-monitor.service 2>/dev/null || true
say_ok "Установлен $SYSTEMD_UNIT_PATH (enable ssh-monitor.service)"
else
say_ok "Скопирован $SYSTEMD_UNIT_PATH (systemctl не найден)"
fi
}
deploy_install_self() {
local self_path
self_path="$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || printf '%s' "$0")"
say_step "Скрипт автообновления"
if [ "$self_path" = "$DEPLOY_INSTALL_PATH" ]; then
say_info "Уже запущен из $DEPLOY_INSTALL_PATH"
return 0
fi
mkdir -p "$(dirname "$DEPLOY_INSTALL_PATH")"
cp "$self_path" "$DEPLOY_INSTALL_PATH"
chmod 750 "$DEPLOY_INSTALL_PATH"
say_ok "update_ssh_monitor.sh → $DEPLOY_INSTALL_PATH"
}
print_config_variables_help() {
say_step "Заполните в $CONFIG_FILE"
cat <<'EOF'
Обязательно хотя бы один канал уведомлений ИЛИ SAC:
TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID
— или MAIL_SMTP_HOST, MAIL_FROM, MAIL_TO
Security Alert Center (рекомендуется dual на пилоте):
UseSAC=dual
SAC_URL=https://sac.kalinamall.ru
SAC_API_KEY=<ключ из SAC sac-api.env / bootstrap>
Проверка после правок:
/usr/local/bin/ssh-monitor --check-config
/usr/local/bin/ssh-monitor --check-sac
systemctl restart ssh-monitor.service
systemctl status ssh-monitor.service
EOF
}
print_deploy_summary() {
say_step "Итог установки"
say_info "Монитор: $LOCAL_SCRIPT_PATH"
say_info "SAC client: ${LOCAL_SCRIPT_PATH%/*}/sac-client.sh"
say_info "Конфиг: $CONFIG_FILE"
say_info "Git-клон: $UPDATE_DIR/$SCRIPT_NAME"
say_info "Updater: $DEPLOY_INSTALL_PATH"
say_info "Лог updater: $LOG_FILE"
}
deploy_post_install() {
deploy_prepare_dirs
deploy_install_config
deploy_install_systemd
deploy_install_self
print_deploy_summary
print_config_variables_help
}
restart_ssh_monitor_service() {
debug_step "Перезапускаем сервис ssh-monitor.service"
if command -v systemctl &>/dev/null; then
@@ -302,25 +509,10 @@ update_script() {
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
actual_script_path="$(find_remote_ssh_monitor_path 2>/dev/null || true)"
debug_step "Путь к ssh-monitor в клоне: ${actual_script_path:-не найден}"
# sac-client.sh — отдельно от ssh-monitor: синхронизируем всегда (нужен для UseSAC/--check-sac)
SAC_CLIENT_CHANGED=0
if ! sync_sac_client; then
return 1
@@ -328,68 +520,49 @@ update_script() {
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 "Локальный скрипт отсутствует, будет установлен новый"
if [ "$DEPLOY_MODE" -eq 1 ]; then
log_message "INFO" "Deploy: installing ssh-monitor to $LOCAL_SCRIPT_PATH"
if install_ssh_monitor_from "$actual_script_path"; then
script_changed=1
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"
else
log_message "INFO" "Remote script found. Comparing checksums..."
if [ ! -f "$LOCAL_SCRIPT_PATH" ]; then
LOCAL_CHECKSUM=""
else
LOCAL_CHECKSUM=$(sha256sum "$LOCAL_SCRIPT_PATH" 2>/dev/null | awk '{print $1}')
fi
REMOTE_CHECKSUM=$(sha256sum "$actual_script_path" 2>/dev/null | awk '{print $1}')
if [ "$LOCAL_CHECKSUM" != "$REMOTE_CHECKSUM" ]; then
log_message "INFO" "Checksums differ. Updating $LOCAL_SCRIPT_PATH..."
if install_ssh_monitor_from "$actual_script_path"; then
script_changed=1
log_message "INFO" "Script updated successfully."
debug_step "Скрипт успешно обновлен"
else
return 1
fi
else
log_message "INFO" "Checksums match. No update needed."
debug_step "Контрольные суммы совпадают, обновление не требуется"
fi
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
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_warn "ssh-monitor не найден в клоне $UPDATE_DIR/$SCRIPT_NAME"
fi
if [ "$DEBUG" -eq 1 ] && [ -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
if [ "$DEPLOY_MODE" -eq 1 ]; then
script_changed=1
elif [ "$script_changed" -eq 1 ] || [ "${SAC_CLIENT_CHANGED:-0}" -eq 1 ]; then
restart_ssh_monitor_service
fi
@@ -401,35 +574,51 @@ update_script() {
# === Основной блок выполнения ===
log_message "INFO" "=== Script update started ==="
parse_command_line "$@"
deploy_require_root
if [ "$DEPLOY_MODE" -eq 1 ]; then
printf '\n=== ssh-monitor: первичная установка (--deploy) ===\n'
say_step "Репозиторий и файлы"
fi
log_message "INFO" "=== Script update started (deploy=$DEPLOY_MODE) ==="
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 "Скрипт успешно завершил работу"
if [ "$exit_code" -eq 0 ] && [ "$DEPLOY_MODE" -eq 1 ]; then
deploy_post_install
restart_ssh_monitor_service
if command -v systemctl &>/dev/null; then
if systemctl start ssh-monitor.service 2>/dev/null; then
say_ok "systemctl start ssh-monitor.service"
else
log_message "INFO" "=== Script update completed with errors (exit code: $exit_code) ==="
debug_step "Скрипт завершил работу с ошибками"
say_warn "systemctl start не выполнен (проверьте --check-config и конфиг)"
fi
fi
fi
if [ $exit_code -eq 0 ]; then
log_message "INFO" "=== Script update completed successfully ==="
if [ "$DEPLOY_MODE" -eq 1 ]; then
printf '\n=== Установка завершена. Отредактируйте %s и перезапустите сервис. ===\n' "$CONFIG_FILE"
fi
else
log_message "INFO" "=== Script update completed with errors (exit code: $exit_code) ==="
fi
# Выход с кодом завершения
exit $exit_code