Blog.nechutny.net

Blog o webu a IT.

RouterOS / Mikrotik hate

Již téměř 3/4 roku děláme jeden projekt na Mikrotiku resp. RouterOS a za tu dobu jsme zjistili, že je to past vedle pasti. Posuďte sami: Představme si poměrně reálnou situaci - spravujete o trošku větší síť, než je vaše domácí a máte v ní těch Mikrotiků víc. Chcete docílit možnosti nějaké hromadné správy - tedy poslat na ně například příkaz pro změnu NTP, přidání uživatele apod.

RouterOS pro tuto funkcionalitu neposkytuje hotové řešení, ale to nevadí. Umožňuje skriptování pomocí RSC, tak si to napíšeme. Asi nejjednodušším na implementaci bude periodický HTTP požadavek na server, který nabídne ke stažení případnou aktualizaci. Vypadá velmi jednoduše. RouterOS má přeci /tool fetch.

V GET požadavku bysme třeba chtěli poslat aktuální verzi, protože uchovávat ji na serveru nemusí být úplně dobrý nápad. Server nemůže předpokládat, že po stažení dojde ḱ úspěšnému aplikování. Takže by se hodilo si někam na routeru uložit číslo verze. Narážíme na první problém - jak vytvořit nový soubor s určitým obsahem? RouterOS vám to lehce neumožní. Jsou jen workaroundy, kdy se spustí nějaký příkaz a jeho výstup se přesměruje do souboru. Následně už pak je možné takto vytvořený soubor upravit na požadovaný obsah. Dokumentace mlčí a kolegovo vlákno na Stack owerflow dopadlo tak, že položil dotaz, zodpověděl, reagoval a aktualizoval odpověd sám. Ovšem v RouterOS 6.30 narazíte na problém, že dříve fungující :execute script=":put yourText" file="newFile" již nefunguje. Nefunguje přesměrování čehokoliv do souboru. Po aktualizaci na 6.34 opět začne fungovat. Narazíte na poměrně častý názor, že je nejbezpečnější řešení soubor stahnout pomocí fetch.

Poslední uvedené řešení vytvoření souboru ovšem trošičku komplikuje další problém nástroje fetch, kterým je maximální počet stažení. Jakmile překročíte tuto bulharskou konstantu, tak vám prostě failne každé stažení. Pomůže jen restart. Doufám, že se jedná o chybu, která bude v budoucnu opravena.

Myslíme trochu na bezpečnost a tak aktualizace budeme stahovat po HTTPS, když pak ono RSC spouštíme. Dobře, na server nasadíme validní certifikáty a zkusíme stahnout. Vše se tváří OK. Pro jistotu to zkusíme s falešným certifikátem. Ejhle, ono to také projde. Takže začneme hledat a zjistíme, že defaultně fetch nekontroluje certifikáty. Je třeba použít přepínač check-certificate=yes. Narazíme na problém s cetifikáty. Exportujeme tedy z Linuxu certifikační autority, importujeme do Mikrotiku a stále fail. Se stejným souborem validace wgetu na čistém OpenWRT projde úspěšně a podvržené certifikáty pozná. Na RouterOS neprojde ani validace pro HTTPS Google.

Dobře, zkusíme to jinak. Vytvoříme šifrované VPN spojení mezi RouterOS a serverem. Sice další vrstva, co se může pokazit, ale budiž. Přidáme kontrolu, že se soubor stahl - co kdyby VPN vypadla. A další fail - nepoznáme. Když v polovině stahování sestřelím na serveru VPN, nebo Apache, tak dostanu výstup status: finished a není nastaven žádný chybový kód. Co to zkusit trochu brutálněji? Dáme stahovat 20 MB soubor, nástroj ukazuje procenta, zná velikost souboru co stahuje a vytahneme z Mikrotiku síťový kabel. Stahování se přeruší, stahne se třeba jen 2 MB, ale vše se tváří zcela v pořádku.

Co tedy napřed získat hash souboru a po stažení zkontrolovat zda odpovídá staženému. Délku hashe ve znacích víme dle použitého algoritmu, takže budeme vědět, zda jsme ho stahli v pořádku. Zde narazíme na další fail - nemáte dostupnou absolutně žádnou hashovací funkci. Jediným řešením je jedna implementace md5 ve formě RSC skriptu, která je strašně pomalá a nedává správné výsledky. Takže tudy cesta nevede.

Spokojit se alespoň s kontrolou velikosti souboru? Na RouterOS prostě přesně nezjistíte velikost. Není žádná cesta, jak zjistit něco přesnějšího než 3195.5KiB, což je nedostačující.

Přeskočíme spoustu experimentování a pokročíme k dalšímu možnému řešení. Je jím MetaRouter s OpenWRT. Ano, prostě taková šílenost, že na RouterOS spustíte kvůli aktualizacím virtuálku s ořezaným linuxem. Naštěstí takové řešení si vystačí s 16 MB RAM a z procesoru si bere jednotky procent. Představa řešení je tedy následující: Periodicky spouštěný skript na RouterOS se po SSH logne do OpenWRT, zde spustí bash skript, který se postará o stažení a ověření aktualizace, nahraje ji do filesystému RouterOS a skončí. Následně RouterOS skript v očekávaném umístění aktualizaci vykoná. Oproti původnímu řešení již poměrně komplikované řešení, ale co naplat.

Přeskočme implementaci na OpenWRT, která byla kupodivu oproti RouterOS jednoduchá. Nastavíme si přihlašování po SSH pomocí certifikátů a jdeme testovat. /system ssh user=root address=192.168.88.10 command=/root/update.sh funguje dokonale. Máme na Mikrotiku připravený ověřený soubor, který stačí spustit. Výborně, teď to jen dáme do automaticky spouštěného skriptu na RouterOS.

@#$%^&*, ono se to neaktualizuje?!?!?! Po zkoumání zjistíte, že soudruzi stojící za RouterOS se rozhodli, že prostě příkaz /system ssh v RSC se přeskočí. Prostě ho neprovedem, stav se třeba na hlavu.

Pokračujeme - soubory nám tedy musí nahrát OpenWRT do RouterOS samo a RouterOS je jen vykoná. Zde nastává několik komplikací, které je třeba řešit. OpenWRT musí vědět, že třeba ještě probíhá aktualizace na RouterOS a nemůže tedy nahrávat další aktualizaci. Stejně tak RouterOS zas, že aktualizace není do souborového systému nahrána ještě celá. Budeme tedy muset vyřešit synchronizaci dvou procesů se sdílenou pamětí. To bývá občas obtížné udělat i v C u dvou procesů na jednom počítači, bez deadlocku. Nakonec jsme skončili u implementace spinlocku po FTP implementovanou v Bash a RSC. To už samo o sobě zní zvráceně.

Při testování narážíme na další problém - větší síťová komunikaci výrazně vytěžuje procesor. Postupným experimentováním jsme došli k tomu, že příčinou je MetaRouter. Pokud běží, tak síťová komunikace vytíží procesor. To by bylo pochopitelné, ale i když MetaRouteru odeberu veškerá síťová zařízení a síťová komunikace ho tedy absolutně nemá jak ovlivnovat, ani on ji, tak je chování systému stejné. Když takto izolovaný MetaRouter vypnu, tak vytížení spadne na jednotky procent při stejné intenzitě provozu.

Vzhledem k stále se objevujícím dalším překážkám je celkem pravděpodobné, že se objeví pokračování tohoto článku.