NAT, tűzfal, address listek és hálózati szeparáció MikroTik RouterOS alatt
Az előző részben bemutattam a hálózat alapjául szolgáló VLAN-struktúrát. Ezzel már sikerült logikailag különválasztanunk az eszközeinket, azonban ez önmagában még kevés a biztonságos üzemeltetéshez. A routerünk egyelőre nem igazán szabályozza a forgalmat úgy, ahogy szeretnénk. Sőt, ahhoz, hogy egyáltalán internetünk legyen, szükség van még néhány beállításra.
Kezdjük az internettel
Jelen konfiguráció mellett, ha a WAN portra csatlakoztatjuk az internetszolgáltató eszközét, valamelyik LAN portra pedig a számítógépünket, akkor a gépünknek a DHCP-szervertől már IP-címet, hálózati maszkot, alapértelmezett átjárót és DNS-szervert kellene kapnia. Ettől azonban még nem feltétlenül lesz internetünk. Hiányzik ugyanis egy fontos elem: a NAT (Network Address Translation), vagyis a hálózati címfordítás. Emlékszem, amikor sok-sok évvel ezelőtt hazahoztam az első MikroTik routeremet. A terv egyszerű volt: csinálok belőle egy TP-Link-szerű otthoni routert. Egy port lesz a WAN, a többi LAN, bedugom a számítógépet, és már működik is az internet. Hát nem. Egy hétvégém ment rá arra, hogy kiderítsem, vajon miért nem működik. Persze visszatölthettem volna a gyári alapkonfigurációt, és valószínűleg néhány perc múlva lett volna internetem. Csakhogy abból nem tanultam volna semmit. Így jutottam el a NAT-ig.
Mi az a NAT?
Az otthoni hálózatunkban privát IP-címeket használunk, például:
192.168.11.100
Ezekkel a címekkel közvetlenül nem tudunk kommunikálni az interneten. A routerünk WAN oldalán viszont van egy, az internetszolgáltatótól kapott címünk. Ahhoz, hogy a belső gépeink ezen keresztül elérhessék az internetet, a routernek át kell írnia a kifelé haladó csomagok forráscímét. MikroTik RouterOS alatt ezt egy srcnat szabállyal oldhatjuk meg, amelynek az actionje: masquerade Például:
/ip firewall nat add chain=srcnat out-interface=<WAN> action=masquerade
A <WAN> helyére természetesen a saját WAN interfészünk nevét kell írni. A szabály jelentése leegyszerűsítve: Ha egy csomag a WAN interfészen keresztül hagyja el a routert, a belső forráscímet cseréljük le a router WAN oldali címére. A router közben nyilvántartja a kapcsolatokat, ezért amikor megérkezik a válasz, tudja, hogy azt melyik belső kliensnek kell továbbítania. Ennek köszönhető, hogy akár egyetlen publikus IPv4-cím mögött egyszerre több számítógép, telefon, televízió, IoT-eszköz és egyéb kliens is képes kommunikálni az internettel. Kívülről nézve ezek a kapcsolatok ugyanarról a címről érkeznek. És ezzel tulajdonképpen már van internetünk.
Csakhogy van egy apró probléma. Attól, hogy működik, még egyáltalán nem biztos, hogy biztonságos is.
A következő lépés ezért a tűzfal felépítése lesz. Megnézzük, hogyan szabályozhatjuk, hogy mi érhesse el magát a routert, milyen forgalmat engedünk a VLAN-ok között, illetve hogyan építhetünk fel egy olyan szabálykészletet, ahol alapvetően nem azt soroljuk fel, amit tiltani szeretnénk, hanem azt engedjük meg, amire valóban szükségünk van. Ehhez tűzfalszabályokat fogunk létrehozni. Első körben ismerkedjünk meg egy tűzfalszabály felépítésével:
chain=forward
| src-address=192.168.11.0/24 | dst-address=192.168.8.0/24 | protocol=tcp | dst-port=443 | action=accept
[ HOL?]
[HONNAN? HOVÁ? MILYEN? MELYIK]
[ PORT? MIT CSINÁLJON?]
Egy tűzfalszabály leegyszerűsítve feltételekből és egy műveletből áll. A fenti szabály azt mondja: ha a forward chainen érkező csomag a 192.168.11.0/24 hálózatból a 192.168.8.0/24 hálózat TCP 443-as portjára tart, akkor engedjük át (accept). Továbbá tűzfalszabályok írása némi odafigyelést igényel, ugyanis a router a szabályokat felülről lefelé haladva vizsgálja. Emiatt egyáltalán nem mindegy, hogy egy szabály hova kerül a listában. Ha például egy csomag illeszkedik egy accept szabályra, akkor engedélyeztük, ha pedig egy drop szabályra, akkor eldobjuk. Ilyenkor az adott csomag feldolgozása ezen a ponton befejeződik.
Amikor én tűzfalat építek, a szabályokat több logikai blokkra bontom. Mielőtt azonban ezt megmutatnám, érdemes tisztázni a MikroTik tűzfalának egyik alapvető fogalmát: a chain, vagyis a lánc szerepét. Elsőre talán bonyolultan hangzik, pedig az alapötlet nagyon egyszerű. Amikor egy csomag megérkezik a routerhez, el kell döntenünk, hová tart. Három alapvető esetünk lehet.
INPUT – amikor maga a router a cél
Ha a csomag célja maga a MikroTik, akkor az input chain szabályai vonatkoznak rá. Például a számítógépünkről belépünk a routerre WinBoxszal vagy SSH-val:
PC → MikroTik
Ebben az esetben nem a routeren keresztül szeretnénk elérni egy másik eszközt. Maga a router a célállomás, ezért ez input forgalom. OUTPUT – amikor maga a router indítja a kapcsolatot Ennek tulajdonképpen az ellenkezője az output. Ilyenkor a forgalmat maga a MikroTik generálja:
MikroTik → másik eszköz
Például amikor a router DNS-lekérdezést végez, NTP-szervert ér el, vagy saját maga kezdeményez valamilyen hálózati kapcsolatot. FORWARD – amikor a router csak továbbítja a csomagot, és a hálózatunk szegmentálása szempontjából főként ez lesz érdekes számunkra. Ilyenkor a csomag megérkezik az egyik interfészen, majd a router továbbítja egy másik hálózat felé:
PC → MikroTik → Internet
vagy például:
VLAN 11 → MikroTik → VLAN 8
A router ebben az esetben nem célja és nem is forrása a kommunikációnak. Csak továbbítja a csomagot, ezért az ilyen forgalom a forward chainen halad keresztül. Leegyszerűsítve tehát:
input → a routerhez
output → a routertől
forward → a routeren keresztül
Ez a különbség később nagyon fontos lesz. Ha például azt szeretnénk megakadályozni, hogy az egyik VLAN kliensei elérjék a másik VLAN gépeit, akkor forward szabályokra lesz szükségünk. Ha viszont azt akarjuk szabályozni, hogy ugyanezek a kliensek elérhetik-e magát a MikroTik routert, akkor már az input chainnel kell foglalkoznunk.
A szabályok sorrendjének nemcsak logikai, hanem teljesítménybeli jelentősége is lehet. Általánosságban érdemes a gyakran illeszkedő szabályokat előrébb helyezni, hogy a routernek minél kevesebb szabályt kelljen megvizsgálnia egy-egy csomagnál.
A saját szabálykészletemet az áttekinthetőség kedvéért két nagy blokkra bontottam. Előre kerültek a forward, utánuk pedig az input chainhez tartozó szabályok. Első pillantásra ez akár ellentmondásnak is tűnhet az előbb leírtaknak. Ha a gyakran illeszkedő szabályokat érdemes előrébb helyezni, akkor például miért kerül hátrébb a két MikroTik közötti WireGuard-kapcsolatot engedélyező input szabály? Azért, mert a különböző chain-eket nem egyetlen hosszú szabálylistaként kell elképzelni. Ha egy csomag célja maga a router, akkor a filter szabályok feldolgozásakor az input chainbe kerül, és a router csak az ehhez a chainhez tartozó szabályok között keresi az illeszkedést. Attól, hogy a listában előtte több forward szabály található, a csomag ezeken nem halad végig. Ugyanez fordítva is igaz: egy VLAN-ok között továbbított csomag a forward chain szabályai között kerül feldolgozásra, az utána következő input szabályok pedig számára nem relevánsak. Ezért nálam a két blokkra bontás elsősorban az áttekinthetőséget szolgálja. A szabályok sorrendjének optimalizálása természetesen továbbra is fontos, de elsősorban az adott chainen belül.
Egy-egy tűzfalszabályt többféleképpen is meg lehet írni. Kezdetben emlékszem rá, hogy ha egy gépnek egy szerver adott szolgáltatásához kellett kapcsolódnia – például egy adatbázis-szerverhez –, akkor létrehoztam erre a külön szabályt. Megadtam a forrás IP-címét, a cél IP-címét, a célportot és a használt protokollt. Ha azonban egy második gépnek is szerettem volna ugyanehhez a
szolgáltatáshoz hozzáférést biztosítani, akkor létrehoztam még egy ugyanilyen szabályt, csak másik forrás IP-címmel. Ez működik, de nem éppen optimális megoldás. Egyrészt a szabálylista gyorsan elkezd növekedni, másrészt feljebb már kitárgyaltuk, hogy minél több szabályt kell a routernek megvizsgálnia, annál több processzoridőt használunk fel. Ráadásul egy hosszú, sok hasonló szabályból álló tűzfal később sokkal nehezebben áttekinthető és karbantartható. A MikroTik erre kínál egy sokkal elegánsabb megoldást: az address list használatát. A fenti példában a két kliens IP-címét felvehetjük ugyanabba a mysql_client nevű address listbe, az adatbázis-szervert pedig egy mysql_server listbe:
add list=mysql_client address=192.168.1.20
add list=mysql_client address=192.168.1.21
add list=mysql_server address=192.168.2.10
Ezután a tűzfalszabályban már nem az egyes IP-címekre, hanem ezekre a listákra hivatkozunk:
add chain=forward src-address-list=mysql_client dst-address-list=mysql_server protocol=tcp dst-port=3306 action=accept
A szabály jelentése így egyszerűen megfogalmazható: ha a forrás IP-címe szerepel a mysql_client listben, a cél IP-címe pedig a mysql_server listben, és a kapcsolat TCP protokollon a 3306-os portra irányul, akkor engedjük át. A korábbi két külön szabály helyett tehát egyetlen tűzfalszabállyal megoldottuk mindkét kliens hozzáférését. Az address list használatának azonban nem csak a szabályok számának csökkentése az előnye. Ha később egy harmadik kliensnek is hozzáférést szeretnénk adni az adatbázis-szerverhez, magához a tűzfalszabályhoz már hozzá sem kell nyúlnunk. Egyszerűen felvesszük az új IP-címet a meglévő listába:
add list=mysql_client address=192.168.1.22
Ettől kezdve az új kliensre ugyanaz a már meglévő tűzfalszabály fog vonatkozni. Ez nemcsak rövidebb szabálylistát eredményez, hanem a konfigurációt is sokkal áttekinthetőbbé teszi. A tűzfalszabályainkban így egyre kevésbé konkrét IP-címekkel, és egyre inkább az eszközök szerepével dolgozhatunk: adatbázis-kliensek, szerverek, adminisztrációs gépek vagy éppen olyan eszközcsoportok, amelyeknek valamilyen közös hozzáférést szeretnénk biztosítani.
Az address listre nemcsak konkrét IP-címeket, hanem teljes címtartományokat, hálózatokat is felvehetünk. A saját hálózatomban az összes VLAN-hoz tartozó hálózatot felvettem egy internal_network nevű address listre. Ez alól az admin hálózat kivétel, ennek okára később még visszatérek. Így egyetlen névvel hivatkozhatok az összes belső hálózatomra. Ezt felhasználhatjuk például arra, hogy egyetlen szabállyal megakadályozzuk új kapcsolatok létrejöttét a belső hálózatok között. Mielőtt azonban ezt a szabályt elkészítjük, érdemes megismerkedni a connection-state fogalmával, mert a továbbiakban ennek fontos szerepe lesz. A MikroTik tűzfala nemcsak az egyes csomagokat képes egymástól függetlenül vizsgálni, hanem a connection tracking segítségével nyilvántartja a kapcsolatokat is. Ennek köszönhetően egy csomagról azt is tudja, hogy egy új kapcsolat létrehozására irányul, egy már korábban létrejött kapcsolat része, vagy valamilyen már létező kapcsolathoz köthető. A tűzfalszabályokban ezt a connection-state segítségével használhatjuk fel. A legfontosabb állapotok:
new – egy új kapcsolat kezdetéhez tartozó csomag;
established – egy már létrejött, nyilvántartott kapcsolathoz tartozó forgalom;
related – egy már létező kapcsolathoz kapcsolódó, de attól különálló kapcsolat;
invalid – olyan csomag, amelyet a connection tracking nem tud érvényes kapcsolathoz rendelni.
Ez azért rendkívül hasznos, mert nem kell egy már engedélyezett kapcsolat minden egyes további csomagját újra végigvizsgálnunk az összes szolgáltatásspecifikus szabállyal. A szabályrendszer elején engedélyezhetjük az established, related állapotú forgalmat, az invalid csomagokat pedig eldobhatjuk. Ezután már elsősorban azt kell meghatároznunk, hogy milyen
új kapcsolatok létrejöttét szeretnénk engedélyezni. Ezt használom ki a belső hálózatok szeparálásánál is. Az internal_network listán szereplő hálózatok között alapértelmezetten nem engedem új kapcsolat létrehozását:
Ez a szabály a szabályrendszer vége felé helyezkedik el. Az ezt megelőző accept szabályokkal engedélyezem azokat az új kapcsolatokat, amelyekre valóban szükség van. Ha egy új kapcsolat egyik ilyen szabályra sem illeszkedik, akkor eljut ehhez a drop szabályhoz, és nem jöhet létre. Ezzel tulajdonképpen megfordítjuk a tűzfalszabályok felépítésének logikáját. Nem próbáljuk felsorolni az összes olyan kommunikációt, amelyet tiltani szeretnénk, hanem azt határozzuk meg, hogy minek szabad működnie. A többi új kapcsolat alapértelmezetten tiltott. A szabályrendszer legvégén pedig egy általános drop szabály áll, amely minden olyan forgalmat eldob, amelyet az azt megelőző szabályok nem kezeltek. Ez adja meg a teljes forward chain végső, default deny jellegű lezárását. Tulajdonképpen furcsa módon már elkészítettük a szabályrendszerünk azon részét, amelyben mindent elszeparáltunk egymástól – ha úgy tetszik, még az internetet is. Ha a szabályrendszer végén lévő általános tiltás, azaz az implicit deny aktív, akkor jelen pillanatban az az állapot áll fenn, hogy semmi nem működik. A belső hálózatok nem kommunikálnak egymással, és az internet felé sem tudunk kapcsolatot létrehozni. Most kezdjük el felépíteni ennek a rendszernek a kivételeit, vagyis azokat a kommunikációs irányokat, amelyeket ténylegesen engedélyezni szeretnénk. Első lépésként dobjuk el az invalid állapotú kapcsolatokat. Ezek olyan csomagok, amelyeket a connection tracking nem tud egy érvényes kapcsolathoz rendelni. Nincs értelme ezeket végigküldeni a teljes szabályrendszeren, hogy aztán végül úgyis az implicit deny dobja el őket. Ha már tudjuk, hogy érvénytelenek, szabaduljunk meg tőlük minél hamarabb. Ezután hozzunk létre egy olyan szabályt, ami engedélyezi az established és related állapotú kapcsolatokat. Az established azt jelenti, hogy a csomag egy már korábban létrejött és a connection tracking által nyilvántartott kapcsolathoz tartozik. Ez azért fontos, mert amikor például engedélyezzük, hogy egy kliens kapcsolatot kezdeményezzen egy szerver felé, akkor nem szeretnénk külön szabályt írni minden egyes válaszcsomagra. Tegyük fel, hogy engedélyeztem egy kliens számára, hogy TCP-kapcsolatot kezdeményezzen egy szerver adott szolgáltatása felé. A kapcsolat létrejötte után a szerver természetesen válaszolni fog a kliensnek. Egy állapotmentes szabályrendszerben
ehhez a visszafelé haladó forgalmat is külön engedélyeznünk kellene. A MikroTik tűzfala azonban kapcsolatkövetést használ. Miután a kapcsolat létrejött, az ahhoz tartozó további csomagokat established állapotúnak látja. Ezért elegendő a szabályrendszer elején egyszer engedélyezni az established, related forgalmat, és nem kell minden engedélyező szabály mellé külön „visszafelé menő” szabályt készítenünk. A related állapot hasonló gondolatot követ, de itt egy már létező kapcsolathoz kapcsolódó másik kapcsolat forgalmáról beszélünk. Ezzel megint jelentősen egyszerűsítettük a szabályrendszert: nekünk elsősorban azt kell meghatároznunk, hogy milyen új kapcsolatok jöhetnek létre. Ha egy kapcsolatot már engedélyeztünk és létrejött, annak további forgalmát az established, related szabály kezeli.
A következő lépésben engedélyezzük az internet felé irányuló forgalmat. Ehhez készítünk egy általános accept szabályt, amelynek feltétele, hogy a csomag a WAN interfészen keresztül hagyja el a routert.
add chain=forward action=accept out-interface=WAN
Fontos megfigyelni, hogy ebben a szabályban nem határozzuk meg, melyik belső interfészről vagy hálózatból érkezhet a forgalom. Ehelyett a kimenő interfészt (out-interface) vizsgáljuk. Vagyis ha egy továbbított csomag útvonala a WAN interfész felé vezet, a szabály engedélyezi azt. Ezzel egyetlen szabállyal engedélyezhetjük az internet felé tartó forgalmat anélkül, hogy minden VLAN-hoz vagy belső hálózathoz külön accept szabályt kellene létrehoznunk.
Van azonban egy kivétel: az IoT-hálózat. Ennél a hálózatnál nem szeretném általánosan engedélyezni az internetelérést. Az IoT-eszközök internetkapcsolata alapértelmezetten tiltott, viszont a Home Assistant számára szükség van rá. Emiatt az általános, WAN felé engedélyező szabály elé két külön szabályt kell elhelyezni. Először az address listben létrehozom az IoT-hálózatot:
192.168.9.0/24
majd külön felveszem a Home Assistant címét:
192.168.9.5
add list=iot address=192.168.9.0/24 comment=”IoT halozat”
add list=HomeAssistant address=192.168.9.5 comment=”Home Assistant”
Ezután két szabályt készítek.
Az első engedélyezi a Home Assistant internet felé irányuló forgalmát, a második pedig tiltja az IoT-hálózat többi eszközének internetelérését.
add chain=forward action=accept src-address-list=HomeAssistant out-interface=WAN comment=”Home Assistant internet engedelyezese”
add chain=forward action=drop src-address-list=iot out-interface=WAN comment=”IoT internet tiltasa”
A sorrend itt ismét fontos. A Home Assistant címe ugyanis az IoT-hálózat része, ezért ha előbb tiltanánk a teljes 192.168.9.0/24 hálózatot, akkor a Home Assistant forgalma is beleesne ebbe a szabályba, és a későbbi engedélyező szabályig már nem jutna el. Ezért először a kivételt engedélyezzük, utána tiltjuk a teljes IoT-hálózatot, majd csak ezután következik az általános WAN irányú accept szabály.
Így az egész internet elérési logika a következő:
1. Home Assistant → WAN → ACCEPT
2. IoT hálózat → WAN → DROP
3. minden más → WAN → ACCEPT
Az internetet tehát már elérik a gépek. A következő lépésben kezdjük el felépíteni azokat a szabályokat, amelyek lehetővé teszik, hogy az egyes hálózatok gépei és eszközei elérhessék azokat a belső szolgáltatásokat, amelyekre szükségük van. A cikksorozatban korábban már bemutattam a szerverek számára létrehozott VLAN-okat. Nálam a publikus és a belső használatú szerverek külön hálózatba kerültek. Mivel ezek a szerverek különféle szolgáltatásokat biztosítanak a hálózat többi része számára, természetesen a hozzájuk való hozzáférést is engedélyeznünk kell a tűzfalon. Nem fogok itt minden, nálam működő szolgáltatáshoz külön szabályt bemutatni, mert abból önmagában egy kisebb könyv lenne. Egy példát azonban érdemes végigvenni. Nálam működik egy AdGuard Home szerver, amely többek között DNS-alapú reklám-, követő- és egyéb nem kívánt tartalmak blokkolására használható. Mivel a hálózat kliensei ezt a szervert használják DNS-feloldásra, biztosítanunk kell számukra a DNS-szolgáltatás elérését. Először vegyük fel az AdGuard Home szerver címét az address listbe dns-server néven:
add list=dns-server address=192.168.8.3 comment=”AdGuard Home DNS”
Ezután engedélyezzük, hogy az internal-ip listán szereplő belső hálózatok elérhessék a DNS-szervert az 53-as porton. A DNS jellemzően UDP-t használ, de bizonyos esetekben TCP-re is szükség van, ezért mindkét protokollhoz készítünk szabályt:
add chain=forward action=accept protocol=udp src-address-list=internal-ip dst-address-list=dns-server dst-port=53 comment=”Internal networks -> DNS UDP”
add chain=forward action=accept protocol=tcp src-address-list=internal-ip dst-address-list=dns-server dst-port=53 comment=”Internal networks -> DNS TCP”
Fontos, hogy itt sem a teljes szerverhez biztosítunk korlátlan hozzáférést. Nem azt mondjuk, hogy az internal-ip hálózatok bármit elérhetnek a DNS-szerveren, hanem pontosan azt engedélyezzük, amire szükségük van: jelen esetben a DNS-szolgáltatást az 53-as porton. Ez a szemlélet a további belső szolgáltatásoknál is végigvihető. Mindig azt határozzuk meg, hogy ki, melyik szervert, milyen szolgáltatáson keresztül érheti el, és csak ehhez készítünk accept szabályt.
Korábban már kitértem arra, hogy az admin hálózatra más szabályok vonatkoznak. Ezt a hálózati szegmenst kifejezetten adminisztratív feladatok elvégzésére hoztam létre. Ennek egyik nagy előnye, hogy az általam használt gépeket, telefonokat és tableteket nem szükséges külön-külön, eszközszinten kezelnem. Nem kell minden egyes eszköz IP-címét külön address listbe felvenni, elegendő azt meghatározni, hogy mely eszközök kerülhetnek az admin hálózatba. Ha egy eszköz ebbe a VLAN-ba kerül, automatikusan ugyanazok a jogosultságok vonatkoznak rá, mint az admin hálózat többi kliensére. Ennek a megoldásnak az előnye igazán akkor válik majd láthatóvá, amikor a dinamikus VLAN-kezelést és a RADIUS használatát tárgyaljuk. Ott ugyanis már nem feltétlenül az dönti el egy eszköz jogosultságait, hogy fizikailag melyik portra vagy access pointra csatlakozik, hanem az, hogy a hitelesítés eredményeként melyik VLAN-ba kerül. Mivel az admin hálózat nálam kiemelt, megbízható hálózati szegmens, innen teljes hozzáférést szeretnék biztosítani a többi hálózat és szolgáltatás felé. Ehhez először vegyük fel az
admin hálózatot az address listbe:
add list=admin-net address=192.168.7.0/24 comment=”Admin halozat”
Ezután hozzuk létre az engedélyező szabályt:
add chain=forward action=accept src-address-list=admin-net comment=”Admin teljes hozzaferes”
Ezt a szabályt célszerű a szabályrendszer elejére, az általános tiltó és szeparációs szabályok elé helyezni. Innentől az admin hálózatból induló forgalom teljes hozzáférést kap a router által továbbított hálózatok felé. Vagyis az admin hálózat kliensei új kapcsolatokat kezdeményezhetnek a többi VLAN, a szerverhálózatok és az internet felé is. A létrejött kapcsolatok válaszforgalmát pedig a korábban létrehozott established, related szabály kezeli.
Végső soron nagyrészt kész is vagyunk a forward résszel, ami azt szabályozza, hogy az egyes hálózati szegmensek elszeparálva legyenek egymástól, és csak kivételes esetben kommunikálhassanak egymással, az elkülönített gépek és azok internet-hozzáférése is ebben a blokkban van meghatározva.
Röviden, tömören valahogy így néz ki:
established, related → ACCEPT
admin hálózat → bárhova → ACCEPT
Home Assistant → internet → ACCEPT
IoT hálózat → internet → DROP
minden további WAN felé irányuló forgalom → ACCEPT
belső hálózatok → DNS → ACCEPT
belső hálózatok → szükséges szerverek/szolgáltatások → ACCEPT
…további szükséges kommunikációk → ACCEPT
internal hálózatok → szerverhálózatok → DROP
internal hálózatok → internal hálózatok → DROP
minden egyéb, korábban nem engedélyezett forgalom → DROP
IMPLICIT_DENY
A router védelme – az input chain Az eddig felépített szabályrendszerrel többé-kevésbé biztonságosan elszeparált hálózatokat kapunk. Van azonban még egy fontos dolog, amire figyelnünk kell. A forward chain segítségével azt szabályozzuk, hogy a routeren keresztülhaladó forgalomból mi engedélyezett. Vagyis például egy VLAN-ban lévő kliens kommunikálhat-e egy másik VLAN-ban található szerverrel, vagy elérheti-e az internetet. Ezzel azonban még nem szabályoztuk azt, hogy az egyes hálózatokból magát a routert hogyan lehet elérni. Erre szolgál az input chain. Itt is érdemes ugyanazt az alapelvet követni, mint a forward szabályoknál: csak azt engedjük meg, amire ténylegesen szükségünk van, minden mást pedig tiltunk. Itt azonban különösen óvatosnak kell lennünk. Ha első lépésként létrehozunk olyan input szabályt, amely minden, a router felé érkező csomagot eldob, könnyen kizárhatjuk saját magunkat is az eszközből. A RouterBOARD világát ismerők persze tudják, hogy ilyen helyzetben még rendelkezésünkre állhat például a MAC Winbox vagy a MAC Telnet, amelyek Layer2 szinten működnek. Konfigurálás közben pedig rendkívül hasznos lehet a Winbox Safe Mode funkciója is, amely sikertelen konfiguráció esetén megmenthet bennünket egy kellemetlen helyzettől. Ezek részletes bemutatása azonban már inkább egy külön bejegyzés témája. Én ezért az input szabályrendszer kialakítását azzal kezdem, hogy biztosítom az admin hálózat számára a router elérését.
Használhatjuk természetesen a korábban létrehozott address listet is:
Ezzel az admin hálózatból érkező kliensek elérhetik magát a routert. Első ránézésre adná magát a gondolat:
admin hálózat → router → ACCEPT
minden más → router → DROP
Csakhogy a valóság általában ennél valamivel bonyolultabb. A router maga is futtathat olyan szolgáltatásokat, amelyeket más hálózatokból vagy akár az internet felől is el kell érnünk. Nálam ilyen például a WireGuard. Ha a MikroTik maga a WireGuard végpont, akkor a VPN-kapcsolat felépítéséhez engedélyezni kell a WireGuard UDP-portjára érkező forgalmat a WAN interfész felől:
add chain=input action=accept protocol=udp in-interface=WAN dst-port=
Természetesen ide azt a portot kell megadni, amelyen az adott WireGuard interfész figyel. Van egy további kivételem is. A munkahelyem internetkapcsolata fix publikus IP-címmel rendelkezik, ezért erről a címtől szintén engedélyezem a router elérését.
add chain=input action=accept src-address=
Ezzel nem általánosan nyitom meg a router menedzsmentjét az internet felől, hanem csak egy előre ismert forráscím számára. Nagyon fontos itt is az established, related szabály. Ha a router már létrejött kapcsolathoz tartozó válaszforgalmat kap, nem szeretnénk minden egyes szolgáltatáshoz külön visszirányú szabályokat készíteni:
add chain=input action=accept connection-state=established,related comment=”Established,related”
Az invalid csomagokat pedig itt is érdemes korán eldobni:
add chain=input action=drop connection-state=invalid comment=”Invalid drop”
A DNS-, NTP- és MikroTik Cloud szolgáltatásoknál érdemes különbséget tenni két eset között. Ha a router szolgáltat DNS- vagy NTP-szerverként a kliensek számára, akkor valóban szükség lehet megfelelő input szabályokra, hiszen ilyenkor a kliensek közvetlenül a routerhez kapcsolódnak. Ha azonban maga a router kérdez le egy külső DNS-szervert, szinkronizál NTP-szerverrel vagy használja a MikroTik Cloud szolgáltatását, akkor ez már a router által kezdeményezett kapcsolat. Ilyenkor nem szükséges külön beengedni az internetről a DNS-, NTP- vagy Cloud-forgalmat az input chainben. A létrejött kapcsolat válaszcsomagjait az established, related szabály kezeli. NTP használata esetén nálam például a magyar pool szerverei használhatók:
0.hu.pool.ntp.org
1.hu.pool.ntp.org
2.hu.pool.ntp.org
3.hu.pool.ntp.org
Ezek elérése azonban önmagában nem indokol egy WAN felőli input engedélyezést.
Egy egyszerű feketelista
Az internet felől érkező automatikus portszkennelések és próbálkozások száma meglepően nagy tud lenni. Emiatt használhatunk egy egyszerű dinamikus address listet is. Például ha valaki a WAN interfész felől olyan TCP-portokon próbálkozik, amelyeket kívülről egyáltalán nem szeretnénk elérhetővé tenni, felvehetjük a forrás IP-címét egy blacklist nevű address listbe:
Ez a szabály önmagában még nem tiltja le a forrást, mindössze felveszi annak IP-címét a blacklist listába. Ezért szükségünk van egy másik szabályra is:
add chain=input action=drop src-address-list=blacklist comment=”Blacklist DROP”
Itt azonban érdemes nagyon megfontolni, mely portokat használjuk csapdaként. Olyan portot ne tegyünk ebbe a listába, amelyen ténylegesen publikus szolgáltatást üzemeltetünk, mert akkor a szabály a jogos klienseket is felveheti a feketelistára. Végül az input chain logikája leegyszerűsítve valahogy így nézhet ki:
established, related → ACCEPT
invalid → DROP
admin hálózat → router → ACCEPT
ismert fix publikus IP → router → ACCEPT
WAN → WireGuard port → ACCEPT
szükséges router-szolgáltatások → ACCEPT
gyanús WAN próbálkozások → BLACKLIST
blacklist → DROP
minden további forgalom → router → DROP
minden további forgalom → router → DROP
A végére pedig jöhet a mindent lezáró szabály:
add chain=input action=drop comment=”INPUT IMPLICIT_DENY”
Ezzel ugyanaz a gondolkodásmód jelenik meg mindkét láncnál: a forward chain a routeren keresztülhaladó forgalmat szabályozza, az input chain pedig magát a routert védi.
Összegzés
Ezzel elértünk a blogsorozat talán legnagyobb, legnehezebb, ugyanakkor egyik leglényegesebb részének a végére. Miközben ezt a bejegyzést írtam, folyamatosan kerültek elő olyan témák, amelyeket önmagukban is oldalakon keresztül lehetne részletezni. Szóba került a VLAN-ok működése, a DHCP, az Option 43, az address listek használata, a NAT, a connection tracking, valamint a tűzfal felépítése és annak logikája is. Ezek közül sok témát szándékosan nem bontottam ki a végletekig. Egyrészt azért, mert akkor ez a bejegyzés valószínűleg soha nem készült volna el, másrészt pedig így is egy meglehetősen hosszú és technikai anyag született, amely talán már nem feltétlenül a teljesen kezdő olvasóknak szól. A célom ugyanakkor nem is az volt, hogy egy minden részletre kiterjedő MikroTik kézikönyvet készítsek. Sokkal inkább azt szerettem volna bemutatni, hogyan építettem fel a saját hálózatomat, milyen problémákra milyen megoldásokat választottam, és ami talán ennél is fontosabb: miért éppen így oldottam meg őket. A bemutatott konfiguráció ezért nem egy olyan recept, amelyet változtatás nélkül érdemes lemásolni. Minden hálózat más, más eszközökkel, más szolgáltatásokkal és más biztonsági igényekkel. Sokkal fontosabbnak tartom megérteni a mögötte lévő gondolkodásmódot, majd azt a saját környezetünkhöz igazítani. A tűzfalnál talán ez látszott a legjobban. Nem azt próbáltuk felsorolni, hogy mi mindent szeretnénk megtiltani, hanem először meghatároztuk, minek kell működnie. Engedélyeztük a szükséges kommunikációt, majd minden mást lezártunk. Ugyanezt a logikát alkalmaztuk a hálózatok közötti forgalomnál és magának a routernek a védelménél is. Ezzel azonban maga a hálózat még korántsem tekinthető befejezettnek. Az eddig felépített infrastruktúra valójában csak az alapot adja azokhoz a megoldásokhoz, amelyek miatt nálam az otthoni hálózat az évek során ilyen összetetté vált. A következő részekben ezért lesz még miről beszélni: előkerülnek a szerverek, a hálózati szolgáltatások, a RADIUS, a dinamikus VLAN-kezelés és még jó néhány olyan megoldás, amelyre ebben a részben már csak utalni tudtam. Az alapok viszont mostanra a helyükre kerültek.
Szeretnék majd külön foglalkozni a naplózással is. A most bemutatott tűzfalszabály-rendszer struktúrájára és a megfelelően kialakított naplózásra építve ugyanis egy egyszerű IDS (Intrusion Detection System) is kialakítható, amellyel már nemcsak szabályozhatjuk a hálózati forgalmat, hanem bizonyos gyanús eseményeket és próbálkozásokat fel is ismerhetünk.
Ennek kialakítása nálam is folyamatban van, ezért ezt majd egy későbbi bejegyzésben szeretném részletesebben bemutatni, amikor már a saját rendszeremben szerzett tapasztalatokról is be tudok számolni.
