«Прозрачный» HTTPS прокси для обхода блокировок Роскомнадзора

habr.png

Привет, хабровчане! Я думаю, многие в последнее время столкнулись с проблемами доступа к нужным ресурсам из-за попыток Роскомпозоранадзора заблокировать Телеграм. И я думаю, комментарии тут излишни. Факт — эти ресурсы ни в чем не виноваты, но они заблокированы. Проблемы возникли с Viber, ReCaptcha, GoogleFonts, Youtube и др. кроме самого телеграма. Это случилось и в моей организации, причем все эти невинные сервисы нужны нам как воздух. В какое-то время решалось все использованием прокси серверов, но они были нестабильны или вовсе отключались (их также блокировал наш великий и могучий (нет) РКН.
После прочтения кучи статей, пришла идея научить Squid пускать отдельные URL через Tor. Какие здесь плюсы и минусы, решать вам. Но скажу, что после реализации пропали все проблемы, которые были до этого. Кому интересно, идем под кат.

Зачем это?

Статья написана исключительно в целях помощи тем, кто неправомерно страдает от тотального бреда, который творится у нас в стране.


Итак, для начала немного теории. Как мы все знаем, Tor — это не HTTP-прокси, его нельзя сделать прямым peer для нижележащего Squid’а. Он предоставляет SOCKS-проксирование (конечно же, не только, но нам нужно именно это). Чтобы нам поженить Tor со Squid, нужно что-то, что могло бы играть роль проводника от Tor к Squid и обратно. И конечно же, дамы и господа, это Privoxy. Как раз таки он способен быть прямым peer, и отправлять все далее в Tor.
Было, как я уже говорил, прочтено куча статей, но ни одна не подходила мне. Попалась вот эта статья, но и она мне не совсем подходила, так как мне не нужен bump. Вообще, все имеющиеся статьи, практически все, подразумевают либо бамп, либо только http, а в моем случае нужно и HTTPS, и splice, и прозрачность.
Я уже ранее писал о том, как сделать прозрачный Squid с проксированием HTTPS без подмены сертификатов. И конечно же, я попробовал реализовать идею на нем. Но меня ждало разочарование. HTTP запросы прекрасно уходили в TOR, а вот HTTPS нет. Проблема не очень-то и известная, и я узнал у одного из разработчиков, что это недостаток старых версий Squid. Но в ходе экспериментов было найдено решение — Squid 3.5.27, в котором исправлен данный баг + красивые доменные имена в логах (https), вместо ip адресов. Но и тут меня ждали несколько разочарований, о которых речь пойдет ниже. Но всё, как говорится, допиливается напильником.
Итак, исходные данные:

  1. Debian Stretch (9) x86 (в х64 не пробовал)
  2. Сорцы Squid 3.5.23 из репозитория
  3. Свеженькие сорцы Squid с оф.сайта
  4. OpenSSL
  5. Libecap3
  6. Tor
  7. Privoxy
  8. Прямые руки и много кофе с печеньками


Собирать Squid ручками или ставить готовые пакеты (ссылки ниже), решать вам. Если вы думаете, что я впендюрил в них блекджек и ш… вирусы, то компильте сами. Также компилировать нужно, если у вас дистрибутив другой (если Убунта, то поставите). Собирать мы будем версию Squid 3.5.23, которая на момент написания статьи валяется в репозитории Stretch, повысив ее до свежих исходников 3.5.27 с оф.сайта. Также нам понадобится для этих дел libecap, его мы тоже соберем. В отличие от моей первой статьи про HTTPS+Squid, собирать будем без Libressl.
Итак, подготовимся к сборке:

apt-get install fakeroot build-essential devscripts
apt-get build-dep squid3
apt-get build-dep libecap2
apt-get install libssl1.0-dev
apt-get install libgnutls28-dev


ВАЖНО!

Очень важно ставить именно libssl1.0-dev, а не другую версию, иначе Squid будет либо лагать, либо не соберется вовсе из-за непонятных ошибок


Далее на время добавим в sources.list дистрибутив Buster:

deb-src http://ftp.de.debian.org/debian/ buster main contrib non-free


и обновим кеш пакетов

apt-get update


Скачаем из Buster нужные исходники для сборки libecap3:

apt-get source libecap3/buster

Далее соберем libecap:

cd libecap-1.0.1/
dpkg-buildpackage -us -uc -nc -d


Удалим старье (если оно вдруг есть), и установим новье:

apt-get purge libecap2
dpkg -i libecap_1.0.1-3.2_i386.deb
libecap3-dev_1.0.1-3.2_i386.deb


Можно убрать из sources.list репозиторий Buster и приступать к самому интересному — красноглазию и костыльству!
Далее получаем исходники Squid 3.5.23

apt-get source squid3


Качаем именно этот архив с исходниками Squid:

wget -O squid-3.5.27-2018.tar.gz http://www.squid-cache.org/Versions/v3/3.5/squid-3.5.27-20180318-r1330042.tar.gz

Переходим в каталог исходников Squid и обновляем исходники до новоскаченных сорцов:

cd squid3-3.5.23/
uupdate -v 3.5.27-2018 ../squid-3.5.27-2018.tar.gz


Переходим в новоиспеченный каталог с обновленными исходниками:

cd ../squid3-3.5.27-2018


Добавляем в debian/rules опции для компиляции:

--enable-ssl \
--enable-ssl-crtd \
--with-openssl


Совет

Можете, кстати, вырубить ненужные вам опции, это ускорит компиляцию


Дальше нужно пропатчить исходники вот таким патчем:

client_side_request.patch

--- src/client_side_request.cc    Thu Aug 18 00:36:42 2016
+++ src/client_side_request.cc    Mon Sep 19 04:41:45 2016
@@ -519,20 +519,10 @@
     // note the DNS details for the transaction stats.
     http->request->recordLookup(dns);
 
-    if (ia != NULL && ia->count > 0) {
-        // Is the NAT destination IP in DNS?
-        for (int i = 0; i < ia->count; ++i) {
-            if (clientConn->local.matchIPAddr(ia->in_addrs[i]) == 0) {
-                debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
-                http->request->flags.hostVerified = true;
-                http->doCallouts();
-                return;
-            }
-            debugs(85, 3, HERE << "validate IP " << clientConn->local << " non-match from Host: IP " << ia->in_addrs[i]);
-        }
-    }
-    debugs(85, 3, HERE << "FAIL: validate IP " << clientConn->local << " possible from Host:");
-    hostHeaderVerifyFailed("local IP", "any domain IP");
+  debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
+  http->request->flags.hostVerified = true;
+  http->doCallouts();
+  return;
 }
 
 void

Для чего он нужен? Я объясню. Когда я писал первую статью про peek and splice, я говорил что более новые версии не работают, и это было так, и вот как раз таки этот патч исправляет ту самую проблему, которая заключалась в том, что Squid выборочно рвет HTTPS соединения, с интересным сообщением в cache.log:

SECURITY ALERT: Host header forgery detected on ... (local IP does not match any domain IP)


Дело в том, что на одном хосте что-то резолвится в один IP, на соседнем иногда в другой, на самом Squid в третий, т.к. существует кеш DNS и обновляется он не синхронно. Squid не находит соответствия ip-домен в своём кеше (потому что обновил свой кеш немного раньше или позже) и прерывает соединение. Вроде как, защита, но в наше время это считается нормальным (round-robin DNS). Разработчики перестраховались. И нам это не нужно совершенно! Тем, кто скажет, что данный патч, возможно, несет в себе угрозу безопасности, я отвечу, что по поводу этого патча я консультировался с Юрием Войновым, который имеет непосредственное отношение к команде разработчиков Squid. Никакой угрозы здесь нет!
Итак, файлик для патча создали, код кинули, надо пропатчить:

patch -p0 -i client_side_request.patch


Далее необходимо отменить применение одного патча при компиляции (иначе получите ошибку, что этот патч применить невозможно, так как он уже применен). Идем в debian/patches/series и закомментим там 0003-SQUID-2018_1.patch, поставив перед ним знак #:

#0003-SQUID-2018_1.patch


Ну, а дальше — компиляция и сборка пакетов!

dpkg-buildpackage -us -uc -nc


Установим squid-langpack

apt-get install squid-langpack


и установим свеженькие пакеты

dpkg -i squid-common_3.5.27-2018-1_all.deb
dpkg -i squid_3.5.27-2018-1_i386.deb
dpkg -i squid3_3.5.27-2018-1_all.deb

Если apt матерится на зависимости, сделайте

apt-get -f install


Далее нужно выключить Squid из автозагрузки (по умолчанию используется init файл, Squid жалуется на недоступность PID файла)

systemctl disable squid

и создать systemd сервис в директории /etc/systemd/system (файл сервиса есть в исходниках, и полностью скопирован сюда)

cat /etc/systemd/system/squid3.service

## Copyright (C) 1996-2018 The Squid Software Foundation and contributors
##
## Squid software is distributed under GPLv2+ license and includes
## contributions from numerous individuals and organizations.
## Please see the COPYING and CONTRIBUTORS files for details.
##

[Unit]
Description=Squid Web Proxy Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/sbin/squid -sYC -N
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

[Install]
WantedBy=multi-user.target

Включим его

systemctl enable squid3.service

