Tuesday, November 24, 2015

Наш ответ Роскомнадзору или Установка TOR на OpenWRT



Для установки tor на OpenWrt достаточно выполнить

opkg install tor

Если вы собираете пакеты для себя вручную, либо хотите встроить этот пакет прямо в прошивку то на этапе конфигурации прошивки OpenWRT нужно выбрать:

Network ---> <M> tor

для сборки в виде пакета, или

Network ---> <*> tor

для того чтобы встроить пакет прямо в прошивку. Затем собираете прошивку способом аналогичным описанному в моей предыдущей статье.

Несколько подводных камней, которые могут возникнуть после инсталляции пакета.

1. Обязательно настройте и синхронизируйте время чеpез ntp. Tor использует системное время для каких то своих действий. Неправильная дата может привести к проблемам при старте tor или к его не корректной работе.

2. При установке tor должен создать пользователя tor и группу tor. При создании группы инсталлятор у меня допустил ошибку и в файле /etc/group не поставил перенос строки для группы tor. Скорее всего это у меня единичный баг, однако будьте внимательны.

3. Проверьте все права на файлы необходимые tor они должны иметь маску 755 и принадлежать tor:tor
Настройка TOR.

Открываем конфигурационный файл /etc/tor/torrc и правим:

1. Если вы хотите чтобы все компьютеры которые подключены к роутеру (неважно по кабелю или wifi) имели возможность получить доступ к TOR через SOCKS proxy необходимо раскоментировать и проправить строку:

SocksListenAddress 192.168.120.1:9100 # listen on this IP:port also

Где 192.168.120.1 – ip адрес вашего роутера для локальной сети, а 9100 – порт на котором будет socks сервер для сети tor.

2. Опциями SocksPolicy можно явно указать какие ip адреса будут иметь (или не иметь) доступ к socks серверу. В файле конфигурации эти опции закомментированы – рекомендую их раскомментировать и настроить под себя.

3. Если по каким то причинам tor не стартует воспользуетсь опцией Log для выяснения проблемы.

Собственно больше никаких настроек не нужно, и можно запускать tor.
Запуск Tor

Запускаем тор следующей командой:

/etc/init.d/tor start

Если tor уже запущен либо при старте возникают ошибки вида:

[warn] Could not bind to 127.0.0.1:9050: Address already in use. Is Tor already running?

необходимо остановить tor:

killall -9 tor

И запустить его заново как написано выше.

Если лог включен, то признаком успешного запуска и подключения к сети tor будет строка:

Feb 21 19:12:17.034 [notice] Tor has successfully opened a circuit. Looks like client functionality is working.
Feb 21 19:12:17.035 [notice] Bootstrapped 100%: Done.

Далее достаточно прописать адрес и порт указанный опцией SocksListenAddress как socks сервер в браузере или любой другой программе на вашем ПК.

Однако есть ряд программ которые не умеют работать с socks протоколом. Эту проблему можно решить средствами самого tor + iptables. Для этого необходимо включить в tor режим прозрачного проксирования. В таком случае на подключенных к роутеру машинах вообще никаких настроек делать не нужно. Весь трафик будет сразу проходить через tor.
Использование прозрачного прокси в Tor

Для этого в конфигурационном файле /etc/tor/torrc правим:

AutomapHostsOnResolve 1
TransPort 9040
DNSPort 53

Тем самым прозрачный прокси будет слушать порт 9040.

Перезапускаем tor:

killall -9 tor
/etc/init.d/tor start

Настройка iptables

Далее необходимо весь трафик завернуть на этот порт. Для этого необходимо использовать iptables с опцией REDIRECT –to-port. Для использования этой опции необходимо дополнительно собрать и установить пакет iptables-mod-nat-extra. В противном случае вы получите ошибку вида:

iptables v1.4.6: unknown option `--to-ports'

Установить пакет можно тривиально выполнив

opkg install kmod-ipt-nat-extra

А можно собрать в своем репозитории или включить этот пакет сразу в прошивку. В конфигураторе OpenWrt этот пакет находится в

-> Network
-> iptables
<M> iptables-mod-nat-extra

После установки пакета необходимо “завернуть” трафик на прозрачный прокси сервер:

iptables -t nat -A PREROUTING -p tcp -d ! 192.168.120.1 --dport 80 -j REDIRECT --to-ports 9040

Что означает – перебрасывать весь трафик идущий на 80-й порт (и не адресованый для 192.168.120.1) на порт 9040, на котором как раз и находится прозрачный прокси.

Тоже делаем и для dns запросов:

iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53

Остальной udp трафик прийдется либо пропускать без анонимизации, либо резать.

Собственно все. Весь трафик приходящий на роутер будет перенаправлен в сеть tor без каких – либо настроек компьютеров в вашей локальной сети! Все ваши действия в сети будут анонимны.



Вариант 2 

Доброго времени суток. Решил сделать вторую подсеть на OpenWrt с выходом через Tor. Но вот добиться работоспособности никак не получается, помогите с настройками.
Настройки файла /etc/tor/torrc


Software: Putty,WinSCP, прошивка OpenWrt Chaos Calmer r45676 версия ядра 3.18.11
Hardware: TP-Link-WR1043N/ND v2
Должны быть установлены следующие пакеты:
kmod-ipt-conntrack
kmod-ipt-conntrack-extra
kmod-ipt-core
kmod-ipt-extra
kmod-ipt-nat
kmod-ipt-nat-extra
iptables
iptables-mod-extra
iptables-mod-conntrack-extra
iptables-mod-nat-extra
tor
tor-fw-helper
tor-geoip
, если пакетов нет их нужно установить 
opkg install kmod-ipt-conntrack kmod-ipt-conntrack-extra kmod-ipt-core kmod-ipt-extra kmod-ipt-nat kmod-ipt-nat-extra iptables iptables-mod-extra iptables-mod-conntrack-extra iptables-mod-nat-extra tor tor-fw-helper tor-geoip

При возникновении ошибок из-за версий ядра
Collected errors:
* satisfy_dependencies_for: Cannot satisfy the following dependencies for kmod-xxxxxxx:
* kernel (= 2.6.32.27-1) * kernel (= 2.6.32.27-1) *
* opkg_install_cmd: Cannot install package kmod-xxxxxx.
Используем принудительную установку
opkg install kmod-ipt-conntrack kmod-ipt-conntrack-extra kmod-ipt-core kmod-ipt-extra kmod-ipt-nat kmod-ipt-nat-extra iptables iptables-mod-extra iptables-mod-conntrack-extra iptables-mod-nat-extra tor tor-fw-helper tor-geoip --force depends
Потом переносим пакеты в папку с соответвующей версие й ядра /lib/modules/xxx
Изменяем права доступа к папке /var/lib/tor 
# chmod -R 775 /var/lib/tor
Все папки и файлы создаваемые tor должны иметь такие же права.
Время должно быть синхронизировано по ntp как это сделать написано здесь: http://wiki.openwrt.org/doc/howto/ntp.client

Редактируем конфиги:
/etc/config/firewall
config zone
option name 'tor'
option network 'tor'
option input 'REJECT'
option forward 'REJECT'
option output 'ACCEPT'

config forwarding
option src 'tor'
option dest 'wan'

config rule
option name 'Allow DNS Queries'
option src 'tor'
option dest_port '53'
option proto 'tcpudp'
option target 'ACCEPT'

config rule
option name 'Allow DHCP request'
option src 'tor'
option src_port '67-68'
option dest_port '67-68'
option proto 'udp'
option target 'ACCEPT'

/etc/config/network
config interface 'tor'
option ifname 'wlan0-1'
option proto 'static'
option ipaddr '192.168.2.1'
option netmask '255.255.255.0'
/etc/config/wireless
config wifi-iface
option device 'radio0'
option network 'tor'
option mode 'ap'
option macaddr '00:01:4a:9f:6f:3c'
option encryption 'psk2'
option key '123456'
option ssid 'Tor_trans'

/etc/tor/torrc
VirtualAddrNetworkIPv4 10.192.0.0/10
AutomapHostsOnResolve 1
TransPort 9040
TransListenAddress 192.168.2.1
DNSPort 53
DNSListenAddress 192.168.2.1

Создаем файл tor.sh и кладем его в папку /etc/init.d/tor.sh
/etc/init.d/tor.sh
#!/bin/sh

# Tor's TransPort
_trans_port="9040"

# your internal interface
_int_if="wlan0-1"

iptables -F
iptables -t nat -F

iptables -t nat -A PREROUTING -i $_int_if -p udp --dport 53 -j REDIRECT --to-ports 53
iptables -t nat -A PREROUTING -i $_int_if -p tcp --syn -j REDIRECT --to-ports $_trans_port

Далее 
#/etc/init.d/tor.sh enable
#/etc/init.d/firewall reload
#/etc/tor/torrc
Проверяем по ссылке: https://check.torproject.org/?lang=ru



https://forum.openwrt.org/viewtopic.php?id=27354

HOWTO: Transparent TOR proxy
Using Openwrt as tansparent proxy to the TOR-network.

This project has been on my mind since a long time. 
I wanted to set up a free hotspot and share me broadband-connection, but I wanted to do it in a secure manner. I just want to avoid the police knocking on my door because someone did something "bad" using my hotspot. The best way I could come up with, was routing the traffic of the hotspot trough the tor-network http://www.torproject.org/
This has two advantages: 
Traffic is routed encryted trough the tor-network and reaches the net through an tor-exitnode, and there is no way to tell that the packets came from my hotspot. 
A client connected to my hotspot doesn't know anything about my networkstructure, my real ip, etc. so it provides more privacy for me.
I use a transparent proxy setup because I want to use a simple setup, especially for the user. A new client gets an IP-Address through DHCP, and can use the net. No need for any additional setup.  
So that's why I'm doing it, but I guess there are lots of other situations where a transparent tor proxy can be usefull.  
info about Tor:  http://www.torproject.org/ 
info about the transparent proxy feature of Tor: https://trac.torproject.org/projects/tor/wiki/TheOnionRouter/TransparentProxy I set up an "Anonymizing Middlebox"
Setup:
I used a wgt634u with a recent backfire-svn checkout (r24007) it's a broadcom chip and I run a linux-2.6 kernel. I guess the stable backfire-release and any other architekture should work too but your router should have at least 32MB RAM (my tor-daemon needs about 13MB RAM) and enough Flash (8MB are enough).
I use only the wifi ("ath0") interface with own firewall-zone "tor" and restricted the access to the dhcp-server and tor-proxy only. But it will work with "br-lan" as well.
You need to install the tor-package (available in the official openwrt-package-repository) and you need iptables-mod-nat and iptables-mod-nat-extra for the iptable-rules in /etc/firewall.user
so here are the relevant sections of my config files:
/etc/conf/network:
config interface tor
        option ifname   "ath0"
        option proto    static
        option ipaddr   192.168.2.1
        option netmask  255.255.255.0
/etc/config/dhcp:
config dhcp tor
    option interface    tor
    option start     100
    option stop    150
    option leasetime    12h
/etc/config/firewall:
config zone
        option name     tor
        option input    REJECT
        option output   ACCEPT
        option forward  REJECT
        option syn_flood 1
        option conntrack 1 #this setting is mandatory

#open the port of the DHCP-Server, so that the clients get an ip
config rule                                
        option src              tor        
        option proto            udp        
        option dest_port        67         
        option target           ACCEPT     
#TOR transparent-proxy-port (set in /etc/tor/torrc)                                           
config rule                                
        option src              tor        
        option proto            tcp        
        option dest_port        9040       
        option target           ACCEPT     
#TOR DNS-proxy-port (set in /etc/tor/torrc)                                            
config rule                                
        option src              tor        
        option proto            udp        
        option dest_port        9053       
        option target           ACCEPT
/etc/firewall.user:
iptables -t nat -A PREROUTING -i ath0 -p udp --dport 53 -j REDIRECT --to-ports 9053 #redirects all DNS-requests on the interface ath0 to the tor-daemon-dns-proxy-port
iptables -t nat -A PREROUTING -i ath0 -p tcp --syn -j REDIRECT --to-ports 9040 #redirects all tcp-requests on the interface ath0 to the tor-daemon-transparent-proxy-port
/etc/tor/torrc:
VirtualAddrNetwork 10.192.0.0/10             
AutomapHostsOnResolve 1                                              
TransPort 9040                                                          
TransListenAddress 192.168.2.1                                          
DNSPort 9053                                                              
DNSListenAddress 192.168.2.1
/etc/conf/wireless:
config wifi-device  wifi0
    option type     atheros
    option channel  auto

    # REMOVE THIS LINE TO ENABLE WIFI:
#    option disabled 1

config wifi-iface
    option device    wifi0
    option network    tor
    option mode    ap
    option ssid    'Hotspot'
    option encryption none
So clients can connect to the SSID "Hotspot" get an ip, and can surf the web, ALL tcp-connections are redirected through the tor-network, not only http. 
Other connections (including connections to local resources) are rejected.       

So everything is working so far. 
The next thing I want to achieve is running a open captive portal an this device so that I can give the users some information. About Tor, Openwrt and about why I'm running this hotspot.
I took a look at nodogsquash but its firewall-rules doesn't seem to work with the redirections for the transparent proxy.
So any feedback on the HOWTO, or ideas about setting up a captive portal in this case, are appreciated!

Прозрачный обход блокировок в домашней сетииз песочницы

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

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

Сама собой напрашивается мысль о том, чтобы обходить блокировку уже на домашнем маршрутизаторе.

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

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

Выход — перенаправлять в VPN/Tor только траффик блокируемых ресурсов, пропуская остальной обычным путем.

Внимание: данная схема не обеспечивает анонимности просмотра заблокированных сайтов: любая внешняя ссылка раскрывает ваш настоящий IP.

Конкретная реализация на OpenWrt приведена в конце статьи. Если не интересуют подробности и альтернативные варианты решения, то можно листать сразу до нее.

Туннелирование и перенаправление траффика в туннель


Настройка VPN или Tor'а сложностей представлять не должна. Tor должен быть настроен, как прозрачный proxy (либо настроить связку из tor и tun2socks). Т.к. конечной целью явлется обход блокировок ркн, то в конфиге Tor'а целесообразно запретить использование выходных узлов на территории РФ (<ExcludeExitNodes {RU}).

В Tor’а траффик перенаправляется правилом с REDIRECT’ом на порт прозрачного прокси в цепочкеPREROUTING таблицы nat netfilter’а.

Для перенаправления в VPN (или Tor + tun2socks) траффик маркируется в таблице mangle, метка затем используется для выбора таблицы маршрутизации, перенаправляющей траффик на соответствующий интерфейс.
В обоих случаях для классификации траффика используется ipset с хостами, подлежащими (раз)блокировке.

Формирование ipset c (раз)блокируемыми хостами


К сожалению, вариант «загнать все IP из реестра» в ipset не работает как хотелось бы: во-первых в списках присутствуют не все IP адреса блокируемых хостов, во-вторых в попытке уйти от блокировки IP адрес у ресурса может измениться (и провайдер об этом уже знает, а мы – еще нет), ну и в третьих – false positives для находящихся на том же shared hosting’е сайтов.

Городить огород с dpi того или иного вида не очень хочется: как-никак работать это должно на довольно слабом железе. Выход достаточно прост и в какой-то степени элегантен: dnsmasq (DNS сервер, который на маршрутизаторе скорее всего уже установлен) умеет при разрешении имен добавлять ip-адреса в соответствующий ipset (одноименная опция в конфиге). Как раз то, что нужно: вносим в конфиг все домены, которые необходимо разблокировать, и дальше по необходимости dnsmasq сам добавляет в ipset именно тот ip адрес, по которому будет идти обращение к заблокированному ресурсу.

У меня были сомнения, что dnsmasq запустится и будет нормально работать с конфигом в полдесятка тысяч строк (примерно столько записей в реестре после усушки и утряски), однако они к счастью оказались безосновательны.

Ложка дегтя в том, что при обновлении списка dnsmasq придется перезапускать, т.к. по SIGHUP он конфиг не перегружает.

Составление списка доменов


Должно происходить автоматически, насколько это возможно.

Первый вариант (который и реализован в примере): формировать список на основе единого реестра блокировок и обновлять его по cron'у.
Роскомнадзор широкой общественности реестр блокировок не предоставляет, однако мир не без добрых людей и есть минимум два ресурса, где с ним можно ознакомиться. И, что отлично, API у них тоже имеется. При разборе списка нужно учесть, что в списке доменных имен помимо собственно доменных имен присутствуют и IP адреса. Их нужно обрабатывать отдельно (или вообще на них забить: их примерно 0,1% от списка и врядли они ведут на интересующие вас ресурсы). Кириллические домены далеко не всегда представлены в punycode. Немалую часть списка занимают поддомены на одном домене второго уровня, указаны домены с www/без www и просто дублирующиеся записи. Все перечисленное в большей степени относится к списку от rublacklist.net (он в добавок еще и странно, местами некорректно, экранирован). Именно для него пришлось городить монструозный lua-script (приводится ниже), нормализующий и сжимающий список почти в два раза. C antizapret.info ситуация сильно лучше и можно было бы обойтись однострочником на awk.

Можно пойти другим путем: многие провайдеры при обращению к заблокированному ресурсу перенаправляют на заглушку об ограничении доступа. Например http://block.mts.ru/?host=<host>&url=<url>&params=<params>. Подменив с помощью того же dnsmasq (address=/block.mts.ru/192.168.1.1) A-запись block.mts.ru на адрес веб-сервера маршрутизатора (и разместив на нем несложный скрипт) можно локально формировать список запрошенных пользователями сети заблокированных ресурсов, добавлять их в конфиг dnsmasq, повторно делать nslookup (чтобы ip адрес добавился в ipset) и еще раз редиректить пользователя на первоначальный URL. Но необходимость каждый раз при этом перезапускать dnsmasq несколько расхолаживает. Да и работать будет только для http.

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

Дополнительные замечания


DNS серверы провайдера использовать в качестве апстрим серверов естественно не стоит. Ибо блокировка может произойти еще на стадии разрешения имени ресурса. Отдаст сервер провайдера на искомый адрес, что это CNAME block.mts.ru и все. Наиболее простое решение server=8.8.8.8, server=8.8.4.4. Модификации провайдерами DNS-ответов сторонних серверов лично я пока не наблюдал. В случае, если начнут — можно отправлять запросы доменов из запретного списка на другой апстрим (через VPN/Tor), однако без надобности я бы конфиг не раздувал.

При использовании Tor'a можно бонусом получить возможность серфинга по .onion сайтам: Tor при разрешении имени через встроенный dns-сервер отобразит его на виртуальный адрес из заранее заданного диапазона. Дальше нужно только перенаправить обращение к этому адресу на прокси Tor'а и voila. Но еще раз напомню, что анонимности подключение с избирательным туннелированием трафика не обеспечивает.

Реализация на OpenWrt (15.05)


Сам маршрутизатор должен быть не самый плохой, особенно при использовании Tor’а. MIPS 400MHz@32MB RAM это тот минимум, который стоит рассматривать.

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

Штатно в прошивках OpenWrt содержится урезанный dnsmasq, не умеющий ipset. Необходимо заменить его на dnsmasq-full.

Из пакетов, по умолчанию не присутствующих, так же потребуются ipsettor и tor-geoip.
Так же необходим либо пакет luasocket, либо (в режиме строгой экономии флеша) отдельно ltn12.luaв папке /usr/lib/lua. Для преобразования кириллических доменов из utf8 в punycode нужны idn.lua в /usr/lib/lua и пакет luabitop (либо отключить опции в конфиге скрипта).

Скрипт обновления списков блокировки

/usr/bin/rublupdate.lua
local config = {
    blSource = "antizapret", -- antizapret или rublacklist
    groupBySld = 32, -- количество поддоменов после которого в список вносится весь домен второго уровня целиком
    neverGroupMasks = { "^%a%a%a?.%a%a$" }, -- не праспространять на org.ru, net.ua и аналогичные
    neverGroupDomains = { ["livejournal.com"] = true, ["facebook.com"] = true , ["vk.com"] = true },
    stripWww = true,
    convertIdn = true,
    torifyNsLookups = false, -- отправлять DNS запросы заблокированных доменов через TOR
    blMinimumEntries = 1000, -- костыль если список получился короче, значит что-то пошло не так и конфиги не обновляем
    dnsmasqConfigPath = "/etc/runblock/runblock.dnsmasq",
    ipsetConfigPath = "/etc/runblock/runblock.ipset",
    ipsetDns = "rublack-dns",
    ipsetIp = "rublack-ip",
    torDnsAddr = "127.0.0.1#9053"
}


local function prequire(package)
    local result, err = pcall(function() require(package) end)
    if not result then
        return nil, err
    end
    return require(package) -- return the package value
end

local idn = prequire("idn")
if (not idn) and (config.convertIdn) then
    error("you need either put idn.lua (github.com/haste/lua-idn) in script dir  or set 'convertIdn' to false")
end

local http = prequire("socket.http")
if not http then
    local ltn12 = require("ltn12")
end
if not ltn12 then
    error("you need either install luasocket package (prefered) or put ltn12.lua in script dir")
end

local function hex2unicode(code)
    local n = tonumber(code, 16)
    if (n < 128) then
        return string.char(n)
    elseif (n < 2048) then
        return string.char(192 + ((n - (n % 64)) / 64), 128 + (n % 64))
    else
        return string.char(224 + ((n - (n % 4096)) / 4096), 128 + (((n % 4096) - (n % 64)) / 64), 128 + (n % 64))
    end
end

local function rublacklistExtractDomains()
    local currentRecord = ""
    local buffer = ""
    local bufferPos = 1
    local streamEnded = false
    return function(chunk)
        local retVal = ""
        if chunk == nil then
            streamEnded = true
        else
            buffer = buffer .. chunk
        end

        while true do
            local escapeStart, escapeEnd, escapedChar = buffer:find("\\(.)", bufferPos)
            if escapedChar then
                currentRecord = currentRecord .. buffer:sub(bufferPos, escapeStart - 1)
                bufferPos = escapeEnd + 1
                if escapedChar == "n" then
                    retVal = currentRecord
                    break
                elseif escapedChar == "u" then
                    currentRecord = currentRecord .. "\\u"
                else
                    currentRecord = currentRecord .. escapedChar
                end
            else
                currentRecord = currentRecord .. buffer:sub(bufferPos, #buffer)
                buffer = ""
                bufferPos = 1
                if streamEnded then
                    if currentRecord == "" then
                        retVal = nil
                    else
                        retVal = currentRecord
                    end
                end
                break
            end
        end
        if retVal and (retVal ~= "") then
            currentRecord = ""
            retVal = retVal:match("^[^;]*;([^;]+);[^;]*;[^;]*;[^;]*;[^;]*.*$")
            if retVal then
                retVal = retVal:gsub("\\u(%x%x%x%x)", hex2unicode)
            else
                retVal = ""
            end
        end
        return (retVal)
    end
end

local function antizapretExtractDomains()
    local currentRecord = ""
    local buffer = ""
    local bufferPos = 1
    local streamEnded = false
    return function(chunk)
        local haveOutput = 0
        local retVal = ""
        if chunk == nil then
            streamEnded = true
        else
            buffer = buffer .. chunk
        end
        local newlinePosition = buffer:find("\n", bufferPos)
        if newlinePosition then
            currentRecord = currentRecord .. buffer:sub(bufferPos, newlinePosition - 1)
            bufferPos = newlinePosition + 1
            retVal = currentRecord
        else
            currentRecord = currentRecord .. buffer:sub(bufferPos, #buffer)
            buffer = ""
            bufferPos = 1
            if streamEnded then
                if currentRecord == "" then
                    retVal = nil
                else
                    retVal = currentRecord
                end
            end
        end
        if retVal and (retVal ~= "") then
            currentRecord = ""
        end
        return (retVal)
    end
end

local function normalizeFqdn()
    return function(chunk)
        if chunk and (chunk ~= "") then
            if config["stripWww"] then chunk = chunk:gsub("^www%.", "") end
            if idn and config["convertIdn"] then chunk = idn.encode(chunk) end
            if #chunk > 255 then chunk = "" end
            chunk = chunk:lower()
        end
        return (chunk)
    end
end

local function cunstructTables(bltables)
    bltables = bltables or { fqdn = {}, sdcount = {}, ips = {} }
    local f = function(blEntry, err)
        if blEntry and (blEntry ~= "") then
            if blEntry:match("^%d+%.%d+%.%d+%.%d+$") then
                -- ip адреса - в отдельную таблицу для iptables
                if not bltables.ips[blEntry] then
                    bltables.ips[blEntry] = true
                end
            else
                -- как можем проверяем, FQDN ли это. заодно выделяем домен 2 уровня (если в bl станут попадать TLD - дело плохо :))
                local subDomain, secondLevelDomain = blEntry:match("^([a-z0-9%-%.]-)([a-z0-9%-]+%.[a-z0-9%-]+)$")
                if secondLevelDomain then
                    bltables.fqdn[blEntry] = secondLevelDomain
                    if 1 > 0 then
                        bltables.sdcount[secondLevelDomain] = (bltables.sdcount[secondLevelDomain] or 0) + 1
                    end
                end
            end
        end
        return 1
    end
    return f, bltables
end

local function compactDomainList(fqdnList, subdomainsCount)
    local domainTable = {}
    local numEntries = 0
    if config.groupBySld and (config.groupBySld > 0) then
        for sld in pairs(subdomainsCount) do
            if config.neverGroupDomains[sld] then
                subdomainsCount[sld] = 0
                break
            end
            for _, pattern in ipairs(config.neverGroupMasks) do
                if sld:find(pattern) then
                    subdomainsCount[sld] = 0
                    break
                end
            end
        end
    end
    for fqdn, sld in pairs(fqdnList) do
        if (not fqdnList[sld]) or (fqdn == sld) then
            local keyValue;
            if config.groupBySld and (config.groupBySld > 0) and (subdomainsCount[sld] > config.groupBySld) then
                keyValue = sld
            else
                keyValue = fqdn
            end
            if not domainTable[keyValue] then
                domainTable[keyValue] = true
                numEntries = numEntries + 1
            end
        end
    end
    return domainTable, numEntries
end

local function generateDnsmasqConfig(configPath, domainList)
    local configFile = assert(io.open(configPath, "w"), "could not open dnsmasq config")
    for fqdn in pairs(domainList) do
        if config.torifyNsLookups then
            configFile:write(string.format("server=/%s/%s\n", fqdn, config.torDnsAddr))
        end
        configFile:write(string.format("ipset=/%s/%s\n", fqdn, config.ipsetDns))
    end
    configFile:close()
end

local function generateIpsetConfig(configPath, ipList)
    local configFile = assert(io.open(configPath, "w"), "could not open ipset config")
    configFile:write(string.format("flush %s-tmp\n", config.ipsetIp))
    for ipaddr in pairs(ipList) do
        configFile:write(string.format("add %s %s\n", config.ipsetIp, ipaddr))
    end
    configFile:write(string.format("swap %s %s-tmp\n", config.ipsetIp, config.ipsetIp))
    configFile:close()
end

local retVal, retCode, url

local output, bltables = cunstructTables()
if config.blSource == "rublacklist" then
    output = ltn12.sink.chain(ltn12.filter.chain(rublacklistExtractDomains(), normalizeFqdn()), output)
    url = "http://reestr.rublacklist.net/api/current"
elseif config.blSource == "antizapret" then
    output = ltn12.sink.chain(ltn12.filter.chain(antizapretExtractDomains(), normalizeFqdn()), output)
    url = "http://api.antizapret.info/group.php?data=domain"
else
    error("blacklist source should be either 'rublacklist' or 'antizapret'")
end

if http then
    retVal, retCode = http.request { url = url, sink = output }
else
    retVal, retCode = ltn12.pump.all(ltn12.source.file(io.popen("wget -qO- " .. url)), output)
end

if (retVal == 1) and ((retCode == 200) or (http == nil)) then
    local domainTable, recordsNum = compactDomainList(bltables.fqdn, bltables.sdcount)
    if recordsNum > config.blMinimumEntries then
        generateDnsmasqConfig(config.dnsmasqConfigPath, domainTable)
        generateIpsetConfig(config.ipsetConfigPath, bltables.ips)
        print(string.format("blacklists updated. %d entries.", recordsNum))
        os.exit(0)
    end
end
os.exit(1)

Настройки dnsmasq

/etc/dnsmasq.conf
server=/onion/127.0.0.1#9053
ipset=/onion/onion

conf-file=/etc/runblock/runblock.dnsmasq

Добавить в секцию dnsmasq /etc/config/dhcp
    list server '8.8.8.8'
    list server '8.8.4.4'
    list rebind_domain 'onion'

Настройки netfilter

Добавить в /etc/config/firewall
config ipset
 option name 'rublack-dns'
 option storage 'hash'
 option match 'dest_ip'
 option timeout '86400'

config ipset
 option name 'rublack-ip'
 option storage 'hash'
 option match 'dest_ip'

config ipset
 option name 'rublack-ip-tmp'
 option storage 'hash'
 option match 'dest_ip'

config ipset
 option name 'onion'
 option storage 'hash'
 option match 'dest_ip'
 option timeout '86400'

config redirect
 option name 'torify-blocked-dns'
 option src 'lan'
 option proto 'tcp'
 option ipset 'rublack-dns'
 option dest_port '9040'
 option dest 'lan'

config redirect
 option name 'torify-blocked-ip'
 option src 'lan'
 option proto 'tcp'
 option ipset 'rublack-ip'
 option dest_port '9040'
 option dest 'lan'

config redirect
 option name 'torify-onion'
 option src 'lan'
 option proto 'tcp'
 option ipset 'onion'
 option dest_port '9040'
 option dest 'lan'

Добавить в /etc/firewall.user
cat /etc/runblock/runblock.ipset | ipset restore

Настройки Tor

/etc/torrc
User tor
PidFile /var/run/tor.pid
DataDirectory /var/lib/tor    
ExcludeExitNodes {RU}
VirtualAddrNetwork 10.254.0.0/16  # виртуальные адреса для .onion ресурсов 
AutomapHostsOnResolve 1
TransPort 9040
TransListenAddress 127.0.0.1
TransListenAddress 192.168.1.1    #адрес LAN интерфейса
DNSPort 9053
DNSListenAddress 127.0.0.1
#AvoidDiskWrites 1 # в OpenWrt /var и так в RAM (tmpfs) не уверен, что в опции есть смысл

Осталось создать каталог /etc/runblock, разово запустить вручную скрипт lua /usr/bin/rublupdate.lua, убедиться, что он отработал без ошибок, добавить его в cron (пару раз в сутки — вполне достаточно) и забыть о роскомнадзоре. Ну до тех пор, пока не начнут блокировать тор, или сайты, публикующие реестр).

No comments: