Использование Proxy auto-config (PAC) для выборочного обхода блокировок Роскомнадзора


Для обхода блокировок сайтов обычно используются разного рода VPN-сервера с перенаправлением всего трафика с вашей рабочей станции через более свободолюбивые страны, но как вы понимаете эти VPN-сервера обычно или вам не подконтрольны, что может создать риск компрометации ваших персональных или финансовых данных или обладают низкой пропускной способностью.

Настройка Squid для обхода блокировок роскомнадзора

Даже если у вас имеется собственный VPS-сервер в Италии со 100 мегабитным каналом, то использование VPN-соединений все равно накладывает свой отпечаток даже на банальное время отклика, что может негативно сказаться на OnLine-играх. Соответственно возникает хороший вопрос, а какого собственно черта я гоняю весь трафик через VPN, если мне надо банально ходить на Linkedin и Рутрекер? Правильно, этой ерундой вам заниматься совершенно не обязательно, хотя VPN вам все же поднять придется так как ряд провайдеров любит покопаться в трафике и попытке сходить на рутрекер используя банальный HTTP-прокси сервер они смогут вычислить на раз.

Инфраструктура для обхода блокировок Роскомнадзора

Приступим к созданию собственной инфраструктуры для обхода блокировок Роскомнадзора, для реализации собственного микросервиса VPN+SQUID+PAC нам понадобится любой недорогой VPS-сервер использующий платформу виртуализации KVM или VmWare (любую полноценную аппаратную виртуализацию), но не используйте контейнерную изоляцию и т.п. так как у вас могут легко на ровном месте появиться проблемы с запуском OpenVPN-сервера.

Выбор VPS-сервера под OpenVPN

В среднем именно такой ценник в месяц у вас и получится, если у вас получается меньше, то скорее всего вы пытаетесь прикупить сервер на платформе OpenVZ или LXD и в 90% случае у вас ничего хорошего из этого не выйдет (у ряда провайдеров использовать TAP/TUN интерфейсы на OpenVZ все же можно, но это скорее исключение из правил).

Что требуется настроить на этом сервере:

  • OpenVPN-сервер
  • Squid-сервер
  • Nginx для отдачи PAC-файлов

Давайте не будем сильно углубляться в детали настройки каждого из компонентов, я думаю что этому вопросу я и сам уделял достаточно много места на страницах своего блога и вы с легкостью сможет найти подробные описания настройки каждого из компонентов, посему просто приведу файлы конфигурации используемые в моей реализации.

Настройка OpenVPN-сервера

OpenVPN у меня настроен в максимально типовом режиме в качестве сервера с TAP-интерфейсами и фиксированной адресацией клиентов:

port 1194
proto udp
dev tap-vpn

ca ca.crt
cert s212-server.crt
key s212-server.key
dh dh2048.pem

server 10.212.1.0 255.255.255.0
ifconfig-pool-persist /etc/openvpn/ipp.txt

client-config-dir ccd
up network-start.sh

client-to-client
keepalive 10 120
cipher AES-256-CBC
comp-lzo

persist-key
persist-tun
status /etc/openvpn/openvpn-status.log

log         /var/log/openvpn/openvpn.log
log-append  /var/log/openvpn/openvpn.log
verb 3

Ключи для клиентов генерируются при помощи пакета скриптов EasyRSA, а в каталоге ccd находятся файлы описания маршрутизации и статической адресации для подключающихся клиентов:

ifconfig-push 10.212.1.5 255.255.255.0
push "route 10.212.4.0 255.255.255.0 10.212.1.4"
push "route 10.212.3.0 255.255.255.0 10.212.1.2"
push "route 10.212.2.0 255.255.255.0 10.212.1.3"

В остальном конфигурация совершенно типовая.

Файлы конфигурации клиентов OpenVPN вообще совершенно идентичные и отличаются только используемыми ключами и сертификатами для каждого клиента:

client
proto udp
remote service-vpn.gita-dev.ru 1194
management localhost 7555
dev tap-master-1
resolv-retry infinite
nobind
persist-key
persist-tun
ns-cert-type server
script-security 2
comp-lzo
verb 3
up /etc/firewall/firewall.sh
ca ca.crt
cert client.crt
key client.key

В другом проекте я использовал сертификаты которые были вшиты в конфигурационный файл и такие конфигурационные файлы генерировались при помощи Ansible.

