Чистим интернет от назойливой рекламы (AD Blocker для MikroTik)
Внимание! Данный пост был опубликован более года назад и, возможно, уже утратил свою былую актуальность. Но это не точно.

Данная статья является копией публикации на хабре (написанной мной же).

Данная статья является логическим завершением небольшой дискуссии с тов. vvzvlad, которая развернулась под топиком “Чистим домашний интернет от очень назойливой рекламы”, где автор с помощью wget, sed и cron на OpenWRT успешно сливает файлы рекламных хостов, парсит и подсовывает dns-серверу dnsmasq.

Переадресовывая клиента при запросе “рекламного” домена, например, на loopback (127.0.0.1 - 127.255.255.255), вместо котента рекламы клиент получит благодатное “ничего” (разумеется, при условии, что у нас не работает локальный веб-сервер который слушает локалхост). Механизм фильтрации довольно старый и не лишен недостатков. Например, нельзя указать маски хостов (*.ad-domain.tld) или “вырезать” рекламу, баннеры которой хостятся на запрашиваемых ресурсах. Но зато не привязан к какому-то либо протоколу и довольно прост в эксплуатации. Более того, если его использовать, например, на домашнем или офисном маршрутизаторе, который используется в качестве DNS сервера, реклама успешно порежется на всех гаджетах, где IP нашей железки прописан первым в качестве DNS сервера.

Но что если у нас вместо роутера с кастомной прошивкой используется.. MikroTik (RouterOS), функционал которого накладывает некоторые ограничения? Под катом вы узнаете каким образом удалось успешно “сконвертировать” файл хостов в пригодный для него формат, как автоматизировать это дело и что для хабралюдей в качестве бонуса был создан небольшой сервис как раз для автоматизации этого процесса (маленький, абсолютно бесплатный и с открытыми исходниками).

Настраиваем MikroTik

Настройка самой железки вряд ли вызовет какие-либо трудности. Укажем IP маршрутизатора в качестве первого DNS сервера в DHCP - “IP” → “DHCP Server” → “Networks” → %default config%. Первым прописываем IP самого MikroTik-а, нажимаем “OK”:

DNS сервер работает “из коробки”, и можно даже не менять его стоковые настройки:

Единственная интересная для нас кнопка - “Static” (“Статические маршруты”), в которых мы должны прописать “рекламные” домены, указав куда такие запросы перенаправлять.

Конвертируем файл хостов

Файл хостов имеет формат:

# Any comments
127.0.0.1 localhost
127.0.0.1 domain-a.tld
127.0.0.1 domain-b.tld

Формат скрипта для MikroTik, использующий статические DNS маршруты:

# Any comments
/ip dns static
add address=127.0.0.1 name=localhost
add address=127.0.0.1 name=domain-a.tld
add address=127.0.0.1 name=domain-b.tld

Для получения актуальных списков первого и преобразования их во второй мы выполним простые команды в шелле на, например, десктопе (bash):

  1. Скачиваем списки и аккуратно складываем их под именами ./hosts_list.1, ./hosts_list.2 и т.д.:

    $ src=('http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext' 'https://adaway.org/hosts.txt'); i=0; for file in ${src[*]}; do i=$((i+1)); wget --no-check-certificate -O "./hosts_list.$i" "$file"; done;
    
  2. Грепаем всё что начинается на 127.0.0.1, удаляем комменты, оставляем только имена доменов, убираем дубликаты, убираем пустые строки и оформляем каждый домен в виде команды для импорта:

    $ in="./hosts_list.*" && out="./adblock_dns.rsc" && host='127.0.0.1'; echo "/ip dns static" > $out && grep '127.0.0.1 ' $in | grep -v '^#' | cut -d' ' -f 2 | sort -u | grep . | sed "s/^/add address=$host name=/" >> $out && rm -f $in; wc -l $out;
    

Да, надо бы использовать регулярку и вообще всё переделать, но самое главное - я передал тебе свою мысль, дальше дело за тобой

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

Импортируем в MikroTik

Для того, чтобы импортировать полученный файл, мы цепляемся к маршрутизатору по ftp, заливаем adblock_dns.rsc, после чего цепляемся по ssh или открываем терминал, в котором выполняем:

  1. Делаем резервную копию (полученный бэкап лучше сохранить у себя на машине):

    /system backup save
    
  2. Если у тебя в таблице нет важных маршрутов, то можем грохнуть все имеющиеся записи:

    /ip dns static remove [/ip dns static find]
    
  3. Импортируем загруженный файл:

    /import adblock_dns.rsc
    
  4. Убираем за собой:

    /file remove adblock_dns.rsc
    

Теперь можешь (опционально) перезагрузить MikroTik, проверить корректность получения адресов DNS серверов по DHCP и попытаться открыть какой-либо ресурс, который ранее был наводнен рекламой - её должно стать ощутимо меньше.

Эмпирически доказано что при импортировании ~5500 записей Mikrotik hAP lite (650MHz @ RAM 32 Mb) встает почти колом при попытке в GUI открыть таблицу статических маршрутов. Перезагрузка помогает

Автоматизация

Один из простейших вариантов автоматизации - скрипт на баше, который по крону поддерживает в актуальном состоянии adblock_dns.rsc, находящийся в открытом доступе по, скажем, ftp. Его основа была уже описана выше, осталось только оформить его по твоему усмотрению (необходима отдельная машина).

Ещё один вариант - это использование MetaRouter на самом или соседнем MikroTik-е, на котором установлен тот же OpenWRT (избавляемся от необходимости в отдельной машине; этот вариант не расписан подробно за неимением достойного MikroTik-а под руками).

Второй скрипт (на MikroTik) забирает его (adblock_dns.rsc), делает бэкап и, если и файл скачался успешно, и бэкап сохранился успешно, его импортирует, предварительно (внимание) грохнув все имеющиеся маршруты:

:local hostScriptUrl "ftp://user:[email protected]_host:21/adblock_dns.rsc";

:local scriptName "adblock_dns.rsc";
:local backupFileName "before_stopad";
:local logPrefix "[StopAD]";

do {
 /tool fetch mode=ftp url=$hostScriptUrl dst-path=("./".$scriptName);
 :if ([:len [/file find name=$scriptName]] > 0) do={
   /system backup save name=$backupFileName;
   :delay 1s;
   :if ([:len [/file find name=($backupFileName.".backup")]] > 0) do={
     /ip dns static remove [/ip dns static find];
     /import file-name=$scriptName;
     /file remove $scriptName;
     :log info "$logPrefix AD block script imported, backup file (\"$backupFileName.backup\") created";
   } else={
     :log warning "$logPrefix Backup file not created, importing AD block script stopped";
   }
 } else={
   :log warning "$logPrefix Backup file not downloaded, script stopped";
 }
} on-error={
 :log warning "$logPrefix AD block script download FAILED";
};

Бонус для хабралюдей

А что делать, если у тебя дома/офисе/гараже стоит MikroTik и хочется или просто порезать рекламу, или автоматизировать обновление хостов, не поднимая никакие метароутеры и хосты с файлами по ftp, вот чтоб просто и без заморочек?

Правильно, нужно чтобы это поднял кто то за тебя. И в качестве бонуса - такая штука уже поднята.

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

  • Выбор из списка публичных файлов-хостов (спасибо AdAway для Android);
  • Хочется указать свои источники? Без проблем! (можно, например, указать три публичных хост-файла и два своих, расположенных на том-же gist.github.com, получив таким образом довольное удобное средство управления статическими маршрутами на подопечных маршрутизаторах);
  • Настраиваемый адрес (IP v4) перенаправления;
  • Указание ограничения (лимита) количества записей в итоговом скрипте (для того чтоб “случайно” не положить “слабую” железку);
  • Указание своих маршрутов (будут просто добавлены первыми в генерируемый скрипт);
  • Возможность указания исключений - нужные домены никогда не будут “заблокированы”;
  • Довольно удобная генерация готового скрипта для MikroTik;

Парсер написан на PHP (потребует php5-curl), лицензия MIT, исходники доступны по ссылке mikrotik-hosts-parser. Сколько сервис проживет - не берусь загадывать, но надеюсь что достаточно долго. Стойкую к хабраэффекту площадку любезно предоставил тов. @drakmail, за что спасибо ему и команде Generate.Club.

ВНИМАНИЕ! Если вы ранее пользовались скриптом по ссылке выше, и у вас внезапно он перестал работать — знайте, причина в его обновлении. Первым делом попробуйте всё там же просто взять его обновленную редакцию, после этого всё должно успешно заработать. Приношу свои извинения за возможные неудобства!

Если после перехода на обновленную версию скрипта у вас появляются в логе микротика ошибки, содержащие текст: script error: failure: entry already exists, то рекомендую или ручками сперва снести все имеющиеся статические маршруты (оставив необходимые, разумеется), или выполнив в консоли /ip dns static remove [/ip dns static find];

Диклеймер: 1. Команда Generate.Club не имеет отношения к сервису. 2. Не хорошо допускать кого-либо к исполнению кода на твоих маршрутизаторах. Поэтому призываю пользоваться им исключительно в ознакомительных целях. Если всё нравится, сделай форк, настрой под себя да пользуйся на здоровье. За возможные перебои в работе, хабраэффект или ошибки ответственности никто не несет. 3. Я приложу все усилия, чтобы всё работало “как надо” максимально продолжительное время, но нужно понимать степень ответственности.

При обнаружении ошибок в работе сервиса или опечаток в тексте - пожалуйста, сообщайте об этом в личку. Заранее спасибо!