Egy Linux-alkalmazás VPN-hez rögzítése UID-szabályokkal
Az UID-szabály csak helyi socketeket lát, és a szokásos loopback-kivétel megöli az alagutat. Először irányítsd a fiókot, aztán szűrj és ellenőrizz minden lépést.
Egyetlen program VPN-hez rögzítése Linuxon főleg útválasztási probléma, és a tűzfal-szabály csak arról dönt, mi történik az útvonal kiválasztása után. Ha a program UID-jét az OUTPUT-ban azelőtt egyezteted, hogy az alagút birtokolná az alapértelmezett útvonalat, a szabály éppen azt a forgalmat dobja el, amelyet védeni akartál. Ugyanez a szabály a feloldó folyamatot sem látja, amely a program DNS-kérdéseire válaszol. Állítsd helyes sorrendbe, és egyetlen kiváltság nélküli fiók megbízható osztottalagút-választóvá válik.
Adj útvonalat a fióknak az ítélet előtt
A tulajdonos-egyeztetés egy kérdésre válaszol: az ehhez az UID-hez tartozó helyi socket építette a csomagot? Nem választ interfészt, és nem tudja mozgatni azt a forgalmat, amelyet a fő tábla már a fizikai NIC-en küld ki. Ezért a megfelelő útvonal nélküli UID-szabály blokkolja a programot ahelyett, hogy átirányítaná.
Először a saját tábláján keresztül küldd a fiókot, aztán hagyd a tűzfalat megerősíteni az eredményt. Ez olyan hoszton működik, amelynek iproute2-je érti az uidrange-et, amit ip rule help egy sorban jelent. A 100-as tábla alapértelmezett útvonalat tart az alagúton, és a szabály csak az UID 1000-et küldi abba a táblába. Minden más a fő táblán marad, és továbbra is a fizikai kapcsolatot használja.
ip rule add uidrange 1000-1000 table 100 ip route add default dev tun0 table 100
A második fiók, amelynek másik alagútra van szüksége, a 101-es táblát és saját szabályt kap, mert két profil nem birtokolhatja egyszerre az alapértelmezett útvonalat ugyanabban a táblában. A NetworkManager-profilok rendszerszintűek, ezért ez a rész kézi marad, és a szabályok újraindításkor eltűnnek, hacsak valami vissza nem állítja őket. Írd a táblákat egy indítószkriptbe az alagút definíciója mellé, hogy az újracsatlakozás ne hagyhasson egy fiókot rossz úton.
Miért öli meg a loopback-kivétel az alagutat
A szabály legtöbb másolt változata eldob minden csomagot, amely nem loopbacken keresztül hagyja el a gépet. Az alagút nem loopback, ezért az a forma az alagút forgalmát is eldobja, és az eredmény halott VPN-nek látszik, nem tűzfalhibának.
Mire az OUTPUT lefut, a kernel már választott kimenő interfészt, ezért -o az útvonal válaszához hasonlítják, nem a csomag fejlécéhez. A tulajdonos-egyeztetés csak az OUTPUT-ban és a POSTROUTING-ban kerül szóba, és a továbbított csomagok nem hordoznak socketet, amelyet olvashatna. Ez a kombináció azt jelenti, hogy a kivételnek meg kell neveznie az alagút interfészét, és tagadnia kell ezt az egyeztetést, nem a loopbacket.
A loopback továbbra is megérdemli a saját ACCEPT-jét a DROP felett, mert a program első DNS-csomagja a helyi stubhoz megy a lo interfészen keresztül. Tedd a két szabályt ebbe a sorrendbe, és a fiók eléri a stubot, aztán az alagutat, és semmi mást.
iptables -I OUTPUT 1 -o lo -m owner --uid-owner vpnapp -j ACCEPT iptables -A OUTPUT -m owner --uid-owner vpnapp ! -o tun0 -j DROP
Az IPv6 külön szabálykészlet, és a csak v4-re szánt pár érintetlenül hagyja a ip6tables elemet. Egy AAAA-rekordokat előnyben részesítő program akkor a fizikai kapcsolaton keresztül menne ki, anélkül hogy valaha elérné a DROP-ot. Tükrözd mindkét szabályt a ip6tables felé, vagy kapcsold ki az IPv6-ot a hoszton, ha az alagút nem visz v6-címet. A kézzel hozzáadott szabályok is eltűnnek újraindításkor, ezért mentsd el őket a netfilter-persistent save segítségével, vagy építsd újra ugyanezt a logikát az indítóegység által betöltött nftables-táblaként.
A DNS-válaszok másik folyamatból jönnek
A program kérése a 127.0.0.53 felé átmegy a loopback ACCEPT-en, és itt áll meg az UID-egyeztetés. A stub feloldó a saját szolgáltatásfiókjából továbbít felfelé, ezért a vpnapp elemhez kötött szabály soha nem látja a második kérést. Ha a feloldó LAN-kiszolgálót választ a kapcsolathoz, a válaszok a fizikai NIC-en jönnek vissza, miközben a program saját csomagjai az alagútban maradnak. A látható eredmény egy működő alagút törött nevekkel, vagy olyan nevek, amelyekre egy általad nem választott kiszolgáló válaszol.
A névfeloldást az alagút kapcsolatán javítsd, ne az alkalmazásban. Adj az alagútprofilnak DNS-kiszolgálót és útválasztási tartományt, majd erősítsd meg a resolvectl query és a resolvectl status tun0 segítségével, hogy a feloldó ezt használja a fontos nevekhez. Egy hálózati névtéren belül a probléma eltűnik, mert a névtér feloldója csak az alagutat ismeri.
Ellenőrizd a szabályt magától a fióktól
Kérdezd a kernelt, milyen útvonalat kap egy UID, aztán indíts kérést azon a fiókon, és hasonlítsd össze a kilépési címet a saját fiókodéval. Ha mindkettő ugyanazt a címet jelenti, a szabály valami másra illeszkedik, mint a tesztelt forgalomra. Az első parancs lentebb az útvonal-keresést mutatja csomag küldése nélkül, ami elválasztja az útválasztási hibát a tűzfalhibától.
ip route get 1.1.1.1 uid 1000 sudo -u vpnapp curl -s https://ifconfig.co
A számlálók megmutatják, hogy egy szabály valaha elsült-e. Futtasd a iptables -vnL OUTPUT parancsot, olvasd le a csomagszámot minden szabály mellett, és a valódi forgalom utáni nullát a DROP mellett egyeztetési hibának tekintsd.
A továbbított forgalom ezen a terven kívül esik. Egy híd mögötti konténernek vagy VM-nek nincs olyan helyi socketje, amely a vpnapp tulajdona lenne, ezért a tulajdonos-egyeztetés soha nem sül el, és azok a csomagok inkább a FORWARD-szabályt követik. Ha azt a terhelést le kell fedni, külön teszteld, és írj a hídhoz vagy a konténer címéhez kötött szabályt. A terv akkor áll, ha a tábla, az interfész-kivétel, a feloldó és a v6-szabályok mind ugyanabba az irányba mutatnak; hagyd ki bármelyiket, és a fiók vagy szivárog, vagy elveszti a kapcsolatot.