feat: harden firewall policies telemetry and operations
quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped

Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
This commit is contained in:
Denozordec committed 2026-10-26 13:10:58 +07:00
1 parent aa475848fd
commit 9fa6269174
242 files changed
+19872 -3057

No files matched your search

+67 -30
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# EvoFirewall Linux install one-liner
# Re-run on an already-installed host updates scripts/timer **and forces nft
# rule re-apply** (clears last_hash), keeping credentials
# rule re-apply** (clears kernel verification), keeping credentials
# (unless EVOFW_INSTALL_FORCE=1 → full re-enroll).
set -euo pipefail
@@ -49,13 +49,12 @@ ensure_pkg() {
ensure_pkg curl curl || exit 1
need_cmd bash || { echo "missing bash" >&2; exit 1; }
# JSON parse for enroll; python3 is an acceptable fallback in sync script.
if ! need_cmd jq && ! need_cmd python3; then
ensure_pkg jq jq || ensure_pkg python3 python3 || {
echo "evofw install: need jq or python3" >&2
exit 1
}
fi
# Policy validation and nft batch generation require Python; jq alone is not
# sufficient. util-linux supplies flock for serialized timer/manual sync runs.
ensure_pkg python3 python3 || exit 1
ensure_pkg flock util-linux || exit 1
ensure_pkg timeout coreutils || exit 1
# Firewall tools: never reinstall/replace if already present.
if need_cmd nft; then
@@ -63,8 +62,8 @@ if need_cmd nft; then
elif need_cmd iptables; then
echo "evofw install: iptables already present — skip nftables/iptables install"
if ! need_cmd ipset; then
echo "evofw install: installing ipset (optional companion for iptables)..."
pkg_install ipset 2>/dev/null || true
echo "evofw install: installing ipset (required companion for iptables)..."
ensure_pkg ipset ipset || exit 1
fi
else
echo "evofw install: no nft/iptables — installing firewall backend..."
@@ -111,7 +110,7 @@ if [[ "$HAS_SYSTEMD" -ne 1 ]]; then
fi
: "${EVOFW_CP_URL:?EVOFW_CP_URL required}"
: "${EVOFW_SEED:?EVOFW_SEED required}"
: "${EVOFW_ENROLLMENT_CREDENTIAL:?Scoped EVOFW_ENROLLMENT_CREDENTIAL required}"
: "${EVOFW_CLIENT_NAME:?EVOFW_CLIENT_NAME required}"
CONF_DIR=/etc/evofw
@@ -161,14 +160,24 @@ write_conf() {
printf 'CLIENT_TOKEN=%s\n' "$(shell_quote "$client_token")"
printf 'CLIENT_NAME=%s\n' "$(shell_quote "$client_name")"
printf 'KERNEL_BACKEND=%s\n' "$(shell_quote "$backend")"
} >"$CONF_FILE"
chmod 600 "$CONF_FILE"
} >"${CONF_FILE}.new"
chmod 600 "${CONF_FILE}.new"
python3 - "${CONF_FILE}.new" "$CONF_FILE" <<'PY_CONF_COMMIT'
import os, sys
with open(sys.argv[1], 'rb') as value: os.fsync(value.fileno())
os.replace(sys.argv[1], sys.argv[2])
fd = os.open(os.path.dirname(sys.argv[2]), os.O_RDONLY)
try: os.fsync(fd)
finally: os.close(fd)
PY_CONF_COMMIT
}
install_sync_and_uninstall() {
local sync_tmp=$1
install -m 755 "$sync_tmp" "$SYNC_SCRIPT"
if curl -fsSL "${CP_URL}/v1/agent/uninstall.sh" -o "$UNINSTALL_SCRIPT" 2>/dev/null; then
bash -n "$sync_tmp" || return 1
install -m 755 "$sync_tmp" "${SYNC_SCRIPT}.new"
mv -f "${SYNC_SCRIPT}.new" "$SYNC_SCRIPT"
if curl --connect-timeout 10 --max-time 60 -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
@@ -214,7 +223,7 @@ UNIT
download_sync_script() {
local out=$1
if ! curl -fsSL "${CP_URL}/v1/agent/sync-script" -o "$out"; then
if ! curl --connect-timeout 10 --max-time 60 -fsSL "${CP_URL}/v1/agent/sync-script" -o "$out"; then
echo "failed to download sync script from ${CP_URL}/v1/agent/sync-script" >&2
return 1
fi
@@ -222,6 +231,7 @@ download_sync_script() {
echo "sync script is not a shell script (CP returned unexpected body)" >&2
return 1
fi
bash -n "$out"
}
SYNC_TMP=$(mktemp)
@@ -251,33 +261,56 @@ if [[ -f "$CONF_FILE" && "${EVOFW_INSTALL_FORCE:-}" != "1" ]]; then
download_sync_script "$SYNC_TMP" || exit 1
write_conf "$CLIENT_ID" "$CLIENT_TOKEN" "$CLIENT_NAME" "$BACKEND"
install_sync_and_uninstall "$SYNC_TMP"
# Force one apply after script refresh (nft set upgrade, counters, port hits, etc.).
rm -f /var/lib/evofw/last_hash
# Invalidate structural verification to force reconciliation after refresh.
rm -f /var/lib/evofw/kernel_state
enable_scheduler_and_run
echo "Updated. Client id=${CLIENT_ID}. Sync script + timer refreshed; nft rules re-applied."
echo "Force sync: $SYNC_SCRIPT"
echo "Uninstall: $UNINSTALL_SCRIPT (or: curl -fsSL ${CP_URL}/v1/agent/uninstall.sh | bash)"
echo "Uninstall: $UNINSTALL_SCRIPT (or: curl --connect-timeout 10 --max-time 60 -fsSL ${CP_URL}/v1/agent/uninstall.sh | bash)"
exit 0
fi
# --- Fresh install (or EVOFW_INSTALL_FORCE=1) ---
CLIENT_TOKEN="$(gen_token)"
mkdir -p "$CONF_DIR" /var/lib/evofw
chmod 700 "$CONF_DIR"
exec 8>/var/lib/evofw/sync.lock
flock -x 8
ENROLL_PENDING="$CONF_DIR/enrollment-pending.json"
CLIENT_TOKEN=$(python3 - "$ENROLL_PENDING" "$CP_URL" "${EVOFW_INSTALL_LINK_ID:-}" <<'PY_ENROLL_PENDING'
import json, os, secrets, sys
from pathlib import Path
path = Path(sys.argv[1]); origin, link = sys.argv[2:]
if not link: raise SystemExit('install link required')
if path.exists():
state = json.loads(path.read_text())
if state.get('origin') != origin or state.get('link') != link: raise SystemExit('pending enrollment belongs to another link')
else:
state = {'origin': origin, 'link': link, 'token': 'evofw_' + secrets.token_urlsafe(32)}
fd = os.open(str(path) + '.new', os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
with os.fdopen(fd, 'w') as out:
json.dump(state, out); out.flush(); os.fsync(out.fileno())
os.replace(str(path) + '.new', path)
fd = os.open(str(path.parent), os.O_RDONLY)
try: os.fsync(fd)
finally: os.close(fd)
print(state['token'])
PY_ENROLL_PENDING
)
HOSTNAME="$(hostname -f 2>/dev/null || hostname)"
# Fail fast: pull sync script before enroll so we never leave a DB agent without a local agent.
download_sync_script "$SYNC_TMP" || exit 1
if [[ -n "${EVOFW_INSTALL_LINK_ID:-}" ]]; then
ENROLL_BODY=$(printf '{"name":"%s","hostname":"%s","platform":"%s","token":"%s","client_version":"install.sh/1","install_link_id":"%s"}' \
"$EVOFW_CLIENT_NAME" "$HOSTNAME" "$PLATFORM" "$CLIENT_TOKEN" "$EVOFW_INSTALL_LINK_ID")
else
ENROLL_BODY=$(printf '{"name":"%s","hostname":"%s","platform":"%s","token":"%s","client_version":"install.sh/1"}' \
"$EVOFW_CLIENT_NAME" "$HOSTNAME" "$PLATFORM" "$CLIENT_TOKEN")
fi
ENROLL_BODY=$(python3 - "$EVOFW_CLIENT_NAME" "$HOSTNAME" "$PLATFORM" "$CLIENT_TOKEN" "$EVOFW_INSTALL_LINK_ID" <<'PY_ENROLL_BODY'
import json, sys
name, hostname, platform, token, link = sys.argv[1:]
print(json.dumps(dict(name=name, hostname=hostname, platform=platform, token=token, client_version='install.sh/2', install_link_id=link)))
PY_ENROLL_BODY
)
ENROLL_CODE=$(curl -sS -o "$ENROLL_TMP" -w "%{http_code}" -X POST "${CP_URL}/v1/agent/enroll" \
ENROLL_CODE=$(curl --connect-timeout 10 --max-time 60 -sS -o "$ENROLL_TMP" -w "%{http_code}" -X POST "${CP_URL}/v1/agent/enroll" \
-H "Content-Type: application/json" \
-H "X-EvoFW-Seed: ${EVOFW_SEED}" \
-H "X-EvoFW-Enrollment-Credential: ${EVOFW_ENROLLMENT_CREDENTIAL}" \
-d "$ENROLL_BODY")
if [[ "$ENROLL_CODE" != "201" && "$ENROLL_CODE" != "200" ]]; then
echo "enroll failed: HTTP ${ENROLL_CODE}" >&2
@@ -304,10 +337,14 @@ if [[ -z "$BACKEND" ]]; then
fi
write_conf "$CLIENT_ID" "$CLIENT_TOKEN" "$EVOFW_CLIENT_NAME" "$BACKEND"
# Config is committed before pending enrollment can be removed.
rm -f "$ENROLL_PENDING"
flock -u 8
exec 8>&-
install_sync_and_uninstall "$SYNC_TMP"
enable_scheduler_and_run
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 "Re-run the same install URL to update scripts without re-enroll."
echo "Uninstall: $UNINSTALL_SCRIPT (or: curl -fsSL ${CP_URL}/v1/agent/uninstall.sh | bash)"
echo "Uninstall: $UNINSTALL_SCRIPT (or: curl --connect-timeout 10 --max-time 60 -fsSL ${CP_URL}/v1/agent/uninstall.sh | bash)"