From 38f7a8296eb6484815f8082f140de51dfe25d904 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Tue, 21 Jul 2026 22:53:28 +0700 Subject: [PATCH] feat(api): add uninstall script and enhance install script functionality - Introduced an uninstall script for agents, allowing users to easily remove the agent with a single command. - Updated `install.sh` to quote configuration values for safety, ensuring compatibility with names containing spaces. - Enhanced the installation process to include a warning if the uninstall script cannot be downloaded. - Updated documentation to reflect the new uninstall functionality and changes in configuration file handling. --- apps/api/src/agent-scripts/install.sh | 27 ++++++++++----- apps/api/src/agent-scripts/uninstall.sh | 45 +++++++++++++++++++++++++ apps/api/src/plugins/auth.ts | 1 + apps/api/src/routes/agent.ts | 5 +++ docs/agents.md | 9 ++++- 5 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 apps/api/src/agent-scripts/uninstall.sh diff --git a/apps/api/src/agent-scripts/install.sh b/apps/api/src/agent-scripts/install.sh index 9324e02..bd9f236 100644 --- a/apps/api/src/agent-scripts/install.sh +++ b/apps/api/src/agent-scripts/install.sh @@ -178,17 +178,25 @@ fi mkdir -p "$CONF_DIR" chmod 700 "$CONF_DIR" -cat >"$CONF_FILE" <"$CONF_FILE" chmod 600 "$CONF_FILE" install -m 755 "$SYNC_TMP" "$SYNC_SCRIPT" +UNINSTALL_SCRIPT=/usr/local/sbin/evofw-uninstall.sh +if curl -fsSL "${CP_URL}/v1/agent/uninstall.sh" -o "$UNINSTALL_SCRIPT" 2>/dev/null; then + chmod 755 "$UNINSTALL_SCRIPT" +else + echo "evofw install: warning — could not download uninstall.sh (optional)" >&2 +fi + if command -v nft >/dev/null 2>&1; then BACKEND=nft elif command -v ipset >/dev/null 2>&1 && command -v iptables >/dev/null 2>&1; then @@ -199,8 +207,8 @@ else echo "no supported firewall backend" >&2 exit 1 fi -sed -i "s/^KERNEL_BACKEND=.*/KERNEL_BACKEND=${BACKEND}/" "$CONF_FILE" 2>/dev/null || \ - echo "KERNEL_BACKEND=${BACKEND}" >>"$CONF_FILE" +sed -i "s/^KERNEL_BACKEND=.*/KERNEL_BACKEND=$(printf '%q' "$BACKEND")/" "$CONF_FILE" 2>/dev/null || \ + printf 'KERNEL_BACKEND=%q\n' "$BACKEND" >>"$CONF_FILE" INTERVAL="${EVOFW_SYNC_INTERVAL:-1min}" if [[ "$HAS_SYSTEMD" -eq 1 ]]; then @@ -239,3 +247,4 @@ fi echo "Installed. Client id=${CLIENT_ID}. Approve in EvoFirewall UI (rules optional — can assign later)." echo "If still offline after Approve, run: $SYNC_SCRIPT" +echo "Uninstall: $UNINSTALL_SCRIPT (or: curl -fsSL ${CP_URL}/v1/agent/uninstall.sh | bash)" diff --git a/apps/api/src/agent-scripts/uninstall.sh b/apps/api/src/agent-scripts/uninstall.sh new file mode 100644 index 0000000..28ba5c6 --- /dev/null +++ b/apps/api/src/agent-scripts/uninstall.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# EvoFirewall Linux agent uninstall +set -euo pipefail + +if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then + echo "evofw uninstall: run as root" >&2 + exit 1 +fi + +echo "evofw uninstall: stopping timer/service..." +if command -v systemctl >/dev/null 2>&1; then + systemctl disable --now evofw-firewall.timer 2>/dev/null || true + systemctl stop evofw-firewall.service 2>/dev/null || true + rm -f /etc/systemd/system/evofw-firewall.timer \ + /etc/systemd/system/evofw-firewall.service + systemctl daemon-reload 2>/dev/null || true + systemctl reset-failed evofw-firewall.service 2>/dev/null || true + systemctl reset-failed evofw-firewall.timer 2>/dev/null || true +fi + +if command -v crontab >/dev/null 2>&1; then + crontab -l 2>/dev/null | grep -v evofw-firewall | crontab - 2>/dev/null || true +fi + +echo "evofw uninstall: removing files..." +rm -f /usr/local/sbin/evofw-firewall.sh +rm -f /usr/local/sbin/evofw-uninstall.sh +rm -rf /etc/evofw /var/lib/evofw +rm -f /var/log/evofw-firewall.log + +echo "evofw uninstall: removing nft table (if any)..." +if command -v nft >/dev/null 2>&1; then + nft delete table inet evofw 2>/dev/null || true +fi + +if command -v iptables >/dev/null 2>&1; then + iptables -D INPUT -m set --match-set evofw_deny_v4 src -j DROP 2>/dev/null || true + iptables -D INPUT -m set --match-set evofw_allow_v4 src -j ACCEPT 2>/dev/null || true +fi +if command -v ipset >/dev/null 2>&1; then + ipset destroy evofw_deny_v4 2>/dev/null || true + ipset destroy evofw_allow_v4 2>/dev/null || true +fi + +echo "evofw uninstall: done (revoke/delete agent in EvoFirewall UI if needed)." diff --git a/apps/api/src/plugins/auth.ts b/apps/api/src/plugins/auth.ts index bf21a00..17d3e06 100644 --- a/apps/api/src/plugins/auth.ts +++ b/apps/api/src/plugins/auth.ts @@ -50,6 +50,7 @@ function isPublicPath(url: string): boolean { if (path.startsWith('/v1/agent/enroll')) return true if (path.startsWith('/v1/agent/install')) return true if (path.startsWith('/v1/agent/sync-script')) return true + if (path.startsWith('/v1/agent/uninstall')) return true if (path.startsWith('/v1/agent/mikrotik')) return true return false } diff --git a/apps/api/src/routes/agent.ts b/apps/api/src/routes/agent.ts index b8cd66d..1cc5a7c 100644 --- a/apps/api/src/routes/agent.ts +++ b/apps/api/src/routes/agent.ts @@ -44,6 +44,11 @@ export const agentRoutes: FastifyPluginAsync<{ config: AppConfig }> = async ( return reply.type('text/x-shellscript').send(body) }) + app.get('/v1/agent/uninstall.sh', async (_req, reply) => { + const body = readFileSync(join(scriptsDir, 'uninstall.sh'), 'utf-8') + return reply.type('text/x-shellscript').send(body) + }) + app.get('/v1/agent/mikrotik-install.rsc', async (_req, reply) => { const body = readFileSync( join(scriptsDir, 'mikrotik-install.rsc'), diff --git a/docs/agents.md b/docs/agents.md index b3af694..4a5afb0 100644 --- a/docs/agents.md +++ b/docs/agents.md @@ -36,7 +36,14 @@ curl -fsSL https:///v1/agent/install.sh | \ Файлы: `/etc/evofw/agent.conf`, `/usr/local/sbin/evofw-firewall.sh`, timer `evofw-firewall.timer` (default 1min). -Install сам ставит зависимости через apt/dnf/yum/apk: `curl`, `jq` (или `python3`), `nftables`/`iptables`(+`ipset`). Планировщик: **systemd timer** если есть `/run/systemd/system`, иначе ставит `cron`/`cronie` и пишет crontab. +Install сам ставит зависимости через apt/dnf/yum/apk: `curl`, `jq` (или `python3`), `nftables`/`iptables`(+`ipset`). Планировщик: **systemd timer** если есть `/run/systemd/system`, иначе ставит `cron`/`cronie` и пишет crontab. Значения в `agent.conf` пишутся через `printf %q` (имена с пробелами безопасны). + +**Uninstall (Linux):** +```bash +curl -fsSL https:///v1/agent/uninstall.sh | bash +# или локально после install: +sudo /usr/local/sbin/evofw-uninstall.sh +``` Backend auto-detect: nft → ipset → iptables.