@ -8,15 +8,26 @@ For english version refer to docs/readme.eng.txt
Для чего это надо
-----------------
О бойти блокировки веб сайтов http.
Автономно, без задействования сторонних серверов, о бойти блокировки веб сайтов http и https на DPI .
Как это работает
----------------
У провайдеров в DPI бывают бреши. Они случаются от того, что правила DPI пишут для
обычных пользовательских программ, опуская все возможные случаи, допустимые по стандартам.
Это делается для простоты и скорости. Нет смысла ловить хакеров, которых 0.01%,
ведь все равно эти блокировки обходятся довольно просто даже обычными пользователями.
В самом простейшем случае вы имеете дело с пассивным DPI. Пассивный DPI может читать трафик
из потока, может инжектить свои пакеты, но не может блокировать проходящие пакеты.
Если запрос "плохой", пассивный DPI инжектит пакет RST, опционально дополняя его пакетом http redirect.
Если фейк пакет инжектится только для клиента, в этом случае можно обойтись командами iptables
для дропа RST и/или редиректа на заглушку по определенным условиям, которые нужно подбирать
для каждого провайдера индивидуально. Так мы обходим последствия срабатывания триггера запрета.
Если пассивный DPI направляет пакет RST в том числе и серверу, то вы ничего с этим не сможете сделать.
Ваша задача - не допустить срабатывания триггера запрета. Одними iptables уже не обойдетесь.
Этот проект нацелен именно на предотвращение срабатывания запрета, а не ликвидацию его последствий.
Активный DPI ставится в разрез провода и может дропать пакеты по любым критериям,
в том числе распознавать TCP потоки и блокировать любые пакеты, принадлежащие потоку.
Как не допустить срабатывания триггера запрета ? Послать то, на что DPI не расчитывает
и что ломает ему алгоритм распознавания запросов и их блокировки.
Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты.
Например, запрос вида "GET / HTTP/1.1\r\nHost: kinozal.tv......"
@ -25,89 +36,66 @@ For english version refer to docs/readme.eng.txt
Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /"
или добавление точки в конце имени хоста : "Host: kinozal.tv."
Существует и более продвинутая магия, направленная на преодоление DPI на пакетном уровне.
Как это реализовать на практике в системе linux
-----------------------------------------------
Как заставить систему разбивать запрос на части ? Можно прогнать всю TCP сессию
через transparent proxy, а можно подменить поле tcp window size на первом входящем TCP пакете с SYN,ACK.
Тогда клиент подумает, что сервер установил для него маленький window size и первый сегмент с данными
отошлет не более указанной длины. В последующих пакетах мы не будем менять ничего.
Дальнейшее поведение системы по выбору размера отсылаемых пакетов зависит от реализованного
в ней алгоритма. Опыт показывает, что linux первый пакет всегда отсылает не более указанной
в window size длины, остальные пакеты до некоторых пор шлет не более max(36,указанный_размер).
После некоторого количества пакетов срабатывает механизм window scaling и начинает
учитываться фактор скалинга, размер пакетов становится не более max(36,указанный_рамер << scale_factor).
Не слишком изящное поведение, но поскольку на размеры входящик пакетов мы не влияем,
а объем принимаемых по http данных обычно гораздо выше объема отсылаемых, то визуально
появятся лишь небольшие задержки.
Windows ведет себя в аналогичном случае гораздо более предсказуемо. Первый сегмент
уходит указанной длины, дальше window size меняется в зависимости от значения,
присылаемого в новых tcp пакетах. То есть скорость почти сразу же восстанавливается
до возможного максимума.
Перехватить пакет с SYN,ACK не представляет никакой сложности средствами iptables.
Однако, возможности редактирования пакетов в iptables сильно ограничены.
Просто так поменять window size стандартными модулями нельзя.
Для этого мы воспользуемся средством NFQUEUE. Это средство позволяет
передавать пакеты на обработку процессам, работающим в user mode.
Процесс, приняв пакет, может его изменить, что нам и нужно.
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass
Будет отдавать нужные нам пакеты процессу, слушающему на очереди с номером 200.
Он подменит window size. PREROUTING поймает как пакеты, адресованные самому хосту,
так и маршрутизируемые пакеты. То есть решение одинаково работает как на клиенте,
так и на роутере. На роутере на базе PC или на базе OpenWRT.
В принципе этого достаточно.
Однако, при таком воздействии на TCP будет небольшая задержка.
Чтобы не трогать хосты, которые не блокируются провайдером, можно сделать такой ход.
Создать список заблоченых доменов или скачать его с rublacklist.
Заресолвить все домены в ipv4 адреса. Загнать их в ipset с именем "zapret".
Добавить в правило :
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass
Таким образом воздействие будет производиться только на ip адреса, относящиеся к заблокированным сайтам.
Список можно обновлять через cron раз в несколько дней.
Если обновлять через rublacklist, то это займет довольно долго. Более часа. Но ресурсов
этот процесс не отнимает, так что никаких проблем это не вызовет, особенно, если система
работает постоянно.
Если DPI не обходится через разделение запроса на сегменты, то иногда срабатывает изменение
"Host:" на "host:". В этом случае нам может не понадобится замена window size, поэтому цепочка
PREROUTING нам не нужна. Вместо нее вешаемся на исходящие пакеты в цепочке POSTROUTING :
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
В этом случае так же возможны дополнительные моменты. DPI может ловить только первый http запрос, игнорируя
последующие запросы в keep-alive сессии. Тогда можем уменьшить нагрузку на проц, отказавшись от процессинга ненужных пакетов.
iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
Случается так, что провайдер мониторит всю HTTP сессию с keep-alive запросами. В этом случае
недостаточно ограничивать TCP window при установлении соединения. Необходимо посылать отдельными
TCP сегментами каждый новый запрос. Эта задача решается через полное проксирование трафика через
transparent proxy (TPROXY или DNAT). TPROXY не работает с соединениями, исходящими с локальной системы,
так что это решение применимо только на роутере. DNAT работает и с локальными соединениеми,
но имеется опасность входа в бесконечную рекурсию, поэтому демон запускается под отдельным пользователем,
и для этого пользователя отключается DNAT через "-m owner". Полное проксирование требует больше ресурсов
процессора, чем манипуляция с исходящими пакетами без реконструкции TCP соединения.
iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to 127.0.0.1:1188
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.1:1188
ПРИМЕЧАНИЕ: DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра route_localnet :
sysctl -w net.ipv4.conf.<incoming_interface_name>.route_localnet=1
Если кратко, то варианты можно классифицировать по следующей схеме :
1) Пассивный DPI, не отправляющий RST серверу. Помогут индивидуально настраиваемые под провайдера команды iptables.
На rutracker в разделе "обход блокировок - другие способы" по этому вопросу существует отдельная тема.
В данном проекте не рассматривается. Если вы не допустите срабатывание триггера запрета, то и не придется
бороться с его последствиями.
2) Модификация TCP соединения на уровне потока. Реализуется через proxy или transparent proxy.
3) Модификация TCP соединения на уровне пакетов. Реализуется через обработчик очереди NFQUEUE и raw сокеты.
Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно.
Чтобы они работали, необходимо их запустить с нужными параметрами и перенаправить на них определенный трафик
средствами iptables.
Для перенаправления tcp соединения на transparent proxy используются команды следующего вида :
проходящий трафик :
iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.1:1188
исходящий трафик :
iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.1:1188
DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра route_localnet :
sysctl -w net.ipv4.conf.<внутренний_интерфейс>.route_localnet=1
Можно использовать "-j REDIRECT --to-port 1188" вместо DNAT , однако в этом случае процесс transparent proxy
должен слушать на ip адресе входящего интерфейса или на всех адресах. Слушать на всех - не есть хорошо
с точки зрения безопасности. Слушать на одном (локальном) можно, но в случае автоматизированного
скрипта придется его узнавать, потом динамически вписывать в команду. В любом случае требуются дополнительные усилия.
Фильтр по owner необходим для исключения рекурсивного перенаправления соединений от самого tpws.
tpws запускается под пользователем "tpws", для него задается исключающее правило.
Для перенаправления на очередь NFQUEUE исходящего и проходящего в сторону внешнего интерфейса трафика используются
команды следующего вида :
iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass
Чтобы не трогать трафик на незаблокированные адреса, можно взять список заблокированных хостов, заресолвить его
в IP адреса и загнать в ipset zapret, затем добавить фильтр в команду :
iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
DPI может ловить только первый http запрос, игнорируя последующие запросы в keep-alive сессии.
Тогда можем уменьшить нагрузку на проц, отказавшись от процессинга ненужных пакетов.
iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 2:4 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass
Если ваше устройство поддерживает аппаратное ускорение (flow offloading, hardware nat, hardware acceleration), то iptables могут не работать.
При включенном offloading пакет не проходит по обычному пути netfilter.
Необходимо его отключить. Да, ваш хилый роутер уже не потянет гигабит, даже 200-250 не потянет, а на торрентах и того меньше.
Необходимо его отключить. Да, ваш хилый роутер уже не потянет гигабит, даже 200-250 может не потянуть, а на торрентах и того меньше.
Но если у вас такой линк, и вы хотите продвинутых вещей от роутера, то вам стоит задуматься о покупке mini pc с двумя или более gigabit ethernet.
В новых ядрах (и в более старых, openwrt портировал изменение на 4.14) присутствует software flow offloading (SFO).
@ -121,6 +109,7 @@ Offload включается через специальный target в iptable
Однако, openwrt не предусматривает выборочного управления offload. Если вы хотите с этим связаться,
вам придется городить свой собственный огород вокруг этого.
Особенности применения ip6tables
--------------------------------
@ -135,6 +124,7 @@ DNAT на localhost (::1) возможен только в цепочке OUTPUT
откуда пришел пакет.
NFQUEUE работает без изменений.
Когда это работать не будет
---------------------------
@ -152,6 +142,7 @@ NFQUEUE работает без изменений.
Мелкие провайдеры могут покупать услугу фильтрации у вышестоящих, чтобы самим не морочиться, и
они уже будут применять DPI.
nfqws
-----
@ -163,7 +154,7 @@ nfqws
--user=<username> ; менять uid процесса
--uid=uid[:gid] ; менять uid процесса
--qnum=200 ; номер очереди
--wsize=4 ; менять tcp window size на указанный размер
--wsize=4 ; менять tcp window size на указанный размер (устарело !)
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase
@ -265,6 +256,7 @@ nfqws способен самостоятельно различать поме
Почему --connbytes 2:4 : 2 - иногда данные идут в 3-м пакете 3-way handshake. 3 - стандартная ситуация. 4 - для надежности. на случай, если выполнялась одна ретрансмиссия
tpws
-----
@ -386,6 +378,7 @@ tpws полностью работает на асинхронных сокет
Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения
подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз.
Способы получения списка заблокированных IP
-------------------------------------------
@ -475,6 +468,7 @@ zapret-ip.txt => zapret-ip6.txt
Помещенные в них IP не участвуют в процессе.
zapret-hosts-user-exclude.txt может содержать домены, ipv4 и ipv6 адреса или подсети.
ip2net
------
@ -516,6 +510,7 @@ ip2net
Учтите, что арифметика mul/div - целочисленная. При превышении разрядной сетки 32 bit результат непредсказуем.
Не надо делать такое : 5000000/10000000. 1/2 - гораздо лучше.
Фильтрация по именам доменов
----------------------------
@ -537,6 +532,7 @@ init скрипт будет запускать tpws с листом zapret-host
При использовании больших списков, в том числе списка РКН, оцените объем RAM на роутере !
Если после запуска демона RAM под завязку или случаются oom, значит нужно отказаться от таких больших списков.
Проверка провайдера
-------------------
@ -561,10 +557,6 @@ init скрипт будет запускать tpws с листом zapret-host
Его нужно просмотреть и при необходимости отредактировать.
Выберите MODE :
nfqws_ipset - использовать nfqws для модификации трафика на порт 80 только на IP из ipset "zapret"
nfqws_ipset_https - использовать nfqws для модификации трафика на порты 80 и 443 только на IP из ipset "zapret"
nfqws_all - использовать nfqws для модификации трафика на порт 80 для всех IP
nfqws_all_https - использовать nfqws для модификации трафика на порты 80 и 443 для всех IP
nfqws_all_desync - использовать nfqws для атаки десинхронизации DPI на http и https для всех IP
nfqws_ipset_desync - использовать nfqws для атаки десинхронизации DPI на http и https только на IP из ipset "zapret"
nfqws_hostlist_desync - использовать nfqws для атаки десинхронизации DPI на http и https только на хосты из hostlist.