server-monitor-manager/deploy/ochenstarik-smm-emergency
ochenstarik-ui b11c277ac7
feat(control): add background link reconciliation (#12)
Co-authored-by: Ochenstarik <ochenstarik@inbox.ru>
2026-08-03 13:55:48 +07:00

153 lines
5.3 KiB
Bash
Executable file

#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
umask 077
readonly PROGRAM="ochenstarik-smm-emergency"
readonly STATE_DIR="/var/lib/ochenstarik-server-monitor-manager"
readonly ETC_DIR="/etc/ochenstarik-server-monitor-manager"
readonly MARKER="$STATE_DIR/mesh/emergency-disabled"
readonly RECONCILE_MARKER="$STATE_DIR/mesh/reconcile-requested"
readonly RECONCILE_LOCK="$STATE_DIR/mesh/reconcile-requested.lock"
readonly CONTROL_UNIT="ochenstarik-smm-control.service"
readonly AGENT_UNIT="ochenstarik-smm-agent.service"
readonly FIREWALL_UNIT="ochenstarik-smm-firewall.service"
readonly WIREGUARD_UNIT="wg-quick@smm0.service"
readonly NFT_FAMILY="inet"
readonly NFT_TABLE="ochenstarik_smm"
fail() { printf '%s: %s\n' "$PROGRAM" "$*" >&2; exit 78; }
log() { printf '%s: %s\n' "$PROGRAM" "$*"; }
usage() {
cat <<'EOF'
Local Server Monitor Manager emergency recovery
Usage:
ochenstarik-smm-emergency status
ochenstarik-smm-emergency mesh-disable
ochenstarik-smm-emergency mesh-enable
ochenstarik-smm-emergency firewall-restore
Commands use only Server Monitor Manager-owned units, interface and nftables
table. They do not modify the host SSH service or unrelated firewall rules.
EOF
}
require_root() {
[[ ${EUID:-$(id -u)} -eq 0 ]] || fail "this action must run as root (use sudo)"
}
unit_state() {
local unit="$1"
if systemctl list-unit-files "$unit" --no-legend 2>/dev/null | grep -q "^$unit"; then
systemctl is-active "$unit" 2>/dev/null || true
else
printf '%s\n' "not-installed"
fi
}
show_status() {
local unit
for unit in "$CONTROL_UNIT" "$AGENT_UNIT" "$FIREWALL_UNIT" "$WIREGUARD_UNIT"; do
printf '%s: %s\n' "$unit" "$(unit_state "$unit")"
done
if command -v ip >/dev/null 2>&1 && ip link show smm0 >/dev/null 2>&1; then
printf '%s\n' "mesh-interface: present"
else
printf '%s\n' "mesh-interface: absent"
fi
if command -v nft >/dev/null 2>&1 && nft list table "$NFT_FAMILY" "$NFT_TABLE" >/dev/null 2>&1; then
printf '%s\n' "mesh-firewall: loaded"
else
printf '%s\n' "mesh-firewall: absent"
fi
[[ -f "$MARKER" ]] && printf '%s\n' "emergency-lock: active" || printf '%s\n' "emergency-lock: inactive"
printf '%s\n' "backups:"
find "$STATE_DIR/bootstrap-backups" -maxdepth 1 -type f \
\( -name '*.tar.gz' -o -name '*.empty' \) -printf ' %f\n' 2>/dev/null | sort -r | head -n 10 || true
}
delete_project_firewall() {
if command -v nft >/dev/null 2>&1; then
nft delete table "$NFT_FAMILY" "$NFT_TABLE" 2>/dev/null || true
fi
}
request_reconciliation() {
local marker_directory temporary_marker generation
marker_directory="$(dirname "$RECONCILE_MARKER")"
install -d -o root -g root -m 0700 "$marker_directory"
exec 9>"$RECONCILE_LOCK"
chown root:root "$RECONCILE_LOCK"
chmod 0600 "$RECONCILE_LOCK"
/usr/bin/flock -x 9
generation="$(</proc/sys/kernel/random/uuid)"
temporary_marker="$(mktemp "$marker_directory/.reconcile-requested.XXXXXXXX")"
if ! printf '%s\n' "$generation" >"$temporary_marker" \
|| ! chown root:root "$temporary_marker" \
|| ! chmod 0600 "$temporary_marker" \
|| ! mv -f -- "$temporary_marker" "$RECONCILE_MARKER"; then
rm -f -- "$temporary_marker"
fail "could not create the Link reconciliation request"
fi
}
mesh_disable() {
require_root
systemctl disable --now "$WIREGUARD_UNIT" 2>/dev/null || true
systemctl disable --now "$FIREWALL_UNIT" 2>/dev/null || true
delete_project_firewall
install -d -m 0700 "$(dirname "$MARKER")"
printf '%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >"$MARKER"
chmod 0600 "$MARKER"
log "Mesh disabled locally; Control and SSH were not changed."
}
restore_project_firewall() {
require_root
[[ -f "$ETC_DIR/mesh.nft" ]] || fail "managed firewall configuration is missing"
command -v nft >/dev/null 2>&1 || fail "nft is unavailable"
if ! nft list table "$NFT_FAMILY" "$NFT_TABLE" >/dev/null 2>&1; then
nft --check -f "$ETC_DIR/mesh.nft"
fi
delete_project_firewall
if ! nft -f "$ETC_DIR/mesh.nft"; then
systemctl disable --now "$WIREGUARD_UNIT" 2>/dev/null || true
fail "managed firewall restore failed; Mesh was disabled to fail closed"
fi
if systemctl list-unit-files "$FIREWALL_UNIT" --no-legend 2>/dev/null | grep -q "^$FIREWALL_UNIT"; then
systemctl enable "$FIREWALL_UNIT" >/dev/null
fi
request_reconciliation
log "Base deny-by-default Mesh firewall restored; Control must reconcile active Links."
}
mesh_enable() {
require_root
[[ -f /etc/wireguard/smm0.conf ]] || fail "WireGuard smm0 configuration is missing"
if [[ -f "$ETC_DIR/mesh.nft" ]]; then
restore_project_firewall
fi
systemctl enable "$WIREGUARD_UNIT" >/dev/null
systemctl restart "$WIREGUARD_UNIT"
request_reconciliation
rm -f -- "$MARKER"
log "Mesh enabled locally."
}
main() {
local action="${1:-help}"
shift || true
[[ $# -eq 0 ]] || fail "unexpected arguments"
case "$action" in
help|-h|--help) usage ;;
status) show_status ;;
mesh-disable) mesh_disable ;;
mesh-enable) mesh_enable ;;
firewall-restore) restore_project_firewall ;;
*) fail "unknown action: $action" ;;
esac
}
main "$@"