- Refined the `collect_nft_stats` function in `evofw-firewall.sh` to iterate over multiple chains, improving packet counting accuracy for dropped and accepted packets. - Updated the port ACL handling to include new chains in the firewall rules, ensuring comprehensive coverage for input, forward, and prerouting. - Enhanced the UI to clarify the behavior of port ACLs, emphasizing the distinction between EvoFW and system rules, and the implications of open ports. - Improved documentation to reflect the updated port ACL logic and its interaction with Docker NAT, ensuring users understand the new behavior. These changes enhance the functionality and clarity of port ACL management, improving user experience and system reliability.
11 KiB
Agents
Short install (рекомендуется)
В UI /agents → Добавить агента:
- Создаётся агент со статусом Invited (сразу виден в таблице) + install-ссылка.
- Скопируйте one-liner (колонка Install или Sheet):
Linux:
curl -fsSL https://<cp>/agent-install/<id> | bash
MikroTik:
/tool fetch url="https://<cp>/agent-install/<id>" dst-path=evofw-install.rsc; /import file-name=evofw-install.rsc
- После enroll статус станет Pending — одобрите агента (Approve).
- Approved — агент синхронизирует политику.
Повторный запуск той же install-ссылки на хосте, где агент уже стоит: обновляет sync-скрипт / timer (или MikroTik scheduler) и пересоздаёт nft rules (в т.ч. deny_port_hits), без повторного enroll — CLIENT_ID/token сохраняются. Сбрасывается last_hash, затем сразу force sync. Полный переустановки с новым токеном: EVOFW_INSTALL_FORCE=1 (Linux).
API (auth): POST /api/v1/install-links { "name": "web-01", "platform": "linux" | "mikrotik" }.
Linux (legacy one-liner)
curl -fsSL https://<cp>/v1/agent/install.sh | \
EVOFW_CP_URL=https://<cp> \
EVOFW_SEED=<seed> \
EVOFW_CLIENT_NAME="web-01" \
bash
Создаёт нового агента со статусом Pending (без Invited).
Файлы: /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. Значения в agent.conf всегда в single quotes (имена с пробелами безопасны). Sync при статусе pending завершается с exit 0 (pending approval), чтобы systemd timer не был failed.
Если /etc/evofw/agent.conf уже есть — install переходит в update: скачивает свежий sync-script + uninstall.sh, перезаписывает unit/timer, оставляет токен. EVOFW_INSTALL_FORCE=1 — полный re-enroll (новый токен; для уже Approved install-link обычно не сработает).
Linux self-update (sync-script)
После того как на хосте стоит скрипт с maybe_self_update, агент сам подтягивает новую версию при каждом timer (~1 мин), до GET /v1/agent/policy (pending тоже обновляются):
- sha256 локального
/usr/local/sbin/evofw-firewall.sh GET /v1/agent/sync-scriptсIf-None-Match: "<sha256>"→ 304 = без изменений- 200: проверка shebang + sha256 тела →
install -m 755, сбросlast_hash,exec EVOFW_SKIP_SELF_UPDATE=1новой копии - Ошибка download/verify — лог и продолжение текущим скриптом
GET /v1/agent/policy содержит script_sha256 (не входит в policy.hash).
Bootstrap: агенты без maybe_self_update не умеют самообновляться. После деплоя API — один re-run install-ссылки (или ручная замена sync-script). Дальше curl не нужен.
MikroTik scheduler не обновляется этим путём — только повторный import install .rsc.
Uninstall (Linux):
curl -fsSL https://<cp>/v1/agent/uninstall.sh | bash
# или локально после install:
sudo /usr/local/sbin/evofw-uninstall.sh
Backend auto-detect: nft → ipset → iptables.
Whitelist: nft chain policy drop + allow set. Blacklist: policy accept + deny set.
Per-IP blocked stats (Linux)
Linux agent reports optional ip_hits in POST /v1/agent/apply-report:
- nft: tries set
deny_v4withflags interval; counter;. If the kernel rejects counters on interval sets, falls back to plain interval (aggregate Traffic ↓ still works; per-IP empty). - Upgrade path: on install-link re-run,
last_hashis cleared once so sets/chain can be recreated (chain deleted before set replace) — обновляет и port-hit правила. - ipset: prefers
hash:net … counterson create; existing sets without counters are left as-is. - Payload: only entries with
packets > 0, top 200 by packets. - Control plane:
agent_ip_block_stats, accumulates deltas of absolute kernel counters (как Traffic ↓). После flush set/chain (policy apply) CP сбрасывает per-IP baseline (last_reported), иначе вторая эпоха счётчиков теряется (Traffic растёт, Blocked IPs — нет). GET /api/v1/agents/:id/blocked-ips, reset viaPOST …/stats/reset.- UI: agent detail → Blocked IPs. Sum of Blocked IPs ≈ Traffic ↓ для deny (при default accept); default-drop / allow в Traffic считаются отдельно.
IPv6 skipped.
Destination ports (Linux nft)
На nft агент ведёт dynamic set deny_port_hits (ipv4_addr . inet_proto . inet_service, timeout 1h, counter):
- Deny rule для tcp/udp:
update @deny_port_hits { ip saddr . meta l4proto . th dport }+ drop; прочие протоколы — plain drop. - В
apply-report: optionalport_hitstop 500{ ip, port, protocol, packets }. - CP:
agent_port_block_stats(absolute deltas). Set не flush’ится на каждый policy apply (элементы живут по timeout) — baseline не сбрасывается при Traffic flush. GET /api/v1/agents/:id/blocked-ports— aggregate top 50 портов; вblocked-ipsу каждого IP —portstop 5.- UI: Top ports + колонка Ports в Blocked IPs (только
platform=linux). - ipset/iptables:
port_hits: []. MikroTik — без port hits. - Агенты с self-update подтянут nft-правила сами; без него — один re-run install one-liner (см. выше).
IPv6 skipped.
Host firewall snapshot + Port ACL (Linux)
Observed (Host firewall)
Каждый sync агент собирает best-effort снимок и шлёт в apply-report.host_firewall:
nft list ruleset,iptables-save, optionalufw/firewall-cmd,ss -lntu- Каждое правило:
ownership: evofw | foreign(метка по имени table/chain/commentevofw) - CP:
agent_host_firewall_snapshots;GET /api/v1/agents/:id/host-firewall - UI agent detail → tab Host firewall (Rules / Listeners)
Foreign правила только отображаются — с CP не редактируются.
Desired (Port ACL)
Per-agent таблица agent_port_rules: open|close, tcp|udp|both, port range, src_kind: all|cidr|list.
- API: CRUD
/api/v1/agents/:id/port-rules, import/port-rules/import(from list или policy set sources) - Policy
apply_version: 3→port_rules[]с expandedsrc_cidrs - nft apply: close drop → open accept (
comment "evofw-port-<id>") → implicit drop для каждого(proto, dport)с хотя бы однимopen(comment "evofw-port-implicit-…"). Одно правило покрывает хост и Docker: hooks prerouting (priority mangle, до Docker NAT — публичный dport) + input + forward. Порт с open = whitelist: src из правила accept, остальные внешние drop.loиct established,relatedвыше по цепочке. prerouting без terminal default drop (прочий трафик идёт дальше). - UI: tab Port ACL (DataGrid + Sheet create/edit + Import). Owner: EvoFW (desired) и system (listeners + foreign allow с хоста). Переопределённые system-строки скрыты в All. Системный порт можно переопределить → desired
open(список/CIDR); ufw/iptables/Docker-цепочки не меняются. - ipset / MikroTik: без L4 apply; секции скрыты для non-linux
Мутация Port ACL бампит policy_generation → agent re-apply. Нужен self-update скрипта или re-run install-ссылки.
MikroTik (RouterOS 7.21+)
В UI /agents → Добавить агента → platform MikroTik. Скопируйте one-liner:
/tool fetch url="https://<cp>/agent-install/<id>" dst-path=evofw-install.rsc; /import file-name=evofw-install.rsc
Или короткий slug: https://<cp>/<slug>.
Install RSC:
- Enroll (с
install_link_id→ агент Invited → Pending). - Создаёт filter-правила
evofw-*в начале цепочекinput/forward(place-before) и address-listEVOFW_DENY/EVOFW_ALLOW/ dynamicEVOFW_HITS. - Scheduler
evofw-syncкаждую минуту:GET /v1/agent/policy(JSON) → rebuild deny/allow + report (+ip_hitsиз HITS). Не использует/importогромного.rsc.
Лог: /log print where message~"evofw". Ручной sync: /system script run evofw-sync.
Traffic ↓/↑ в UI — сумма packets с evofw-deny-drop-* / evofw-allow-* / evofw-default-drop-* (накопительно, пока правила не пересозданы re-install).
Per-IP / Blocked IPs (MikroTik)
Цепочка deny: hit → drop (семантика drop/allow/default как раньше):
evofw-deny-hit-input/forward—add-src-to-address-list→EVOFW_HITS,address-list-timeout=1h(passthrough).evofw-deny-drop-input/forward—dropпоEVOFW_DENY.
В EVOFW_HITS попадают реальные src /32. Policy rebuild не чистит HITS (только DENY/ALLOW). Sync шлёт top-200 в ip_hits; CP mode presence: last_seen обновляется, пока IP в HITS; packets (Hits) увеличивается только при первом появлении или повторном входе после исчезновения из списка (~>2.5 мин без report), а не на каждый sync.
UI: agent detail → Blocked IPs (колонка Hits).
Default action задаётся на агенте (default_action: accept | drop):
- accept — пакет вне deny/allow пропускается
- drop — пакет вне deny/allow отбрасывается (forward)
Цепочка: deny-hit → deny-drop → allow-accept → default. Наборы несут только правила deny/allow, без exclusive mode.
Force sync
sudo rm -f /var/lib/evofw/last_hash
sudo /usr/local/sbin/evofw-firewall.sh