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

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

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

Собственно, поднять на маршрутизаторе и гонять весь траффик через 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 (пару раз в сутки — вполне достаточно) и забыть о роскомнадзоре. Ну до тех пор, пока не начнут блокировать тор, или сайты, публикующие реестр).

Комментарии (26)


  1. Alkop
    11.11.2015 16:32
    +3

    Класс!
    Как бы это еще все под Mikrotik заточить :)


    1. Zolg
      11.11.2015 16:37

      с микротиком никогда дела не имел, но разве там не тот же линукс с тем же netfilter и iproute?


      1. Alkop
        11.11.2015 17:24

        Там tor поднять можно только на виртуальном OpenWRT — а сборка онная с интегрированным ТORом уже давно не обновлялась.
        Ну и плюс я не так силён в микротике, что бы понять что куда заводить…
        Думаю «профи» смогут это сделать проще… Ну или… впринципе у меня на домашнем сервере поднят tor, надо написать скрипт обновления запрещщенных узлов и как то научить микротик при переходе на такой узел редиректить на сервер с TORом.


      1. k0ldbl00d
        11.11.2015 17:33
        +1

        Там линукс, но не тот. Без доступа к шеллу, и всё там «чуть-чуть» другое. Есть конечно HunterTik, превращающий MikroTik в элегантные шорты полноценный линукс, но это частный случай для x86.

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


        1. slonopotamus
          11.11.2015 20:24
          +3

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


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


        1. Lure_of_Chaos
          12.11.2015 12:31

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

          Потому что отхабренный, раз уж был отхабрен, «по определению» (Хабра) неспособен разместить ссылку или картинку, которая не оскорбит чувств захабренных.


    1. chelaxe
      11.11.2015 17:08

      Я делал у себя все на сервере. i2p и tor через privoxy. В squid по списку в privoxy или напрямую. На микротики прозрачный на их Web-proxy, а в нем указан squid. Ну а дальше lightsquid и sqstat для наглядности.


      1. chelaxe
        12.11.2015 08:44

        Список делал на основе 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


        Минус:

        Многие сайты не посмотреть.


  1. toxicdream
    11.11.2015 17:18
    -1

    Модификации провайдерами DNS-ответов сторонних серверов лично я пока не наблюдал.
    А я вот наблюдаю :(
    Вернее, насколько понял, блокируется трафик с чужих днс-серверов и да, сайты блочатся по IP.
    Провайдер АлмаТВ, Астана.


  1. PavelMSTU
    11.11.2015 17:47

    Мечта лентяя.

    «Вот если бы сделать openSource проектик, который устанавливается через юм или apt-get в один клик...»


    Спасибо за пост — в избранное!


  1. ProRunner
    11.11.2015 20:08

    Ещё бы адаптированную для dd-wrt инструкцию. Как я понимаю, одним веб-интерфейсом там не обойтись?


  1. ValdikSS
    11.11.2015 20:45

    bitbucket.org/ValdikSS/antizapret
    Только ipset нет, а все добавляется в таблицу iptables.
    Ну и парсить удобней csv, а не сайт:
    github.com/zapret-info/z-i


    1. mickvav
      11.11.2015 21:24

      iptables на >10000 записей в одной табличке подряд — тот ещё тормоз. Только для ручной выжимки из реестра с сотней нужных сайтов из горы мусора, которая там лежит, и сгодится.


      1. ValdikSS
        11.11.2015 21:25

        В общем-то, вы правы, но у меня все это крутится в контейнере OpenVZ без поддержки ipset, поэтому так.


        1. mickvav
          11.11.2015 21:27
          +2

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


    1. Zolg
      12.11.2015 10:57

      Если правильно понял логику — все хосты из списка ркн предварительно резолвятся и все получившиеся айпи добавляются в правила?
      Не очень здорово это. Вот попал в список к примеру какой-нибудь gde-kupit-veschestva.livejournal.com (который очень врядли когда-либо будет вами посещен) и что из-за этого — весь lj через tor читать? Занести lj в белый список тоже так себе вариант: тогда не выйдет прочесть бложик какого-нибудь забаненого борца с системой, если вдруг захочется.
      По хорошему — нужно разбирать трафик на L7. Но это не совсем то, с чем без потерь производительности справится бытовой маршрутизатор за 50-100$.
      Добавлять в список хосты после первого разрешения имени заблокированного ресурса — вполне работающий костыль разумный компромисс между избирательностью и скоростью работы.


      1. ValdikSS
        12.11.2015 11:00

        весь lj через tor читать?
        Да. Это универсальное решение, а некоторые провайдеры все ещё блокируют по IP.
        Если что, это исходники antizapret.prostovpn.org


        1. Zolg
          12.11.2015 11:14

          Для публичного VPN-сервиса решение конечно же вполне оправданное: пользователей там далеко не три с половиной и оптимизация «не вносить в список, пока не обращались» особо много не наоптимизирует.


      1. chelaxe
        12.11.2015 12:56

        Делал по L7 на микротике (есть в моих постах) там так же создать список ip адресов и только их отрабатывать используя L7. Работает нормально на MikroTik rb951g


      1. zabbius
        12.11.2015 14:30

        Не совсем так. Все хосты резолвятся и HTTP трафик перенаправляется на DPI. Если там тоже совпадает — тогда блокируют. По HTTPS либо не трогают, либо в наглую творят MITM.


        1. chelaxe
          12.11.2015 14:42

          Вот как еще можно HTTPS.


          1. zabbius
            12.11.2015 16:38

            Красиво, но, если я правильно понял, все равно заблокирует весь сервер, так как host и путь передаются уже внутри SSL.


            1. Zolg
              12.11.2015 16:58

              Если я все правильно понял, то эта техника эксплуатирует SNI: технологию, фактически придуманную для хостинга нескольких https сайтов (с разными сертификатами) на одном IP.
              И соответственно имя сервера (строго говоря это не HTTP host, но в мирной жизни — должны совпадать) передается ДО установления ssl-сессии.


              1. zabbius
                12.11.2015 17:30

                Согласен, таким образом мы прибьем только один сайт, но весь.


                1. chelaxe
                  12.11.2015 18:13

                  это лучше, чем ничего


  1. grumbler66rus
    17.11.2015 10:31

    вообще мне представляется достаточно здравой идея не использовать встроенный флеш для регулярно перезаписываемых данных


    Мне представляется, что для часто обновляемых данных имеет смысл задействовать tmpfs, тем более, что в OpenWRT он уже смонтирован. При включении маршрутизатора, сразу после установления внешнего канала, можно скачать и обработать список на /var