VPN клиентът се свързва, браузърът работи, а единствената команда, която наистина ви интересува, все още излиза през домашната ви връзка. Или се случва обратното: тунелът поема маршрута по подразбиране и локалният файлов сървър изчезва от работния плот. Сблъсках се с това при агент за изграждане, който трябваше да достигне вътрешно огледало на артефакти, докато останалата част от хоста оставаше в офис LAN, и клиентът предлагаше точно два избора: целия трафик или нищо. Заявката и в двете посоки е една и съща — това приложение, а не цялата машина — и повечето клиенти нямат отметка за нея, защото таблицата за маршрутизиране никога не е знала какво е приложение.

Защо таблицата за маршрутизиране не вижда приложението ви

Когато процес изпрати пакет, стекът търси адреса на местоназначението в таблицата за маршрутизиране. Нито името на изпълнимия файл, нито портът влизат в това търсене. Домейн имената се провалят по същата причина: тунелът носи трафик до адрес и докато името се превърне в такъв, маршрутизирането е единственият лост, който ядрото още има. Затова „VPN само за този сайт“ накрая означава прокси, а „VPN само за това приложение“ означава един от малък набор механизми.

MechanismWhat selectsCoversOn tunnel drop
Socket bindsource addressa client you controlconnections fail
UID rule (Linux)owning userevery process of one accountconnections fail
Firewall profile (Windows)adapter profileone programfails closed
Network namespacenamespace membershipeverything started insideconnections fail
SOCKS hopapp proxy settingone app or one browser profileconnections fail

Нито един от тези не е маршрут за отделен процес. Всеки премества решението на слой, който вижда приложението, потребителя или собствената конфигурация на приложението.

Една единствена дестинация все още е маршрут, а не правило за приложение. На macOS обичайната рецепта е да махнете отметката „Изпращай целия трафик през VPN“ и да добавите маршрут до хост: sudo route add -host internal-tuner.example -interface ppp0, премахван отново с sudo route delete -host internal-tuner.example -interface ppp0. Първо прочетете името на интерфейса от ifconfig, защото ppp0 е само пример. Шаблоните не работят в маршрут до хост, така че услуга, която говори с няколко имена, се нуждае от един запис за всяко име, а правилото покрива всеки процес на машината, не този, който сте имали предвид.

Свържете сокета с адреса на тунела

Когато контролирате клиента, свързването е най-евтиният селектор. Сокет, свързан с локалния адрес на тунела, излиза през този интерфейс, докато там съществува маршрут до местоназначението, а всичко останало на машината запазва нормалния си път. Тънкостта е, че свързването избира адреса на източника, докато таблицата за маршрутизиране все още избира изхода. Самото свързване не мести трафик, който няма маршрут в тунела, и точно това е грешката зад повечето оплаквания „свързах го, а то пак излиза по грешния път“.

import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))

Инструментите от по-високо ниво предлагат същата идея: curl --interface 10.8.0.2, CURLOPT_INTERFACE в PHP и аргументът source_address в HTTP клиентите на Python. Адресът трябва да принадлежи на тунела и печатна грешка тук се проваля като обикновена грешка при свързване, а не като оплакване за маршрутизиране. Свързването с име на устройство вместо с адрес е привилегия на Linux и не се пренася към Windows или macOS, затова преносимите скриптове подават адреса.

Linux: съпоставете собственика, не името на приложението

Ядрото може да съпостави потребителя собственик на сокет, затова отделен акаунт става селекторът. Поставете тунела в собствена таблица за маршрутизиране и насочете там само трафика на този акаунт:

ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100

Това работи само ако тунелът вече не е маршрутът по подразбиране. Стартирайте клиента с --route-nopull или филтрирайте изтласканото пренасочване с --pull-filter ignore "redirect-gateway" на OpenVPN 2.4 и по-нови, след което добавете записа в таблицата сами. Иначе съществуват и двете маршрути по подразбиране и печели тази с по-нисък приоритет. Умишлената версия на това е да насочите маршрута по подразбиране към шлюза на тунела или да го направите от скрипт при прекъсване: държи трафика извън интернет, когато тунелът падне, но това е прекъсвач, а не селектор, и остава, докато някой не го премахне.

