fix(agent99): fail closed host110 runtime replay

This commit is contained in:
Your Name
2026-07-19 02:40:26 +08:00
parent d1797a1c87
commit 2c2dc086b0
8 changed files with 1878 additions and 42 deletions

View File

@@ -9,14 +9,74 @@
set -euo pipefail
if [[ "${BASH_SOURCE[0]}" == /backup/scripts/* ]] && [ "${BACKUP_RUNTIME_SHARED_LOCK_HELD:-0}" != "1" ]; then
resolve_backup_runtime_script_path() {
local raw_path="$1" candidate="" resolved="" resolved_directory=""
[ -n "${raw_path}" ] || return 1
case "${raw_path}" in /*) candidate="${raw_path}" ;; *) candidate="${PWD}/${raw_path}" ;; esac
if command -v realpath >/dev/null 2>&1; then
resolved="$(realpath -e -- "${candidate}" 2>/dev/null || realpath -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then printf '%s\n' "${resolved}"; return 0; fi
fi
if command -v readlink >/dev/null 2>&1; then
resolved="$(readlink -f -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then printf '%s\n' "${resolved}"; return 0; fi
fi
[ ! -L "${candidate}" ] || return 1
resolved_directory="$(cd -P -- "$(dirname -- "${candidate}")" 2>/dev/null && pwd -P)" || return 1
[ -e "${resolved_directory}/$(basename -- "${candidate}")" ] || return 1
printf '%s/%s\n' "${resolved_directory}" "$(basename -- "${candidate}")"
}
backup_runtime_inherited_fd_status() {
local expected_path="$1" fd_path="" fd_target=""
if [ -d "/proc/$$/fd" ]; then
fd_path="/proc/$$/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
fd_target="$(resolve_backup_runtime_script_path "${fd_path}")" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
return 0
fi
fd_path="/dev/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
command -v python3 >/dev/null 2>&1 || return 2
fd_target="$(python3 - <<'PY'
import fcntl
import os
try:
target = os.readlink("/dev/fd/197")
except OSError:
value = fcntl.fcntl(197, getattr(fcntl, "F_GETPATH", 50), b"\0" * 1024)
target = os.fsdecode(value.split(b"\0", 1)[0])
print(os.path.realpath(target))
PY
)" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
}
runtime_script_path="$(resolve_backup_runtime_script_path "${BASH_SOURCE[0]:-}")" || {
echo "backup runtime gate unavailable: script identity unresolved" >&2
exit 69
}
if [[ "${runtime_script_path}" == /backup/scripts/* ]]; then
command -v flock >/dev/null 2>&1 || { echo "backup runtime gate unavailable" >&2; exit 69; }
[ ! -L /tmp/agent99-host110-backup-runtime.lock ] || { echo "backup runtime gate unsafe" >&2; exit 69; }
(umask 077; : >> /tmp/agent99-host110-backup-runtime.lock)
[ -f /tmp/agent99-host110-backup-runtime.lock ] && [ -O /tmp/agent99-host110-backup-runtime.lock ] || { echo "backup runtime gate owner invalid" >&2; exit 69; }
exec 197>>/tmp/agent99-host110-backup-runtime.lock
runtime_lock_path="$(resolve_backup_runtime_script_path /tmp/agent99-host110-backup-runtime.lock)" \
|| { echo "backup runtime gate unavailable: lock identity unresolved" >&2; exit 69; }
if backup_runtime_inherited_fd_status "${runtime_lock_path}"; then
:
else
inherited_fd_status=$?
if [ "${inherited_fd_status}" -eq 1 ]; then
exec 197>>/tmp/agent99-host110-backup-runtime.lock
else
echo "backup runtime gate unsafe: inherited fd 197 identity mismatch" >&2
exit 69
fi
fi
flock -s -w 60 197 || { echo "backup runtime deployment is active; retry later" >&2; exit 75; }
export BACKUP_RUNTIME_SHARED_LOCK_HELD=1
fi
BACKUP_BASE="${BACKUP_BASE:-/backup}"

View File

@@ -3,14 +3,78 @@
# Production backup entrypoints share this stable gate. Agent99 takes the
# exclusive side while promoting a complete, digest-bound runtime bundle.
resolve_backup_runtime_source_path() {
local raw_path="$1"
local candidate=""
local resolved=""
local resolved_directory=""
[ -n "${raw_path}" ] || return 1
case "${raw_path}" in
/*) candidate="${raw_path}" ;;
*) candidate="${PWD}/${raw_path}" ;;
esac
if command -v realpath >/dev/null 2>&1; then
resolved="$(realpath -e -- "${candidate}" 2>/dev/null || realpath -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then
printf '%s\n' "${resolved}"
return 0
fi
fi
if command -v readlink >/dev/null 2>&1; then
resolved="$(readlink -f -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then
printf '%s\n' "${resolved}"
return 0
fi
fi
[ ! -L "${candidate}" ] || return 1
resolved_directory="$(cd -P -- "$(dirname -- "${candidate}")" 2>/dev/null && pwd -P)" || return 1
[ -e "${resolved_directory}/$(basename -- "${candidate}")" ] || return 1
printf '%s/%s\n' "${resolved_directory}" "$(basename -- "${candidate}")"
}
backup_runtime_inherited_fd_status() {
local expected_path="$1"
local fd_path=""
local fd_target=""
if [ -d "/proc/$$/fd" ]; then
fd_path="/proc/$$/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
fd_target="$(resolve_backup_runtime_source_path "${fd_path}")" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
return 0
fi
fd_path="/dev/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
command -v python3 >/dev/null 2>&1 || return 2
fd_target="$(python3 - <<'PY'
import fcntl
import os
try:
target = os.readlink("/dev/fd/197")
except OSError:
value = fcntl.fcntl(197, getattr(fcntl, "F_GETPATH", 50), b"\0" * 1024)
target = os.fsdecode(value.split(b"\0", 1)[0])
print(os.path.realpath(target))
PY
)" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
}
acquire_backup_runtime_shared_lock() {
local caller_path="${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}"
local caller_path=""
local lock_path="/tmp/agent99-host110-backup-runtime.lock"
local lock_canonical=""
local inherited_fd_status=0
caller_path="$(resolve_backup_runtime_source_path "${BASH_SOURCE[1]:-${BASH_SOURCE[0]:-}}")" || {
echo "backup runtime gate unavailable: caller identity unresolved" >&2
return 69
}
case "${caller_path}" in
/backup/scripts/*) ;;
*) return 0 ;;
esac
[ "${BACKUP_RUNTIME_SHARED_LOCK_HELD:-0}" != "1" ] || return 0
command -v flock >/dev/null 2>&1 || {
echo "backup runtime gate unavailable: flock missing" >&2
return 69
@@ -21,12 +85,25 @@ acquire_backup_runtime_shared_lock() {
}
(umask 077; : >> "${lock_path}") || return 69
[ -f "${lock_path}" ] && [ -O "${lock_path}" ] || return 69
exec 197>>"${lock_path}"
lock_canonical="$(resolve_backup_runtime_source_path "${lock_path}")" || {
echo "backup runtime gate unavailable: lock identity unresolved" >&2
return 69
}
if backup_runtime_inherited_fd_status "${lock_canonical}"; then
:
else
inherited_fd_status=$?
if [ "${inherited_fd_status}" -eq 1 ]; then
exec 197>>"${lock_path}"
else
echo "backup runtime gate unsafe: inherited fd 197 identity mismatch" >&2
return 69
fi
fi
flock -s -w 60 197 || {
echo "backup runtime deployment is active; retry later" >&2
return 75
}
export BACKUP_RUNTIME_SHARED_LOCK_HELD=1
}
acquire_backup_runtime_shared_lock || {

View File

@@ -5,14 +5,74 @@
set -euo pipefail
if [[ "${BASH_SOURCE[0]}" == /backup/scripts/* ]] && [ "${BACKUP_RUNTIME_SHARED_LOCK_HELD:-0}" != "1" ]; then
resolve_backup_runtime_script_path() {
local raw_path="$1" candidate="" resolved="" resolved_directory=""
[ -n "${raw_path}" ] || return 1
case "${raw_path}" in /*) candidate="${raw_path}" ;; *) candidate="${PWD}/${raw_path}" ;; esac
if command -v realpath >/dev/null 2>&1; then
resolved="$(realpath -e -- "${candidate}" 2>/dev/null || realpath -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then printf '%s\n' "${resolved}"; return 0; fi
fi
if command -v readlink >/dev/null 2>&1; then
resolved="$(readlink -f -- "${candidate}" 2>/dev/null)" || resolved=""
if [ -n "${resolved}" ]; then printf '%s\n' "${resolved}"; return 0; fi
fi
[ ! -L "${candidate}" ] || return 1
resolved_directory="$(cd -P -- "$(dirname -- "${candidate}")" 2>/dev/null && pwd -P)" || return 1
[ -e "${resolved_directory}/$(basename -- "${candidate}")" ] || return 1
printf '%s/%s\n' "${resolved_directory}" "$(basename -- "${candidate}")"
}
backup_runtime_inherited_fd_status() {
local expected_path="$1" fd_path="" fd_target=""
if [ -d "/proc/$$/fd" ]; then
fd_path="/proc/$$/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
fd_target="$(resolve_backup_runtime_script_path "${fd_path}")" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
return 0
fi
fd_path="/dev/fd/197"
[ -e "${fd_path}" ] || [ -L "${fd_path}" ] || return 1
command -v python3 >/dev/null 2>&1 || return 2
fd_target="$(python3 - <<'PY'
import fcntl
import os
try:
target = os.readlink("/dev/fd/197")
except OSError:
value = fcntl.fcntl(197, getattr(fcntl, "F_GETPATH", 50), b"\0" * 1024)
target = os.fsdecode(value.split(b"\0", 1)[0])
print(os.path.realpath(target))
PY
)" || return 2
[ "${fd_target}" = "${expected_path}" ] || return 2
}
runtime_script_path="$(resolve_backup_runtime_script_path "${BASH_SOURCE[0]:-}")" || {
echo "backup runtime gate unavailable: script identity unresolved" >&2
exit 69
}
if [[ "${runtime_script_path}" == /backup/scripts/* ]]; then
command -v flock >/dev/null 2>&1 || { echo "backup runtime gate unavailable" >&2; exit 69; }
[ ! -L /tmp/agent99-host110-backup-runtime.lock ] || { echo "backup runtime gate unsafe" >&2; exit 69; }
(umask 077; : >> /tmp/agent99-host110-backup-runtime.lock)
[ -f /tmp/agent99-host110-backup-runtime.lock ] && [ -O /tmp/agent99-host110-backup-runtime.lock ] || { echo "backup runtime gate owner invalid" >&2; exit 69; }
exec 197>>/tmp/agent99-host110-backup-runtime.lock
runtime_lock_path="$(resolve_backup_runtime_script_path /tmp/agent99-host110-backup-runtime.lock)" \
|| { echo "backup runtime gate unavailable: lock identity unresolved" >&2; exit 69; }
if backup_runtime_inherited_fd_status "${runtime_lock_path}"; then
:
else
inherited_fd_status=$?
if [ "${inherited_fd_status}" -eq 1 ]; then
exec 197>>/tmp/agent99-host110-backup-runtime.lock
else
echo "backup runtime gate unsafe: inherited fd 197 identity mismatch" >&2
exit 69
fi
fi
flock -s -w 60 197 || { echo "backup runtime deployment is active; retry later" >&2; exit 75; }
export BACKUP_RUNTIME_SHARED_LOCK_HELD=1
fi
DUMP_ZIP="${AIOPS_GITEA_FULL_BACKUP_DRILL_DUMP_ZIP:-}"

View File

@@ -27,11 +27,46 @@ RUNTIME_LOCK = Path("/tmp/agent99-host110-backup-runtime.lock")
def acquire_runtime_lock():
if Path(__file__).resolve().parent != Path("/backup/scripts") or os.environ.get("BACKUP_RUNTIME_SHARED_LOCK_HELD") == "1":
if Path(__file__).resolve().parent != Path("/backup/scripts"):
return None
if RUNTIME_LOCK.is_symlink():
raise RuntimeError("backup_runtime_gate_unsafe")
handle = RUNTIME_LOCK.open("a+")
inherited_fd_path: Path | None = None
proc_fd_root = Path(f"/proc/{os.getpid()}/fd")
if proc_fd_root.is_dir():
candidate = proc_fd_root / "197"
if candidate.exists() or candidate.is_symlink():
inherited_fd_path = candidate
else:
candidate = Path("/dev/fd/197")
if candidate.exists() or candidate.is_symlink():
inherited_fd_path = candidate
if inherited_fd_path is None:
handle = RUNTIME_LOCK.open("a+")
else:
if not RUNTIME_LOCK.is_file():
raise RuntimeError("backup_runtime_gate_unavailable")
if proc_fd_root.is_dir():
try:
inherited_target = inherited_fd_path.resolve(strict=True)
expected_target = RUNTIME_LOCK.resolve(strict=True)
except OSError as exc:
raise RuntimeError("backup_runtime_inherited_fd_unresolved") from exc
else:
try:
raw_target = fcntl.fcntl(197, getattr(fcntl, "F_GETPATH", 50), b"\0" * 1024)
inherited_target = Path(os.fsdecode(raw_target.split(b"\0", 1)[0])).resolve(strict=True)
expected_target = RUNTIME_LOCK.resolve(strict=True)
except OSError as exc:
raise RuntimeError("backup_runtime_inherited_fd_unresolved") from exc
if inherited_target != expected_target:
raise RuntimeError("backup_runtime_inherited_fd_mismatch")
try:
handle = os.fdopen(os.dup(197), "a+")
except OSError as exc:
raise RuntimeError("backup_runtime_inherited_fd_unavailable") from exc
if RUNTIME_LOCK.stat().st_uid != os.getuid():
handle.close()
raise RuntimeError("backup_runtime_gate_owner_invalid")