Настройка прокси-сервера Squid

Как вы видите конфигурация OpenVPN в моем случае не предусматривает роутинга трафика через сервер OpenVPN (хотя такой функционал имеется и легко может быть переопределен в конфигурации клиента) и для обхода блокировок будет использоваться Proxy-сервер SQUID.

Конфигурация у него вообще простая до безобразия:

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
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localhost manager
http_access deny manager
http_access allow localhost
 
acl vpnnet src 10.212.0.0/16
http_access allow vpnnet
 
visible_hostname proxy-server
 
http_access deny all
http_port 3128
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 (Release|Packages(.gz)*)$     0      20%    2880
refresh_pattern .              0      20%    4320

Как вы видите переопределена одна ACL для сети VPN и для нее разрешено абсолютно все. Вы конечно можете напрямую указать в настройках вашего браузера использовать прокси сервер по его IP-адресу и все даже будет работать, но мы ведь не этого добивались.

Настройка параметров прокси-сервера в браузере Firefox

Настройка Web-сервера Nginx

Вне зависимости от того будете вы генерировать ваши PAC-файлы программно или же это будут просто текстовые файлы, вам в любом случае понадобится предоставить к ним доступ по протоколу HTTP или лучше даже HTTPS и для этого вам понадобится Web-сервер Nginx, я вам сейчас продемонстрирую типовую конфигурацию виртуального хоста со статическим контентом и этого честно говоря вполне достаточно если вы делаете сервис для себя любимого:

server {
        server_name unlock.gita-dev.ru;

        root /var/www/unlock.gita-dev.ru;
        index index.html;

        location / {
                try_files $uri $uri/ =404;
        }
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/unlock.gita-dev.ru/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/unlock.gita-dev.ru/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = unlock.gita-dev.ru) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
        listen 80;
        server_name unlock.gita-dev.ru;
    return 404; # managed by Certbot
}

За основу взята типовая конфигурация для хоста со статическими файлами и автоматически добавлены правила Certbot (бесплатные SSL-сертификаты Let's Encrypt).

Выборочное проксирование по правилам в статическом PAC-файле

Вот теперь наконец мы и подошли к самому интересному, а именно описанию того как мы будем выборочно заворачивать трафик до ряда интеренет-ресурсов, а на чистые-белые-непорочные сайты будем ходить напрямую через провайдера демонстрируя госпоже Яровой трафик.

Для того чтобы выборочно заворачивать трафик до определенных ресурсов вы можете использовать или маршрутизацию или так называемые файлы "автоконфигурации прокси". Я пошел именно вторым путем, хотя и первым методом я так же активно пользовался когда борцы с Телеграмм положили Play Station Network (конечно совершенно случайно вместе с половиной AWS), но для того чтобы банально ходить до того же Linkedin больше подойдет именно первый метод.

Параметры автоконфигурации прокси в Firefox

Итак, что же это такое за файлы автоконфигурации прокси? Это на самом деле обычный текстовый файл который может располагаться как на web-сервере, так и локально на вашем компьютере. Он содержит как это не странно код на Java Script, а точнее одну функцию которая принимает ряд входных параметров и в качестве результата может возвращать метод подключения к хосту.

Там простор для фантазии довольно большой и я в своем небольшом проекте пришел вот к такой конструкции:

function FindProxyForURL(url, host)
    {
        var use_proxy = "PROXY 10.212.4.2:3128";
        var no_proxy = "DIRECT";
        if (shExpMatch(url, "(http|https)://*flibusta.is*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*lib.rus.ec*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*freelancer.com*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*kinozal.tv*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*linkedin.com*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*lnkd.in*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*nnm-club.me*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*nnm-club.ws*")) { return use_proxy; }
        if (shExpMatch(url, "(http|https)://*rutracker.org*")) { return use_proxy; }
        return no_proxy;
    }

Даже если вы далеки от JS-разработки, я думаю вам будет понятно, что мы проверяем url страницы на соответствие регулярному выражению и в случае совпадения возвращаем адрес прокси который должен использоваться, в случае если ничего не совпало, мы говорим что дальше будем идти на прямую.

Писать такой файл можно и руками, но если вы делаете какой-то VPN-сервис с выборочным проксированием (что конечно категорически запрещено), то вам лучше использовать какой-нибудь шаблонизатор и хранить данные о хостах в базе данных.