OpenBSD tűzfal (pf) – I. rész – alapok és a szűrés

Az OpenBSD pf először a 3.0-ás kiadásban mutatkozott be először, miután az addig használt IPFilter-t (ipf) eltávolították belőle. Mivel az OpenBSD talán leggyakoribb “felhasználási területe” a firewalling, így a csomagszűrő nélkül maradt OS-be nagyon hamar integrálták a már létező pf-et (említsük meg a fejlesztő nevét, megérdemli: Daniel Hartmeier), és ez folyamatos fejlesztése mellett a mai napig is szerves részét képezi az OpenBSD-nek. A pf-hez szorosan kötődik a CARP redundancia protokoll illetve a pfsync, melynek segítségével több gépből álló,  állapottartó redundáns tűzfal készíthető, ahol a tűzfal szabályok a gépek között egymással szinkronban tarthatók.

OpenBSD tűzfal (pf) – I. rész – alapok és a szűrés

Vegyük sorra néhány pontban, milyen lehetőségei vannak a pf-nek:

  • csomagszűrés
  • sávszélesség szabályzás (traffic shape)
  • IP groupok létrehozása és ezek használata a pf szabályokban
  • makrózás
  • hálózati címfordítás (NAT)
  • szűrés op. rendszer ujjlenyomat alapján (OS fingerprint)
  • scrubbing (csomagok normalizálása)
  • anchor-ok (a szabályok egy halmazának külön kezelése)
  • átirányítás (redirection)
  • stateful packet inspection (állapottartó tűzfal)
  • naplózás (logging)
  • loadbalancing

Most, hogy áttekintettük, mire is használható a pf, vágjunk is bele, konkrét példákon keresztül. Előrebocsátom: nem szeretnék egy új, átfogalmazott pf dokumentációt írni; ennek a terjedelme sok-sok oldalra rúgna, és ezt egyébként is megtették már a fejlesztők, akik kiváló dokumentációt írtak a pf-hez, ezt itt és most ide is linkelem, annak, aki szeretne mélyebben belemerülni. Nem utolsósorban én is fogok az eredeti angolból magyarra fordítva részeket kiemelni.

A pf alapból engedélyezett a rendszer indulásakor. Amennyiben szeretnénk tiltani, adjuk (ill. állítsuk) az /etc/rc.conf fileban a pf=YES értékét NO-ra. Továbbá tilthatjuk vagy engedélyezhetjük a következő indulásig kézzel:

# pfctl -d
# pfctl -e

A pf fő konfigurációs fájlja a /etc/pf.conf. Ebben vannak általában a szabályok, esetenként hivatkozások más fájlokra, melyek pl. IP csoportokat tartalmaznak.

A leggyakrabban használt parancsok:

# pfctl -f /etc/pf.conf     Betölti a pf.conf-ban szereplő szabályokat
# pfctl -nf /etc/pf.conf    Parse-olja, de nem tölti be a szabályokat
# pfctl -Fa                     Kiűrít minden a futó konfigból
# pfctl -Fa -f /etc/pf.conf Flush, majd teljes szabályrendszer újratöltése
# pfctl -sr                 Megmutatja a futó tűzfalszabályokat
# pfctl -ss                 Megmutatja a futó állapottáblát
# pfctl -si                 Megmutatja a szűrő statisztikáit
# pfctl -sa                 Megmutat minden létező változót a szűrőről

Listák és makrók:

Ahelyett, hogy pl. minden forrás IP címre külön szabályt hoznánk létre, használhatunk listákat. A szabályok betöltésekor a listában szereplő változókra automatikusan fog szabályt létrehozni a pf. Például:

Ez a két sor egy sorban is leírható a listák használatával.

block out on em0 from 192.168.0.1 to any
block out on em0 from 10.5.32.6 to any

Így:

block out on em0 from { 192.168.0.1, 10.5.32.6 } to any

Létrehozhatunk elnevezett listát is, amire aztán hivakozunk; ilyenformán:

untrusted = “{ 192.168.1.2 192.168.5.36 }”
block in inet proto tcp from { $untrusted } to port 23

A fenti két sor az ‘untrusted’ listában szereplő IP címekről jövő kéréseket eldobja, amennyiben ez a tcp/23-as (telnet) portra érkezik.

Makrók:

A makrók olyan változók, melyek tartalmazhatnak IP-ket, interfész neveket, portokat, stb. Nagyban megkönnyítik és átláthatóbbá teszik a tűzfalszabályok létrehozását ill. a szabályokat magukat.

external_if = “em0”
block in on $external_if from any to any

vagy:

enemies = “{ 192.168.1.1, 10.0.2.5, 192.168.43.53 }”
pass in inet proto tcp from $enemies to any

Ez, ha lekérdezzük a futó szabályokat, így fog kinézni:

Táblák:

