feat: release 2.1.8-SAC — security phase 1 and SAC-first update hardening

Updater applies chmod/chown on config, spool and state paths on every run.
SAC is the primary update path; optional cron uses the same behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 09:44:24 +10:00
parent ec0857b8f2
commit 2048162057
11 changed files with 618 additions and 243 deletions
+27 -5
View File
@@ -2,6 +2,26 @@
# Версия для SAC = SSH_MONITOR_VERSION в ssh-monitor (отдельный номер здесь не задаётся).
# shellcheck shell=bash
sac_tls_insecure_enabled() {
case "${SAC_TLS_INSECURE:-0}" in
1 | yes | true | on) return 0 ;;
esac
return 1
}
sac_warn_tls_insecure() {
if sac_tls_insecure_enabled; then
write_log 'CRITICAL: SAC_TLS_INSECURE=1 — проверка TLS для SAC отключена (риск MITM). Только для краткой отладки в lab.'
fi
}
sac_curl() {
if sac_tls_insecure_enabled; then
curl -k -sS "$@"
else
curl -sS "$@"
fi
}
sac_normalize_mode() {
printf '%s' "${1:-off}" | tr '[:upper:]' '[:lower:]'
}
@@ -31,7 +51,7 @@ sac_agent_instance_id() {
local dir
dir="$(dirname "$id_file")"
if [ ! -d "$dir" ]; then
mkdir -p "$dir" 2>/dev/null || id_file="/tmp/ssh-monitor-agent_instance_id"
ssh_monitor_secure_dir "$dir" 2>/dev/null || mkdir -p "$dir" 2>/dev/null || id_file="/tmp/ssh-monitor-agent_instance_id"
fi
if [ -f "$id_file" ]; then
cat "$id_file"
@@ -39,6 +59,7 @@ sac_agent_instance_id() {
fi
local new_id
new_id="$(python3 -c 'import uuid; print(uuid.uuid4())')"
ssh_monitor_secure_file "$id_file" 2>/dev/null || true
printf '%s\n' "$new_id" >"$id_file" 2>/dev/null || true
printf '%s' "$new_id"
}
@@ -50,7 +71,7 @@ sac_check_health() {
if [ "${DRY_RUN:-0}" = "1" ]; then
return 0
fi
code="$(curl -sS -o /dev/null -w '%{http_code}' \
code="$(sac_curl -o /dev/null -w '%{http_code}' \
--connect-timeout "${SAC_TIMEOUT_SEC:-45}" \
--max-time "${SAC_TIMEOUT_SEC:-45}" \
"${base}/health" || echo 000)"
@@ -61,7 +82,8 @@ sac_spool_write() {
local event_id="$1"
local body="$2"
local dir="${SAC_SPOOL_DIR:-/var/lib/ssh-monitor/sac-spool}"
mkdir -p "$dir" 2>/dev/null || return 1
ssh_monitor_secure_dir "$dir" 2>/dev/null || mkdir -p "$dir" 2>/dev/null || return 1
ssh_monitor_secure_file "${dir}/${event_id}.json" 2>/dev/null || true
printf '%s' "$body" >"${dir}/${event_id}.json"
}
@@ -89,7 +111,7 @@ sac_write_fail_count() {
local n="$1"
local f
f="$(sac_fail_count_file)"
mkdir -p "$(dirname "$f")" 2>/dev/null || true
ssh_monitor_secure_file "$f" 2>/dev/null || mkdir -p "$(dirname "$f")" 2>/dev/null || true
printf '%s' "$n" >"$f"
}
@@ -147,7 +169,7 @@ sac_post_payload() {
tmp="$(mktemp)"
resp="$(mktemp)"
printf '%s' "$payload" >"$tmp"
http_code="$(curl -sS -o "$resp" -w '%{http_code}' \
http_code="$(sac_curl -o "$resp" -w '%{http_code}' \
-X POST "$(sac_ingest_url)" \
-H "Authorization: Bearer ${SAC_API_KEY}" \
-H "Content-Type: application/json" \