From f2a2310d3eeb5a35ad318c13f2d60095aa907944 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 23 Jul 2026 22:10:21 +0700 Subject: [PATCH] refactor(api): streamline mikrotik-install script and enhance synchronization logic - Consolidated the `mikrotik-install.rsc` script to utilize a single `evofw-sync` script for improved clarity and efficiency in synchronization processes. - Removed the legacy `evofw-env` script, simplifying the token management and ensuring a more reliable update mechanism. - Updated the token generation logic to enhance security and reliability, while ensuring compatibility with RouterOS 7+. - Adjusted test assertions in `install-links.test.ts` to verify the presence of new script components and ensure accurate testing of the installation logic. These changes improve the overall robustness and maintainability of the agent installation and synchronization process. --- .../src/agent-scripts/mikrotik-install.rsc | 76 +++++++++++-------- apps/api/src/services/install-links.test.ts | 2 + 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/apps/api/src/agent-scripts/mikrotik-install.rsc b/apps/api/src/agent-scripts/mikrotik-install.rsc index 1e5ffaa..795803a 100644 --- a/apps/api/src/agent-scripts/mikrotik-install.rsc +++ b/apps/api/src/agent-scripts/mikrotik-install.rsc @@ -1,11 +1,8 @@ # EvoFirewall MikroTik install (RouterOS 7.21+) # Short-link sets EvofwCpUrl / EvofwSeed / EvofwName / EvofwInstallLinkId before body. -# Legacy: set globals, then /import file-name=mikrotik-install.rsc # -# Re-import on an already-enrolled router: skips enroll, refreshes sync + scheduler (token kept). -# -# Sync uses GET /v1/agent/policy (JSON) + address-list rebuild — not /import of huge .rsc -# (large /import often fails silently while apply-report still looks "ok"). +# One script only: evofw-sync (credentials + sync). Legacy evofw-env is removed. +# Sync: GET /v1/agent/policy (JSON) → rebuild address-lists. LastHash in RAM (full rebuild after reboot). :global EvofwCpUrl :global EvofwSeed @@ -19,19 +16,40 @@ :if ([:typeof $EvofwName] = "nothing" || [:len $EvofwName] = 0) do={ :set EvofwName [/system identity get name] } :local already 0 + +# Prefer token already baked into evofw-sync :do { - /system script run evofw-env - :if ([:typeof $EvofwToken] != "nothing" && [:len $EvofwToken] > 0) do={ :set already 1 } + :local src [/system script get [find name="evofw-sync"] source] + :local key "EvofwToken \"" + :local i [:find $src $key] + :if ([:typeof $i] != "nil") do={ + :local rest [:pick $src ($i + [:len $key]) [:len $src]] + :local j [:find $rest "\""] + :if ([:typeof $j] != "nil") do={ + :local tok [:pick $rest 0 $j] + :if ([:len $tok] > 10) do={ + :set EvofwToken $tok + :set already 1 + } + } + } } on-error={} +# Legacy: migrate from evofw-env if present +:if ($already = 0) do={ + :do { + /system script run evofw-env + :if ([:typeof $EvofwToken] != "nothing" && [:len $EvofwToken] > 0) do={ :set already 1 } + } on-error={} +} + :if ($already = 0) do={ - # Portable token: :rndstr is RouterOS 7+; avoid /certificate scep-server nonce (often missing). :local token ("evofw_" . [:rndstr length=32 from="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"]) :if ([:len $token] < 20) do={ :set token ("evofw_" . [:tostr [/system clock get time]] . [:tostr [/system resource get cpu-load]] . [:tostr [/system resource get free-memory]] . [:tostr [:rndnum from=100000 to=999999]]) } - :local body ("{\"name\":\"" . $EvofwName . "\",\"hostname\":\"" . [/system identity get name] . "\",\"platform\":\"mikrotik\",\"token\":\"" . $token . "\",\"client_version\":\"rsc/2\"") + :local body ("{\"name\":\"" . $EvofwName . "\",\"hostname\":\"" . [/system identity get name] . "\",\"platform\":\"mikrotik\",\"token\":\"" . $token . "\",\"client_version\":\"rsc/3\"") :if ([:typeof $EvofwInstallLinkId] != "nothing" && [:len $EvofwInstallLinkId] > 0) do={ :set body ($body . ",\"install_link_id\":\"" . $EvofwInstallLinkId . "\"") } @@ -42,38 +60,29 @@ } on-error={ :error "evofw: enroll failed — check EvofwCpUrl / EvofwSeed / connectivity" } - - :do { /system script remove [find name="evofw-env"] } on-error={} - /system script add name=evofw-env policy=read,write,policy,test source=(" :global EvofwCpUrl \"" . $EvofwCpUrl . "\"; :global EvofwToken \"" . $token . "\"; :global EvofwLastHash \"\" ") :set EvofwToken $token - :set EvofwLastHash "" } else={ - :put "evofw: already enrolled — updating sync script (token kept); forcing full list rebuild" - :set EvofwLastHash "" - :do { /system script remove [find name="evofw-env"] } on-error={} - /system script add name=evofw-env policy=read,write,policy,test source=(" :global EvofwCpUrl \"" . $EvofwCpUrl . "\"; :global EvofwToken \"" . $EvofwToken . "\"; :global EvofwLastHash \"\" ") + :put "evofw: already enrolled — updating single sync script (token kept)" } -# Filter rules (idempotent by comment) — names must match policy toggles below -:do { /ip firewall filter remove [find comment~"^evofw-"] } on-error={} +:set EvofwLastHash "" +# Filter rules (idempotent by comment) +:do { /ip firewall filter remove [find comment~"^evofw-"] } on-error={} /ip firewall filter add chain=input action=drop src-address-list=EVOFW_DENY comment=evofw-deny-drop-input disabled=no /ip firewall filter add chain=forward action=drop src-address-list=EVOFW_DENY comment=evofw-deny-drop-forward disabled=no /ip firewall filter add chain=forward action=accept src-address-list=EVOFW_ALLOW comment=evofw-allow-accept-forward disabled=no /ip firewall filter add chain=forward action=drop comment=evofw-default-drop-forward disabled=yes -# Sync: JSON policy → rebuild address-lists (reliable for ~1k CIDRs; no nested /import) -:do { /system script remove [find name="evofw-sync"] } on-error={} -/system script add name=evofw-sync policy=read,write,policy,test source={ +# Build sync body as a template script, then prepend baked credentials → one script +:do { /system script remove [find name="evofw-sync-body"] } on-error={} +/system script add name=evofw-sync-body policy=read,write,policy,test source={ :global EvofwCpUrl :global EvofwToken :global EvofwLastHash - :if ([:typeof $EvofwCpUrl] = "nothing" || [:typeof $EvofwToken] = "nothing") do={ - /system script run evofw-env - } - :if ([:typeof $EvofwCpUrl] = "nothing" || [:typeof $EvofwToken] = "nothing") do={ + :if ([:typeof $EvofwCpUrl] = "nothing" || [:len $EvofwCpUrl] = 0 || [:typeof $EvofwToken] = "nothing" || [:len $EvofwToken] = 0) do={ :log error "evofw: missing EvofwCpUrl/EvofwToken" - :error "evofw env missing" + :error "evofw missing credentials" } :if ([:typeof $EvofwLastHash] = "nothing") do={ :set EvofwLastHash "" } @@ -114,8 +123,6 @@ :do { /ip firewall filter set [find comment=evofw-default-drop-forward] disabled=yes } on-error={} } :set EvofwLastHash $hash - :do { /system script remove [find name="evofw-env"] } on-error={} - /system script add name=evofw-env policy=read,write,policy,test source=(" :global EvofwCpUrl \"" . $EvofwCpUrl . "\"; :global EvofwToken \"" . $EvofwToken . "\"; :global EvofwLastHash \"" . $hash . "\" ") :log info ("evofw: applied " . $hash) :set syncOk 1 } @@ -138,6 +145,13 @@ } } +:local bodySrc [/system script get [find name="evofw-sync-body"] source] +:local head (":global EvofwCpUrl \"" . $EvofwCpUrl . "\"; :global EvofwToken \"" . $EvofwToken . "\"; ") +:do { /system script remove [find name="evofw-sync-body"] } on-error={} +:do { /system script remove [find name="evofw-sync"] } on-error={} +:do { /system script remove [find name="evofw-env"] } on-error={} +/system script add name=evofw-sync policy=read,write,policy,test source=($head . $bodySrc) + :do { /system scheduler remove [find name="evofw-sync"] } on-error={} /system scheduler add name=evofw-sync interval=1m on-event=evofw-sync policy=read,write,policy,test @@ -146,7 +160,7 @@ } :if ($already = 1) do={ - :put ("EvoFirewall updated for " . $EvofwName . " — sync script + scheduler refreshed") + :put ("EvoFirewall updated for " . $EvofwName . " — single script evofw-sync") } else={ - :put ("EvoFirewall enrolled as " . $EvofwName . " — approve in UI; scheduler evofw-sync every 1m") + :put ("EvoFirewall enrolled as " . $EvofwName . " — approve in UI; script/scheduler evofw-sync every 1m") } diff --git a/apps/api/src/services/install-links.test.ts b/apps/api/src/services/install-links.test.ts index 47f90fd..28097c0 100644 --- a/apps/api/src/services/install-links.test.ts +++ b/apps/api/src/services/install-links.test.ts @@ -143,6 +143,8 @@ describe('install-links', () => { expect(byId.body).toContain('evofw-deny-drop-input') expect(byId.body).toContain('/v1/agent/policy') expect(byId.body).toContain(':deserialize') + expect(byId.body).toContain('evofw-sync-body') + expect(byId.body).toContain('remove [find name="evofw-env"]') }) it('approved agent can fetch empty policy without rule sets', async () => {