Установим tor, privoxy

apt-get install tor privoxy


Конфиг Tor я лично вообще не трогал, а вот конфиг Privoxy можно привести к такому виду:

listen-address 127.0.0.1:8118
toggle 0
enable-remote-toggle 0
enable-remote-http-toggle 0
enable-edit-actions 0
forward-socks5t / 127.0.0.1:9050 .
max-client-connections 500

Почти готово. Перейдем в каталог /etc/squid, кое-что там изменим. Создадим pem файлик, необходимый для splice:

openssl req -new -newkey rsa:1024 -days 365 -nodes -x509 -keyout squidCA.pem -out squidCA.pem

И приведем squid.conf к следующему виду:

acl localnet src 192.168.0.0/24 # Ваша локалка

acl SSL_ports port 443
acl Safe_ports port 80          # http
acl Safe_ports port 21          # ftp
acl Safe_ports port 443         # https
acl Safe_ports port 70          # gopher
acl Safe_ports port 210         # wais
acl Safe_ports port 1025-65535  # unregistered ports
acl Safe_ports port 280         # http-mgmt
acl Safe_ports port 488         # gss-http
acl Safe_ports port 591         # filemaker
acl Safe_ports port 777         # multiling http
acl CONNECT method CONNECT
acl SSL method CONNECT

#Укажем DNS для Squid. Крайне рекомендую использовать одинаковые DNS тут и у клиентов
dns_nameservers 77.88.8.8 


# Список доменов, которые нужно пустить через Tor
acl rkn url_regex "/etc/squid/tor_url"
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localnet
http_access allow localhost
http_access deny all
icp_access deny all 
htcp_access deny all

#прозрачный порт указывается опцией intercept
http_port 192.168.0.1:3128 intercept options=NO_SSLv3:NO_SSLv2

#также нужно указать непрозрачный порт, ибо если захотите вручную указать адрес
#прокси в браузере, указав прозрачный порт, вы получите ошибку доступа, поэтому нужно
#указывать непрозрачный порт в браузере, если конечно такое желание будет, к тому же в логах #сыпятся ошибки о том, что непрохрачный порт не указан=) 
http_port 192.168.0.1:3130 options=NO_SSLv3:NO_SSLv2 

#и наконец, указываем HTTPS порт с нужными опциями
https_port 192.168.0.1:3129 intercept ssl-bump options=ALL:NO_SSLv3:NO_SSLv2 connection-auth=off cert=/etc/squid/squidCA.pem

sslproxy_cert_error allow all
sslproxy_flags DONT_VERIFY_PEER
#укажем правило со списком блокируемых ресурсов (в файле домены вида .domain.com)
acl blocked ssl::server_name  "/etc/squid/blocked_https.txt"
acl step1 at_step SslBump1
ssl_bump peek step1

#терминируем соединение, если клиент заходит на запрещенный ресурс
ssl_bump terminate blocked
ssl_bump splice all


# Никогда не пускать напрямую домены, указанные в списке РКН
 never_direct allow rkn
      
 # Указываем прокси, куда отправлять домены из списка, в нашем случае - Privoxy
 cache_peer 127.0.0.1 parent 8118 0 no-query no-digest default  
   
cache_peer_access 127.0.0.1 allow rkn
cache_peer_access 127.0.0.1 deny all 

sslcrtd_program /usr/lib/squid/ssl_crtd -s /var/lib/ssl_db -M 4MB

coredump_dir /var/spool/squid
refresh_pattern ^ftp:           1440    20%     10080
refresh_pattern ^gopher:        1440    0%      1440
refresh_pattern -i (/cgi-bin/|\?) 0     0%      0
refresh_pattern .               0       20%     4320
logfile_rotate 4
pid_filename /var/run/squid.pid



Список url_regex имеет примерно такой вид:

zenway\.ru
lukomore\.org
\.*google\.com
\.*viber\.*
\.amazon\.com
telegram.*
tdesktop.*
thegeekdiary\.com.*
\.fbcdn\.net
telegra\.ph
\.slack\.*
media\.api.\viber\.com*
static\.viber\.com*
secure\.viber.*
media-share.*
\*.cloudfront\.net
t\.me
cdn4\.telesco\.pe
fonts\.gstatic\.com
med-edu\.ru


Более подробно про этот формат списка читайте в оф документации. Результат себя долго ждать не заставил — все заработало, как и было запланировано. И работает по сей день. Возможно, со статьей опоздал, но она, вероятно, пригодится в будущем)
Тему буду пополнять по возможности.
Готовые пакеты Squid и Libecap (.deb x86)
Огромное спасибо Юрию Войнову, который помогал в решении проблем с работой данной связки!

© Habrahabr.ru