По-силната версия е мрежово пространство от имена. Създайте го, пуснете клиента вътре и стартирайте приложението с ip netns exec. Нищо извън пространството от имена не вижда интерфейса на тунела, така че приложението не може случайно да се върне към маршрута на хоста. Цената е реална: пространството се нуждае от двойка veth, за да достигне физическата мрежа, и от собствен файл за резолвер, защото пространството за монтиране не споделя настройките за разрешаване на хоста.

Два режима на отказ обясняват повечето изтичания. Разрешаването на имена все още се случва в резолвера на хоста, затова заявката излиза извън тунела дори когато TCP връзката влиза вътре. А IPv6 се нуждае от собствено ip -6 rule, защото правило за v4 не го покрива.

Windows и macOS: ограничете защитната стена или отстъпете прокси

Windows също няма селектор на маршрут за отделно приложение, но правилата на защитната му стена могат да се ограничат до профил, а VPN адаптер може да бъде класифициран като «Публичен», докато домашната LAN остава «Частен». Създайте изходящи правила за блокиране на приложението в профилите «Частен» и «Домейн», оставете «Публичен» отворен и приложението може да достигне мрежата само през тунела. Първо задайте мрежовата категория на връзката, после потвърдете, че правилото е съвпаднало в панела за наблюдение на защитната стена. Това е блокировка, не маршрут: тя се проваля затворено, когато тунелът падне, и не може да спре приложение, което игнорира защитната стена.

macOS следва същата логика с други инструменти. SOCKS слушател, достиган през SSH, е най-малко инвазивната опция, защото ssh -D 1080 user@host създава един локален порт и само приложения, конфигурирани да го използват, поемат този път. Правило pf може да блокира приложение по потребител или група, но все още не може да избере тунела само за това приложение. Tunnelblick изобщо не може да филтрира по домейн, защото маршрутите работят с адреси; потребителите на OpenConnect прибягват до vpn-slice, когато сървърът изтласка твърде широк списък с префикси.

Две заобиколни решения покриват това, което инструментите на платформата пропускат. Разширенията за браузър, които сменят прокси по домейн, са практичният отговор за „само този сайт“, защото действат на слоя на приложението, където имената още съществуват; те покриват един браузър и нищо друго. Грубият вариант е да пуснете клиента на доставчика във виртуална машина, така че гостът да притежава промяната на маршрута, докато хостът запазва своя път, с цената на два мрежови стека за поддръжка.

SOCKS прескок за инструменти без опция за свързване

Повечето приложения могат да бъдат насочени към HTTP или SOCKS прокси, дори когато не знаят нищо за интерфейси. SSH клиент предоставя такова без промяна на сървърната страна:

ssh -N -D 127.0.0.1:9999 [email protected]

Всичко, конфигурирано да използва този порт, излиза при бастиона, докато останалата част от работния плот запазва нормалната си връзка. Локално копие на 3proxy, свързано с адреса на тунела, прави същото за един порт без SSH, а stunnel покрива един порт, обвит в TLS. Ограниченията са практически: проксито носи само протоколи, които знаят как да го използват, UDP се нуждае от SOCKS5 UDP асоциация, която настройката „SOCKS5“ на много клиенти не включва, а DNS трябва да се разрешава от проксито (socks5h в curl), иначе името изтича към локалния резолвер.

Дръжте слушателя на локалния интерфейс, освен ако друга машина не се нуждае от него.

Проверете пътя със снимка на трафика, не със съобщение за състояние

Прозорците за състояние на приложението описват настройката, не пакета. Уловете и на двата интерфейса и сравнете:

tcpdump -i any -n port 443
ss -tnp | grep 443

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

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

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