Возникла задача заворачивать трафик в тоннель, основываясь на имени домена ресурса, к которому идёт обращение.Многие интернет-ресурсы имеют большой пул ip-адресов, более того, этот пул может меняться. Делать nslookup для каждого интересующего сервиса и заворачивать все выдаваемые подсети - неудобно и неэлегантно. На помощь может придти прокси-сервер squid, настроенный прозрачно с функцией ssl_bump.
Сборка squid из исходников описана в сети многократно, в том числе и на habr. В Ubuntu версии 22.04 появился пакет squid-openssl, который, вроде бы, должен избавлять от необходимости такой сборки, но свежие squid 5 и 6 версии, собранные вручную или установленные из репозитория, почему-то некоректно устанавливают ssl-соединения с некоторыми сайтами, в т.ч. иногда и google.com. Возникает ошибка в браузере:
SSL_ERROR_RX_RECORD_TOO_LONG
В причине возникновения данной ошибки я разобраться не смог — просто откатился в версии squid до 4.10. Инструкции в интернете обычно описывали процесс сборки из исходников squid3 — с ним всё работает. На 4й версии у меня тоже всё получилось: версия 4.10 прямо сейчас работает у меня на двух шлюзах в качестве прозрачного прокси с ограничением доступа по имени сайта. При устанвке squid 4.10 на ubuntu 24.04 возникают проблемы с зависимостями от старых версий libldap-2.4-2 и libnettle7. Эти проблемы я решал временным подключением репозиториев bionic и jammy. Собственно и исходники самого squid4.10 тоже нужно брать из старых репозиториев. Я же когда-то собрал для себя набор пакетов и храню их в виде готовых deb-файлов:
squid_4.10-1ubuntu1.2_amd64.deb
squidclient_4.10-1ubuntu1.2_amd64.deb
squid-purge_4.10-1ubuntu1.2_amd64.deb
libressl_3.0.2-1_amd64.deb
squid-cgi_4.10-1ubuntu1.2_amd64.deb
squid-common_4.10-1ubuntu1.2_all.deb
Все эксперименты производились в виртуальной среде
Есть условный шлюз на Ubuntu 24.04 с тремя сетевыми интерфейсами: eth0, eth1 и wg0. Трафик из локальной сети (eth1) заворачивается на порты сквида вместо NAT и выходит в глобальную сеть (eth0). Есть также интерфейс wg0 — тонель в некоторую другую подсеть, через которую мы, в качестве эксперимента, хотим попробовать завернуть интересующий нас трафик. Трафик признаётся интересующим нас на основании имени домена, к которому идёт обращение.
Для начала опишем конфигурацию iptables на шлюзе:
#!/bin/bash #Очищаем правила IPTABLES iptables -F iptables -X iptables -t nat -F iptables -t nat -X # Разрешаем по умолчанию только исходящий трафик iptables -P INPUT DROP iptables -P OUTPUT ACCEPT iptables -P FORWARD DROP # Разрешаем ответы на установленные соединения iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT #Разрешаем доступ по ssh (если надо извне) iptables -A INPUT -p tcp --dport 22 -j ACCEPT #Разрешаем входящие соединения для локальной сети iptables -A INPUT -i eth1 -j ACCEPT #Перенаправляем web-трафик на прокси iptables -A PREROUTING -t nat -i eth1 -p tcp -m multiport --dport 80,8080 -j DNAT --to-destination 10.10.10.1:3129 iptables -A PREROUTING -t nat -i eth1 -p tcp -m multiport --dport 443 -j DNAT --to-destination 10.10.10.1:3130
Поднимаем интерфейс wg0
Скрипты wg-quick, для сложных режимов маршрутизации, я использовать не люблю, так как диррективу AllowedIPs они воспринимают и как разрешение и как правила маршрутизации. Данные скрипты делают, на мой взгляд, не совсем прозрачные изменеия в ip route и ip rule. Ввиду этого, будем поднимать интерфейс средствами команды ip.
Создадим файл конфигурации /etc/wireguard/wg0.conf вида:
[Interface] PrivateKey = uMгщр78н8ZCот7867orkmaOWwK+fJZMc088898999NQC4Xc= ListenPort = 51820 [Peer] #Удалённый сервер Endpoint = 62.63.64.65:51820 PublicKey = aevUokB2цсцусgCqPCnKкикеиM3z5/hнгьнгS6о= AllowedIPs = 0.0.0.0/0
Как видно, AllowedIPs = 0.0.0.0/0, но этот параметр отвечает только за разрешение на доступ, а не маршрутизацию. Внутренний ip-адрес для интерфейса в этом файле не задаётся.
Далее создадим и сконфигурируем интерфейс:
#!/bin/bash ip link add dev wg0 type wireguard ip address add dev wg0 10.80.0.3/24 wg setconf wg0 /etc/wireguard/wg0.conf ip link set up dev wg0 # Добавим описание таблицы маршрутизации 200 — tunnel: echo 200 tunnel >> /etc/iproute2/rt_tables # Зададим дефолтный гейт для таблицы 200: ip r replace default dev wg0 table tunnel # Укажем пакетам с fwmark 5 использовать таблицу 200 ip ru add from all fwmark 0x5 lookup tunnel
Для проверки функционирования пустим пинг на тот конец тоннеля:
root@L7test:/# ping 10.80.0.1 PING 10.80.0.1 (10.80.0.1) 56(84) bytes of data. 64 bytes from 10.80.0.1: icmp_seq=1 ttl=64 time=9.69 ms 64 bytes from 10.80.0.1: icmp_seq=2 ttl=64 time=4.06 ms
(Пинг в обратном направлении не будет проходить, пока мы не разрешим icmp-трафик на вход wg0)
Далее сконфигурируем squid
В данном тестовом случае ограничивать доступ средствами squid мы не будем — только «L7-маршрутизация».
nano /etc/squid/squid.conf:
dns_v4_first on visible_hostname L7-router check_hostnames off max_filedescriptors 65535 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 localnet src 10.10.10.0/24 acl ssl13 dst "/etc/squid/ssl13.txt" acl redirect_http dstdomain "/etc/squid/redirect.txt" acl redirect_https ssl::server_name "/etc/squid/redirect.txt" acl step1 at_step SslBump1 http_port 3128 http_port 3129 intercept https_port 3130 intercept ssl-bump options=ALL:NO_SSLv3:NO_SSLv2 connection-auth=off cert=/etc/squid/sslcert/squid.pem key=/etc/squid/sslcert/squid.key sslcrtd_program /usr/lib/squid/security_file_certgen -s /var/lib/ssl_db -M 4MB http_access deny !Safe_ports http_access deny CONNECT !SSL_ports http_access allow localhost http_access allow localnet http_access deny manager http_access deny all tcp_outgoing_mark 5 redirect_http tcp_outgoing_mark 5 redirect_https ssl_bump peek step1 !ssl13 sslproxy_cert_error allow all sslproxy_flags DONT_VERIFY_PEER tls_outgoing_options flags=DONT_VERIFY_PEER ssl_bump splice all ssl_bump terminate all no_cache deny all always_direct allow localnet 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 cache deny all cache_dir aufs /var/spool/squid 2 1 1 maximum_object_size 1 KB minimum_object_size 1 KB cache_swap_low 90 cache_swap_high 95 maximum_object_size_in_memory 512 KB memory_replacement_policy lru logfile_rotate 2
В SQUID есть директива tcp_outgoing_mark, позволяющая маркировать исходящие пакеты и применять соответствующие правила маршрутизации ip rule для эти�� пакетов.
домены для альтернативной маршрутизации указываются в файле вида /etc/squid/redirect.txt
.habr.com .vk.com .ok.ru ...
Если доступ к указанным ресурсам осуществляется через http, то ресурс обрабатывается через
acl redirect_http dstdomain "/etc/squid/redirect.txt"
Если https, то
acl redirect_https ssl::server_name "/etc/squid/redirect.txt"
При этом домены можно описывать в одном файле.
Squid, в данном случае, работает в режиме intercept (прозрачно). Применяется алгоритм peek and splice. Сервисы, использующие ssl_v1.3 не проходять через peek. К таким сервисам относится Telegram и большиство банк-клиентов. Для этих сервисов создаётся файл ssl13.txt с указанием ip и подсетей вида:
91.108.56.0/22 91.108.4.0/22 ...
После чего создаём acl вида:
acl ssl13 dst "/etc/squid/ssl13.txt"
Далее описыаем директивы peek and splice
ssl_bump peek step1 !ssl13 (не подглядываем в заголовок сертификата для ssl_v1.3) ssl_bump splice all
На основе подсмотренного в заголовке применяем маркировку исходящего пакета
tcp_outgoing_mark 5 redirect_http tcp_outgoing_mark 5 redirect_https
Следует обратить внимание, что некоторые ресурсы используют разные сертификаты и имена узлов для функционирования сервисов. Если какой-то ресурс работает частично и загружается не до конца, следует покопаться в последних записях /var/log/squid/access.log и посмотреть какие имена узлов ещё задействованы при обращении к сервису.
Пример:
Вы настроили для туннелирования .foo.com и .spam.ru, но ресурсы работают некорректно или вовсе не грузятся, но в логах есть обращения на cdnfoo.com и spam-video.ru, соответственно необходимо добавить записи типа:
.cdnfoo.com .spam-video.ru
Точка в начале означает «все поддомены верхних уровней»