A táblák, szemben a listákkal, csak IPv4 ill. IPv6 címek vagy hálózatok tárolására alkalmasak, viszont -ellentétben a listákkal- ezek beolvasása még nagyszámú, többszáz soros lista esetén is kevéssé processzor és memóriaigényes, mint a listáké.

Két fajtájuk van:

const – itt a tábla tartalma nem változtatható, ha egyszer létre lett hozva
persist – ezt a kernel memóriába teszi, akkor is, ha nincs rá vonatkozó szabály. Enélkül az opció nélkül ‘flush’ esetén kiürül a tábla is.

Nézzünk példát mindegyikre és írjuk hozzá a /etc/pf.conf-hoz a következőket:

table <goodguys> { 192.0.2.0/24 }
table <rfc1918> const { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }
table <spammers> persist

block in on em0 from {  <spammers> } to any
pass  in on em0 from <goodguys> to any

table <spammers> persist file “/etc/spammers.table”

block in on em0 from <spammers> to any

Nézzük most a futó szabályokat a tűzfal konfig újratöltése után!

Tehát: a spammers táblában szereplő IP blokkokból a 25-ös portunkra érkező forgalom ki van tiltva (innentől Korea egy jó részéből nem fognak spamek érkezni a mailszerverünkre, hurrá 🙂 ) Ezenkívűl a goodguys táblában szereplő hálózatból mindenkit beengedünk mindenhova. Az rfc1918 táblára pedig nincs illeszkedő szabály, így azt nem is töltötte be. Ilyen egyszerű ez.

Megjegyezhetjük, hogy meglevő táblához adhatunk vagy törölhetünk bejegyzést az alábbi módon is:

# pfctl -t spammers -T add 218.70.0.0/16
# pfctl -t spammers -T delete 218.70.0.0/16

Ennyit most a táblákról.

Csomagszűrés:

Ha az erre vonatkozó összes példát szeretnénk végignézni, az meglehetősen hosszú cikket szülne, ezért megpróbálom kicsit általánosabban leírni. A szintakszis így néz ki:

action [direction] [log] [quick] [on interface] [af] [proto protocol] [from src_addr [port src_port]] [to dst_addr [port dst_port]] [flags tcp_flags] [state]

Ez gondolom, így kicsit ijesztő, de helyettesítsük be a változókat egy konkrét értékkel, és mindjárt a helyükre kerülnek a dolgok:

block in log quick on em0 inet proto tcp from 1.1.1.1 port 1 to 2.2.2.2 port 80 flags any no state

Fordítsuk le pf-ről magyarra 🙂

Minden olyan tcp csomag, mely az em0 interfacen jön be az 1.1.1.1-es IP 1-es portjáról és a 2.2.2.2-es IP 80 portjára érkezik, tekintet nélkül a flagekre és az állapotra, naplózásra  és eldobásra kerül, függetlenül attól, hogy van-e más illeszkedő szabály.

Az implicit tiltás:

block in  all
block out all

A quick opció:

Nos, ez egy nagyon fontos dolog a pf szabályokban. Ugyanis alapvetően a pf szekvenciálisan, fentről lefelé kezeli a tűzfal szabálysorait. Amennyiben több szabálysor van, amely egy adott forrás IP-re illeszkedik pl. , akkor mindkettőt feldolgozza és az utolsó győz alapon a második fog érvényesülni. Ennek a viselkedésnek a megváltoztatására való a ‘quick ‘. Amennyiben ezt egy szabálysorba beletesszük, akkor illeszkedés esetén semmit nem vizsgál tovább, ezt a sort fogja alkalmazni. Magyarán, az utolsó illeszkedő szabály győz helyett az első illeszkedő szabály győz elve fog érvényesülni.

Monitorozás:

Időközben kaptam egy kérdést, hogy vajon milyen a monitorozhatósága a tűzfalnak? Ez teljesen jogos kérdés, és a válasz is adott: nagyon jól követhető az egyes szabályok használtsága, ráadásul többféle módon is. Nézzük meg ezeket dióhéjban:

1., A pf saját statisztikái:

# pfctl -v -s r

2., A másik megoldás a tcpdump használata, bár ez inkább debugolásnál hasznos a pf esetében:

# tcpdump -i em0 -n -v

3., Külső alkalmazás: a pftop (http://www.eee.metu.edu.tr/~canacar/pftop/ )

4., Grafikus módon: pfstat (http://www.benzedrine.cx/pfstat.html )

Hát, azt hiszem, lehet válogatni 🙂 A külső alkalmazások elérhetők a port-treeben, így tulajdonképpen ezek is nagyon könnyen telepíthetőek.

Összegzés:

Ennek a cikknek a végigolvasása után remélhetőleg sikerült megértened a pf alapjait. Ebből és majd az ezután következőkből látjuk, hogy egy nagyon jól átgondolt kialakítású, logikusan, következetesen és rugalmasan konfigurálható tűzfal alkalmazásról van szó, mely igazából több is, mint tűzfal, hiszen NAT és traffic shape funkciókkal is rendelkezik.