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

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

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

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

Собственно, поднять на маршрутизаторе и гонять весь траффик через 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.

Из пакетов, по умолчанию не присутствующих, так же потребуются ipset, tor и 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 (пару раз в сутки — вполне достаточно) и забыть о роскомнадзоре. Ну до тех пор, пока не начнут блокировать тор, или сайты, публикующие реестр).
Поделиться публикацией

Комментарии 45

    +3
    Класс!
    Как бы это еще все под Mikrotik заточить :)
      0
      с микротиком никогда дела не имел, но разве там не тот же линукс с тем же netfilter и iproute?
        0
        Там tor поднять можно только на виртуальном OpenWRT — а сборка онная с интегрированным ТORом уже давно не обновлялась.
        Ну и плюс я не так силён в микротике, что бы понять что куда заводить…
        Думаю «профи» смогут это сделать проще… Ну или… впринципе у меня на домашнем сервере поднят tor, надо написать скрипт обновления запрещщенных узлов и как то научить микротик при переходе на такой узел редиректить на сервер с TORом.
          +1
          Там линукс, но не тот. Без доступа к шеллу, и всё там «чуть-чуть» другое. Есть конечно HunterTik, превращающий MikroTik в элегантные шорты полноценный линукс, но это частный случай для x86.

          *upd* Ну вот почему для отхабренных не работает html-форматирование?
            +3
            *upd* Ну вот почему для отхабренных не работает html-форматирование?


            Разжигание классовой ненависти между отхабренными и захабренными же. Думаете захабренные испытывают какие-то теплые чувства когда, например, вместо кликов по ссылкам и просмотра картинок, размещенных отхабренными, вынуждены выделять/копировать/вставлять их URLы в адресную строку?
              0
              *upd* Ну вот почему для отхабренных не работает html-форматирование?

              Потому что отхабренный, раз уж был отхабрен, «по определению» (Хабра) неспособен разместить ссылку или картинку, которая не оскорбит чувств захабренных.
            0
            Я делал у себя все на сервере. i2p и tor через privoxy. В squid по списку в privoxy или напрямую. На микротики прозрачный на их Web-proxy, а в нем указан squid. Ну а дальше lightsquid и sqstat для наглядности.
              0
              Список делал на основе zapret-info/z-i вот скриптик:
              #!/bin/bash
              # git clone https://github.com/zapret-info/z-i.git
              cd z-i > /dev/null 2>&1;
              git pull > /dev/null 2>&1;
              URL_LIST=`cat dump.csv | csvtool -t ';' col 2 - | sed -e '/^[0-9\.]*$/d' -e '/^[0-9\.a-z-]*$/!d' | sed -e 's/^.*\.\([a-z0-9\-]\{2,63\}\.[a-z0-9\-]\{2,63\}\)$/\1/' | sort | uniq 2>/dev/null`;
              cat /dev/null > /etc/squid3/eais.list 2>/dev/null;
              for URL in $URL_LIST; do
                      echo $URL | sed -e 's/\./\\\./g' -e 's/^/^http(s)?:\\\/\\\/([a-zA-Z0-9]+\\.)?/g' -e 's/$/.*$/g' >> /etc/squid3/eais.list 2>/dev/null;
              done;
              /usr/sbin/squid3 -k reconfigure
              


              получаем файл вида:
              ^http(s)?:\/\/([a-zA-Z0-9]+\.)?habrahabr\.ru.*$

              в конфиге squid:
              acl eais_list url_regex "/etc/squid3/eais.list"

              acl i2phost dstdomain .i2p
              acl torhost dstdomain .onion

              cache_peer 127.0.0.1 parent 8118 0 no-query proxy-only
              cache_peer_access 127.0.0.1 allow i2phost
              cache_peer_access 127.0.0.1 allow torhost
              cache_peer_access 127.0.0.1 allow eais_list
              cache_peer_access 127.0.0.1 deny all


              Минус:

              Многие сайты не посмотреть.
              0
              Как закрыли рутрекер — запилил себе нечто подобное на микротике, автоматическим добавлением запрещенных сайтов
              –1
              Модификации провайдерами DNS-ответов сторонних серверов лично я пока не наблюдал.
              А я вот наблюдаю :(
              Вернее, насколько понял, блокируется трафик с чужих днс-серверов и да, сайты блочатся по IP.
              Провайдер АлмаТВ, Астана.
                0
                Мечта лентяя.
                «Вот если бы сделать openSource проектик, который устанавливается через юм или apt-get в один клик...»


                Спасибо за пост — в избранное!
                  0
                  Ещё бы адаптированную для dd-wrt инструкцию. Как я понимаю, одним веб-интерфейсом там не обойтись?
                  0
                  bitbucket.org/ValdikSS/antizapret
                  Только ipset нет, а все добавляется в таблицу iptables.
                  Ну и парсить удобней csv, а не сайт:
                  github.com/zapret-info/z-i
                    0
                    iptables на >10000 записей в одной табличке подряд — тот ещё тормоз. Только для ручной выжимки из реестра с сотней нужных сайтов из горы мусора, которая там лежит, и сгодится.
                      0
                      В общем-то, вы правы, но у меня все это крутится в контейнере OpenVZ без поддержки ipset, поэтому так.
                        +2
                        ну, тогда можно хотя бы дерево из табличек построить. Если по первым двум октетам раскидать — уже хорошо будет.
                      0
                      Если правильно понял логику — все хосты из списка ркн предварительно резолвятся и все получившиеся айпи добавляются в правила?
                      Не очень здорово это. Вот попал в список к примеру какой-нибудь gde-kupit-veschestva.livejournal.com (который очень врядли когда-либо будет вами посещен) и что из-за этого — весь lj через tor читать? Занести lj в белый список тоже так себе вариант: тогда не выйдет прочесть бложик какого-нибудь забаненого борца с системой, если вдруг захочется.
                      По хорошему — нужно разбирать трафик на L7. Но это не совсем то, с чем без потерь производительности справится бытовой маршрутизатор за 50-100$.
                      Добавлять в список хосты после первого разрешения имени заблокированного ресурса — вполне работающий костыль разумный компромисс между избирательностью и скоростью работы.
                        0
                        весь lj через tor читать?
                        Да. Это универсальное решение, а некоторые провайдеры все ещё блокируют по IP.
                        Если что, это исходники antizapret.prostovpn.org
                          0
                          Для публичного VPN-сервиса решение конечно же вполне оправданное: пользователей там далеко не три с половиной и оптимизация «не вносить в список, пока не обращались» особо много не наоптимизирует.
                          0
                          Делал по L7 на микротике (есть в моих постах) там так же создать список ip адресов и только их отрабатывать используя L7. Работает нормально на MikroTik rb951g
                            0
                            Не совсем так. Все хосты резолвятся и HTTP трафик перенаправляется на DPI. Если там тоже совпадает — тогда блокируют. По HTTPS либо не трогают, либо в наглую творят MITM.
                              0
                              Вот как еще можно HTTPS.
                                0
                                Красиво, но, если я правильно понял, все равно заблокирует весь сервер, так как host и путь передаются уже внутри SSL.
                                  0
                                  Если я все правильно понял, то эта техника эксплуатирует SNI: технологию, фактически придуманную для хостинга нескольких https сайтов (с разными сертификатами) на одном IP.
                                  И соответственно имя сервера (строго говоря это не HTTP host, но в мирной жизни — должны совпадать) передается ДО установления ssl-сессии.
                                    0
                                    Согласен, таким образом мы прибьем только один сайт, но весь.
                                      0
                                      это лучше, чем ничего
                          0
                          вообще мне представляется достаточно здравой идея не использовать встроенный флеш для регулярно перезаписываемых данных


                          Мне представляется, что для часто обновляемых данных имеет смысл задействовать tmpfs, тем более, что в OpenWRT он уже смонтирован. При включении маршрутизатора, сразу после установления внешнего канала, можно скачать и обработать список на /var
                            0
                            А где взять dnsmasq-full? Штатный пакетный менеджер openwrt не знает про такое…
                              0
                              Возможно ли все это применить для Tomatousb?
                                0
                                Конечно возможно!
                                0
                                Объясните пожалуйста что делает следующая строка, из файла /etc/firewall.user?

                                cat /etc/runblock/runblock.ipset | ipset restore

                                Файл runblock.ipset состоит из списка ип адресов. Правильно ли я понимаю что все адреса из этого списка будут разблокированы, т.е. переадресованны в сеть tor?
                                  0
                                  Объясните пожалуйста что делает следующая строка, из файла /etc/firewall.user?
                                  В OpenWRT /etc/firewall.user — это обычный shell-скрипт, исполняемый при (пере)запуске брандмауэра.
                                  Упомянутая строчка, очевидно, читает /etc/runblock/runblock.ipset и скармливает его ipset restore

                                  Файл runblock.ipset состоит из списка ип адресов.
                                  Не совсем. Он состоит из команд ipset, по большей части
                                  add %IpSetName% %IpAddress%

                                  Правильно ли я понимаю что все адреса из этого списка будут разблокированы
                                  Да, трафик к адресам из %IpSetName% перенаправляется в Tor

                                    0
                                    Спасибо! Разобрался, применил для прошивки Tomato, все работает!
                                      0
                                      Если не сложно, то можно подробнее о реализации на прошивке Tomato?
                                        +1

                                        Advanced > DHCP/DNC > Dnsmasq Custom configuration


                                        server=/onion/127.0.0.1#9053
                                        ipset=/onion/onion
                                        conf-file=/jffs/runblock/runblock.dnsmasq

                                        Administration > Scripts


                                        Init


                                        # эта строку можно опустить, если не используется entware
                                        echo "LABEL=ENTWARE /opt ext3 rw,noatime 1 1" >> /etc/fstab
                                        
                                        modprobe ip_set
                                        modprobe ip_set_hash_ip
                                        modprobe ip_set_hash_net
                                        modprobe ip_set_bitmap_ip
                                        modprobe ip_set_list_set
                                        modprobe xt_set
                                        
                                        ipset -N rublack-dns iphash
                                        ipset -N rublack-ip iphash
                                        ipset -N rublack-ip-tmp iphash
                                        ipset -N onion iphash
                                        
                                        cat /jffs/runblock/runblock.ipset | ipset restore

                                        Firewall


                                        iptables -t nat -I PREROUTING -i br0 -p tcp -m set --match-set rublack-dns dst -j REDIRECT --to-ports 9040
                                        iptables -t nat -I PREROUTING -i br0 -p tcp -m set --match-set rublack-ip dst -j REDIRECT --to-ports 9040
                                        iptables -t nat -I PREROUTING -i br0 -p tcp -m set --match-set onion dst -j REDIRECT --to-ports 9040

                                        WAN Up


                                        # Автозапуск тора средствами томато у меня отключён, и поэтому запускается здесь
                                        if [ $(ps | grep '[t]or -f' | wc -l) == 0 ]; then
                                         tor -f /jffs/etc/tor.conf
                                        fi

                                        То что находится в папке jffs
                                        ps: файл runblock.dnsmasq, я обновлял вручную, без всяких авто скриптов.

                                          +1

                                          Вообще томато все это уже умеет делать из коробки с VPN (VPN Tunelling > OpenVPN Client > Routing Policy). Если интересно смотрите скрип /usr/sbin/vpnrouting case 3. Можно все тоже самое скопипастить но вместо vpn подставить tor.


                                          Забыл упомянуть, что все это я тестировал на Tomato Version 1.28 by shibby

                                  0
                                  Уважаемый автор, этот мануал ещё актуален или что-то изменилось? Настроил всё как написано, но заблокированные ресурсы не открываются.
                                    0
                                    Вполне актуален, чему там ломаться? Tor пока не блокируют, и antizapret и rublacklist списки отдают.

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

                                    Как ресурсы не открываются-то? С заглушкой провайдера, или с таймаутом?
                                    Если с таймаутом — проблемы в работе Tor.
                                    Если с заглушкой — либо не скачались списки, либо не настроен iptables.

                                      0
                                      Ресурсы вообще никакие не открываются, браузер показывает ошибку DNS. Но пингуется любой удаленный сайт, если по IP. Настраивал по порядку, указанному в статье, как есть. Скрипт выполняется, но не всегда с первого раза — часто ругается на что-то с «not enough memory», потом со второго-третьего раза, вроде бы, обновляет. Роутер — TP-Link TL-WR1043N/ND v2, LEDE Reboot 17.01.4.
                                      0
                                      TransListenAddress вроде как deprecated, попробуйте TransPort 192.168.1.1:9040. У меня взлетело с таким конфигом
                                      0
                                      Роутер под LEDE
                                      При запуске скрипта ругается:
                                      /usr/bin$ /usr/bin/rublupdate.lua
                                      /usr/bin/rublupdate.lua: local: line 1: not in a function
                                      /usr/bin/rublupdate.lua: line 2: blSource: not found
                                      /usr/bin/rublupdate.lua: line 3: groupBySld: not found
                                      /usr/bin/rublupdate.lua: line 4: neverGroupMasks: not found
                                      /usr/bin/rublupdate.lua: line 5: neverGroupDomains: not found
                                      /usr/bin/rublupdate.lua: line 6: stripWww: not found
                                      /usr/bin/rublupdate.lua: line 7: convertIdn: not found
                                      /usr/bin/rublupdate.lua: line 8: torifyNsLookups: not found
                                      /usr/bin/rublupdate.lua: line 9: blMinimumEntries: not found
                                      /usr/bin/rublupdate.lua: line 10: dnsmasqConfigPath: not found
                                      /usr/bin/rublupdate.lua: line 11: ipsetConfigPath: not found
                                      /usr/bin/rublupdate.lua: line 12: ipsetDns: not found
                                      /usr/bin/rublupdate.lua: line 13: ipsetIp: not found
                                      /usr/bin/rublupdate.lua: line 14: torDnsAddr: not found
                                      /usr/bin/rublupdate.lua: line 15: syntax error: unexpected "}"

                                      Куда копать?
                                        0
                                        Куда копать?
                                        есть подозрение, что в сторону line ending. Емнип lua на openwrt настойчиво хочет только CR (без LF)
                                          0
                                          У меня скрипт стал исправно запускаться при добавлении первой строки:
                                          #!/usr/bin/lua


                                          Собсно потому и написал про «входной тест» — думал вы специально первую строку опустили…
                                        0
                                        … первая строка опущена в коде скрипта как «входной тест» ;)?

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое