diff --git a/changes.txt b/changes.txt index d4aced4..ab04bd6 100644 --- a/changes.txt +++ b/changes.txt @@ -10,27 +10,31 @@ ISP support : tested on mns.ru and beeline (corbina) init scripts : rewritten init scripts for simple choise of ISP create_ipset : now using 'ipset restore', it works much faster readme : updated. now using UTF-8 charset. - -v3 - -tpws : added transparent proxy (supports TPROXY and DNAT). - can help when ISP tracks whole HTTP session, not only the beginning -ipset : added zapret-hosts-user.txt which contain user defined host names to be resolved - and added to zapret ip list -ISP support : dom.ru support via TPROXY/DNAT -ISP support : successfully tested sknt.ru on 'domru' configuration - other configs will probably also work, but cannot test -compile : openwrt compile howto - -v4 - -tpws : added ability to insert extra space after http method : "GET /" => "GET /" -ISP support : TKT support - -v5 - -nfqws : ipv6 support in nfqws - -v6 - -ipset : added "get_antizapret.sh" + +v3 + +tpws : added transparent proxy (supports TPROXY and DNAT). + can help when ISP tracks whole HTTP session, not only the beginning +ipset : added zapret-hosts-user.txt which contain user defined host names to be resolved + and added to zapret ip list +ISP support : dom.ru support via TPROXY/DNAT +ISP support : successfully tested sknt.ru on 'domru' configuration + other configs will probably also work, but cannot test +compile : openwrt compile howto + +v4 + +tpws : added ability to insert extra space after http method : "GET /" => "GET /" +ISP support : TKT support + +v5 + +nfqws : ipv6 support in nfqws + +v6 + +ipset : added "get_antizapret.sh" + +v7 + +tpws : added ability to insert "." after Host: name diff --git a/iptables.txt b/iptables.txt index 3d59b39..0ead65c 100644 --- a/iptables.txt +++ b/iptables.txt @@ -1,33 +1,33 @@ -For window size changing : - -iptables -t raw -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass -iptables -t raw -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 - -For outgoing data manipulation ("Host:" case changing) : - -iptables -t mangle -I POSTROUTING -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass -iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass -iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -j NFQUEUE --queue-num 200 --queue-bypass - - -For TPROXY : - -sysctl -w net.ipv4.ip_forward=1 -iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE - -ip -f inet rule add fwmark 1 lookup 100 -ip -f inet route add local default dev lo table 100 -# prevent loop -iptables -t filter -I INPUT -p tcp --dport 1188 -j REJECT -iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j MARK --set-mark 1 -iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188 - -iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m set --match-set zapret dst -j MARK --set-mark 1 -iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m mark --mark 0x1/0x1 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188 - -For DNAT : - -# run tpws as user "tpws". its required to avoid loops. -sysctl -w net.ipv4.conf.eth1.route_localnet=1 -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 +For window size changing : + +iptables -t raw -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t raw -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 + +For outgoing data manipulation ("Host:" case changing) : + +iptables -t mangle -I POSTROUTING -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t mangle -I POSTROUTING -p tcp --dport 80 -m set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -j NFQUEUE --queue-num 200 --queue-bypass + + +For TPROXY : + +sysctl -w net.ipv4.ip_forward=1 +iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + +ip -f inet rule add fwmark 1 lookup 100 +ip -f inet route add local default dev lo table 100 +# prevent loop +iptables -t filter -I INPUT -p tcp --dport 1188 -j REJECT +iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j MARK --set-mark 1 +iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188 + +iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m set --match-set zapret dst -j MARK --set-mark 1 +iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m mark --mark 0x1/0x1 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 1188 + +For DNAT : + +# run tpws as user "tpws". its required to avoid loops. +sysctl -w net.ipv4.conf.eth1.route_localnet=1 +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 diff --git a/readme.txt b/readme.txt index 2a933f2..ad6aec7 100644 --- a/readme.txt +++ b/readme.txt @@ -1,255 +1,259 @@ -zapret v.6 - -Для чего это надо ------------------ - -Обойти блокировки веб сайтов http. - -Как это работает ----------------- - -У провайдеров в DPI бывают бреши. Они случаются от того, что правила DPI пишут для -обычных пользовательских программ, опуская все возможные случаи, допустимые по стандартам. -Это делается для простоты и скорости. Нет смысла ловить хакеров, которых 0.01%, -ведь все равно эти блокировки обходятся довольно просто даже обычными пользователями. - -Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты. -Например, запрос вида "GET / HTTP/1.1\r\nHost: kinozal.tv......" -мы посылаем 2 частями : сначала идет "GET ", затем "/ HTTP/1.1\r\nHost: kinozal.tv.....". -Другие DPI спотыкаются, когда заголовок "Host:" пишется в другом регистре : например, "host:". -Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /". - -Как это реализовать на практике в системе 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 raw -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 raw -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 set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -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 - -nfqws ------ - -Эта программа - модификатор пакетов и обработчик очереди NFQUEUE. -Она берет следующие параметры : - --qnum=200 ; номер очереди - --wsize=4 ; менять tcp window size на указанный размер - --hostcase ; менять регистр заголовка "Host:" - --daemon ; демонизировать прогу - -tpws ------ - -tpws - это transparent proxy. - --bind-addr ; на каком адресе слушать. может быть ipv4 или ipv6 адрес. если не указано, то слушает на всех адресах ipv4 и ipv6 - --port= ; на каком порту слушать - --split-http-req=method|host ; способ разделения http запросов на сегменты : около метода (GET,POST) или около заголовка Host - --hostcase ; замена "Host:" => "host:" - --methodspace ; добавить пробел после метода : "GET /" => "GET /" - --daemon ; демонизировать прогу - --user= ; менять uid процесса - -Провайдеры ----------- - -mns.ru : нужна замена window size на 4 -beeline (corbina) : нужна замена регистра "Host:" на протяжении всей http сессии -dom.ru : нужно проксирование HTTP сессий через tpws с заменой регистра "Host:" и разделение TCP сегментов на хедере "Host:". - Ахтунг ! Домру блокирует все поддомены заблоченого домена. IP адреса всевозможных поддоменов узнать невозможно из реестра - блокировок, поэтому если вдруг на каком-то сайте вылезает блокировочный баннер, то идите в консоль firefox, вкладка network. - Загружайте сайт и смотрите куда идет редирект. Потом вносите домен в zapret-hosts-user.txt. Например, на kinozal.tv имеются - 2 запрашиваемых поддомена : s.kinozal.tv и st.kinozal.tv с разными IP адресами. -sknt.ru : проверена работа с tpws с параметром "--split-http-req=method". возможно, будет работать nfqueue, пока возможности - проверить нет -tkt : помогает разделение http запроса на сегменты, настройки mns.ru подходят - ТКТ был куплен ростелекомом, используется фильтрация ростелекома. - Поскольку DPI не отбрасывает входящую сессию, а только всовывает свой пакет, который приходит раньше ответа от настоящего сервера, - блокировки так же обходятся без применения "тяжелой артиллерии" следующим правилом : - iptables -t raw -I PREROUTING -p tcp --sport 80 -m string --hex-string "|0D0A|Location: http://95.167.13.50" --algo bm -j DROP --from 40 --to 200 -Ростелеком : см tkt - -Способы получения списка заблокированных IP -------------------------------------------- - -1) Внесите заблокирванные домены в ipset/zapret-hosts-user.txt и запустите ipset/get_user.sh -На выходе получите ipset/zapret-ip-user.txt с IP адресами. - -2) ipset/get_reestr.sh получает список доменов от rublacklist и дальше их ресолвит в ip адреса -в файл ipset/zapret-ip.txt. В этом списке есть готовые IP адреса, но судя во всему они там в точности в том виде, -что вносит в реестр РосКомПозор. Адреса могут меняться, позор не успевает их обновлять, а провайдеры редко -банят по IP : вместо этого они банят http запросы с "нехорошим" заголовком "Host:" вне зависимости -от IP адреса. Поэтому скрипт ресолвит все сам, хотя это и занимает много времени. -Дополнительное требование - объем памяти в /tmp для сохранения туда скачанного файла, размер которого -несколько Мб и продолжает расти. На роутерах openwrt /tmp представляет собой tmpfs , то есть ramdisk. -В случае роутера с 32 мб памяти ее может не хватить, и будут проблемы. В этом случае используйте -следующий скрипт. -Скрипт автоматически вызывает ipset/get_user.sh и обновляет ipset - -3) ipset/get_anizapret.sh. быстро и без нагрузки на роутер получает лист с http://antizapret.prostovpn.org. -Скрипт автоматически вызывает ipset/get_user.sh и обновляет ipset - -На роутерах не рекомендуется вызывать эти скрипты чаще раза за 2 суток, поскольку сохранение идет -либо во внутреннюю флэш память роутера, либо в случае extroot - на флэшку. -В обоих случаях слишком частая запись может убить флэшку, но если это произойдет с внутренней -флэш памятью, то вы просто убьете роутер. - -Обновление ipset выполняет скрипт ipset/create_ipset.sh - -Пример установки на debian 7 ----------------------------- -Debian 7 изначально содержит ядро 3.2. Оно не умеет делать DNAT на localhost. -Конечно, можно не привязывать tpws к 127.0.0.1 и заменить в правилах iptables "DNAT 127.0.0.1" на "REDIRECT", -но лучше установить более свежее ядро. Оно есть в стабильном репозитории : - apt-get update - apt-get install linux-image-3.16 -Установить пакеты : - apt-get update - apt-get install libnetfilter-queue-dev ipset curl -Скопировать директорию "zapret" в /opt. -Собрать nfqws : - cd /opt/zapret/nfq - make -Собрать tpws : - cd /opt/zapret/tpws - make -Скопировать /opt/zapret/init.d/debian7/zapret в /etc/init.d. -В /etc/init.d/zapret выбрать пераметр "ISP". В зависимости от него будут применены нужные правила. -Там же выбрать параметр SLAVE_ETH, соответствующий названию внутреннего сетевого интерфейса. -Включить автостарт : chkconfig zapret on -(опционально) Вручную первый раз получить новый список ip адресов : /opt/zapret/ipset/get_antizapret.sh -Зашедулить задание обновления листа : - crontab -e - Создать строчку "0 12 * * */2 /opt/zapret/ipset/get_antizapret.sh". Это значит в 12:00 каждые 2 дня обновлять список. -Запустить службу : service zapret start -Попробовать зайти куда-нибудь : http://ej.ru, http://kinozal.tv, http://grani.ru. -Если не работает, то остановить службу zapret, добавить правило в iptables вручную, -запустить nfqws в терминале под рутом с нужными параметрами. -Пытаться подключаться к заблоченым сайтам, смотреть вывод программы. -Если нет никакой реакции, значит скорее всего указан неверный номер очереди или ip назначения нет в ipset. -Если реакция есть, но блокировка не обходится, значит параметры обхода подобраные неверно, или это средство -не работает в вашем случае на вашем провайдере. -Никто и не говорил, что это будет работать везде. -Попробуйте снять дамп в wireshark или "tcpdump -vvv -X host ", посмотрите действительно ли первый -сегмент TCP уходит коротким и меняется ли регистр "Host:". - -ubuntu 12,14 ------------- - -Имеется готовый конфиг для upstart : zapret.conf. Его нужно скопировать в /etc/init и настроить по аналогии с debian. -Запуск службы : "start zapret" -Останов службы : "stop zapret" - -Другие linux системы --------------------- - -Существует несколько основных систем запуска служб : sysvinit, upstart, systemd. -Настройка зависит от системы, используемой в вашем дистрибутиве. -Типичная стратегия - найти скрипт или конфигурацию запуска других служб и написать свой по аналогии, -при необходимости почитывая документацию по системе запуска. -Нужные команды можно взять из предложенных скриптов. - -Фаерволлы ---------- - -Если вы используете какую-то систему управления фаерволом, то она может вступать в конфликт -с имеющимся скриптом запуска. В этом случае правила для iptables должны быть прикручены -к вашему фаерволу отдельно от скрипта запуска tpws или nfqws. -Именно так решается вопрос в случае с openwrt, поскольку там своя система управления фаерволом. -При повторном применении правил она могла бы поломать настройки iptables, сделанные скриптом из init.d. - -Что делать с openwrt --------------------- - -Установить дополнительные пакеты : -opkg update -opkg install iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt ipset curl bind-tools - -Самая главная трудность - скомпилировать программы на C. -Это можно сделать средствами кросс-компиляции на любой традиционной linux системе. -Читайте compile/build_howto_openwrt.txt. -Ваша задача - получить ipk файлы для tpws и nfqws. -Скопировать директорию "zapret" в /opt на роутер. -Установить ipk. Для этого сначала копируем на роутер ipk в /tmp, потом opkg install /tmp/*.ipk. -Смотрим, что появились исполняемые файлы /opt/zapret/tpws/tpws, /opt/zapret/nfq/nfqws. -Скопировать /opt/zapret/init.d/zapret в /etc/init.d. -В /etc/init.d/zapret выбрать пераметр "ISP". В зависимости от него будут применены нужные правила. -/etc/init.d/zapret enable -/etc/init.d/zapret start -В зависимости от вашего провайдера либо внести нужные записи в /etc/firewall.user, либо -скопировать 99-zapret в /etc/hotplug.d/firewall (сначала нужно mkdir /etc/hotplug.d/firewall). -В /etc/hotplug.d/firewall/99-zapret выбрать нужного провайдера. -/etc/init.d/firewall restart -Посмотреть через iptables -L или через luci вкладку "firewall" появились ли нужные правила. -Зашедулить задание обновления листа : - crontab -e - Создать строчку "0 12 * * */2 /opt/zapret/ipset/get_antizapret.sh". Это значит в 12:00 каждые 2 дня обновлять список. - -Если у вас linux x64, то вместо компиляции toolchain можно использовать пре-компилированный SDK от разработчиков openwrt. -https://downloads.openwrt.org/ -Найдите вашу версию openwrt, найдите вашу архитектуру, скачайте файл "OpenWrt-SDK-*". -Фактически это тот же buildroot, только в нем уже подготовлен toolchain для нужной версии openwrt, -нужной target архитектуры и хост-системы linux x64. - +zapret v.7 + +Для чего это надо +----------------- + +Обойти блокировки веб сайтов http. + +Как это работает +---------------- + +У провайдеров в DPI бывают бреши. Они случаются от того, что правила DPI пишут для +обычных пользовательских программ, опуская все возможные случаи, допустимые по стандартам. +Это делается для простоты и скорости. Нет смысла ловить хакеров, которых 0.01%, +ведь все равно эти блокировки обходятся довольно просто даже обычными пользователями. + +Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты. +Например, запрос вида "GET / HTTP/1.1\r\nHost: kinozal.tv......" +мы посылаем 2 частями : сначала идет "GET ", затем "/ HTTP/1.1\r\nHost: kinozal.tv.....". +Другие DPI спотыкаются, когда заголовок "Host:" пишется в другом регистре : например, "host:". +Кое-где работает добавление дополнительного пробела после метода : "GET /" => "GET /" +или добавление точки в конце имени хоста : "Host: kinozal.tv." + +Как это реализовать на практике в системе 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 raw -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 raw -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 set --match-set zapret dst -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:5 -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 + +nfqws +----- + +Эта программа - модификатор пакетов и обработчик очереди NFQUEUE. +Она берет следующие параметры : + --daemon ; демонизировать прогу + --qnum=200 ; номер очереди + --wsize=4 ; менять tcp window size на указанный размер + --hostcase ; менять регистр заголовка "Host:" +Параметры манипуляции могут сочетаться в любых комбинациях. + +tpws +----- + +tpws - это transparent proxy. + --bind-addr ; на каком адресе слушать. может быть ipv4 или ipv6 адрес. если не указано, то слушает на всех адресах ipv4 и ipv6 + --port= ; на каком порту слушать + --daemon ; демонизировать прогу + --user= ; менять uid процесса + --split-http-req=method|host ; способ разделения http запросов на сегменты : около метода (GET,POST) или около заголовка Host + --hostcase ; замена "Host:" => "host:" + --hostdot ; добавление точки после имени хоста : "Host: kinozal.tv." + --methodspace ; добавить пробел после метода : "GET /" => "GET /" +Параметры манипуляции могут сочетаться в любых комбинациях. + +Провайдеры +---------- + +mns.ru : нужна замена window size на 4 +beeline (corbina) : нужна замена регистра "Host:" на протяжении всей http сессии +dom.ru : нужно проксирование HTTP сессий через tpws с заменой регистра "Host:" и разделение TCP сегментов на хедере "Host:". + Ахтунг ! Домру блокирует все поддомены заблоченого домена. IP адреса всевозможных поддоменов узнать невозможно из реестра + блокировок, поэтому если вдруг на каком-то сайте вылезает блокировочный баннер, то идите в консоль firefox, вкладка network. + Загружайте сайт и смотрите куда идет редирект. Потом вносите домен в zapret-hosts-user.txt. Например, на kinozal.tv имеются + 2 запрашиваемых поддомена : s.kinozal.tv и st.kinozal.tv с разными IP адресами. +sknt.ru : проверена работа с tpws с параметром "--split-http-req=method". возможно, будет работать nfqueue, пока возможности + проверить нет +tkt : помогает разделение http запроса на сегменты, настройки mns.ru подходят + ТКТ был куплен ростелекомом, используется фильтрация ростелекома. + Поскольку DPI не отбрасывает входящую сессию, а только всовывает свой пакет, который приходит раньше ответа от настоящего сервера, + блокировки так же обходятся без применения "тяжелой артиллерии" следующим правилом : + iptables -t raw -I PREROUTING -p tcp --sport 80 -m string --hex-string "|0D0A|Location: http://95.167.13.50" --algo bm -j DROP --from 40 --to 200 +Ростелеком : см tkt + +Способы получения списка заблокированных IP +------------------------------------------- + +1) Внесите заблокирванные домены в ipset/zapret-hosts-user.txt и запустите ipset/get_user.sh +На выходе получите ipset/zapret-ip-user.txt с IP адресами. + +2) ipset/get_reestr.sh получает список доменов от rublacklist и дальше их ресолвит в ip адреса +в файл ipset/zapret-ip.txt. В этом списке есть готовые IP адреса, но судя во всему они там в точности в том виде, +что вносит в реестр РосКомПозор. Адреса могут меняться, позор не успевает их обновлять, а провайдеры редко +банят по IP : вместо этого они банят http запросы с "нехорошим" заголовком "Host:" вне зависимости +от IP адреса. Поэтому скрипт ресолвит все сам, хотя это и занимает много времени. +Дополнительное требование - объем памяти в /tmp для сохранения туда скачанного файла, размер которого +несколько Мб и продолжает расти. На роутерах openwrt /tmp представляет собой tmpfs , то есть ramdisk. +В случае роутера с 32 мб памяти ее может не хватить, и будут проблемы. В этом случае используйте +следующий скрипт. +Скрипт автоматически вызывает ipset/get_user.sh и обновляет ipset + +3) ipset/get_anizapret.sh. быстро и без нагрузки на роутер получает лист с http://antizapret.prostovpn.org. +Скрипт автоматически вызывает ipset/get_user.sh и обновляет ipset + +На роутерах не рекомендуется вызывать эти скрипты чаще раза за 2 суток, поскольку сохранение идет +либо во внутреннюю флэш память роутера, либо в случае extroot - на флэшку. +В обоих случаях слишком частая запись может убить флэшку, но если это произойдет с внутренней +флэш памятью, то вы просто убьете роутер. + +Обновление ipset выполняет скрипт ipset/create_ipset.sh + +Пример установки на debian 7 +---------------------------- +Debian 7 изначально содержит ядро 3.2. Оно не умеет делать DNAT на localhost. +Конечно, можно не привязывать tpws к 127.0.0.1 и заменить в правилах iptables "DNAT 127.0.0.1" на "REDIRECT", +но лучше установить более свежее ядро. Оно есть в стабильном репозитории : + apt-get update + apt-get install linux-image-3.16 +Установить пакеты : + apt-get update + apt-get install libnetfilter-queue-dev ipset curl +Скопировать директорию "zapret" в /opt. +Собрать nfqws : + cd /opt/zapret/nfq + make +Собрать tpws : + cd /opt/zapret/tpws + make +Скопировать /opt/zapret/init.d/debian7/zapret в /etc/init.d. +В /etc/init.d/zapret выбрать пераметр "ISP". В зависимости от него будут применены нужные правила. +Там же выбрать параметр SLAVE_ETH, соответствующий названию внутреннего сетевого интерфейса. +Включить автостарт : chkconfig zapret on +(опционально) Вручную первый раз получить новый список ip адресов : /opt/zapret/ipset/get_antizapret.sh +Зашедулить задание обновления листа : + crontab -e + Создать строчку "0 12 * * */2 /opt/zapret/ipset/get_antizapret.sh". Это значит в 12:00 каждые 2 дня обновлять список. +Запустить службу : service zapret start +Попробовать зайти куда-нибудь : http://ej.ru, http://kinozal.tv, http://grani.ru. +Если не работает, то остановить службу zapret, добавить правило в iptables вручную, +запустить nfqws в терминале под рутом с нужными параметрами. +Пытаться подключаться к заблоченым сайтам, смотреть вывод программы. +Если нет никакой реакции, значит скорее всего указан неверный номер очереди или ip назначения нет в ipset. +Если реакция есть, но блокировка не обходится, значит параметры обхода подобраные неверно, или это средство +не работает в вашем случае на вашем провайдере. +Никто и не говорил, что это будет работать везде. +Попробуйте снять дамп в wireshark или "tcpdump -vvv -X host ", посмотрите действительно ли первый +сегмент TCP уходит коротким и меняется ли регистр "Host:". + +ubuntu 12,14 +------------ + +Имеется готовый конфиг для upstart : zapret.conf. Его нужно скопировать в /etc/init и настроить по аналогии с debian. +Запуск службы : "start zapret" +Останов службы : "stop zapret" + +Другие linux системы +-------------------- + +Существует несколько основных систем запуска служб : sysvinit, upstart, systemd. +Настройка зависит от системы, используемой в вашем дистрибутиве. +Типичная стратегия - найти скрипт или конфигурацию запуска других служб и написать свой по аналогии, +при необходимости почитывая документацию по системе запуска. +Нужные команды можно взять из предложенных скриптов. + +Фаерволлы +--------- + +Если вы используете какую-то систему управления фаерволом, то она может вступать в конфликт +с имеющимся скриптом запуска. В этом случае правила для iptables должны быть прикручены +к вашему фаерволу отдельно от скрипта запуска tpws или nfqws. +Именно так решается вопрос в случае с openwrt, поскольку там своя система управления фаерволом. +При повторном применении правил она могла бы поломать настройки iptables, сделанные скриптом из init.d. + +Что делать с openwrt +-------------------- + +Установить дополнительные пакеты : +opkg update +opkg install iptables-mod-extra iptables-mod-nfqueue iptables-mod-filter iptables-mod-ipopt ipset curl bind-tools + +Самая главная трудность - скомпилировать программы на C. +Это можно сделать средствами кросс-компиляции на любой традиционной linux системе. +Читайте compile/build_howto_openwrt.txt. +Ваша задача - получить ipk файлы для tpws и nfqws. +Скопировать директорию "zapret" в /opt на роутер. +Установить ipk. Для этого сначала копируем на роутер ipk в /tmp, потом opkg install /tmp/*.ipk. +Смотрим, что появились исполняемые файлы /opt/zapret/tpws/tpws, /opt/zapret/nfq/nfqws. +Скопировать /opt/zapret/init.d/zapret в /etc/init.d. +В /etc/init.d/zapret выбрать пераметр "ISP". В зависимости от него будут применены нужные правила. +/etc/init.d/zapret enable +/etc/init.d/zapret start +В зависимости от вашего провайдера либо внести нужные записи в /etc/firewall.user, либо +скопировать 99-zapret в /etc/hotplug.d/firewall (сначала нужно mkdir /etc/hotplug.d/firewall). +В /etc/hotplug.d/firewall/99-zapret выбрать нужного провайдера. +/etc/init.d/firewall restart +Посмотреть через iptables -L или через luci вкладку "firewall" появились ли нужные правила. +Зашедулить задание обновления листа : + crontab -e + Создать строчку "0 12 * * */2 /opt/zapret/ipset/get_antizapret.sh". Это значит в 12:00 каждые 2 дня обновлять список. + +Если у вас linux x64, то вместо компиляции toolchain можно использовать пре-компилированный SDK от разработчиков openwrt. +https://downloads.openwrt.org/ +Найдите вашу версию openwrt, найдите вашу архитектуру, скачайте файл "OpenWrt-SDK-*". +Фактически это тот же buildroot, только в нем уже подготовлен toolchain для нужной версии openwrt, +нужной target архитектуры и хост-системы linux x64. + diff --git a/tpws/tpws.c b/tpws/tpws.c index fd8c8a9..ea305e0 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -33,7 +33,7 @@ struct params_s gid_t gid; uint16_t port; bool daemon; - bool hostcase,methodcase,methodspace; + bool hostcase,hostdot,methodspace; enum splithttpreq split_http_req; int maxconn; }; @@ -106,8 +106,8 @@ bool handle_epollin(tproxy_conn_t *conn,int *data_transferred){ { if (bOutgoing) { - char buf[RD_BLOCK_SIZE+1],*p; - ssize_t l,split_pos=0,pos; + char buf[RD_BLOCK_SIZE+2],*p,*phost=NULL; + ssize_t l,split_pos=0,method_split_pos=0,host_split_pos=0,pos; const char **split_array,**split_item,**item; rd = recv(fd_in,buf,RD_BLOCK_SIZE,MSG_DONTWAIT); @@ -128,19 +128,48 @@ bool handle_epollin(tproxy_conn_t *conn,int *data_transferred){ memmove(p+1,p,bs-pos); *p = ' '; // insert extra space bs++; // block will grow by 1 byte - split_pos = pos; // remember split positing and use it if required + method_split_pos = pos; // remember split position and use it if required break; } } } + if (params.hostdot) + { + if (phost=find_bin(buf,bs,"\r\nHost: ",8)) + { + host_split_pos = phost-buf+7; + p = phost+8; + while(p<(buf+bs) && *p!='\r' && *p!='\n') p++; + if (p<(buf+bs)) + { + pos = p-buf; + printf("Adding dot to host name at pos %d\n",pos); + memmove(p+1,p,bs-pos); + *p = '.'; // insert dot + bs++; // block will grow by 1 byte + } + } + } switch (params.split_http_req) { case split_method: // do we have already split position ? if so use it without another search - split_array = split_pos ? NULL : http_split_methods; + if (method_split_pos) + { + split_array = NULL; + split_pos = method_split_pos; + } + else + split_array = http_split_methods; break; case split_host: - split_array = http_split_host; + if (host_split_pos) + { + split_array = NULL; + split_pos = host_split_pos; + } + else + split_array = http_split_host; break; default: split_array = NULL; @@ -148,6 +177,7 @@ bool handle_epollin(tproxy_conn_t *conn,int *data_transferred){ } if (split_array) { + // we havent found split post yet. need to search. for(split_item=split_array;*split_item;split_item++) { l = strlen(*split_item); @@ -162,27 +192,15 @@ bool handle_epollin(tproxy_conn_t *conn,int *data_transferred){ } if (params.hostcase) { - if (p=find_bin(buf,bs,"\r\nHost: ",8)) + if (phost || (phost=find_bin(buf,bs,"\r\nHost: ",8))) { - printf("Changing 'Host:' => 'host:' at pos %d\n",p-buf); - p[2]='h'; - } - } - if (params.methodcase) - { - for(split_item=http_split_methods;*split_item;split_item++) - { - l = strlen(*split_item); - if (p=find_bin(buf,bs,*split_item,l)) - { - printf("Changing '%s' case\n",*split_item); - *p += 'a'-'A'; - break; - } + printf("Changing 'Host:' => 'host:' at pos %d\n",phost-buf); + phost[2]='h'; } } if (split_pos) { + printf("Splitting at pos %d\n",split_pos); wr=send_with_flush(fd_out,buf,split_pos,0); if (wr>=0) wr=send(fd_out,buf+split_pos,bs-split_pos,0); @@ -366,7 +384,7 @@ int8_t block_sigpipe(){ void exithelp() { - printf(" --bind-addr=|\n --port=\n --maxconn=\n --split-http-req=method|host\n --hostcase\t\t; change Host: => host:\n --methodcase\t\t; change GET => gET, POST=>pOST, ...\n --methodspace\t\t; add extra space after method\n --daemon\t\t; daemonize\n --user=\t; drop root privs\n"); + printf(" --bind-addr=|\n --port=\n --maxconn=\n --split-http-req=method|host\n --hostcase\t\t; change Host: => host:\n --hostdot\t\t; add \".\" after Host: name\n --methodspace\t\t; add extra space after method\n --daemon\t\t; daemonize\n --user=\t; drop root privs\n"); exit(1); } @@ -387,7 +405,7 @@ void parse_params(int argc, char *argv[]) {"user",required_argument,0,0},// optidx=5 {"maxconn",required_argument,0,0},// optidx=6 {"hostcase",no_argument,0,0},// optidx=7 - {"methodcase",no_argument,0,0},// optidx=8 + {"hostdot",no_argument,0,0},// optidx=8 {"split-http-req",required_argument,0,0},// optidx=9 {"methodspace",no_argument,0,0},// optidx=10 {NULL,0,NULL,0} @@ -440,8 +458,8 @@ void parse_params(int argc, char *argv[]) case 7: /* hostcase */ params.hostcase = true; break; - case 8: /* methodcase */ - params.methodcase = true; + case 8: /* hostdot */ + params.hostdot = true; break; case 9: /* split-http-req */ if (!strcmp(optarg,"method"))