Merge pull request #60 from ochenstarik-ui/codex/uninstall-mode
feat: add complete server uninstall mode
This commit is contained in:
commit
0bbee800dd
9 changed files with 470 additions and 19 deletions
|
|
@ -11,13 +11,18 @@ readonly ENROLLMENT_DIR="${STATE_DIR}-enrollment"
|
|||
readonly BACKUP_DIR="${STATE_DIR}/bootstrap-backups"
|
||||
readonly CONTROL_USER="ochenstarik-smm-control"
|
||||
readonly AGENT_USER="ochenstarik-smm-agent"
|
||||
readonly MONITOR_USER="ochenstarik-monitor"
|
||||
readonly MONITOR_HOME="/var/lib/ochenstarik-monitor"
|
||||
readonly CONTROL_UNIT="ochenstarik-smm-control.service"
|
||||
readonly AGENT_UNIT="ochenstarik-smm-agent.service"
|
||||
readonly PROVISIONING_HELPER_UNIT="ochenstarik-smm-provisioning-helper.service"
|
||||
readonly POLICY_HELPER="/usr/local/libexec/ochenstarik-smm-policy-apply"
|
||||
readonly EMERGENCY_COMMAND="/usr/local/sbin/ochenstarik-smm-emergency"
|
||||
readonly BOOTSTRAP_COMMAND="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"
|
||||
readonly METRICS_SCRIPT="/usr/local/libexec/ochenstarik-smm-metrics"
|
||||
readonly SUDOERS_FILE="/etc/sudoers.d/ochenstarik-smm-control"
|
||||
readonly SYSCTL_FILE="/etc/sysctl.d/90-ochenstarik-smm-mesh.conf"
|
||||
readonly SYSTEM_WG_CONFIG="/etc/wireguard/smm0.conf"
|
||||
readonly MESH_DIR="${STATE_DIR}/mesh"
|
||||
readonly WG_DIR="${ETC_DIR}/wireguard"
|
||||
readonly FIREWALL_UNIT="ochenstarik-smm-firewall.service"
|
||||
|
|
@ -91,6 +96,8 @@ Usage:
|
|||
ochenstarik-server-monitor-manager.sh status
|
||||
ochenstarik-server-monitor-manager.sh uninstall-agent [--purge]
|
||||
ochenstarik-server-monitor-manager.sh uninstall-control --confirm-destroy-control
|
||||
ochenstarik-server-monitor-manager.sh uninstall-system-plan [--purge-data]
|
||||
ochenstarik-server-monitor-manager.sh uninstall-system --confirm-uninstall [--purge-data --confirm-destroy-data]
|
||||
ochenstarik-server-monitor-manager.sh version
|
||||
|
||||
ARCHIVE must have a matching ARCHIVE.sha256 file. Agent enrollment reads the
|
||||
|
|
@ -1438,10 +1445,9 @@ install_monitor() {
|
|||
local public_key="$1"
|
||||
require_root
|
||||
validate_platform
|
||||
|
||||
local metrics_script="/usr/local/libexec/ochenstarik-smm-metrics"
|
||||
local monitor_user="ochenstarik-monitor"
|
||||
local monitor_home="/var/lib/ochenstarik-monitor"
|
||||
local metrics_script="$METRICS_SCRIPT"
|
||||
local monitor_user="$MONITOR_USER"
|
||||
local monitor_home="$MONITOR_HOME"
|
||||
|
||||
if ! id -u "$monitor_user" >/dev/null 2>&1; then
|
||||
useradd -r -s /usr/sbin/nologin -d "$monitor_home" -M "$monitor_user"
|
||||
|
|
@ -1516,9 +1522,9 @@ EOF
|
|||
|
||||
uninstall_monitor() {
|
||||
require_root
|
||||
local monitor_user="ochenstarik-monitor"
|
||||
local monitor_home="/var/lib/ochenstarik-monitor"
|
||||
local metrics_script="/usr/local/libexec/ochenstarik-smm-metrics"
|
||||
local monitor_user="$MONITOR_USER"
|
||||
local monitor_home="$MONITOR_HOME"
|
||||
local metrics_script="$METRICS_SCRIPT"
|
||||
|
||||
if id -u "$monitor_user" >/dev/null 2>&1; then
|
||||
userdel -f "$monitor_user" || true
|
||||
|
|
@ -1543,6 +1549,215 @@ uninstall_control() {
|
|||
log "Control role and its state were removed."
|
||||
}
|
||||
|
||||
owned_unit_exists() {
|
||||
local unit="$1"
|
||||
[[ -e "/etc/systemd/system/$unit" || -L "/etc/systemd/system/$unit" ]] && return 0
|
||||
systemctl list-unit-files "$unit" --no-legend 2>/dev/null | grep -q "^${unit}[[:space:]]"
|
||||
}
|
||||
|
||||
path_has_content() {
|
||||
local path="$1"
|
||||
[[ -f "$path" || -L "$path" ]] && return 0
|
||||
[[ -d "$path" ]] || return 1
|
||||
find "$path" -mindepth 1 -maxdepth 1 -print -quit 2>/dev/null | grep -q .
|
||||
}
|
||||
|
||||
uninstall_system_plan() {
|
||||
local purge_data="${1:-}" found=0 unit user path
|
||||
local -a units=(
|
||||
"$CONTROL_UNIT" "$AGENT_UNIT" "$PROVISIONING_HELPER_UNIT"
|
||||
"$FIREWALL_UNIT" wg-quick@smm0.service
|
||||
)
|
||||
local -a users=("$CONTROL_USER" "$AGENT_USER" "$MONITOR_USER")
|
||||
local -a files=(
|
||||
"$POLICY_HELPER" "$METRICS_SCRIPT" "$EMERGENCY_COMMAND" "$BOOTSTRAP_COMMAND"
|
||||
"$SUDOERS_FILE" "$SYSCTL_FILE" "$SYSTEM_WG_CONFIG"
|
||||
)
|
||||
local -a program_paths=("$LIB_DIR" "$MONITOR_HOME" "$ENROLLMENT_DIR")
|
||||
printf '%s\n' 'Server Monitor Manager objects found on this machine:'
|
||||
for unit in "${units[@]}"; do
|
||||
if owned_unit_exists "$unit"; then
|
||||
printf ' unit: %s\n' "$unit"
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
for user in "${users[@]}"; do
|
||||
if id -u "$user" >/dev/null 2>&1; then
|
||||
printf ' user/group: %s\n' "$user"
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
if command -v nft >/dev/null 2>&1 \
|
||||
&& nft list table inet ochenstarik_smm >/dev/null 2>&1; then
|
||||
printf '%s\n' ' nftables table: inet ochenstarik_smm'
|
||||
found=1
|
||||
fi
|
||||
if command -v ip >/dev/null 2>&1 && ip link show smm0 >/dev/null 2>&1; then
|
||||
printf '%s\n' ' network interface: smm0'
|
||||
found=1
|
||||
fi
|
||||
for path in "${files[@]}" "${program_paths[@]}"; do
|
||||
if path_has_content "$path"; then
|
||||
printf ' path: %s\n' "$path"
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
for path in "$ETC_DIR" "$STATE_DIR"; do
|
||||
if path_has_content "$path"; then
|
||||
if [[ "$purge_data" == "--purge-data" ]]; then
|
||||
printf ' data path: %s\n' "$path"
|
||||
else
|
||||
printf ' program files under: %s (Control database, backups, and CA are preserved)\n' "$path"
|
||||
fi
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
if [[ "$found" == "0" ]]; then
|
||||
printf '%s\n' ' nothing installed'
|
||||
fi
|
||||
}
|
||||
|
||||
stop_owned_services() {
|
||||
local unit
|
||||
local -a units=(
|
||||
"$CONTROL_UNIT" "$AGENT_UNIT" "$PROVISIONING_HELPER_UNIT"
|
||||
"$FIREWALL_UNIT" wg-quick@smm0.service
|
||||
)
|
||||
for unit in "${units[@]}"; do
|
||||
systemctl disable --now "$unit" >/dev/null 2>&1 || true
|
||||
done
|
||||
}
|
||||
|
||||
terminate_owned_processes() {
|
||||
local user running=0
|
||||
local -a users=("$CONTROL_USER" "$AGENT_USER" "$MONITOR_USER")
|
||||
for user in "${users[@]}"; do
|
||||
if id -u "$user" >/dev/null 2>&1 && pgrep -u "$user" >/dev/null 2>&1; then
|
||||
pkill -TERM -u "$user" || true
|
||||
running=1
|
||||
fi
|
||||
done
|
||||
[[ "$running" == "0" ]] || sleep 1
|
||||
for user in "${users[@]}"; do
|
||||
if id -u "$user" >/dev/null 2>&1 && pgrep -u "$user" >/dev/null 2>&1; then
|
||||
pkill -KILL -u "$user" || true
|
||||
fi
|
||||
done
|
||||
for user in "${users[@]}"; do
|
||||
if id -u "$user" >/dev/null 2>&1 && pgrep -u "$user" >/dev/null 2>&1; then
|
||||
fail "Processes owned by $user are still running."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
remove_owned_accounts() {
|
||||
local user
|
||||
local -a users=("$CONTROL_USER" "$AGENT_USER" "$MONITOR_USER")
|
||||
for user in "${users[@]}"; do
|
||||
if id -u "$user" >/dev/null 2>&1; then
|
||||
userdel "$user"
|
||||
fi
|
||||
done
|
||||
for user in "${users[@]}"; do
|
||||
if getent group "$user" >/dev/null 2>&1; then
|
||||
groupdel "$user" || log "Group $user remains because it is still in use."
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
remove_owned_network() {
|
||||
if command -v ip >/dev/null 2>&1 && ip link show smm0 >/dev/null 2>&1; then
|
||||
ip link delete smm0
|
||||
fi
|
||||
if command -v nft >/dev/null 2>&1 \
|
||||
&& nft list table inet ochenstarik_smm >/dev/null 2>&1; then
|
||||
nft delete table inet ochenstarik_smm
|
||||
fi
|
||||
}
|
||||
|
||||
remove_owned_files() {
|
||||
local purge_data="$1"
|
||||
rm -f -- \
|
||||
"/etc/systemd/system/$CONTROL_UNIT" \
|
||||
"/etc/systemd/system/$AGENT_UNIT" \
|
||||
"/etc/systemd/system/$PROVISIONING_HELPER_UNIT" \
|
||||
"/etc/systemd/system/$FIREWALL_UNIT" \
|
||||
"$POLICY_HELPER" "$METRICS_SCRIPT" "$EMERGENCY_COMMAND" \
|
||||
"$SUDOERS_FILE" "$SYSCTL_FILE" "$SYSTEM_WG_CONFIG"
|
||||
rm -rf -- "$LIB_DIR" "$MONITOR_HOME" "$ENROLLMENT_DIR"
|
||||
if [[ "$purge_data" == "--purge-data" ]]; then
|
||||
rm -rf -- "$ETC_DIR" "$STATE_DIR"
|
||||
else
|
||||
rm -f -- \
|
||||
"$ETC_DIR/agent.env" "$ETC_DIR/control.env" \
|
||||
"$ETC_DIR/control-public-url" "$ETC_DIR/control-server.pfx" \
|
||||
"$ETC_DIR/mesh.env" "$ETC_DIR/mesh.nft" \
|
||||
"$ETC_DIR/installed-version-agent" "$ETC_DIR/installed-version-control"
|
||||
rm -rf -- "$WG_DIR" "$STATE_DIR/agent" "$STATE_DIR/mesh" "$STATE_DIR/provisioning"
|
||||
fi
|
||||
rm -f -- "$BOOTSTRAP_COMMAND"
|
||||
rmdir "$ETC_DIR" "$STATE_DIR" 2>/dev/null || true
|
||||
systemctl daemon-reload
|
||||
systemctl reset-failed >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
port_state() {
|
||||
local port="$1"
|
||||
if command -v ss >/dev/null 2>&1 \
|
||||
&& ss -H -lntu "sport = :$port" 2>/dev/null | grep -q .; then
|
||||
printf '%s' 'in-use'
|
||||
else
|
||||
printf '%s' 'free'
|
||||
fi
|
||||
}
|
||||
|
||||
show_uninstall_result() {
|
||||
local unit user units=() users=()
|
||||
printf 'port-7443: %s\n' "$(port_state 7443)"
|
||||
printf 'port-51820: %s\n' "$(port_state 51820)"
|
||||
if command -v ip >/dev/null 2>&1 \
|
||||
&& ip route show 2>/dev/null | grep -Fq '10.77.0.'; then
|
||||
printf '%s\n' 'mesh-routes: present'
|
||||
else
|
||||
printf '%s\n' 'mesh-routes: absent'
|
||||
fi
|
||||
while IFS= read -r unit; do
|
||||
[[ -n "$unit" ]] && units+=("${unit%%[[:space:]]*}")
|
||||
done < <(systemctl list-unit-files 'ochenstarik-smm-*' --no-legend 2>/dev/null || true)
|
||||
if (( ${#units[@]} == 0 )); then
|
||||
printf '%s\n' 'owned-units: absent'
|
||||
else
|
||||
printf 'owned-units: present (%s)\n' "${units[*]}"
|
||||
fi
|
||||
for user in "$CONTROL_USER" "$AGENT_USER" "$MONITOR_USER"; do
|
||||
id -u "$user" >/dev/null 2>&1 && users+=("$user")
|
||||
done
|
||||
if (( ${#users[@]} == 0 )); then
|
||||
printf '%s\n' 'owned-users: absent'
|
||||
else
|
||||
printf 'owned-users: present (%s)\n' "${users[*]}"
|
||||
fi
|
||||
}
|
||||
|
||||
uninstall_system() {
|
||||
local confirmation="${1:-}" purge_data="${2:-}" data_confirmation="${3:-}"
|
||||
[[ "$confirmation" == "--confirm-uninstall" ]] \
|
||||
|| fail "System removal requires --confirm-uninstall"
|
||||
if [[ -n "$purge_data" ]]; then
|
||||
[[ "$purge_data" == "--purge-data" && "$data_confirmation" == "--confirm-destroy-data" ]] \
|
||||
|| fail "Permanent data removal requires --purge-data --confirm-destroy-data"
|
||||
fi
|
||||
require_root
|
||||
uninstall_system_plan "$purge_data"
|
||||
stop_owned_services
|
||||
terminate_owned_processes
|
||||
remove_owned_network
|
||||
remove_owned_files "$purge_data"
|
||||
remove_owned_accounts
|
||||
show_uninstall_result
|
||||
log "System removal completed${purge_data:+, including Control data and CA}."
|
||||
}
|
||||
|
||||
preflight() {
|
||||
validate_platform
|
||||
local command_name
|
||||
|
|
@ -1580,6 +1795,8 @@ main() {
|
|||
status) [[ $# -eq 0 ]] || fail "status takes no arguments"; show_status ;;
|
||||
uninstall-agent) [[ $# -eq 0 || ( $# -eq 1 && "$1" == "--purge" ) ]] || fail "uninstall-agent accepts only [--purge]"; uninstall_agent "${1:-}" ;;
|
||||
uninstall-control) [[ $# -eq 1 ]] || fail "uninstall-control requires confirmation"; uninstall_control "$1" ;;
|
||||
uninstall-system-plan) [[ $# -eq 0 || ( $# -eq 1 && "$1" == "--purge-data" ) ]] || fail "uninstall-system-plan accepts only [--purge-data]"; uninstall_system_plan "${1:-}" ;;
|
||||
uninstall-system) [[ $# -eq 1 || $# -eq 3 ]] || fail "uninstall-system requires --confirm-uninstall [--purge-data --confirm-destroy-data]"; uninstall_system "$@" ;;
|
||||
*) fail "Unknown action: $action (run with --help)" ;;
|
||||
esac
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ Usage:
|
|||
smm-setup.sh [--tag TAG] [--repository OWNER/REPO] COMMAND [ARG...]
|
||||
|
||||
With no arguments, an interactive terminal guides a complete Hub or Node
|
||||
installation. Non-interactive commands remain available:
|
||||
installation or a full removal. Non-interactive commands remain available:
|
||||
install-hub PUBLIC_HOST [HTTPS_PORT] [WG_PORT]
|
||||
install-node
|
||||
uninstall-system --confirm-uninstall [--purge-data --confirm-destroy-data]
|
||||
|
||||
Other commands are passed to the verified ochenstarik-server-monitor-manager.sh
|
||||
asset. Use -- before a command to force pass-through. Common commands:
|
||||
|
|
@ -45,6 +46,7 @@ smm-setup: interactive installation requires a terminal on stdin and stdout.
|
|||
For automation, choose an explicit command:
|
||||
sudo ./smm-setup.sh install-hub PUBLIC_HOST [HTTPS_PORT] [WG_PORT]
|
||||
sudo ./smm-setup.sh install-node
|
||||
sudo ./smm-setup.sh uninstall-system --confirm-uninstall
|
||||
Run ./smm-setup.sh --help for pass-through commands.
|
||||
HELP
|
||||
}
|
||||
|
|
@ -168,7 +170,7 @@ inspect_node_code() {
|
|||
choose_interactive_action() {
|
||||
local role host https_port wg_port existing default_host
|
||||
show_machine
|
||||
read -r -p 'Choose role: 1) Hub 2) Node: ' role
|
||||
read -r -p 'Choose role: 1) Hub 2) Node 3) Uninstall: ' role
|
||||
case "${role,,}" in
|
||||
1|hub)
|
||||
action=install-hub; existing="$(role_status Hub)"
|
||||
|
|
@ -186,7 +188,12 @@ choose_interactive_action() {
|
|||
[[ -n "$node_code" ]] || die "SMMNODE2 code is empty"
|
||||
action_args=()
|
||||
;;
|
||||
*) die "choose Hub (1) or Node (2)" ;;
|
||||
3|uninstall|remove)
|
||||
action=uninstall-system
|
||||
action_args=()
|
||||
return
|
||||
;;
|
||||
*) die "choose Hub (1), Node (2), or Uninstall (3)" ;;
|
||||
esac
|
||||
if [[ "$existing" == "installed" ]] && ! confirm "This role is already installed. Reinstall or update it?"; then
|
||||
printf 'No changes were made.\n'; exit 0
|
||||
|
|
@ -201,6 +208,31 @@ choose_interactive_action() {
|
|||
fi
|
||||
}
|
||||
|
||||
run_interactive_uninstall() {
|
||||
local depth answer
|
||||
local -a plan_args=()
|
||||
printf '\nRemoval depth:\n'
|
||||
printf '%s\n' ' 1) Remove programs and accounts; preserve Control database, backups, and CA'
|
||||
printf '%s\n' ' 2) Permanently remove everything, including Control database, backups, and CA'
|
||||
read -r -p 'Choose removal depth [1]: ' depth
|
||||
case "${depth:-1}" in
|
||||
1|preserve) ;;
|
||||
2|purge) plan_args=(--purge-data) ;;
|
||||
*) die "choose removal depth 1 or 2" ;;
|
||||
esac
|
||||
|
||||
printf '\n'
|
||||
"$cached_script" uninstall-system-plan "${plan_args[@]}"
|
||||
read -r -p 'Type UNINSTALL to remove the objects listed above: ' answer
|
||||
[[ "$answer" == "UNINSTALL" ]] || die "removal cancelled: confirmation word did not match"
|
||||
if (( ${#plan_args[@]} > 0 )); then
|
||||
read -r -p 'Type DESTROY-DATA to permanently delete Control data, backups, and CA: ' answer
|
||||
[[ "$answer" == "DESTROY-DATA" ]] || die "removal cancelled: data-destruction confirmation did not match"
|
||||
exec "$cached_script" uninstall-system --confirm-uninstall --purge-data --confirm-destroy-data
|
||||
fi
|
||||
exec "$cached_script" uninstall-system --confirm-uninstall
|
||||
}
|
||||
|
||||
original_count=$#
|
||||
pass_through=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
|
|
@ -232,6 +264,15 @@ if (( pass_through == 0 )); then
|
|||
case "$action" in
|
||||
install-hub) [[ ${#action_args[@]} -ge 1 && ${#action_args[@]} -le 3 ]] || die "install-hub requires PUBLIC_HOST [HTTPS_PORT] [WG_PORT]" ;;
|
||||
install-node) [[ ${#action_args[@]} -eq 0 ]] || die "install-node takes no arguments" ;;
|
||||
uninstall-system)
|
||||
if (( interactive == 0 )); then
|
||||
[[ ( ${#action_args[@]} -eq 1 && "${action_args[0]}" == "--confirm-uninstall" ) \
|
||||
|| ( ${#action_args[@]} -eq 3 && "${action_args[0]}" == "--confirm-uninstall" \
|
||||
&& "${action_args[1]}" == "--purge-data" \
|
||||
&& "${action_args[2]}" == "--confirm-destroy-data" ) ]] \
|
||||
|| die "uninstall-system requires --confirm-uninstall [--purge-data --confirm-destroy-data]"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
|
@ -311,5 +352,9 @@ case "$action" in
|
|||
exec "$cached_script" install-node "$archive"
|
||||
fi
|
||||
;;
|
||||
uninstall-system)
|
||||
if (( interactive == 1 )); then run_interactive_uninstall; fi
|
||||
exec "$cached_script" "$action" "${action_args[@]}"
|
||||
;;
|
||||
*) exec "$cached_script" "$action" "${action_args[@]}" ;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ chmod 700 smm-setup.sh
|
|||
sudo ./smm-setup.sh
|
||||
```
|
||||
|
||||
Интерактивный режим показывает дистрибутив, архитектуру, определённый публичный адрес и уже установленные роли, затем предлагает выбрать Hub или Node. Для Hub он предлагает публичный адрес и порты HTTPS `7443`/WireGuard `51820`. Для Node он принимает готовый `SMMNODE2`, показывает извлечённые адрес Hub и SHA-256 fingerprint CA и продолжает только после подтверждения оператора. Загрузки сопровождаются индикатором; системные зависимости и закреплённый `cosign` устанавливаются и проверяются до использования.
|
||||
Интерактивный режим показывает дистрибутив, архитектуру, определённый публичный адрес и уже установленные роли, затем предлагает выбрать Hub, Node или полное удаление. Для Hub он предлагает публичный адрес и порты HTTPS `7443`/WireGuard `51820`. Для Node он принимает готовый `SMMNODE2`, показывает извлечённые адрес Hub и SHA-256 fingerprint CA и продолжает только после подтверждения оператора. Загрузки сопровождаются индикатором; системные зависимости и закреплённый `cosign` устанавливаются и проверяются до использования.
|
||||
|
||||
После установки Hub установщик печатает код регистрации устройства `SMMDEV1-...` и fingerprint CA — оба значения предназначены для подключения приложения оператора. После установки Node он печатает `SMMPEER1...`; вставьте этот код в карточку соответствующего Node в приложении оператора.
|
||||
|
||||
|
|
@ -146,6 +146,25 @@ sudo ./ochenstarik-server-monitor-manager.sh uninstall-agent --purge
|
|||
sudo ./ochenstarik-server-monitor-manager.sh uninstall-control --confirm-destroy-control
|
||||
```
|
||||
|
||||
## Полное удаление с сервера
|
||||
|
||||
Запустите `sudo ./smm-setup.sh`, выберите `3) Uninstall`, затем глубину очистки. Перед изменениями установщик показывает найденные units, пользователей, сетевые объекты и каталоги проекта. Удаление начинается только после ввода слова `UNINSTALL`, а необратимое удаление данных — после дополнительного ввода `DESTROY-DATA`.
|
||||
|
||||
Обычная глубина останавливает сервисы, завершает процессы системных пользователей проекта и удаляет программы, accounts, интерфейс `smm0` и только nftables-таблицу `inet ochenstarik_smm`. При этом сохраняются база Control, резервные копии bootstrap и локальный Control CA. Полная глубина удаляет также эти данные без возможности восстановления. Объекты `3x-ui` и другие посторонние users, units, интерфейсы, таблицы и файлы не затрагиваются.
|
||||
|
||||
Для автоматизации интерактивных вопросов нет; обязательные флаги являются явным подтверждением:
|
||||
|
||||
```bash
|
||||
# Удалить программу, сохранив базу Control, backups и CA
|
||||
sudo ./smm-setup.sh uninstall-system --confirm-uninstall
|
||||
|
||||
# Необратимо удалить также базу Control, backups и CA
|
||||
sudo ./smm-setup.sh uninstall-system --confirm-uninstall \
|
||||
--purge-data --confirm-destroy-data
|
||||
```
|
||||
|
||||
Команда без `--confirm-uninstall`, а полная очистка без `--confirm-destroy-data`, завершается отказом до удаления. Повторный запуск на уже очищенной машине безопасен и возвращает успешный результат. В конце выводится состояние портов `7443` и `51820`, маршрутов `10.77.0.*`, units и пользователей проекта.
|
||||
|
||||
Update создаёт root-only backup перед заменой binaries и автоматически восстанавливает предыдущую версию, если сервис не запускается. Перед alpha-тестом на реальных серверах обязательно сохраните отдельную консольную/SSH-сессию и не закрывайте основной административный доступ firewall-правилами проекта.
|
||||
|
||||
## Локальное аварийное восстановление
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
set -Eeuo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
archive="${1:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}"
|
||||
bootstrap="${2:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}"
|
||||
archive="$(realpath "${1:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}")"
|
||||
bootstrap="$(realpath "${2:?usage: run-native-systemd-smoke.sh ARCHIVE BOOTSTRAP}")"
|
||||
port="${SMM_SMOKE_PORT:-17443}"
|
||||
system_bootstrap="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"
|
||||
probe_dir=""
|
||||
foreign_user="smm-uninstall-foreign"
|
||||
foreign_unit="smm-uninstall-foreign.service"
|
||||
|
||||
# If manifest+sig are not shipped alongside the archive (CI-only builds),
|
||||
# allow unsigned verification via .sha256 fallback.
|
||||
|
|
@ -19,8 +21,11 @@ cleanup() {
|
|||
if [[ -n "$probe_dir" ]]; then
|
||||
rm -rf -- "$probe_dir"
|
||||
fi
|
||||
sudo "$system_bootstrap" uninstall-agent --purge >/dev/null 2>&1 || true
|
||||
sudo "$system_bootstrap" uninstall-control --confirm-destroy-control >/dev/null 2>&1 || true
|
||||
sudo "$bootstrap" uninstall-system --confirm-uninstall --purge-data \
|
||||
--confirm-destroy-data >/dev/null 2>&1 || true
|
||||
sudo rm -f -- "/etc/systemd/system/$foreign_unit"
|
||||
sudo userdel "$foreign_user" >/dev/null 2>&1 || true
|
||||
sudo systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
|
|
@ -116,4 +121,30 @@ sudo curl --fail --silent --show-error --retry 15 --retry-all-errors --retry-del
|
|||
--cacert /etc/ochenstarik-server-monitor-manager/control-ca.crt \
|
||||
"https://127.0.0.1:$port/healthz"
|
||||
|
||||
sudo install -d -m 0700 /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups
|
||||
sudo touch /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups/smoke-preserve
|
||||
sudo useradd --system --no-create-home "$foreign_user"
|
||||
printf '[Unit]\nDescription=Foreign smoke unit\n' | sudo tee "/etc/systemd/system/$foreign_unit" >/dev/null
|
||||
sudo "$system_bootstrap" uninstall-system --confirm-uninstall
|
||||
sudo test -s /var/lib/ochenstarik-server-monitor-manager/control/control.db
|
||||
sudo test -f /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups/smoke-preserve
|
||||
sudo test -f /etc/ochenstarik-server-monitor-manager/control-ca.pfx
|
||||
sudo test -f /etc/ochenstarik-server-monitor-manager/control-ca.crt
|
||||
sudo id "$foreign_user"
|
||||
sudo test -f "/etc/systemd/system/$foreign_unit"
|
||||
if sudo ss -H -ltn "sport = :$port" | grep -q .; then
|
||||
printf 'Control port survived uninstall: %s\n' "$port" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo "$bootstrap" uninstall-system --confirm-uninstall --purge-data --confirm-destroy-data
|
||||
sudo test ! -e /etc/ochenstarik-server-monitor-manager
|
||||
sudo test ! -e /var/lib/ochenstarik-server-monitor-manager
|
||||
sudo test ! -e /var/lib/ochenstarik-server-monitor-manager-enrollment
|
||||
sudo id "$foreign_user"
|
||||
sudo test -f "/etc/systemd/system/$foreign_unit"
|
||||
empty_uninstall_output="$(sudo "$bootstrap" uninstall-system --confirm-uninstall \
|
||||
--purge-data --confirm-destroy-data)"
|
||||
grep -Fq 'nothing installed' <<<"$empty_uninstall_output"
|
||||
|
||||
printf '%s\n' "NATIVE_SYSTEMD_SMOKE=PASS"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ port="17443"
|
|||
smoke_dir="/root/smm-smoke"
|
||||
remote_archive="$smoke_dir/release.tar.gz"
|
||||
remote_bootstrap="$smoke_dir/ochenstarik-server-monitor-manager.sh"
|
||||
system_bootstrap="/usr/local/sbin/ochenstarik-server-monitor-manager.sh"
|
||||
|
||||
cleanup() {
|
||||
docker rm -f "$name" >/dev/null 2>&1 || true
|
||||
|
|
@ -78,4 +79,63 @@ docker exec "$name" curl --fail --silent --show-error --retry 15 --retry-all-err
|
|||
"https://127.0.0.1:$port/healthz"
|
||||
docker exec "$name" /usr/local/sbin/ochenstarik-smm-emergency status
|
||||
|
||||
# Exercise both removal depths with live services, owned network objects, and
|
||||
# similarly placed foreign objects that must survive.
|
||||
docker exec "$name" install -d -m 0700 /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups
|
||||
docker exec "$name" touch /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups/smoke-preserve
|
||||
docker exec "$name" useradd --system --user-group --no-create-home ochenstarik-smm-agent
|
||||
docker exec "$name" useradd --system --user-group --no-create-home ochenstarik-monitor
|
||||
docker exec "$name" install -d -m 0700 -o ochenstarik-monitor -g ochenstarik-monitor /var/lib/ochenstarik-monitor
|
||||
docker exec "$name" useradd --system --no-create-home smm-uninstall-foreign
|
||||
docker exec "$name" sh -c "printf '[Unit]\nDescription=Foreign smoke unit\n' >/etc/systemd/system/smm-uninstall-foreign.service"
|
||||
docker exec "$name" ip link add smm0 type dummy
|
||||
docker exec "$name" nft add table inet ochenstarik_smm
|
||||
|
||||
docker exec "$name" "$system_bootstrap" uninstall-system --confirm-uninstall
|
||||
docker exec "$name" test -s /var/lib/ochenstarik-server-monitor-manager/control/control.db
|
||||
docker exec "$name" test -f /var/lib/ochenstarik-server-monitor-manager/bootstrap-backups/smoke-preserve
|
||||
docker exec "$name" test -f /etc/ochenstarik-server-monitor-manager/control-ca.pfx
|
||||
docker exec "$name" test -f /etc/ochenstarik-server-monitor-manager/control-ca.crt
|
||||
docker exec "$name" id smm-uninstall-foreign
|
||||
docker exec "$name" test -f /etc/systemd/system/smm-uninstall-foreign.service
|
||||
docker exec "$name" sh -c "! ss -H -ltn 'sport = :$port' | grep -q ."
|
||||
for owned_user in ochenstarik-smm-control ochenstarik-smm-agent ochenstarik-monitor; do
|
||||
if docker exec "$name" id "$owned_user" >/dev/null 2>&1; then
|
||||
printf 'owned user survived uninstall: %s\n' "$owned_user" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
for owned_unit in ochenstarik-smm-control.service ochenstarik-smm-agent.service \
|
||||
ochenstarik-smm-provisioning-helper.service ochenstarik-smm-firewall.service; do
|
||||
if docker exec "$name" test -e "/etc/systemd/system/$owned_unit"; then
|
||||
printf 'owned unit survived uninstall: %s\n' "$owned_unit" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if docker exec "$name" ip link show smm0 >/dev/null 2>&1; then
|
||||
printf '%s\n' 'owned smm0 interface survived uninstall' >&2
|
||||
exit 1
|
||||
fi
|
||||
if docker exec "$name" nft list table inet ochenstarik_smm >/dev/null 2>&1; then
|
||||
printf '%s\n' 'owned nftables table survived uninstall' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker exec "$name" "$remote_bootstrap" uninstall-system --confirm-uninstall \
|
||||
--purge-data --confirm-destroy-data
|
||||
for owned_path in /etc/ochenstarik-server-monitor-manager \
|
||||
/var/lib/ochenstarik-server-monitor-manager \
|
||||
/var/lib/ochenstarik-server-monitor-manager-enrollment \
|
||||
/var/lib/ochenstarik-monitor; do
|
||||
if docker exec "$name" test -e "$owned_path"; then
|
||||
printf 'owned path survived purge: %s\n' "$owned_path" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
docker exec "$name" id smm-uninstall-foreign
|
||||
docker exec "$name" test -f /etc/systemd/system/smm-uninstall-foreign.service
|
||||
empty_uninstall_output="$(docker exec "$name" "$remote_bootstrap" uninstall-system \
|
||||
--confirm-uninstall --purge-data --confirm-destroy-data)"
|
||||
grep -Fq 'nothing installed' <<<"$empty_uninstall_output"
|
||||
|
||||
printf '%s\n' "SYSTEMD_CONTAINER_SMOKE=PASS image=$base_image"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ ENV DEBIAN_FRONTEND=noninteractive
|
|||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl iproute2 openssl procps sudo systemd systemd-sysv \
|
||||
ca-certificates curl iproute2 nftables openssl procps sudo systemd systemd-sysv \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,15 @@ emergency="$root/deploy/ochenstarik-smm-emergency"
|
|||
acceptance="$root/tests/acceptance/three-server-mesh.sh"
|
||||
unified_setup="$root/deploy/smm-setup.sh"
|
||||
unified_test="$root/tests/bootstrap/test-unified-installer.sh"
|
||||
uninstall_test="$root/tests/bootstrap/test-uninstall-mode.sh"
|
||||
|
||||
bash -n "$unified_setup" "$unified_test"
|
||||
bash -n "$unified_setup" "$unified_test" "$uninstall_test"
|
||||
if command -v shellcheck >/dev/null 2>&1; then
|
||||
shellcheck --severity=error "$unified_setup" "$unified_test"
|
||||
shellcheck --severity=error "$unified_setup" "$unified_test" "$uninstall_test"
|
||||
fi
|
||||
if [[ "$(uname -s)" != MINGW* ]]; then
|
||||
bash "$unified_test"
|
||||
bash "$uninstall_test"
|
||||
fi
|
||||
|
||||
grep -Fq 'listing="$(/usr/sbin/nft -a list chain' "$helper" || {
|
||||
|
|
@ -147,7 +149,8 @@ if ! bash "$bootstrap" >/dev/null 2>&1; then
|
|||
exit 1
|
||||
fi
|
||||
for action in verify-release install-control install-agent install-node mesh-init peer-add \
|
||||
update-control update-agent rollback node-code control-device-code node-token uninstall-control; do
|
||||
update-control update-agent rollback node-code control-device-code node-token uninstall-control \
|
||||
uninstall-system; do
|
||||
if bash "$bootstrap" "$action" >/dev/null 2>&1; then
|
||||
printf 'bootstrap action accepted missing arguments: %s\n' "$action" >&2
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -20,6 +20,12 @@ case "$1" in
|
|||
[[ -n "${SMM_ENROLL_CODE:-}" && "${SMM_ACCEPT_CA_FINGERPRINT:-}" == 1 ]]
|
||||
printf '%s\n' 'SMMPEER1.test-node.test-address.test-key'
|
||||
;;
|
||||
uninstall-system-plan)
|
||||
printf '%s\n' 'Server Monitor Manager objects found on this machine:'
|
||||
printf '%s\n' ' unit: ochenstarik-smm-control.service'
|
||||
[[ "${2:-}" != "--purge-data" ]] || printf '%s\n' ' data path: /var/lib/ochenstarik-server-monitor-manager'
|
||||
;;
|
||||
uninstall-system) printf 'FAKE_UNINSTALL=%s\n' "$*" ;;
|
||||
*) printf 'PASSTHROUGH=%s\n' "$*" ;;
|
||||
esac
|
||||
INNER
|
||||
|
|
@ -101,6 +107,21 @@ grep -Fq 'CA SHA-256:' "$fixture/node.out"
|
|||
grep -Fq 'SMMPEER1.test-node.test-address.test-key' "$fixture/node.out"
|
||||
grep -Fq 'operator application' "$fixture/node.out"
|
||||
|
||||
run_tty $'3\n1\nUNINSTALL\n' "$fixture/uninstall-preserve.out"
|
||||
grep -Fq 'unit: ochenstarik-smm-control.service' "$fixture/uninstall-preserve.out"
|
||||
grep -Fq 'FAKE_UNINSTALL=uninstall-system --confirm-uninstall' "$fixture/uninstall-preserve.out"
|
||||
|
||||
run_tty $'3\n2\nUNINSTALL\nDESTROY-DATA\n' "$fixture/uninstall-purge.out"
|
||||
grep -Fq 'data path: /var/lib/ochenstarik-server-monitor-manager' "$fixture/uninstall-purge.out"
|
||||
grep -Fq 'FAKE_UNINSTALL=uninstall-system --confirm-uninstall --purge-data --confirm-destroy-data' "$fixture/uninstall-purge.out"
|
||||
|
||||
if PATH="$fixture/bin:$PATH" FIXTURE_RELEASE="$fixture/release" SMM_CACHE_DIR="$fixture/cache" \
|
||||
bash "$setup" uninstall-system >"$fixture/uninstall-refused.out" 2>&1; then
|
||||
printf '%s\n' 'uninstall without confirmation flag unexpectedly succeeded' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq 'requires --confirm-uninstall' "$fixture/uninstall-refused.out"
|
||||
|
||||
tampered_ca_part="$(printf '%s' 'not-a-certificate' | b64url)"
|
||||
tampered_code="SMMNODE2.$control_part.$tampered_ca_part.$node_part.$token_part.$endpoint_part.$hub_key_part.$address_part.$network_part"
|
||||
for bad_code in "${valid_code%.*}" "$valid_code.extra" "$tampered_code"; do
|
||||
|
|
|
|||
55
tests/bootstrap/test-uninstall-mode.sh
Normal file
55
tests/bootstrap/test-uninstall-mode.sh
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
bootstrap="$root/deploy/ochenstarik-server-monitor-manager.sh"
|
||||
setup="$root/deploy/smm-setup.sh"
|
||||
refusal_output="$(mktemp -t smm-uninstall-refused.XXXXXXXX)"
|
||||
trap 'rm -f -- "$refusal_output"' EXIT
|
||||
|
||||
bash -n "$bootstrap" "$setup"
|
||||
|
||||
if bash "$bootstrap" uninstall-system >/dev/null 2>"$refusal_output"; then
|
||||
printf '%s\n' 'uninstall-system accepted a request without confirmation' >&2
|
||||
exit 1
|
||||
fi
|
||||
grep -Fq -- '--confirm-uninstall' "$refusal_output"
|
||||
|
||||
definition="$(awk '
|
||||
/^uninstall_system\(\) \{/ { active=1 }
|
||||
active { print }
|
||||
active && /^}$/ { exit }
|
||||
' "$bootstrap")"
|
||||
stop_line="$(grep -Fn ' stop_owned_services' <<<"$definition" | cut -d: -f1)"
|
||||
term_line="$(grep -Fn ' terminate_owned_processes' <<<"$definition" | cut -d: -f1)"
|
||||
network_line="$(grep -Fn ' remove_owned_network' <<<"$definition" | cut -d: -f1)"
|
||||
files_line="$(grep -Fn ' remove_owned_files' <<<"$definition" | cut -d: -f1)"
|
||||
accounts_line="$(grep -Fn ' remove_owned_accounts' <<<"$definition" | cut -d: -f1)"
|
||||
(( stop_line < term_line && term_line < network_line && network_line < files_line && files_line < accounts_line ))
|
||||
|
||||
grep -Fq 'pkill -TERM -u "$user"' "$bootstrap"
|
||||
grep -Fq 'pkill -KILL -u "$user"' "$bootstrap"
|
||||
grep -Fq 'nft delete table inet ochenstarik_smm' "$bootstrap"
|
||||
grep -Fq 'ip link delete smm0' "$bootstrap"
|
||||
grep -Fq 'port-7443:' "$bootstrap"
|
||||
grep -Fq 'port-51820:' "$bootstrap"
|
||||
grep -Fq 'mesh-routes:' "$bootstrap"
|
||||
grep -Fq 'owned-units:' "$bootstrap"
|
||||
grep -Fq 'owned-users:' "$bootstrap"
|
||||
grep -Fq 'control-ca.pfx' "$bootstrap"
|
||||
grep -Fq 'bootstrap-backups' "$bootstrap"
|
||||
grep -Fq 'Type UNINSTALL' "$setup"
|
||||
grep -Fq 'Type DESTROY-DATA' "$setup"
|
||||
grep -Fq '3) Uninstall' "$setup"
|
||||
|
||||
if grep -Eiq '3x-ui|x-ui' "$bootstrap" "$setup"; then
|
||||
printf '%s\n' 'uninstall implementation references protected 3x-ui objects' >&2
|
||||
exit 1
|
||||
fi
|
||||
if grep -Eq "rm -rf --? [\"']?/(etc|var|usr)(/|[\"']|$)" "$bootstrap"; then
|
||||
printf '%s\n' 'uninstall implementation contains a broad recursive deletion' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' 'UNINSTALL_MODE=PASS'
|
||||
Loading…
Reference in a new issue