mitmproxy, DevTools, Gradle: три слоя зад VPN
Loopback никога не влиза в тунела. Причисли симптом към слушател, хранилище за доверие или пакетен филтър и поправи слоя, който наистина се е счупил.
VPN се свързва и три несвързани инструмента се провалят по три различни начина същия следобед. mitmproxy спира да вижда заявки, Chrome отказва на дебъгера, а Gradle хвърля BindException докато завързва локален сокет. Общото подозрение е, че тунелът блокира localhost. Той не може: 127.0.0.1 се обработва вътре в ядрото и никога не достига до адаптер, камо ли до шлюз.
Три слоя, три различни повреди
Това, което се промени с клиента, не е маршрутът към loopback. Това са локален прокси, машинно хранилище за доверие и пакетен филтър, и всяко от тях чупи различна част от работния процес на разработчика. Подреждането на симптом в правилния ред е по-голямата част от диагностиката:
| Symptom | Layer | First check |
|---|---|---|
| Proxy sees no traffic | Local proxy or listener | ss -lntp, or netstat -ano on Windows |
| TLS error in one tool only | Trust store | Which CA bundle does that runtime read? |
| Local bind or connect refused | Packet filter or port owner | Who holds the port, and what rule drops it? |
Почти всеки доклад от този вид започва от последния ред и върви нагоре, което е обратното. Тунелът е единственият слой, който доказуемо не може да види трафик към loopback, затова заслужава най-малко подозрение. Започнете с това, което процесът е завързал, после попитайте какви сертификати доверява, и едва след това погледнете политиката на машината.
Започнете със слушателя, не с тунела
Когато прокси на 127.0.0.1:8080 спре да получава трафик, едно от три неща се е случило: никога не е стартирало, завързало е друг порт или филтърът е изпуснал loopback връзката. Изброяването на слушащите сокети отговаря на това с една команда и също показва кой процес притежава порта, когато два инструмента не са съгласни кой е бил първи.
Локален адрес при търсене на маршрут потвърждава същото от другата страна. ip route get срещу дестинация loopback отчита loopback устройството, никога тунела, което е полезно изречение, когато някой предложи промяна в маршрутизирането като решение.
Слоят на проксито: средата не е конфигурация
Много корпоративни клиенти пускат HTTP прокси и очакват приложенията да го използват. Задаването на HTTPS_PROXY в обвивка поправя инструментите от командния ред, които го уважават, и не прави нищо за инструмент, който чете собствения си файл с настройки. Gradle, Selenium и gRPC се обръщат към собствената си конфигурация, а прокси, експортирано в един терминал, е невидимо за демон, стартиран от IDE.
Инструментите за контейнери са обичайното място, където това става видимо. Loopback на контейнера е негов собствен, затова 127.0.0.1 вътре в контейнера никога не означава проксито на хоста; нужен е адресът на хоста или настройка за хост мрежа. Поправка, която работи в обвивката и се проваля при билда, обикновено е този ред, а не тунелът.
Слоят на доверие: инструментът чете друго хранилище
Прихващането е втората промяна и произвежда най-подвеждащите грешки. Клиентът представя свой сертификат за проверяваните връзки, затова браузър, който доверява на корпоративния корен, успява, а инструмент с вградено CA хранилище се проваля. Python requests през certifi е класическият случай: хранилището на операционната система може да е правилно и заявката пак да се проваля, защото тази среда за изпълнение никога не го чете. Текстът на грешката рядко споменава прихващане, затова този ред бива диагностициран погрешно като счупена крайна точка.
Сравнете какво всъщност представя крайната точка с това, което инструментът е валидирал. openssl s_client показва веригата, която сървърът е изпратил; собственият изход за валидация на инструмента показва какво е решил. Когато не съвпадат, REQUESTS_CA_BUNDLE или SSL_CERT_FILE е лостът за тази среда за изпълнение, а корпоративният корен принадлежи в хранилището, което тази среда чете, а не на глобално място, което уважават само някои инструменти.
Промяна на версия, която поправя SSL грешка, е доказателство за политиката на доверие и шифри, не за VPN. Понижаването на Python, което накара SOAP клиент да работи, промени вграденото CA хранилище и настройките по подразбиране за TLS; мрежовият път не се премести.
Слоят на филтъра: той също може да изпусне loopback
Третата промяна е пакетен филтър и някои продукти инсталират правила, които изпускат трафик, който не влиза в тунела. Локалните слушатели могат да бъдат включени. Това превръща работещ сокет в отказана или изтекла по време връзка и получената грешка изглежда като бъг в приложението, а не като политика на машината.
Собствеността на порта е другата половина на този слой. BindException означава, че сокетът не е могъл да бъде отворен, което е различен провал от отказана връзка. Проверете дали друг процес вече държи порта, преди да обвинявате филтъра, после търсете правило, което изпуска локален трафик, и добавете най-тясното разрешение, което продуктът поддържа. Два инструмента, състезаващи се за един и същ порт за дебъг, дават точно същото съобщение като блокиране от политика и само един от тях се поправя с промяна на защитната стена.
Портът за дебъг на Chrome се проваля в същото семейство. Браузърът стартира, драйверът не може да достигне крайната точка на DevTools и съобщението назовава DevTools, а не мрежата. Допълнителни флагове за стартиране и излишен мениджър на драйвери променят кой процес притежава порта, затова премахването им понякога изглежда като поправка на VPN.
Поправете слоя, който се счупи
Поправете слоя, който се счупи, а не тунела. Недостъпен loopback слушател се нуждае от поправка на слушателя или порта. Ненадежден сертификат се нуждае от корпоративния корен, инсталиран там, където точно тази среда за изпълнение гледа, с тясно изключение. Филтър, който изпуска локален трафик, се нуждае от конкретно правило за разрешаване, а не от изключен филтър, защото изключването отваря отново и всичко останало, което той е защитавал.
Повторното свързване на VPN не тества нито един от тези слоеве. То само пуска отново промяната, която ги е счупила, затова същите три провала се връщат след всяко повторно свързване и изчезват след всяко рестартиране, което оставя клиента неактивен.