Закачването на една програма към VPN под Linux е предимно проблем с маршрутизацията, а правилото на защитната стена решава само какво се случва след избора на маршрут. Ако съпоставите UID на програмата в OUTPUT преди тунелът да притежава маршрута по подразбиране, правилото ще изхвърли точно трафика, който сте искали да защитите. Същото правило не вижда и процеса на резолвера, който отговаря на DNS заявките на програмата. Подредете нещата правилно и един акаунт без привилегии се превръща в надежден селектор на разделен тунел.

Дайте на профила маршрут преди присъда

Съпоставянето по собственик отговаря на един въпрос: локален сокет, принадлежащ на този UID, ли е изградил пакета? То не избира интерфейс и не може да премести трафик, който основната таблица вече изпраща през физическия NIC. Затова UID правило без съответстващ маршрут блокира програмата, вместо да я пренасочи.

Първо пуснете профила през собствената му таблица, после оставете защитната стена да потвърди резултата. Това работи на хост, чийто iproute2 разбира uidrange, което ip rule help съобщава в един ред. Таблица 100 държи маршрут по подразбиране през тунела и правилото изпраща само UID 1000 към нея. Всичко останало остава в основната таблица и продължава да ползва физическата връзка.

ip rule add uidrange 1000-1000 table 100
ip route add default dev tun0 table 100

Втори акаунт, който се нуждае от друг тунел, получава таблица 101 и собствено правило, защото два профила не могат да притежават едновременно маршрута по подразбиране в една таблица. Профилите на NetworkManager са системни, така че тази част остава ръчна и правилата изчезват при рестарт, освен ако нещо не ги възстанови. Запишете таблиците в скрипт за зареждане до дефиницията на тунела, за да не остави повторното свързване един акаунт на грешния път.

Защо изключението за loopback убива тунела

Повечето копирани версии на правилото изхвърлят всеки пакет, който не излиза през loopback. Тунелът не е loopback, така че тази форма изхвърля и трафика на тунела и резултатът изглежда като мъртъв VPN, а не като грешка в защитната стена.

Докато OUTPUT работи, ядрото вече е избрало изходящ интерфейс, затова -o се сравнява с отговора на маршрута, а не със заглавката на пакета. Съпоставянето по собственик се преглежда само в OUTPUT и POSTROUTING, а препратените пакети не носят сокет, който то да прочете. Тази комбинация означава, че изключението трябва да назове интерфейса на тунела и да отрече това съпоставяне, вместо да отрича loopback.

Loopback все още заслужава собствен ACCEPT над DROP, защото първият DNS пакет от програмата отива към локалния stub през lo. Сложете двете правила в този ред и акаунтът достига stub, после тунела и нищо друго.

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

IPv6 е отделен набор правила и двойка само за v4 оставя ip6tables недокоснат. Програма, която предпочита AAAA записи, тогава би излязла през физическата връзка, без изобщо да достигне DROP. Огледайте двете правила в ip6tables, или изключете IPv6 на хоста, когато тунелът не носи v6 адрес. Правилата, добавени на ръка, също изчезват при рестарт, затова ги запазете с netfilter-persistent save или изградете същата логика наново като nftables таблица, зареждана от модула за зареждане.

DNS отговорите идват от друг процес

Заявката на програмата към 127.0.0.53 минава през ACCEPT за loopback и точно там съпоставянето по UID спира. Stub резолверът препраща нагоре от своя служебен акаунт, така че правило, насочено към vpnapp никога не вижда втората заявка. Ако резолверът избере LAN сървър за връзката, отговорите се връщат през физическия NIC, докато собствените пакети на програмата остават в тунела. Видимият резултат е работещ тунел със счупени имена или имена, на които отговаря сървър, който не сте избрали.

Поправете разрешаването на имена на връзката на тунела, не в приложението. Дайте на профила на тунела DNS сървър и домейн за маршрутизация, после потвърдете с resolvectl query и resolvectl status tun0 че резолверът го използва за имената, които имат значение. Вътре в мрежово пространство от имена проблемът изчезва, защото резолверът на пространството познава само тунела.

Проверете правилото от самия акаунт

Попитайте ядрото какъв маршрут получава UID, после направете заявка като този акаунт и сравнете изходния адрес с този на собствения си акаунт. Ако и двата докладват един и същ адрес, правилото съпоставя нещо друго, а не трафика, който сте тествали. Първата команда по-долу показва търсенето на маршрут без изпращане на пакет, което отделя грешка в маршрутизацията от грешка в защитната стена.

ip route get 1.1.1.1 uid 1000
sudo -u vpnapp curl -s https://ifconfig.co

Броячите показват дали правило някога е задействало. Изпълнете iptables -vnL OUTPUT, прочетете броя пакети до всяко правило и приемете нула до DROP след реален трафик като грешка в съпоставянето.

Препратеният трафик стои извън този дизайн. Контейнер или VM зад мост няма локален сокет, притежаван от vpnapp, така че съпоставянето по собственик никога не се задейства и тези пакети следват политиката FORWARD. Ако този товар трябва да бъде покрит, тествайте го отделно и напишете политика, насочена към моста или адреса на контейнера. Дизайнът издържа, когато таблицата, изключението за интерфейса, резолверът и v6 правилата сочат в една посока; пропуснете дори едно от тях и акаунтът или изтича, или губи връзка.

Подобни статии