Возникла задача заворачивать трафик в тоннель, основываясь на имени домена ресурса, к которому идёт обращение.Многие интернет-ресурсы имеют большой пул 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Точка в начале означает «все поддомены верхних уровней»
