Squid в качестве MitM
Jan. 19th, 2026 10:19 pmЕсть такой древний и местами незаслуженно забытый прокси-север под названием "Squid". Забыли его в основном потому, что в эпоху тотального TLS что-либо кешировать не представляется возможным. Но можно побыть немножечко "человеком посередине" (Man-in-the-Middle, MitM). И тогда снова становится реально кешировать. И контролировать-логировать.
Самая очевидная сфера применения — когда условный босс или бизапасник хочет проверять, не смотрят ли его сотрудники на рабочем месте прон вместо выполнения своих прямых обязанностей. Подвариант — когда на весь офис приходят условные 10 МБит/с, и надо их раздать так, чтобы все худо-бедно могли работать и не забивали бы ютупчиком / рутупчиком весь аплинк. Но конкретно в моём случае актуальнее другое: чтобы сборочные пайплайны не выкачивали бы из интернетов каждый раз заново все требующиеся им фреймворки и библиотеки. И каналы экономятся, и сборка ускоряется, и увеличивается предсказуемость в эпоху тотальных блокировок. Удобно.
В принципе, в интернетах полно мануалов как настроить Squid в таком режиме (он называется SSL Bumping). Не пишут, однако, что есть некоторые нюансы, связанные с алгоритмом проверки цепочек X509-сертификатов. Вкратце, если у них отсутствуют X509v3-расширения, то цепочка выстраивается по регистронезависимому сравнению строковых полей Issuer дочернего сертификата и Subject родительского сертификата согласно RFC5280. Если же присутствуют v3-расширения "Authority Key Identifier" (AKI) и "Subject Key Identifier" (SKI), то сравнение идёт именно по ним.
Так вот, "из коробки" Squid производит динамическую генерацию серверных сертификатов именно без v3-расширений, причём заполняет только характеристики CN и DNS Alternative Name. И записывает в них один-единственный хост — тот, к которому обратился клиент. Так делается ради упрощения и ускорения, ведь на тот момент когда клиент приходит на прокси-сервер, последний ещё не связался с "настоящим" веб-сервером и не знает что тот вернёт в ответ. Поэтому отдает клиенту самый примитивный сертификат "лишь бы зохавал", а дальше сам начинает общение с внешним ресурсом. Но есть проблема. Этот самый "мега-примитивный" динамически сгенерированный сертификат не содержит в себе AKI (Authority Key Identifier). А Python начиная с версии 3.13 его прям очень-очень хочет, аж кушать не может. И это нигде не отключается.
Вот непонятно в чём проблема динамически сгенерировать какой-нибудь "левый" AKI, но я нашёл только один способ отдать его клиенту. Это задействовать другую фичу Squid-а: мимикрия серверного сертификата (Sever Certificate Mimic). В этом режиме Squid запрашивает у "настоящего" сервера его "настоящий" сертификат, копирует из него все поля, а потом подписывает результат своим ключом. Таким образом, клиенту приезжает сертификат максимально похожий на "настоящий", но только с другими ключами-подписями. И в нём уже будет присутствовать AKI.
Чтобы включить этот режим, однако, надо заморочиться. Документации мало и написано она довольно корявым языком. Самый полезный фолиант на тему, пожалуй, вот. Из него мы узнаем, что сам процесс SSL Bumping-а Squid для себя подразделяет на три шага, из которых всегда выполняется только первый, а два последующих "как повезёт". Следуют они именно в таком порядке. И пресловутый "bump" может произойти или не произойти на любом из них.
- Смотрим что клиент прислал в запросе CONNECT и/или определяем IPшник куда он хочет пойти.
- Общаемся с клиентом и запрашиваем у него Client Hello. На этом этапе становится известен SNI (если есть).
- Общаемся с сервером и запрашиваем у него Server Hello. На этом этапе становится известен серверный сертификат.
Таким образом, для "мимикрии" обязательно требуется пройти все три шага. То есть bump должен случиться именно на последнем, третьем. А по умолчанию он происходит на первом. Так что необходимо подшаманить конфигурацию Squid-а. Например, вот так.
На первых двух шагах делаем "stare", и только на третьем "bump". Впрочем, всё то же самое можно записать сильно короче, но менее человекочитаемо. Вот так.
Тут прикол в том, что на первом шаге мы явно приказываем Squid-у уйти на второй ("stare all"), где он снова делает "stare" всё по той же причине "stare all" и таким образом провоцирует необходимость третьего шага. Где действием по умолчанию после stare на предыдущем шаге является именно bump, так как в третий раз сделать stare уже нельзя. Неочевидно, но такая у кальмара логика. Некстати, весьма запутанная. Несколько часов потратил прежде чем догнал.
Недостатком подобной конфигурации является то, что если клиент или сервер используют какие-то TLS-расширения, которые непонятны или не реализованы Squid-ом, то TLS-сессия просто не установится. Клиент получит отлуп. Тут поможет разве что только перечислить такие "проблемные" сервера в отдельной ACL-ке и не делать им bump, а туннелировать напрямую без кеширования и влезания вовнутрь TLS-сессии (splice). Наподобие вот такого.
Такое вот кеширование нонче пошло непростое.
... И вот, значит, только победил я эту проблему с мимикрией и AKI, как выяснилось, что питону оно всё равно невкусно потому что "CA CERT does not include key usage extension". Но это уже совсем другая история. ©