Правилата за портове 80 и 443 не прекарват браузъра през VPN
Съвпадението по порт избира TCP потоци, не браузър. HTTP/3 работи върху UDP, DNS може да поеме друг път, а програма за обновяване се събира със същото правило, което сте написали.
Някой иска браузърът да използва тунела и заявката се връща като правило на защитна стена, съвпадащо с портовете на местоназначението 80 и 443. Изглежда точно и не е. Съвпадението по порт избира пакети по транспортния им заглавен блок, а не по това коя програма е отворила сокета, и съвременното сърфиране не се ограничава до тези два порта по TCP. Правилото ще пропусне част от трафика, който сте искали да включите, и ще улови трафик, който никога не сте възнамерявали да докосвате. Преди да го напишете, решете коя от трите различни граници всъщност искате.
Правилото описва транспортен клас, не приложение
В Linux съвпадението по порт на местоназначението преглежда TCP заглавката и нищо над нея. Маркирането на тези пакети и насочването им с политически маршрут работи точно както е документирано и никой от тези механизми не знае какво означава полезният товар и кой процес го е изпратил. Марката е число, прикрепено към пакет, а не идентичност, прикрепена към приложение.
nft add rule inet mangle output tcp dport { 80, 443 } meta mark set 0x1
ip rule add fwmark 0x1 table 100
ip route add default dev wg0 table 100
Веднага следват две последици. Програма за обновяване, която се свързва с TCP 443, получава същото отношение като браузъра, което може да е добре или може да е прекъсване, чакащо следващия цикъл на обновяване. А уеб услуга, публикувана умишлено на друг порт, да речем 8443, изцяло изпада от правилото, макар че все още е уеб трафикът, за който потребителят е бил загрижен.
Числата са законни селектори, когато изискването наистина е за тези портове. Те стават фикция, когато в заявката е написано „браузър“, а в набора от правила е реализиран диапазон от портове. Тази разлика не е документационна дребнавост. Тя е разликата между правило, което можете да защитите при преглед, и такова, което никой не може да тества.
HTTP/3 тихо изоставя вашето TCP правило
HTTP/3 работи върху QUIC, а QUIC се пренася в UDP. Правило, което съвпада само с TCP, не може да го види, така че същият браузър, който удовлетворява тестовата ви страница, може да изпрати следваща заявка по UDP и да заобиколи тунела, без никой да промени настройка. RFC 9114 описва HTTP/3 като протокол, базиран на QUIC, а QUIC използва UDP с порт по подразбиране 443 при обичайното разгръщане.
Дали даден браузър ще опита HTTP/3 зависи от конфигурацията му, от сайта и от това дали предишен опит е успял. Това го прави лош обект за разсъждение по памет. Попитайте инструмента вместо това:
curl --http3 -sS -o /dev/null -w '%{http_version} %{time_total}\n' https://example.com
ss -tunp | grep -E ':443\b'
Редът curl отпечатва договорената версия, така че 3 потвърждава, че е използван пътят през UDP. Списъкът на сокетите после показва дали тази връзка е вързана към интерфейса на тунела, или към физическия. Ако HTTP/3 липсва в компилацията ви на curl, флагът се проваля шумно, вместо тихо да отстъпи, което си е негова малка милост.
DNS е отделно решение, което трябва да вземете
Правило за порт за уеб трафик не казва нищо за разрешаването на имена. Браузърът може да използва системния резолвер, резолвер, конфигуриран вътре в браузъра, или такъв, изпратен от последното свързване. Всеки от тях може да изпраща заявки по различен път от този, който избира вашето правило, което дава класическото разделение: страницата се зарежда, името се разрешава някъде, където не сте очаквали, а вътрешният сайт се разрешава до публичен адрес или изобщо не се разрешава.
Решете го изрично. Ако вътрешните имена трябва да се разрешават през тунела, резолверът трябва да е вътре в него, а наборът от правила трябва да разрешава DNS към този резолвер, като държи другите заявки навън. Ако браузърът използва DNS over HTTPS към публичен доставчик, заявката заобикаля изцяло вашия резолвер и правилото за порт не може да помогне. Запишете избора в същия запис за промяна като правилото за маршрутизиране, защото двете се четат заедно от този, който ще отстранява следващия отказ.
После проверете с резолвера, не със страницата:
resolvectl query intranet.example.internal
resolvectl status | grep -A3 'Current DNS Server'
Липсващият отговор е проблем с разрешаването дори когато съобщението за грешка на браузъра обвинява мрежата. Тези два отказа изглеждат еднакви за потребителя и са напълно различни задачи.
Три граници, три различни механизма
Повечето от объркването изчезва, щом назовете границата, която искате. Те не са взаимозаменяеми и всяка има различен режим на отказ при прекъсване на тунела.
| What you actually want | Mechanism that fits | What it does not cover |
|---|---|---|
| These transport ports use this exit | Firewall mark plus policy route | Applications, HTTP/3 over UDP, DNS |
| This browsing session stays separate | Application-aware VPN, or a separate network namespace plus a proxy | Other applications in the same session |
| Only these private sites are reachable | Destination-based routes with private DNS | Public sites on the same address family |
Отделен профил на браузъра е капанът в средния ред. Той разделя бисквитки и история и не променя нищо в таблицата за маршрутизиране на хоста, така че двата профила поемат един и същ път. Изолацията изисква нещо, което ядрото налага, тоест пространство от имена, контейнер или приложение, което притежава собствен интерфейс за тунел.
Който и ред да важи, запишете какво трябва да се случи, когато тунелът падне. Маршрут на базата на марка, който изчезва с интерфейса, обикновено се връща към физическия маршрут по подразбиране, което е поведение fail-open. Това може да е приемливо за сърфиране и неприемливо за всичко друго и е решение, а не подробност на реализацията.
Тествайте потоците, които твърдите, че покривате
Приемателният тест трябва да следва границата, която сте записали, а не номерата на портове, които са били удобни. Изберете един поток, който трябва да минава през тунела, един, който не трябва, и един, който използва транспорт, за който не сте се сетили:
ip route get 203.0.113.40
curl -sS -o /dev/null -w 'v4 %{http_version} %{remote_ip}\n' https://example.com
nft list ruleset | grep -A2 'dport'
Търсенето на маршрут ви казва кой интерфейс всъщност използва дадено местоназначение, а това е фактът, който повечето доклади сбъркват. Редът curl потвърждава договорената версия и семейството адреси. Извеждането на набора от правила улавя правилото, което сте добавили по време на тестването и сте забравили да ограничите, и точно така временна марка се превръща в постоянна маршрутизация с fail-open.
Запишете и трите изхода с времева марка и ги пазете с промяната. Когато по-късно някой съобщи, че „VPN счупи вътрешната мрежа“, разликата между предвидената и реализираната граница ще се види в този запис и поправката ще бъде промяна в правило, а не спор.