Удалённое управление правилами MikroTik через веб-панель. Включайте и выключайте firewall-правила и kid-control с телефона.
Основной экран — пресеты-режимы, секция устройств с паузой, группы сервисов с мастер-тогглами и групповыми таймерами, обратный отсчёт и продление, статус роутера. Синий тумблер у устройства = kid-control активен (режим расписания), красный у сервиса = блокировка. Поддерживается светлая и тёмная тема.
Шаблоны — каталог готовых блокировок по группам с импортом в один тап. Аналитика — графики использования таймеров за 1/7/30 дней.
- Веб-панель (Go, Docker) — toggle-переключатели, таймеры временной разблокировки, аудит-лог
- Скрипт RouterOS 7 — по таймеру читает состояние с сервера и включает/выключает правила по тегам
hook:<name>
[Web UI / PWA] → [Go Backend + Bearer Auth] ← [MikroTik скрипт по таймеру]
↕
state.json + audit.json
Параметр имеет тип (kind): device — kid-control-запись (тег в name, инвертированная логика), service — firewall/DNS-блок (тег в comment). Сервисы можно объединять в группы (group) — UI показывает их секциями с мастер-тогглом. Группировка живёт целиком на сервере: роутер получает плоский params и о группах не знает. Старый state.json (только inverted) мигрирует автоматически при первом старте.
- Toggle — включение/выключение firewall-правил и kid-control
- Разделы — параметры двух типов: устройства (kid-control) и сервисы (firewall/DNS-блоки); UI рендерит их отдельными секциями
- Группы сервисов — игры / видео и стриминг / соцсети (+ свои через UI); мастер-тоггл и таймер на всю группу одним действием
- Пауза для всех устройств — одна кнопка временно ограничивает все устройства (kid-control), с таймером и кнопкой «Возобновить»
- Пресеты-режимы — «Уроки» (заблокировать все группы) и «Свободный час» (разблокировать всё на час); свои пресеты — правкой
state.json - Шаблоны сервисов — готовые заготовки блокировок (YouTube, TikTok, Twitch, Netflix, Kick, Steam, Roblox, Minecraft, Fortnite, Twitter/X, Facebook, Instagram, Snapchat, Discord, VK): кнопка в приложении создаёт параметры по группам, сгенерированный
templates.rscсоздаёт правила в фаерволе роутера - Таймеры — временная разблокировка на 5м, 15м, 30м, 1ч, 3ч, 6ч или «до HH:MM» (нативный time-picker, локальная TZ браузера) с обратным отсчётом
- Продление таймеров — кнопки +5м/+15м/+30м/+1ч/+3ч и для активного, и для pending-таймера
- Pending-таймеры — отсчёт начинается только когда роутер забирает настройки
- Kid-control — инвертированная логика (включён = ребёнок ограничен)
- Conntrack clearing — при включении блокировки сбрасываются существующие соединения по src/dst-address-list
- Temp-block — временная полная блокировка устройства (30 сек) при включении правила, убивает game sessions даже если game server IP не в address-list
- Автообновление скрипта — роутер автоматически обновляет скрипт при появлении новой версии на сервере
- Аудит-лог — история всех действий (toggle, таймеры, expired), до 2000 записей
- Аналитика — графики использования таймеров за 1/7/30 дней с разбивкой по суткам
- Статус роутера — heartbeat (онлайн/офлайн) с точным временем последнего обращения
- Версионирование скрипта — предупреждение в UI если скрипт на роутере устарел
- PWA — устанавливается на телефон как приложение
- Pull-to-refresh — свайп вниз для обновления
- CI/CD — GitHub Actions собирает Docker-образ и пушит в ghcr.io
# Самый простой способ — pull готового образа и запуск
make up
# С явным указанием IP и токена
HOST_IP=10.0.0.5 AUTH_TOKEN=secret docker compose up -d
# Через .env файл
echo "HOST_IP=10.0.0.5" > .env
echo "AUTH_TOKEN=secret" >> .env
docker compose up -d
# Локальная сборка (без CI)
make build-local
# Без Docker
go build -o hook-server ./server/
AUTH_TOKEN=secret ./hook-serverHOST_IP автоматически подставляется в скрипт remote-hook.rsc при старте контейнера — MikroTik скачает скрипт с уже правильным адресом сервера.
Веб-панель доступна на http://<HOST_IP>:8080.
| Команда | Описание |
|---|---|
make up |
Запуск (образ из ghcr.io, IP определяется автоматически) |
make down |
Остановка |
make logs |
Просмотр логов |
make pull |
Подтянуть новый образ из ghcr.io |
make restart |
Pull + пересоздание контейнера (обновление на сервере) |
make build-local |
Локальная сборка и запуск |
После пуша в master GitHub Actions автоматически соберёт образ. На сервере:
make restart| Переменная | Default | Описание |
|---|---|---|
LISTEN_ADDR |
:8080 |
Адрес и порт для прослушивания |
DATA_DIR |
./data |
Директория для хранения состояния |
AUTH_TOKEN |
— | Bearer token для защиты API |
Для firewall — в поле comment, для kid-control — в поле name:
/ip/firewall/filter add chain=forward action=drop dst-address-list=roblox comment="hook:roblox-drop" disabled=yes
/ip/kid-control add name="hook:Eva-Laptop" fri=6h-20h mon=6h-19h ...
Скрипт доступен для скачивания прямо с сервера (авторизация не требуется):
# Скачайте скрипт с сервера на роутер
/tool/fetch url="http://your-server:8080/mikrotik/remote-hook.rsc" dst-path=remote-hook.rsc
# Отредактируйте url и token в начале скрипта, затем создайте скрипт
/system/script add name=remote-hook source=[/file/get remote-hook.rsc contents]
# Создайте расписание (каждую минуту)
/system/scheduler add name=remote-hook interval=1m on-event="/system/script/run remote-hook"
Скрипт обновляется автоматически — при обнаружении новой версии на сервере роутер скачивает и применяет обновлённый скрипт при следующем poll.
При необходимости обновить вручную:
/tool/fetch url="http://your-server:8080/mikrotik/remote-hook.rsc" dst-path=remote-hook.rsc
/system/script set remote-hook source=[/file/get remote-hook.rsc contents]
Примечание: используется синтаксис RouterOS 7.
При включении blocking-правила скрипт:
- Собирает src IP устройств из
src-address-list/src-addressправила, или сканирует conntrack поdst-address-list - Включает правило — новые соединения блокируются
- Temp-block — добавляет src IP в
_temp-blockaddress-list на 30 сек (полный бан устройства, убивает game sessions) - Очищает conntrack — удаляет все существующие соединения от заблокированных устройств
- Через 30 сек temp-block истекает, интернет возвращается, но DNS/TLS блокировка не даёт переподключиться
Drop-правило _temp-block автоматически создаётся и позиционируется перед accept established,related.
| Раздел | Тег в поле | Логика |
|---|---|---|
/ip/firewall/filter |
comment |
Прямая (enabled = правило активно) |
/ip/firewall/nat |
comment |
Прямая |
/ip/firewall/mangle |
comment |
Прямая |
/ip/kid-control |
name |
Инвертированная (enabled = ограничения сняты) |
/ip/dns/static |
comment |
Прямая (при тоггле DNS-кеш сбрасывается) |
Для добавления новых разделов — отредактируйте массив sections в скрипте. Для инвертированной логики — также invertedSections.
Каталог готовых блокировок (server/templates.go). На каждый сервис:
- address-list
blocked-<id>— наполняется трёхслойно: апекс-FQDN как база, статические сети у игровых (Roblox AS22697, Valve), и главное — DNS-харвестинг: постоянно включённые записи/ip/dns/static type=FWD match-subdomain=yes address-list=blocked-<id>(тегtpl:<id>, требуют ROS ≥ 7.8). Любой резолв любого сабдомена через DNS роутера отвечает клиенту нормально, но складывает полученные IP в лист — так покрываются ротирующиеся CDN-сабдомены (rN---sn-*.googlevideo.comи т.п.), которые апекс-резолв не видит. Апстрим FWD-записей импорт-скрипт берёт из настроек DNS самого роутера. - drop-правило по address-list с тегом
hook:<id>, ограниченное детскими устройствами (src-address-list=kids-devices) — блокировки не задевают остальную сеть; резолвы взрослых устройств тоже пополняют листы, усиливая блок для детей. - SNI-правила (
tls-host=*<домен>, tcp/443) — по одному на домен, тот же тегhook:<id>, тоглятся вместе с сервисом. Матчат hostname прямо в TLS ClientHello — режут соединения к IP, которых листы никогда не видели (DoH-обход, свежие CDN-адреса). - Блок QUIC (
tpl:quic-block, udp/443, всегда включён для kids-devices) — SNI виден только в TCP TLS; без QUIC браузеры откатываются на TCP, где SNI-правила работают.
NXDOMAIN-записей шаблоны не создают: их нельзя скоупить по источнику (валили резолв всей LAN) и они отравляли собственный резолвер роутера. DNS здесь — сенсор, а не блокировщик; блокирует только фаервол. Остаточный обход — ECH (шифрование SNI) + DoH одновременно; на 2026 год встречается редко.
Импорт двухсторонний:
- В приложение — раздел «Шаблоны»: кнопка на каждый сервис или «Импортировать всё». Создаются параметры в соответствующих группах; уже существующие параметры не сбрасываются (обновляется только описание/группа).
- В фаервол роутера — автоматически (скрипт ≥ v22): сервер отдаёт роутеру
templates_hash— отпечаток сгенерированного скрипта по импортированным в приложение сервисам. Роутер хранит последний применённый хеш и при расхождении (импортирован новый сервис / обновились домены каталога) один раз скачиваетtemplates.rsc?imported=1и импортирует. Совпадает — ничего не качает, обычный цикл не замедляется. Скрипт идемпотентен: существующие записи не дублируются, всё новое создаётсяdisabled=yes.
Ручной импорт всего каталога (не только импортированного в приложение):
/tool/fetch url="http://your-server:8080/mikrotik/templates.rsc" dst-path=templates.rsc
/import file-name=templates.rsc
Фильтр по сервисам — query-параметр: templates.rsc?ids=youtube,roblox.
При скане роутер собирает все найденные hook:-теги и отправляет их следующим циклом в заголовке X-Seen-Params. Параметры, которых нет среди найденных на роутере, UI помечает красным бейджем «нет правила на роутере» (данные отстают на один цикл: после автосоздания правил бейдж гаснет за ~2 минуты).
При обновлении с ранних версий шаблонов миграция выполняется автоматически: существующие шаблонные правила получают src-address-list=kids-devices, шаблонные DNS-записи удаляются.
Единый список «чьи IP детские», на него ссылаются все шаблонные drop-правила. Наполняется автоматически (скрипт ≥ v24, каждый цикл):
- Берутся устройства kid-control, привязанные к юзерам с именем
hook:*(то есть к тем, кем управляет панель):/ip/kid-control/device→ MAC. - MAC мапится на IP через DHCP lease (включая reservations офлайн-устройств, обе DHCP-зоны).
- Список синхронизируется: авто-записи помечены комментарием
kc:auto(устаревшие удаляются, новые добавляются), ручные записи не трогаются — можно добавлять IP и вручную.
Чтобы устройство попало под детские блокировки: привязать его в kid-control (/ip/kid-control/device add user="hook:<имя>" mac-address=<MAC>) и завести статический DHCP lease (рекомендуется). Проверка: /ip/firewall/address-list print where list=kids-devices.
Один тег hook:<name> вешается на пару правил — DNS-уровень и L3-уровень:
# 1. DNS: клиенты, использующие DNS роутера, перестают резолвить домен (+ все поддомены)
/ip/dns/static add name=example.com type=NXDOMAIN match-subdomain=yes comment="hook:example" disabled=yes
# 2. Firewall: address-list с FQDN — RouterOS сам резолвит домен и динамически
# поддерживает актуальные IP (включая новые CDN-адреса)
/ip/firewall/address-list add list=blocked-example address=example.com
/ip/firewall/filter add chain=forward dst-address-list=blocked-example action=drop comment="hook:example" disabled=yes
Тоггл в UI включает оба правила сразу. DNS-запись отсекает новые резолвы (кеш сбрасывается автоматически), а firewall-правило блокирует трафик даже у клиентов с закешированным IP или DoH. Conntrack clearing и temp-block обрабатываются штатным firewall-потоком скрипта (по dst-address-list).
match-subdomain=yesтребует RouterOS ≥ 7.6.
Игровые клиенты часто получают IP сервера через API (не DNS) и играют по UDP напрямую — FQDN-записи такой трафик не покрывают, и живая игровая сессия переживает блокировку. Нужны два дополнения:
# 1. Сети игровых серверов статикой (Roblox = AS22697)
/ip/firewall/address-list add list=roblox address=128.116.0.0/17 comment="Roblox AS22697 game servers"
# 2. src-address-list с устройствами детей в блок-правиле — при включении блока
# скрипт temp-блочит устройства на 30 сек и убивает ВСЕ их соединения,
# игровая сессия гибнет даже если IP сервера нет в списках
/ip/firewall/address-list add list=kids-devices address=10.0.0.55
/ip/firewall/filter set [find comment="hook:roblox-drop"] src-address-list=kids-devices
После temp-block интернет возвращается через 30 сек, но перезайти в игру не выйдет — матчмейкинг закрыт DNS- и firewall-слоями.
Важно:
src-address-listдолжен стоять на каждом блок-правиле с этим тегом. Правило только с dst-листом включает fallback-скан conntrack, а FQDN-записи резолвятся в IP общих CDN — под 30-секундный temp-block может попасть постороннее устройство, у которого были соединения к тем же CDN-IP.
| Ситуация | Поведение |
|---|---|
| Сервер недоступен | Скрипт прерывается, правила не трогаются |
| Пустой/невалидный ответ | Скрипт прерывается, правила не трогаются |
| Параметр есть на роутере, нет на сервере | Правило не трогается |
| Параметр есть на сервере, нет на роутере | Игнорируется |
| Таймаут | 10 секунд, затем прерывание |
Запросы к /api/* требуют заголовок Authorization: Bearer <token> (если AUTH_TOKEN задан). Остальные URL доступны без авторизации.
| Метод | URL | Auth | Описание |
|---|---|---|---|
GET |
/api/state |
да | Состояние: params + groups + presets (роутер по User-Agent получает только params) |
POST |
/api/state |
да | Изменить параметр ({name, enabled}) |
POST |
/api/group |
да | Мастер-тоггл группы ({name, enabled}) — фан-аут по сервисам группы |
POST |
/api/timer |
да | Таймер разблокировки ({name, minutes} или {group, minutes}) |
POST |
/api/pause |
да | Пауза всех устройств ({minutes}) — временно включает kid-control |
POST |
/api/resume |
да | Снять паузу с устройств |
POST |
/api/preset |
да | Применить пресет ({name}) |
GET |
/api/templates |
да | Каталог шаблонов + статус импорта |
POST |
/api/templates/import |
да | Импорт шаблонов в приложение ({ids: []}, пустой = все) |
POST |
/api/params |
да | Добавить параметр ({name, description, kind: service|device, group}); существующий — переклассифицируется без сброса состояния |
DELETE |
/api/params?name=xxx |
да | Удалить параметр |
POST |
/api/groups |
да | Создать/переименовать группу ({id, title}) |
DELETE |
/api/groups?id=xxx |
да | Удалить пустую группу |
GET |
/api/log |
да | Последние 50 событий аудит-лога |
GET |
/api/stats |
да | Статистика по параметрам |
GET |
/api/stats?days=N |
да | Аналитика таймеров за N дней (1–90) с разбивкой по суткам |
GET |
/api/heartbeat |
да | Статус подключения роутера + версия скрипта |
GET |
/mikrotik/remote-hook.rsc |
нет | Скачать скрипт |
GET |
/mikrotik/templates.rsc |
нет | Скрипт импорта шаблонов в фаервол (?ids=a,b — фильтр) |
GET |
/ |
нет | Веб-панель (PWA) |