Add role-aware update rollback and uninstall
This commit is contained in:
parent
71cc35e986
commit
01968d79a7
5 changed files with 172 additions and 12 deletions
3
.github/workflows/bash-syntax.yml
vendored
3
.github/workflows/bash-syntax.yml
vendored
|
|
@ -49,3 +49,6 @@ jobs:
|
||||||
|
|
||||||
- name: Test Server Monitor Manager Link policies
|
- name: Test Server Monitor Manager Link policies
|
||||||
run: bash tests/test-monitor-manager-links.sh
|
run: bash tests/test-monitor-manager-links.sh
|
||||||
|
|
||||||
|
- name: Test Server Monitor Manager lifecycle
|
||||||
|
run: bash tests/test-monitor-manager-lifecycle.sh
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -111,6 +111,16 @@ sudo ochenstarik-smm links
|
||||||
|
|
||||||
The Node installer creates its private WireGuard key locally and prints an `SMMREQ1` request. In a second terminal run `sudo ochenstarik-smm node-enroll` on the Hub, paste the request at the hidden prompt, and return the resulting `SMMACK1` code to the Node installer. The enrollment code expires after 10 minutes and is consumed by the first successful registration.
|
The Node installer creates its private WireGuard key locally and prints an `SMMREQ1` request. In a second terminal run `sudo ochenstarik-smm node-enroll` on the Hub, paste the request at the hidden prompt, and return the resulting `SMMACK1` code to the Node installer. The enrollment code expires after 10 minutes and is consumed by the first successful registration.
|
||||||
|
|
||||||
|
Role-aware maintenance commands create a root-only backup before destructive changes:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh update
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh rollback
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-node
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-hub
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-monitor
|
||||||
|
```
|
||||||
|
|
||||||
## Installation flow
|
## Installation flow
|
||||||
|
|
||||||
1. Select the wizard dialog language.
|
1. Select the wizard dialog language.
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,16 @@ sudo ochenstarik-smm links
|
||||||
|
|
||||||
Установщик Node создаст приватный WireGuard-ключ локально и покажет запрос `SMMREQ1`. Во втором терминале выполните на Hub `sudo ochenstarik-smm node-enroll`, вставьте запрос по скрытому приглашению и верните полученный код `SMMACK1` в установщик Node. Enrollment-код действует 10 минут и погашается при первой успешной регистрации.
|
Установщик Node создаст приватный WireGuard-ключ локально и покажет запрос `SMMREQ1`. Во втором терминале выполните на Hub `sudo ochenstarik-smm node-enroll`, вставьте запрос по скрытому приглашению и верните полученный код `SMMACK1` в установщик Node. Enrollment-код действует 10 минут и погашается при первой успешной регистрации.
|
||||||
|
|
||||||
|
Команды обслуживания учитывают установленную роль и создают root-only backup перед изменениями:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh update
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh rollback
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-node
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-hub
|
||||||
|
sudo ./ochenstarik-server-monitor-manager.sh uninstall-monitor
|
||||||
|
```
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
|
|
||||||
- Ubuntu Server или совместимая Debian-система с `systemd`;
|
- Ubuntu Server или совместимая Debian-система с `systemd`;
|
||||||
|
|
|
||||||
|
|
@ -59,14 +59,14 @@ read_public_key() {
|
||||||
install_monitor_dependencies() {
|
install_monitor_dependencies() {
|
||||||
apt-get update
|
apt-get update
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
ca-certificates openssh-server openssh-client coreutils gawk
|
ca-certificates openssh-server openssh-client coreutils gawk sudo
|
||||||
systemctl enable --now ssh
|
systemctl enable --now ssh
|
||||||
}
|
}
|
||||||
|
|
||||||
install_mesh_dependencies() {
|
install_mesh_dependencies() {
|
||||||
apt-get update
|
apt-get update
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
wireguard-tools nftables iproute2 jq openssl
|
wireguard-tools nftables iproute2 iputils-ping jq openssl
|
||||||
}
|
}
|
||||||
|
|
||||||
verify_public_key() {
|
verify_public_key() {
|
||||||
|
|
@ -857,14 +857,84 @@ install_server_part() {
|
||||||
show_status
|
show_status
|
||||||
}
|
}
|
||||||
|
|
||||||
uninstall_server_part() {
|
confirm_action() {
|
||||||
local answer
|
local prompt="$1" answer
|
||||||
if [[ -r /dev/tty ]]; then
|
if [[ -r /dev/tty ]]; then
|
||||||
IFS= read -r -p 'Удалить пользователя мониторинга и forced-command? [y/N]: ' answer < /dev/tty
|
IFS= read -r -p "$prompt [y/N]: " answer < /dev/tty
|
||||||
else
|
else
|
||||||
die "Для удаления требуется интерактивный терминал"
|
die "Для этой операции требуется интерактивный терминал"
|
||||||
fi
|
fi
|
||||||
[[ "$answer" =~ ^[Yy]$ ]] || { log "Отменено"; return 0; }
|
[[ "$answer" =~ ^[Yy]$ ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
backup_state() {
|
||||||
|
local backup_dir="/var/backups/${APP_NAME}" backup_file timestamp path
|
||||||
|
local -a paths=()
|
||||||
|
install -d -m 0700 -o root -g root "$backup_dir"
|
||||||
|
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
|
backup_file="${backup_dir}/${timestamp}.tar.gz"
|
||||||
|
for path in \
|
||||||
|
"$MESH_DIR" "$MONITOR_HOME" "$MONITOR_COMMAND" "$HUB_HELPER" "$HUB_CLI" "$WG_CONFIG" \
|
||||||
|
/etc/systemd/system/ochenstarik-smm-firewall.service \
|
||||||
|
/etc/systemd/system/ochenstarik-smm-firewall.timer \
|
||||||
|
/etc/sudoers.d/ochenstarik-smm-hub \
|
||||||
|
/etc/sysctl.d/90-ochenstarik-smm-forward.conf; do
|
||||||
|
[[ -e "$path" || -L "$path" ]] && paths+=("${path#/}")
|
||||||
|
done
|
||||||
|
((${#paths[@]} > 0)) || { warn "Нет установленных файлов для backup"; return 0; }
|
||||||
|
tar -C / -czf "$backup_file" -- "${paths[@]}"
|
||||||
|
chmod 0600 "$backup_file"
|
||||||
|
log "Backup: $backup_file"
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback_state() {
|
||||||
|
local backup_dir="/var/backups/${APP_NAME}" latest
|
||||||
|
latest="$(find "$backup_dir" -maxdepth 1 -type f -name '*.tar.gz' -printf '%T@ %p\n' 2>/dev/null \
|
||||||
|
| sort -nr | awk 'NR == 1 { sub(/^[^ ]+ /, ""); print }')"
|
||||||
|
[[ -n "$latest" && -f "$latest" ]] || die "Backup для rollback не найден"
|
||||||
|
confirm_action "Восстановить $latest" || { log "Отменено"; return 0; }
|
||||||
|
tar -C / -xzf "$latest"
|
||||||
|
systemctl daemon-reload
|
||||||
|
[[ ! -f "$WG_CONFIG" ]] || systemctl restart "wg-quick@${WG_INTERFACE}.service"
|
||||||
|
[[ ! -x "$HUB_HELPER" ]] || "$HUB_HELPER" firewall-restore
|
||||||
|
[[ ! -x "$MONITOR_COMMAND" ]] || systemctl reload ssh
|
||||||
|
log "Rollback завершён: $latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
existing_public_key() {
|
||||||
|
[[ -r "$AUTHORIZED_KEYS" ]] || die "Не найден существующий SSH-ключ мониторинга"
|
||||||
|
sed -n 's/^.*\(ssh-ed25519 [A-Za-z0-9+\/=]*.*\)$/\1/p' "$AUTHORIZED_KEYS" | head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
|
update_installed() {
|
||||||
|
local role=monitor
|
||||||
|
backup_state
|
||||||
|
SERVER_MONITOR_PUBLIC_KEY="$(existing_public_key)"
|
||||||
|
export SERVER_MONITOR_PUBLIC_KEY
|
||||||
|
if [[ -r "$HUB_CONFIG" ]]; then
|
||||||
|
role=hub
|
||||||
|
SMM_HUB_ENDPOINT="$(awk -F= '$1 == "HUB_ENDPOINT" { print $2; exit }' "$HUB_CONFIG")"
|
||||||
|
SMM_WG_PORT="$(awk -F= '$1 == "WG_PORT" { print $2; exit }' "$HUB_CONFIG")"
|
||||||
|
export SMM_HUB_ENDPOINT SMM_WG_PORT
|
||||||
|
install_hub
|
||||||
|
elif [[ -r "$MESH_DIR/node.conf" ]]; then
|
||||||
|
role=node
|
||||||
|
install_monitoring
|
||||||
|
install_mesh_dependencies
|
||||||
|
systemctl enable --now "wg-quick@${WG_INTERFACE}.service"
|
||||||
|
systemctl restart "wg-quick@${WG_INTERFACE}.service"
|
||||||
|
else
|
||||||
|
install_server_part
|
||||||
|
fi
|
||||||
|
unset SERVER_MONITOR_PUBLIC_KEY SMM_HUB_ENDPOINT SMM_WG_PORT
|
||||||
|
log "Обновление роли $role завершено"
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_monitor() {
|
||||||
|
[[ ! -r "$HUB_CONFIG" && ! -r "$MESH_DIR/node.conf" ]] \
|
||||||
|
|| die "Сначала удалите роль командой uninstall-hub или uninstall-node"
|
||||||
|
confirm_action "Удалить пользователя мониторинга и forced-command" || { log "Отменено"; return 0; }
|
||||||
|
backup_state
|
||||||
rm -f -- "$MONITOR_COMMAND"
|
rm -f -- "$MONITOR_COMMAND"
|
||||||
if getent passwd "$MONITOR_USER" >/dev/null; then
|
if getent passwd "$MONITOR_USER" >/dev/null; then
|
||||||
userdel --remove "$MONITOR_USER" 2>/dev/null || userdel "$MONITOR_USER"
|
userdel --remove "$MONITOR_USER" 2>/dev/null || userdel "$MONITOR_USER"
|
||||||
|
|
@ -872,18 +942,63 @@ uninstall_server_part() {
|
||||||
log "Серверная часть мониторинга удалена"
|
log "Серверная часть мониторинга удалена"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uninstall_node() {
|
||||||
|
local ssh_port
|
||||||
|
[[ -r "$MESH_DIR/node.conf" ]] || die "Роль Node не установлена"
|
||||||
|
confirm_action "Отключить и удалить WireGuard Node" || { log "Отменено"; return 0; }
|
||||||
|
backup_state
|
||||||
|
systemctl disable --now "wg-quick@${WG_INTERFACE}.service" 2>/dev/null || true
|
||||||
|
ssh_port="$(sshd -T 2>/dev/null | awk '$1 == "port" { print $2; exit }')"
|
||||||
|
if command -v ufw >/dev/null 2>&1 && [[ -n "$ssh_port" ]]; then
|
||||||
|
ufw --force delete allow in on "$WG_INTERFACE" to any port "$ssh_port" proto tcp >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
rm -f -- "$WG_CONFIG" "$MESH_DIR/node.conf"
|
||||||
|
rmdir "$MESH_DIR" 2>/dev/null || true
|
||||||
|
log "WireGuard Node удалён; monitoring identity оставлена"
|
||||||
|
}
|
||||||
|
|
||||||
|
uninstall_hub() {
|
||||||
|
local wg_port
|
||||||
|
[[ -r "$HUB_CONFIG" ]] || die "Роль Hub не установлена"
|
||||||
|
confirm_action "Удалить Hub, все Node identities, Links и аудит" || { log "Отменено"; return 0; }
|
||||||
|
backup_state
|
||||||
|
wg_port="$(awk -F= '$1 == "WG_PORT" { print $2; exit }' "$HUB_CONFIG")"
|
||||||
|
systemctl disable --now ochenstarik-smm-firewall.timer ochenstarik-smm-firewall.service 2>/dev/null || true
|
||||||
|
systemctl disable --now "wg-quick@${WG_INTERFACE}.service" 2>/dev/null || true
|
||||||
|
nft delete table inet ochenstarik_smm >/dev/null 2>&1 || true
|
||||||
|
if command -v ufw >/dev/null 2>&1; then
|
||||||
|
ufw --force delete allow "$wg_port/udp" >/dev/null 2>&1 || true
|
||||||
|
ufw --force route delete allow in on "$WG_INTERFACE" out on "$WG_INTERFACE" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
rm -f -- \
|
||||||
|
/etc/systemd/system/ochenstarik-smm-firewall.service \
|
||||||
|
/etc/systemd/system/ochenstarik-smm-firewall.timer \
|
||||||
|
/etc/sudoers.d/ochenstarik-smm-hub \
|
||||||
|
/etc/sysctl.d/90-ochenstarik-smm-forward.conf \
|
||||||
|
"$HUB_HELPER" "$HUB_CLI" "$WG_CONFIG"
|
||||||
|
rm -rf -- "$MESH_DIR"
|
||||||
|
systemctl daemon-reload
|
||||||
|
sysctl --system >/dev/null
|
||||||
|
log "Mesh Hub удалён; monitoring identity оставлена"
|
||||||
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local action="${1:-install}"
|
local action="${1:-install}"
|
||||||
require_root
|
require_root
|
||||||
detect_system
|
detect_system
|
||||||
case "$action" in
|
case "$action" in
|
||||||
install) install_server_part ;;
|
install|install-monitor) install_server_part ;;
|
||||||
hub) install_hub ;;
|
hub|install-hub) install_hub ;;
|
||||||
node) install_node ;;
|
node|install-node) install_node ;;
|
||||||
hub-code) [[ $# -eq 2 ]] || die "Использование: $0 hub-code NAME"; exec "$HUB_HELPER" node-code "$2" ;;
|
hub-code) [[ $# -eq 2 ]] || die "Использование: $0 hub-code NAME"; exec "$HUB_HELPER" node-code "$2" ;;
|
||||||
status) show_status ;;
|
status) show_status ;;
|
||||||
uninstall) uninstall_server_part ;;
|
update) update_installed ;;
|
||||||
*) die "Использование: $0 {install|hub|node|hub-code NAME|status|uninstall}" ;;
|
rollback) rollback_state ;;
|
||||||
|
uninstall-monitor) uninstall_monitor ;;
|
||||||
|
uninstall-node) uninstall_node ;;
|
||||||
|
uninstall-hub) uninstall_hub ;;
|
||||||
|
uninstall) die "Укажите роль: uninstall-monitor, uninstall-node или uninstall-hub" ;;
|
||||||
|
*) die "Использование: $0 {install-monitor|install-hub|install-node|hub-code NAME|status|update|rollback|uninstall-monitor|uninstall-node|uninstall-hub}" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
tests/test-monitor-manager-lifecycle.sh
Normal file
22
tests/test-monitor-manager-lifecycle.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
|
||||||
|
script="ochenstarik-server-monitor-manager.sh"
|
||||||
|
|
||||||
|
[[ -s "$script" ]]
|
||||||
|
grep -Fq 'sudo' "$script"
|
||||||
|
grep -Fq 'iputils-ping' "$script"
|
||||||
|
grep -Fq 'backup_state()' "$script"
|
||||||
|
grep -Fq 'rollback_state()' "$script"
|
||||||
|
grep -Fq 'update_installed()' "$script"
|
||||||
|
grep -Fq 'uninstall_monitor()' "$script"
|
||||||
|
grep -Fq 'uninstall_node()' "$script"
|
||||||
|
grep -Fq 'uninstall_hub()' "$script"
|
||||||
|
grep -Fq '/var/backups/${APP_NAME}' "$script"
|
||||||
|
grep -Fq 'systemctl disable --now ochenstarik-smm-firewall.timer' "$script"
|
||||||
|
grep -Fq 'nft delete table inet ochenstarik_smm' "$script"
|
||||||
|
grep -Fq 'sysctl --system' "$script"
|
||||||
|
grep -Fq 'uninstall) die "Укажите роль:' "$script"
|
||||||
|
|
||||||
|
printf 'Server Monitor Manager lifecycle checks passed.\n'
|
||||||
Loading…
Reference in a new issue