Compare commits

...

6 Commits

Author SHA1 Message Date
PapaTramp 9e756674d0 fix: daily report без локального Telegram при fallback SAC (2.1.6-SAC)
Суточные report.daily.* уходят только в SAC/spool; SAC_TIMEOUT_SEC=45;
приоритет report.* в sac_flush_spool, до 50 файлов за цикл.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 12:00:35 +10:00
PapaTramp 0a80effd77 docs: sync README version with version.txt 2.1.5-SAC
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:14:43 +10:00
PapaTramp de61c6c771 fix: daily report active users count matches who sessions (2.1.5-SAC)
Include local/console who lines; active_count from session list not raw who line count.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 14:41:52 +10:00
PapaTramp c96e870542 feat: enrich SAC events with session_id, tty and source_ip (2.1.4-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 10:13:40 +10:00
PapaTramp 489d071208 feat(deploy): sync SERVER_IPV4 hint and DAILY_REPORT_ENABLED on update (2.1.3-SAC)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 10:01:16 +10:00
PapaTramp 1361fd264c feat: merge missing SAC keys into config on deploy (2.1.2-SAC)
Append absent SAC variables from example instead of overwriting ssh-monitor.conf; set UseSAC=fallback on install and update.
2026-06-21 12:20:33 +10:00
8 changed files with 288 additions and 95 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
# ssh-monitor
**Версия:** `2.1.1-SAC`
**Версия:** `2.1.5-SAC`
Bash-мониторинг **SSH**, **sudo**, **systemd-logind**: Telegram/email, бан IP (ipset), ежедневный отчёт, heartbeat.
@@ -161,7 +161,7 @@ bash -n ./ssh-monitor
## Релизный архив
Версия задаётся в **`SSH_MONITOR_VERSION`** в файле **`ssh-monitor`** и дублируется в **`version.txt`** (сейчас **2.1.1-SAC**). Модуль **`sac-client.sh`** использует ту же версию для SAC `product_version`. Сборка tarball из текущего git-дерева:
Версия задаётся в **`SSH_MONITOR_VERSION`** в файле **`ssh-monitor`** и дублируется в **`version.txt`** (та же строка, что в шапке README). Модуль **`sac-client.sh`** использует ту же версию для SAC `product_version`. Сборка tarball из текущего git-дерева:
```bash
make dist
+3 -1
View File
@@ -1,6 +1,8 @@
# Автообновление ssh-monitor
Скрипт **`update_ssh_monitor.sh`** в корне репозитория обновляет установленный **`/usr/local/bin/ssh-monitor`** из git-репозитория и перезапускает systemd-сервис. Конфиг **`/etc/ssh-monitor.conf`**, watchdog и таймеры **не изменяются**.
Скрипт **`update_ssh_monitor.sh`** в корне репозитория обновляет установленный **`/usr/local/bin/ssh-monitor`** из git-репозитория и перезапускает systemd-сервис. Watchdog и таймеры **не изменяются**.
**Конфиг `/etc/ssh-monitor.conf`:** при первой установке копируется из `ssh-monitor.conf.example`. При каждом deploy/обновлении в **существующий** конфиг **дописываются** отсутствующие SAC-переменные (`UseSAC`, `SAC_URL`, `SAC_API_KEY`, spool, timeout и т.д.) и выставляется **`UseSAC=fallback`**. Telegram/SMTP и прочие ключи **не перезаписываются**.
Установка самого монитора и unit-файлов: [README.md](../README.md), зависимости: [install-prerequisites.ru.md](install-prerequisites.ru.md).
+1 -1
View File
@@ -154,7 +154,7 @@ bash -n ./ssh-monitor
## Release tarball
Version is set as **`SSH_MONITOR_VERSION`** in **`ssh-monitor`** and **`version.txt`** (currently **2.1.1-SAC**). **`sac-client.sh`** reports the same version to SAC. Build from the git tree:
Version is set as **`SSH_MONITOR_VERSION`** in **`ssh-monitor`** and **`version.txt`** (same string as in the README header). **`sac-client.sh`** reports the same version to SAC. Build from the git tree:
```bash
make dist
+63 -13
View File
@@ -51,8 +51,8 @@ sac_check_health() {
return 0
fi
code="$(curl -sS -o /dev/null -w '%{http_code}' \
--connect-timeout "${SAC_TIMEOUT_SEC:-12}" \
--max-time "${SAC_TIMEOUT_SEC:-12}" \
--connect-timeout "${SAC_TIMEOUT_SEC:-45}" \
--max-time "${SAC_TIMEOUT_SEC:-45}" \
"${base}/health" || echo 000)"
[ "$code" = "200" ]
}
@@ -152,8 +152,8 @@ sac_post_payload() {
-H "Authorization: Bearer ${SAC_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ${event_id}" \
--connect-timeout "${SAC_TIMEOUT_SEC:-12}" \
--max-time "${SAC_TIMEOUT_SEC:-12}" \
--connect-timeout "${SAC_TIMEOUT_SEC:-45}" \
--max-time "${SAC_TIMEOUT_SEC:-45}" \
-d @"$tmp" 2>/dev/null || echo 000)"
rm -f "$tmp"
@@ -170,9 +170,36 @@ sac_post_payload() {
return 1
}
# Список spool: report.daily.* первыми (старые раньше), затем остальное.
sac_flush_spool_list_files() {
local dir="${1:-}"
[ -n "$dir" ] && [ -d "$dir" ] || return 0
SPOOL_DIR="$dir" python3 <<'PY'
import glob
import json
import os
dir_path = os.environ.get("SPOOL_DIR", "")
daily, other = [], []
for path in glob.glob(os.path.join(dir_path, "*.json")):
event_type = ""
try:
with open(path, encoding="utf-8") as handle:
event_type = json.load(handle).get("type", "") or ""
except Exception:
pass
mtime = os.path.getmtime(path)
bucket = daily if event_type in ("report.daily.ssh", "report.daily.rdp") else other
bucket.append((mtime, path))
for bucket in (daily, other):
for _, path in sorted(bucket):
print(path)
PY
}
# Повторная отправка файлов из SAC_SPOOL_DIR (до max_files за вызов).
sac_flush_spool() {
local mode max_files="${1:-20}"
local mode max_files="${1:-${SAC_SPOOL_FLUSH_MAX_FILES:-50}}"
mode="$(sac_normalize_mode "${UseSAC:-off}")"
case "$mode" in
off) return 0 ;;
@@ -184,17 +211,14 @@ sac_flush_spool() {
local dir="${SAC_SPOOL_DIR:-/var/lib/ssh-monitor/sac-spool}"
[ -d "$dir" ] || return 0
local f count=0
shopt -s nullglob
for f in "$dir"/*.json; do
[ -f "$f" ] || continue
local f count=0 payload
while IFS= read -r f; do
[ -n "$f" ] && [ -f "$f" ] || continue
count=$((count + 1))
[ "$count" -gt "$max_files" ] && break
local payload
payload="$(cat "$f")"
sac_post_payload "$payload" || true
done
shopt -u nullglob
done < <(sac_flush_spool_list_files "$dir")
return 0
}
@@ -333,6 +357,13 @@ sac_is_heartbeat_only_event() {
[ "${1:-}" = "agent.heartbeat" ]
}
sac_is_daily_report_event() {
case "${1:-}" in
report.daily.ssh|report.daily.rdp) return 0 ;;
*) return 1 ;;
esac
}
# Добавляет generated_by / telegram_via в JSON details для ingest.
sac_merge_notify_details() {
local details_json="${1:-}"
@@ -365,7 +396,7 @@ notify_or_sac() {
if [ -z "$details_json" ] && [ -n "${SAC_D_USER:-}${SAC_D_IP:-}${SAC_D_COMMAND:-}" ]; then
details_json="$(sac_build_details_json)"
fi
unset SAC_D_USER SAC_D_IP SAC_D_COMMAND SAC_D_ATTEMPT SAC_D_MAX SAC_D_RUN_AS SAC_D_PWD 2>/dev/null || true
unset SAC_D_USER SAC_D_IP SAC_D_SOURCE_IP SAC_D_PORT SAC_D_COMMAND SAC_D_ATTEMPT SAC_D_MAX SAC_D_RUN_AS SAC_D_PWD SAC_D_SESSION_ID SAC_D_TTY SAC_D_PTS SAC_D_SINCE 2>/dev/null || true
local mode
mode="$(sac_normalize_mode "${UseSAC:-off}")"
local tg_via=""
@@ -393,6 +424,25 @@ notify_or_sac() {
esac
fi
# Суточный отчёт — только SAC/spool; при сбое ingest не дублировать в локальный Telegram.
if sac_is_daily_report_event "$event_type"; then
case "$mode" in
off)
return 0
;;
exclusive|dual|fallback)
if sac_send_event "$event_type" "$severity" "$title" "$summary" "$details_json"; then
return 0
fi
write_log "WARN: daily report не принят SAC — остаётся в spool (локальный Telegram пропущен)"
return 1
;;
*)
return 0
;;
esac
fi
case "$mode" in
off)
notify_send "$telegram_message"
+41 -9
View File
@@ -7,7 +7,7 @@ IFS=$'\n\t'
# ============================================
CONFIG_FILE="/etc/ssh-monitor.conf"
SSH_MONITOR_VERSION="2.1.1-SAC"
SSH_MONITOR_VERSION="2.1.6-SAC"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$SCRIPT_DIR/sac-client.sh" ]; then
@@ -34,7 +34,9 @@ UseSAC="off"
SAC_URL=""
SAC_API_KEY=""
SAC_SPOOL_DIR="/var/lib/ssh-monitor/sac-spool"
SAC_TIMEOUT_SEC="12"
SAC_TIMEOUT_SEC="45"
SAC_SPOOL_FLUSH_MAX_FILES="50"
SAC_SPOOL_MAX_AGE_HOURS="72"
SAC_SEND_HEARTBEAT="1"
SAC_FALLBACK_FAILURES="5"
@@ -224,7 +226,9 @@ load_config() {
local cfg_sac_url="${SAC_URL:-}"
local cfg_sac_api_key="${SAC_API_KEY:-}"
local cfg_sac_spool_dir="${SAC_SPOOL_DIR:-/var/lib/ssh-monitor/sac-spool}"
local cfg_sac_timeout_sec="${SAC_TIMEOUT_SEC:-12}"
local cfg_sac_timeout_sec="${SAC_TIMEOUT_SEC:-45}"
local cfg_sac_spool_flush_max="${SAC_SPOOL_FLUSH_MAX_FILES:-50}"
local cfg_sac_spool_max_age_h="${SAC_SPOOL_MAX_AGE_HOURS:-72}"
local cfg_mail_smtp_host="${MAIL_SMTP_HOST:-}"
local cfg_mail_smtp_port="${MAIL_SMTP_PORT:-587}"
local cfg_mail_smtp_user="${MAIL_SMTP_USER:-}"
@@ -297,6 +301,8 @@ load_config() {
SAC_API_KEY="$(trim_value "${SAC_API_KEY:-$cfg_sac_api_key}")"
SAC_SPOOL_DIR="$(trim_value "${SAC_SPOOL_DIR:-$cfg_sac_spool_dir}")"
SAC_TIMEOUT_SEC="$(trim_value "${SAC_TIMEOUT_SEC:-$cfg_sac_timeout_sec}")"
SAC_SPOOL_FLUSH_MAX_FILES="$(trim_value "${SAC_SPOOL_FLUSH_MAX_FILES:-$cfg_sac_spool_flush_max}")"
SAC_SPOOL_MAX_AGE_HOURS="$(trim_value "${SAC_SPOOL_MAX_AGE_HOURS:-$cfg_sac_spool_max_age_h}")"
MAIL_SMTP_HOST="$(trim_value "${MAIL_SMTP_HOST:-$cfg_mail_smtp_host}")"
MAIL_SMTP_PORT="${MAIL_SMTP_PORT:-$cfg_mail_smtp_port}"
MAIL_SMTP_USER="$(trim_value "${MAIL_SMTP_USER:-$cfg_mail_smtp_user}")"
@@ -1234,7 +1240,6 @@ get_active_ssh_sessions() {
local line user tty login_time ip
while IFS= read -r line; do
[[ -z "$line" ]] && continue
[[ "$line" != *"("* ]] && continue
user=$(awk '{print $1}' <<<"$line")
tty=$(awk '{print $2}' <<<"$line")
login_time=$(awk '{print $3, $4}' <<<"$line")
@@ -1316,14 +1321,14 @@ monitor_ssh() {
message="${message}👤 Пользователь: ${user}"$'\n'
message="${message}🌐 IP адрес: ${ip:-local}"$'\n'
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
SAC_D_USER="$user" SAC_D_IP="${ip:-}" \
SAC_D_USER="$user" SAC_D_SOURCE_IP="${ip:-}" SAC_D_IP="${ip:-}" SAC_D_PORT="${dedupe_port:-}" \
notify_or_sac "ssh.login.success" "info" "Успешное SSH" "${user} с ${ip:-local}" "$message"
write_log "SSH успешный вход: $user с IP ${ip:-local}"
if [ "$user" = "root" ]; then
local rmsg=" 🔑 SSH ВХОД ПОД ROOT "$'\n'
rmsg="${rmsg}🌐 IP: ${ip:-local}"$'\n'
rmsg="${rmsg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
SAC_D_USER="root" SAC_D_IP="${ip:-}" \
SAC_D_USER="root" SAC_D_SOURCE_IP="${ip:-}" SAC_D_IP="${ip:-}" SAC_D_PORT="${dedupe_port:-}" \
notify_or_sac "ssh.login.success" "warning" "SSH вход под root" "root с ${ip:-local}" "$rmsg"
fi
fi
@@ -1457,8 +1462,11 @@ monitor_sudo() {
[[ -n "$fp" && -n "${SUDO_DEDUP_BATCH[$fp]:-}" ]] && continue
[[ -n "$fp" ]] && SUDO_DEDUP_BATCH[$fp]=1
local user sudo_cmd pwd run_as
local user sudo_cmd pwd run_as sudo_tty=""
parse_sudo_audit_line "$line" user sudo_cmd pwd run_as
if [[ "$line" =~ TTY=([^[:space:];]+) ]]; then
sudo_tty="${BASH_REMATCH[1]}"
fi
if [ -n "$user" ] && [ -n "${sudo_cmd:-}" ]; then
local message=" ⚠️ ИСПОЛЬЗОВАНИЕ SUDO "$'\n'
@@ -1471,6 +1479,7 @@ monitor_sudo() {
message="${message}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
local sac_summary="${user}: ${sudo_cmd}"
SAC_D_USER="$user" SAC_D_COMMAND="$sudo_cmd" SAC_D_RUN_AS="${run_as:-}" SAC_D_PWD="${pwd:-}" \
SAC_D_TTY="${sudo_tty:-}" \
notify_or_sac "privilege.sudo.command" "warning" "Использование sudo" "$sac_summary" "$message"
write_log "SUDO: $user выполнил $sudo_cmd${run_as:+ (USER=$run_as)}"
fi
@@ -1549,6 +1558,27 @@ monitor_security_events() {
# Локальные сессии (systemd-logind, journalctl)
# ============================================
enrich_sac_logind_session_details() {
local sid="$1"
[ -n "$sid" ] || return 0
SAC_D_SESSION_ID="$sid"
command -v loginctl >/dev/null 2>&1 || return 0
local tty remote since
tty=$(loginctl show-session "$sid" -p TTY --value 2>/dev/null || true)
remote=$(loginctl show-session "$sid" -p RemoteHost --value 2>/dev/null || true)
since=$(loginctl show-session "$sid" -p Timestamp --value 2>/dev/null || true)
if [ -n "$tty" ] && [ "$tty" != "n/a" ]; then
SAC_D_TTY="$tty"
case "$tty" in pts/*) SAC_D_PTS="${tty#pts/}" ;; esac
fi
if [ -n "$remote" ] && [ "$remote" != "n/a" ]; then
SAC_D_SOURCE_IP="$remote"
fi
if [ -n "$since" ]; then
SAC_D_SINCE="$since"
fi
}
# Возврат 0 = не слать Telegram по logind (дубль с monitor_ssh): Type=ssh или Service=*sshd*.
# Пустой ответ loginctl — короткие повторы (гонка с journal).
logind_session_is_ssh_duplicate_for_notify() {
@@ -1645,6 +1675,7 @@ monitor_logind() {
msg="${msg}👤 Пользователь: ${user:-неизвестно}"$'\n'
msg="${msg}🆔 Сессия: ${sid:-?}"$'\n'
msg="${msg}🕐 Время: $(notification_date '+%d.%m.%Y %H:%M:%S')"
enrich_sac_logind_session_details "$sid"
SAC_D_USER="${user:-}" \
notify_or_sac "session.logind.new" "info" "Новая сессия logind" "${user:-?} sid=${sid:-?}" "$msg"
write_log "LOGIND: новая сессия: ${line}"
@@ -1740,7 +1771,8 @@ send_daily_report() {
local active_sessions
active_sessions=$(get_active_ssh_sessions)
local active_count
active_count=$(who 2>/dev/null | grep -c "." || echo "0")
active_count=$(printf '%s' "$active_sessions" | grep -c '👤' || true)
active_count=${active_count:-0}
local active_bans=0
local ban_line
@@ -2020,7 +2052,7 @@ main() {
monitor_logind
check_daily_report
check_heartbeat
sac_flush_spool 20 2>/dev/null || true
sac_flush_spool "${SAC_SPOOL_FLUSH_MAX_FILES:-50}" 2>/dev/null || true
write_health_and_metrics
local current_time
+3 -1
View File
@@ -35,7 +35,9 @@ SAC_API_KEY="sac_UkOsAT3UWiQS54KK5OJPBDCSucysQDrKFju28wmYiz8"
SAC_SPOOL_DIR="/var/lib/ssh-monitor/sac-spool"
SAC_FAIL_COUNT_FILE="/var/lib/ssh-monitor/sac-fail.count"
SAC_FALLBACK_FAILURES="5"
SAC_TIMEOUT_SEC="12"
SAC_TIMEOUT_SEC="45"
SAC_SPOOL_FLUSH_MAX_FILES="50"
SAC_SPOOL_MAX_AGE_HOURS="72"
# SMTP (python3). Для авто-режима почты нужны MAIL_SMTP_HOST, MAIL_FROM, MAIL_TO.
MAIL_SMTP_HOST=""
+171 -64
View File
@@ -74,11 +74,26 @@ update_ssh_monitor.sh — обновление или первичная уст
Лог действий: /var/log/update_script.log (в конце каждого прогона — блок «Итог обновления»).
При первой установке или апгрейде с UseSAC=off / пустым SAC_API_KEY / без sac-client.sh
example из git перезаписывает /etc/ssh-monitor.conf (старый файл — .bak.*).
При первой установке создаётся /etc/ssh-monitor.conf из example.
При обновлении в существующий конфиг дописываются отсутствующие SAC-переменные
и выставляется UseSAC=fallback (Telegram/SMTP не перезаписываются).
EOF
}
# Ключи SAC: дописываются из ssh-monitor.conf.example, если отсутствуют в /etc.
SAC_CONFIG_KEYS=(
UseSAC
SAC_URL
SAC_API_KEY
SAC_SPOOL_DIR
SAC_FAIL_COUNT_FILE
SAC_FALLBACK_FAILURES
SAC_TIMEOUT_SEC
SAC_SPOOL_FLUSH_MAX_FILES
SAC_SPOOL_MAX_AGE_HOURS
)
SAC_USE_MODE_DEFAULT="fallback"
find_ssh_monitor_conf_example() {
local example="$UPDATE_DIR/$SCRIPT_NAME/ssh-monitor.conf.example"
if [ -f "$example" ]; then
@@ -102,28 +117,20 @@ config_conf_value() {
printf '%s\n' "$val"
}
config_needs_sac_bootstrap() {
local sac_client use_sac api_key
sac_client="${LOCAL_SCRIPT_PATH%/*}/sac-client.sh"
if [ ! -f "$sac_client" ]; then
return 0
fi
if [ ! -f "$CONFIG_FILE" ]; then
return 0
fi
use_sac=$(config_conf_value "UseSAC" "$CONFIG_FILE" 2>/dev/null || true)
api_key=$(config_conf_value "SAC_API_KEY" "$CONFIG_FILE" 2>/dev/null || true)
if [ -n "$use_sac" ] && [ "$use_sac" != "off" ] \
&& [ -n "$api_key" ] && [ "${api_key#sac_}" != "$api_key" ]; then
return 1
fi
return 0
config_example_value() {
local key="$1" example="$2"
config_conf_value "$key" "$example" 2>/dev/null || true
}
ensure_ssh_monitor_config_from_example() {
backup_config_if_needed() {
local file="$1" flag_var="$2"
if [ -z "${!flag_var:-}" ] || [ "${!flag_var}" -eq 0 ]; then
cp "$file" "${file}.bak.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
printf -v "$flag_var" '%s' 1
fi
}
ensure_ssh_monitor_config_file() {
local example
CONFIG_CHANGED=0
@@ -132,27 +139,17 @@ ensure_ssh_monitor_config_from_example() {
return 0
}
if [ -f "$CONFIG_FILE" ] && ! config_needs_sac_bootstrap; then
log_message "INFO" "Config $CONFIG_FILE: SAC already configured, not overwriting."
if [ -f "$CONFIG_FILE" ]; then
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_info "Уже существует: $CONFIG_FILE (SAC настроен, не перезаписываем)"
say_info "Уже существует: $CONFIG_FILE (дополним SAC-переменные при необходимости)"
fi
return 0
fi
if [ -f "$CONFIG_FILE" ]; then
cp "$CONFIG_FILE" "${CONFIG_FILE}.bak.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
log_message "INFO" "Upgrading config without SAC: backup created, applying ssh-monitor.conf.example from git."
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_info "Апгрейд без SAC: резервная копия, применяем example (Telegram + fallback)."
fi
else
log_message "INFO" "Creating $CONFIG_FILE from ssh-monitor.conf.example"
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_ok "Создан $CONFIG_FILE из ssh-monitor.conf.example"
fi
fi
if ! cp "$example" "$CONFIG_FILE"; then
log_message "INFO" "ERROR: Failed to copy ssh-monitor.conf.example to $CONFIG_FILE"
return 1
@@ -162,6 +159,82 @@ ensure_ssh_monitor_config_from_example() {
return 0
}
sync_sac_config_keys() {
local file="$CONFIG_FILE"
local example backed_up=0
local key val current changed=0 added=0
[ -f "$file" ] || return 1
example=$(find_ssh_monitor_conf_example) || {
log_message "INFO" "WARNING: ssh-monitor.conf.example not found; SAC merge skipped"
return 0
}
if ! config_key_exists "UseSAC" "$file"; then
backup_config_if_needed "$file" backed_up
{
printf '\n# --- Security Alert Center (SAC) ---\n'
printf '# off | exclusive | dual | fallback — см. security-alert-center docs/agent-integration.md\n'
} >>"$file"
log_message "INFO" "Config $file: добавлен блок SAC (ключи отсутствовали)."
changed=1
fi
for key in "${SAC_CONFIG_KEYS[@]}"; do
if config_key_exists "$key" "$file"; then
continue
fi
val=$(config_example_value "$key" "$example")
if [ -z "$val" ] && [ "$key" != "SAC_API_KEY" ]; then
case "$key" in
UseSAC) val="$SAC_USE_MODE_DEFAULT" ;;
SAC_SPOOL_DIR) val="$SAC_SPOOL_DIR_DEFAULT" ;;
SAC_FAIL_COUNT_FILE) val="/var/lib/ssh-monitor/sac-fail.count" ;;
SAC_FALLBACK_FAILURES) val="5" ;;
SAC_TIMEOUT_SEC) val="45" ;;
SAC_SPOOL_FLUSH_MAX_FILES) val="50" ;;
SAC_SPOOL_MAX_AGE_HOURS) val="72" ;;
SAC_URL) val="https://sac.kalinamall.ru" ;;
esac
fi
backup_config_if_needed "$file" backed_up
set_config_kv "$key" "$val" "$file"
log_message "INFO" "Config $file: добавлен ${key}=${val:-<пусто>}"
changed=1
added=$((added + 1))
done
current=$(config_conf_value "UseSAC" "$file" 2>/dev/null || true)
if [ "$current" != "$SAC_USE_MODE_DEFAULT" ]; then
backup_config_if_needed "$file" backed_up
set_config_kv "UseSAC" "$SAC_USE_MODE_DEFAULT" "$file"
log_message "INFO" "Config $file: UseSAC ${current:-<нет>}$SAC_USE_MODE_DEFAULT (deploy policy)."
changed=1
fi
current=$(config_conf_value "SAC_TIMEOUT_SEC" "$file" 2>/dev/null || true)
if [ "$current" = "12" ]; then
backup_config_if_needed "$file" backed_up
set_config_kv "SAC_TIMEOUT_SEC" "45" "$file"
log_message "INFO" "Config $file: SAC_TIMEOUT_SEC 12 → 45 (ingest timeout)."
changed=1
fi
if [ "$changed" -eq 0 ]; then
return 1
fi
if [ "$DEPLOY_MODE" -eq 1 ]; then
if [ "$added" -gt 0 ]; then
say_info "SAC: добавлено ключей — $added; UseSAC=$SAC_USE_MODE_DEFAULT."
else
say_info "SAC: UseSAC=$SAC_USE_MODE_DEFAULT."
fi
fi
CONFIG_CHANGED=1
return 0
}
set_config_kv() {
local key="$1" val="$2" file="$3"
if grep -qE "^[[:space:]]*${key}=" "$file" 2>/dev/null; then
@@ -176,6 +249,53 @@ config_key_exists() {
grep -qE "^[[:space:]]*${key}=" "$file" 2>/dev/null
}
config_key_mentioned() {
local key="$1" file="$2"
grep -qE "^[[:space:]]*(#.*)?${key}=" "$file" 2>/dev/null
}
sync_server_ipv4_hint_config() {
local file="$CONFIG_FILE" backed_up=0
[ -f "$file" ] || return 1
if config_key_mentioned "SERVER_IPV4" "$file"; then
return 1
fi
backup_config_if_needed "$file" backed_up
{
printf '\n# --- Явный IPv4 для SAC host.ipv4 (раскомментируйте при необходимости) ---\n'
printf '# SERVER_IPV4="10.0.0.10"\n'
} >>"$file"
log_message "INFO" "Config $file: добавлена подсказка # SERVER_IPV4."
CONFIG_CHANGED=1
return 0
}
sync_daily_report_enabled_config() {
local file="$CONFIG_FILE" backed_up=0 current
[ -f "$file" ] || return 1
if config_key_exists "DAILY_REPORT_ENABLED" "$file"; then
current=$(config_conf_value "DAILY_REPORT_ENABLED" "$file" 2>/dev/null || true)
if [ "$current" = "1" ]; then
return 1
fi
backup_config_if_needed "$file" backed_up
set_config_kv "DAILY_REPORT_ENABLED" "1" "$file"
log_message "INFO" "Config $file: DAILY_REPORT_ENABLED ${current:-<пусто>} → 1 (отчёт с агента)."
else
backup_config_if_needed "$file" backed_up
{
printf '\n# --- Суточный отчёт report.daily.ssh (1 = с агента; 0 = только SAC) ---\n'
printf 'DAILY_REPORT_ENABLED="1"\n'
} >>"$file"
log_message "INFO" "Config $file: добавлен DAILY_REPORT_ENABLED=1."
fi
CONFIG_CHANGED=1
return 0
}
append_heartbeat_interval_block() {
local file="$1"
{
@@ -247,25 +367,6 @@ sync_heartbeat_interval_config() {
return 0
}
sync_use_sac_fallback_mode() {
local file="$CONFIG_FILE"
[ -f "$file" ] || return 1
if grep -qE '^[[:space:]]*UseSAC=[[:space:]]*["'"'"']?fallback["'"'"']?[[:space:]]*(#.*)?$' "$file" 2>/dev/null; then
return 1
fi
if ! grep -qE '^[[:space:]]*UseSAC=' "$file" 2>/dev/null; then
return 1
fi
cp "$file" "${file}.bak.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
sed -i -E 's/^[[:space:]]*UseSAC=.*/UseSAC="fallback"/' "$file"
log_message "INFO" "Config $file: UseSAC set to fallback (deploy policy on script upgrade)."
if [ "$DEPLOY_MODE" -eq 1 ]; then
say_info "UseSAC=fallback (рабочий режим SAC + локальный fallback)."
fi
CONFIG_CHANGED=1
return 0
}
parse_command_line() {
DEPLOY_MODE=0
while [ $# -gt 0 ]; do
@@ -759,7 +860,11 @@ deploy_prepare_dirs() {
deploy_install_config() {
say_step "Конфигурация"
ensure_ssh_monitor_config_from_example || return 1
ensure_ssh_monitor_config_file || return 1
sync_sac_config_keys || true
sync_heartbeat_interval_config || true
sync_server_ipv4_hint_config || true
sync_daily_report_enabled_config || true
}
deploy_install_systemd() {
@@ -1103,14 +1208,22 @@ update_script() {
fi
CONFIG_CHANGED=0
if ! ensure_ssh_monitor_config_from_example; then
if ! ensure_ssh_monitor_config_file; then
log_update_summary
return 1
fi
if [ "${CONFIG_CHANGED:-0}" -eq 1 ]; then
SUMMARY_CONFIG="обновлён из example"
if sync_sac_config_keys; then
SUMMARY_CONFIG="SAC-ключи дополнены, UseSAC=fallback"
else
SUMMARY_CONFIG="без изменений"
SUMMARY_CONFIG="SAC без изменений"
fi
sync_heartbeat_interval_config || true
sync_server_ipv4_hint_config || true
sync_daily_report_enabled_config || true
if [ "${CONFIG_CHANGED:-0}" -eq 1 ] && [ "$SUMMARY_CONFIG" = "SAC без изменений" ]; then
SUMMARY_CONFIG="heartbeat/watchdog выровнены"
elif [ "${CONFIG_CHANGED:-0}" -eq 1 ] && [ "$SUMMARY_CONFIG" != "SAC без изменений" ]; then
SUMMARY_CONFIG="${SUMMARY_CONFIG}; heartbeat при необходимости"
fi
if [ -n "$actual_script_path" ] && [ -f "$actual_script_path" ]; then
@@ -1175,12 +1288,6 @@ update_script() {
script_changed=1
fi
if [ "$script_changed" -eq 1 ]; then
sync_use_sac_fallback_mode || true
fi
# Выравнивать интервал heartbeat при каждом deploy, даже если бинарник не менялся.
sync_heartbeat_interval_config || true
if [ "$script_changed" -eq 1 ] || [ "${SAC_CLIENT_CHANGED:-0}" -eq 1 ] || [ "${CONFIG_CHANGED:-0}" -eq 1 ]; then
restart_ssh_monitor_service
else
+1 -1
View File
@@ -1 +1 @@
2.1.1-SAC
2.1.6-SAC