- 1. Введение: Эволюция безопасности DNS в эпоху RouterOS v7
- 2. Архитектурная карта решения (Блок-схема)
- 3. Детализация и реализация подсистем
- 4. Организация автоматического мониторинга и отказоустойчивости DNS в RouterOS v7.20.6
- 5. Верификация и отладка архитектуры
- 6. Возможные проблемы и их решение (полевой опыт)
- 6.1. Отсутствие доверенного CA: интернет «падает»
- 6.2. CA для Cloudflare не угадать «из головы»
- 6.3. NTP выключен или сбит — сертификат не проверяется
- 6.4. device-mode «home» блокирует fetch и планировщик
- 6.5. Force-DNS: лучше action=redirect, а не dst-nat на локальный адрес
- 6.6. Health-check DoH через /tool fetch: 400 и 405 — это не «DoH упал»
- 6.7. use-doh-server — единственный путь: без фейловера резолв встаёт
- 6.8. Статические DNS-записи перекрывают семейный фильтр
- 6.9. Динамические серверы провайдера и проверка «не по памяти»
- 7. Заключение

Исходники и обновления: github.com/paulmann/mikrotik-doh-failover — проверяемые .rsc-скрипты, схемы и changelog. Команды ниже проверены на RouterOS 7.23.1 и 7.24.5 (Cloudflare Family DoH).
1. Введение: Эволюция безопасности DNS в эпоху RouterOS v7
В 2025 году шифрование DNS-трафика перестало быть опцией — это обязательный стандарт для любой производственной сети. DNS-over-HTTPS (DoH) решает критические уязвимости традиционного DNS, обеспечивая:
- Конфиденциальность: Запросы шифруются HTTPS/TLS, скрывая метаданные от провайдера и MITM-атак.
- Целостность данных: Цифровая подпись и проверка сертификатов (
verify-doh-cert) гарантируют подлинность ответов. - Обход цензуры: Трафик DoH неотличим от обычного HTTPS, что усложняет блокировку на сетевом уровне.
Ключевой вызов в RouterOS v7.20.6 — полный рефакторинг DNS-подсистемы. Устаревшие параметры (dynamic-servers, разбиение команд через \) удалены, а новые механизмы (/ip dns static-server type=doh) требуют системного подхода к проектированию отказоустойчивости.
Данная статья представляет законченную архитектурную модель, где каждый компонент детально обоснован с учетом особенностей v7 и снабжен проверенными командами.
2. Архитектурная карта решения (Блок-схема)
Следующая блок-схема представляет собой системную карту, отображающую все компоненты решения и логику их взаимодействия. Нумерация подразделов статьи соответствует блокам на схеме для удобства навигации.
flowchart TD
Start[Начало настройки] --> A1
subgraph A1[1. Инициализация сертификатов]
A1_1[Загрузка CA] --> A1_2[Импорт] --> A1_3{Верификация}
A1_3 -->|Да| A1_4[verify-doh-cert=yes]
A1_3 -->|Нет| A1_5[verify-doh-cert=no]
end
A1_4 --> A2
A1_5 --> A2
subgraph A2[2. Настройка DNS]
A2_1[Серверы] --> A2_2[DoH эндпоинт] --> A2_3[ACL]
end
A2 --> A3
subgraph A3[3. Резервирование]
A3_1[Основной DoH] --> A3_2[Резервный DoH] --> A3_3[Plain DNS]
A3_4[Алгоритм перебора]
end
A2 --> A4
subgraph A4[6. Мониторинг и переключение]
A4_1[Планировщик] --> A4_2[Скрипт проверки] --> A4_3{DoH доступен?}
A4_3 -->|Да| A4_4[Режим DoH]
A4_3 -->|Нет| A4_5[Переключение]
end
A2 --> B
subgraph B[5. CAPsMAN интеграция]
B1[Контроллер] --> B2[Конфигурация] --> B3[Привязка к мосту] --> B4[DHCP для клиентов]
end
A2 --> C
subgraph C[4. Безопасность]
C1[Перенаправление DNS] --> C2[Фильтрация] --> C3[Блокировка утечек]
end
A3_4 -.-> A4_5
A4_5 -.-> A3_4
Client1[Клиент CAPsMAN] --> C1
Client2[Проводной клиент] --> G1{Проверка ACL}
G1 -->|Разрешено| C1
G1 -->|Заблокировано| Deny[Отказ]
C1 --> DNS_Resolver[Резолвер роутера]
DNS_Resolver --> CacheCheck{Кеш?}
CacheCheck -->|Да| Ответ1[Немедленный ответ]
CacheCheck -->|Нет| External[Внешний запрос]
External --> ModeCheck{Режим?}
ModeCheck -->|DoH| DoH_Query[HTTPS/TLS запрос] --> DoH_Server1[Сервер Comss.one]
ModeCheck -->|Plain DNS| DNS_Query[UDP запрос] --> DNS_Server[Сервер 9.9.9.9]
DoH_Server1 --> ResponseCheck{Ответ?}
DNS_Server --> ResponseCheck
ResponseCheck -->|Да| Success[Успех, кеширование]
ResponseCheck -->|Нет| Fail[Счетчик ошибок] --> Retry{Превышен лимит?}
Retry -->|Нет| ModeCheck
Retry -->|Да| Switch[Переключение приоритета] --> ModeCheck
Success --> ClientAnswer[Ответ клиенту]
Ответ1 --> ClientAnswer
style Start fill:#555,color:#fff
style Deny fill:#d9534f,color:#fff
style Success fill:#5cb85c,color:#fff3. Детализация и реализация подсистем
3.1. Подсистема инициализации и сертификатов
Архитектурное обоснование: Без доверенного хранилища корневых сертификатов (CA) функция verify-doh-cert=yes неработоспособна. Это подвергает сеть риску атак типа «злоумышленник в середине» (MITM).
Реализация для v7.20.6:
# Загрузка корневого сертификата удостоверяющего центра
/tool fetch url="https://curl.se/ca/cacert.pem" dst-path="root-ca.pem" mode=https check-certificate=no http-header-field="User-Agent: MikroTik/7.20.6"
:delay 2s
# Импорт сертификатов. RouterOS автоматически игнорирует дубликаты.
/certificate import file-name="root-ca.pem" passphrase=""
# Критически важная проверка
:local trustedCerts [/certificate find where trusted=yes]
:if ([:len $trustedCerts] < 50) do={
:log error "Недостаточно доверенных сертификатов. Проверка DoH может не работать."
} else={
:log info "Импортировано доверенных сертификатов: $[:len $trustedCerts]"
}
/file remove root-ca.pem
3.2. Подсистема настройки DNS и резервирования
Реализация для v7.20.6:
# Базовые серверы. Первый в списке - основной.
/ip dns set servers=195.133.25.16,9.9.9.9,1.1.1.1
# Активация основного DoH. Параметр dynamic-servers в v7 УДАЛЕН.
/ip dns set use-doh-server="https://router.comss.one/dns-query" verify-doh-cert=yes
# Разрешаем клиентам использовать наш DNS.
/ip dns set allow-remote-requests=yes max-concurrent-queries=200 cache-size=4096KiB
# Добавление резервных DoH-серверов как статических.
/ip dns static-server add address=1.1.1.1 type=doh use-doh-server="https://cloudflare-dns.com/dns-query" verify-doh-cert=yes comment="backup-1"
/ip dns static-server add address=9.9.9.9 type=doh use-doh-server="https://dns.quad9.net/dns-query" verify-doh-cert=yes comment="backup-2"
3.3. Подсистема принудительной безопасности
Реализация для v7.20.6:
# Перенаправление ВСЕХ DNS-запросов от клиентов
/ip firewall nat add chain=dstnat protocol=udp dst-port=53 in-interface=bridge-local action=dst-nat to-address=192.168.88.1 to-port=53 comment="Force DNS to router (UDP)"
/ip firewall nat add chain=dstnat protocol=tcp dst-port=53 in-interface=bridge-local action=dst-nat to-address=192.168.88.1 to-port=53 comment="Force DNS to router (TCP)"
# Защита DNS-сервиса от внешнего мира
/ip firewall filter add chain=input protocol=udp dst-port=53 in-interface=bridge-local action=accept comment="Allow DNS from LAN"
/ip firewall filter add chain=input protocol=udp dst-port=53 action=drop comment="Drop external DNS"
# Блокировка нежелательного контента через списки адресов
/ip firewall address-list add list=Blocked_Sites address=youtube.com comment="Пример блокировки"
/ip firewall filter add chain=forward dst-address-list=Blocked_Sites action=reject reject-with=icmp-network-unreachable comment="Block sites from list"
3.4. Подсистема CAPsMAN
Реализация для v7.20.6:
# Убедитесь, что в конфигурации CAPsMAN указан правильный bridge
/caps-man configuration set [find] bridge=bridge-local client-isolation=no
# Настройка DHCP для сети, в которой находятся клиенты CAPsMAN
:local bridgeNet [/ip address get [find interface="bridge-local"] network]
/ip dhcp-server network set [find where address=$bridgeNet] dns-server=192.168.88.1 gateway=192.168.88.1
4. Организация автоматического мониторинга и отказоустойчивости DNS в RouterOS v7.20.6
4.1. Архитектурный подход: система как состояние, а не файлы
В RouterOS v7 автоматизация строится на трёх фундаментальных системных объектах:
- Скомпилированные скрипты (
/system script): исполняемый код, хранящийся внутри бинарной конфигурации. - Планировщики (
/system scheduler): триггеры, которые запускают скрипты по событиям. - Глобальные переменные (
:global): разделяемая память для хранения состояния системы.
4.2. Блок-схема: Автоматическое обновление корневых сертификатов
flowchart TD
Start[Запуск планировщиком] --> A1
subgraph A1[Этап 1: Загрузка]
A1_1[Отправка HTTPS запроса] --> A1_2{Файл загружен?}
A1_2 -->|Успешно| A1_3[Сохранение cacert.pem]
A1_2 -->|Ошибка| A1_4[Лог: ошибка загрузки] --> CriticalEnd
end
A1_3 --> A2
subgraph A2[Этап 2: Валидация]
A2_1[Проверка размера файла] --> A2_2{Размер >200KB?}
A2_2 -->|Да| A2_3[Файл валиден]
A2_2 -->|Нет| A2_4[Лог: файл слишком мал] --> CriticalEnd
end
A2_3 --> A3
subgraph A3[Этап 3: Импорт]
A3_1[Импорт в хранилище] --> A3_2{Импорт успешен?}
A3_2 -->|Да| A3_3[Активация trusted store]
A3_2 -->|Нет| A3_4[Лог: ошибка импорта] --> CriticalEnd
end
A3_3 --> A4
subgraph A4[Этап 4: Применение]
A4_1{DoH настроен?} -->|Да| A4_2[Сброс SSL сессии]
A4_1 -->|Нет| A4_3[Пропуск сброса]
A4_2 --> A4_4[Обновление глоб. переменной]
A4_3 --> A4_4
end
A4_4 --> A5
subgraph A5[Этап 5: Финализация]
A5_1[Удаление временного файла] --> A5_2[Лог: успешное завершение]
A5_2 --> NormalEnd
end
CriticalEnd[Аварийное завершение] --> End
NormalEnd[Успешное завершение] --> End
End[Конец процесса]
style Start fill:#555,color:#fff
style CriticalEnd fill:#d9534f,color:#fff
style NormalEnd fill:#5cb85c,color:#fff
style End fill:#777,color:#fffСоздание системных объектов для обновления сертификатов:
/system script add name="CERT_Update_Root_CA" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source={
:log info "CERT: Starting root CA update procedure"
:global LastCertUpdate
/tool fetch url="https://curl.se/ca/cacert.pem" dst-path="cacert.pem" mode=https check-certificate=no http-header-field="User-Agent: MikroTik/7.20.6"
:delay 3s
:local fileSize [/file get [find name="cacert.pem"] size]
:if ($fileSize < 200000) do={
:log error "CERT: Downloaded file too small. Aborting."
/file remove "cacert.pem"
:error "CA bundle validation failed"
}
/certificate import file-name="cacert.pem" passphrase=""
:delay 2s
/certificate settings set builtin-trust-anchors=trusted
:local currentDoh [/ip dns get use-doh-server]
:if ($currentDoh != "") do={
/ip dns set verify-doh-cert=no
:delay 1
/ip dns set verify-doh-cert=yes
:log info "CERT: DoH SSL session context reset"
}
/file remove "cacert.pem"
:set LastCertUpdate [/system clock get time]
:log info "CERT: Root CA update completed successfully at $LastCertUpdate"
}
/system scheduler add name="Scheduler_CERT_Weekly_Update" interval=7d start-time="03:00:00" on-event="/system script run CERT_Update_Root_CA" comment="Weekly update of trusted root certificate authorities" disabled=no
4.3. Блок-схема: Мониторинг DoH/DNS и интеллектуальный фейловер
flowchart TD
Start[Запуск каждые 3 минуты] --> Init
subgraph Init[Инициализация]
I1[Чтение конфигурации] --> I2[Получение текущего состояния]
end
Init --> Level1
subgraph Level1[Уровень 1: Первичный DoH]
L1_1[DoH запрос к Comss.one] --> L1_2{Ответ корректен?}
L1_2 -->|Да| L1_3[Статус: Работает] --> SuccessPath
L1_2 -->|Нет| L1_4[Лог: WARNING] --> Level2
end
subgraph Level2[Уровень 2: Резервный DoH]
L2_1[DoH запрос к Cloudflare] --> L2_2{Ответ корректен?}
L2_2 -->|Да| L2_3[Статус: Резерв активен] --> SuccessPath
L2_2 -->|Нет| L2_4[Лог: ERROR] --> Level3
end
subgraph Level3[Уровень 3: Plain DNS]
L3_1[UDP запросы к 9.9.9.9, 1.1.1.1] --> L3_2{Хотя бы один ответил?}
L3_2 -->|Да| L3_3[Статус: Аварийный режим] --> SuccessPath
L3_2 -->|Нет| L3_4[Лог: CRITICAL] --> FailurePath
end
SuccessPath --> Decision{Режим изменился?}
Decision -->|Нет| NoChange[Лог: Статус стабилен] --> NormalEnd
Decision -->|Да| ApplyChange
subgraph ApplyChange[Применение изменений]
AC1[Лог: Смена режима] --> AC2{Новый режим DoH?}
AC2 -->|Да| AC3[Включить DoH+верификацию]
AC2 -->|Нет| AC4[Отключить DoH]
AC3 --> AC5[Обновление переменных]
AC4 --> AC5
AC5 --> AC6[Сброс DNS кеша]
end
ApplyChange --> NormalEnd
FailurePath[Все серверы недоступны] --> Emergency[Сохранение последней конфигурации] --> AlertEnd
NormalEnd[Цикл завершен] --> Wait[Ожидание след. запуска]
AlertEnd[Тревожное завершение] --> Wait
style Start fill:#555,color:#fff
style FailurePath fill:#d9534f,color:#fff
style AlertEnd fill:#d9534f,color:#fff
style NormalEnd fill:#5cb85c,color:#fffРеализация скрипта мониторинга DNS:
/system script add name="DNS_HealthMonitor_Failover" policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source={
:log info "DNS Monitor: Starting health check cycle"
:local primaryDohUrl "https://router.comss.one/dns-query"
:local backupDohUrl "https://1.1.1.1/dns-query"
:local backupPlainDns "9.9.9.9,1.1.1.1"
:local testDomain "example.com"
:local healthCheckTimeout "3s"
:local maxFailures 2
:global DNSCurrentMode
:global DNSFailureCount
:if ([:typeof $DNSCurrentMode] = "nothing") do={ :global DNSCurrentMode "unknown" }
:if ([:typeof $DNSFailureCount] = "nothing") do={ :global DNSFailureCount 0 }
:local isDohAlive do={
:local url $1
:local alive false
/tool fetch url=$url dst-path="/doh-check.tmp" mode=https http-method=post http-data="{\"type\":\"A\", \"name\":\"$testDomain\"}" http-header-field="content-type: application/dns-json" timeout=$healthCheckTimeout
:delay 1s
:if ([/file get [find name="doh-check.tmp"] size] > 0) do={ :set alive true }
/file remove [find name="doh-check.tmp"]
:return $alive
}
:local isDnsAlive do={
:local server $1
:local result ""
:local alive false
:set result [:resolve $testDomain server=$server timeout=$healthCheckTimeout]
:if ([:len $result] > 0 && [:typeof $result] = "ip") do={ :set alive true }
:return $alive
}
:local desiredMode ""
:local desiredServers ""
:local healthCheckPassed false
:if ([$isDohAlive $primaryDohUrl]) do={
:set desiredMode "doh-primary"
:set desiredServers "195.133.25.16"
:set healthCheckPassed true
:log info "DNS Monitor: Primary DoH is healthy"
} else={
:log warning "DNS Monitor: Primary DoH failed, testing backup"
:if ([$isDohAlive $backupDohUrl]) do={
:set desiredMode "doh-backup"
:set desiredServers "1.1.1.1"
:set healthCheckPassed true
:log warning "DNS Monitor: Switched to backup DoH"
} else={
:log error "DNS Monitor: All DoH failed, testing plain DNS"
:local plainOk false
:foreach server in=[:toarray $backupPlainDns] do={
:if ([$isDnsAlive $server]) do={
:set plainOk true
:break
}
}
:if ($plainOk) do={
:set desiredMode "plain-dns"
:set desiredServers $backupPlainDns
:set healthCheckPassed true
:log error "DNS Monitor: Switched to emergency plain DNS"
} else={
:set DNSFailureCount ($DNSFailureCount + 1)
:log critical "DNS Monitor: CRITICAL - All DNS servers unreachable! Failure count: $DNSFailureCount"
:if ($DNSFailureCount >= $maxFailures) do={
:log alert "DNS Monitor: Maximum failures reached. System may be offline."
}
:return
}
}
}
:if ($desiredMode != $DNSCurrentMode && $healthCheckPassed) do={
:log warning "DNS Monitor: Applying mode change from '$DNSCurrentMode' to '$desiredMode'"
:if ($desiredMode ~ "doh") do={
/ip dns set use-doh-server=$primaryDohUrl verify-doh-cert=yes servers=$desiredServers
} else={
/ip dns set use-doh-server="" servers=$desiredServers
}
:set DNSCurrentMode $desiredMode
:set DNSFailureCount 0
/ip dns cache flush
:log info "DNS Monitor: Configuration updated, cache flushed"
} else={
:log info "DNS Monitor: No change required. Current mode: '$DNSCurrentMode'"
}
:log info "DNS Monitor: Health check cycle completed"
}
/system scheduler add name="Scheduler_DNS_Health_Check" interval=3m start-time=startup on-event="/system script run DNS_HealthMonitor_Failover" comment="Active DNS/DoH health monitoring and automatic failover" disabled=no
4.4. Таблица состояний и реакций системы
| Состояние системы | Код ошибки / Признак | Действие скрипта | Обновление состояния | Логирование |
|---|---|---|---|---|
| Норма | DoH-запрос успешен, ответ валиден | Поддержание конфигурации | DNSCurrentMode="doh-primary" | INFO |
| Сбой первичного DoH | Таймаут или SSL-ошибка | Переключение на проверку резервного DoH | — | WARNING |
| Сбой всех DoH | Оба DoH-сервера не отвечают | Переключение на проверку plain DNS | — | ERROR |
| Сбой всех серверов | Все проверки (DoH и DNS) не прошли | Увеличение счетчика сбоев, сохранение последней конфигурации | DNSFailureCount++ | CRITICAL |
| Восстановление связи | Первичный DoH снова отвечает | Возврат на основной DoH в следующем цикле | DNSCurrentMode="doh-primary", DNSFailureCount=0 | INFO |
5. Верификация и отладка архитектуры
Команды для проверки и управления системой:
# 1. Проверка состояния системы
:put "=== DNS Monitor System Status ==="
:put "Current Mode: $[:global DNSCurrentMode]"
:put "Failure Count: $[:global DNSFailureCount]"
:put "Last Cert Update: $[:global LastCertUpdate]"
# 2. Просмотр активных планировщиков
/system scheduler print where name~"CERT|DNS"
# 3. Проверка логов системы
/log print where prefix~"DNS" or prefix~"CERT" topics=info,warning,error,critical limit=10
# 4. Ручной запуск проверок
/system script run CERT_Update_Root_CA
/system script run DNS_HealthMonitor_Failover
# 5. Просмотр текущей DNS-конфигурации
/ip dns print
/ip dns static-server print
# 6. Экстренный сброс в plain DNS
:global DNSCurrentMode "plain-dns"
/ip dns set use-doh-server="" servers="9.9.9.9,1.1.1.1"
/ip dns cache flush
6. Возможные проблемы и их решение (полевой опыт)
Этот раздел собран по итогам реального развёртывания схемы на парке MikroTik (RouterOS 7.23.1 и 7.24.5: Chateau PRO ax, RB4011iGS+, hAP ax², RB951Ui, cAP ax). Ниже — типичные отказы в порядке частоты: как их распознать, как исправить и чем обойти ограничения, которые штатно не убираются.
| Проблема | Симптом | Решение |
|---|---|---|
| Нет доверенного CA | DoH server connection error: SSL: no trusted CA certificate found (6), резолв встаёт | Импорт CA + trusted=yes |
| Не тот CA | connection error не уходит после импорта | Cloudflare = SSL.com TLS ECC Root CA 2022 |
| NTP выключен/сбит | Проверка сертификата не проходит | Включить NTP → synchronized |
device-mode=home | not allowed by device-mode на /tool fetch и планировщике | ping вместо fetch; фейловер вручную |
| Сбой DoH | Резолв встаёт, servers не выручают | Фейловер-скрипт либо use-doh-server="" |
| Статический DNS | Фильтр «пропускает» отдельные домены | Проверить /ip dns static |
6.1. Отсутствие доверенного CA: интернет «падает»
Самый частый отказ. verify-doh-cert=yes без импортированного корневого CA заканчивается ошибкой в журнале и полной остановкой резолва: в RouterOS 7.20+ из коробки доверенных публичных CA нет, а при заданном use-doh-server DoH — единственный путь резолва.
/log print where message~"connection error"
# DoH server connection error: SSL: ssl: no trusted CA certificate found (6)
Лечение — импортировать CA, пометить доверенным и передёрнуть verify-doh-cert (это сбрасывает SSL-контекст DoH):
/certificate import file-name=cacert.pem passphrase=""
/certificate set [find where name~"cacert.pem"] trusted=yes
/ip dns set verify-doh-cert=no
:delay 1
/ip dns set verify-doh-cert=yes
6.2. CA для Cloudflare не угадать «из головы»
Для family.cloudflare-dns.com и cloudflare-dns.com цепочка — Cloudflare Inc → SSL.com → Comodo AAA, поэтому нужен именно SSL.com TLS ECC Root CA 2022. Это НЕ DigiCert и НЕ GlobalSign. Если после импорта ошибка осталась — CA не тот или у записи нет trusted=yes. Для семейного фильтра (1.1.1.3 / 1.0.0.3) endpoint один: https://family.cloudflare-dns.com/dns-query. Важно: встроенное хранилище (/certificate settings builtin-trust-store=default) цепочку Cloudflare на 7.23/7.24 не покрывает, а параметра builtin-trust-anchors не существует — импорт CA обязателен (проверено).
6.3. NTP выключен или сбит — сертификат не проверяется
Проверка сертификата требует точного времени. При выключенном NTP часы дрейфуют, и verify-doh-cert=yes рано или поздно отваливается. Проверить и включить:
/system ntp client print # ожидается status: synchronized
/system ntp client set enabled=yes servers=162.159.200.1,216.239.35.0
Публичные NTP по IP (а не по имени) удобны тем, что не зависят от DNS — это снимает проблему «курица и яйцо» на этапе, когда резолв ещё не поднят.
6.4. device-mode «home» блокирует fetch и планировщик
На устройствах с /system device-mode mode=home (например, RB4011iGS+) часть функций отключена: /tool fetch отвечает not allowed by device-mode, а /system scheduler add отклоняется с той же ошибкой. Сам DoH и force-DNS при этом работают.
- Проверку DoH на таком устройстве делать через
ping <домен>и/ip dns cache print, а не через/tool fetch. - Автоматический фейловер-планировщик не завести. Обход — либо перевести устройство в
advanced(требует перезагрузки и решения владельца), либо запускать скрипт фейловера вручную при инциденте.
6.5. Force-DNS: лучше action=redirect, а не dst-nat на локальный адрес
Перенаправлять все DNS-запросы LAN на роутер можно двумя способами. dst-nat to-addresses=<IP роутера> работает, но RouterOS может пометить такое правило флагом invalid (обычно транзиентно). Надёжнее канонический redirect — он переписывает адрес на локальный адрес входного интерфейса и не требует хардкода IP:
/ip firewall nat add chain=dstnat action=redirect protocol=udp dst-port=53 in-interface=bridge comment="force DNS UDP"
/ip firewall nat add chain=dstnat action=redirect protocol=tcp dst-port=53 in-interface=bridge comment="force DNS TCP"
Флаг I (invalid) сразу после массового добавления правил — часто временный: RouterOS пересчитывает цепочку, и через несколько секунд print показывает чистое правило. Проверять повторным чтением, а не удалять вслепую.
6.6. Health-check DoH через /tool fetch: 400 и 405 — это не «DoH упал»
POST к /dns-query без корректного DNS-JSON возвращает 400 Bad Request, GET — 405 Method Not Allowed. Оба ответа означают, что TLS-соединение установлено и сертификат проверен, то есть endpoint жив. Надёжнее проверять фейловер через реальный путь резолвера, а не через HTTP-код:
:put [:resolve cloudflare.com] # реальный путь (DoH)
:put [:resolve cloudflare.com server=1.1.1.3] # прямой plain-запрос, обходит DoH
Параметр server= у :resolve поддерживается (проверено на 7.23.1/7.24.5), а timeout= — нет (bad parameter timeout).
6.7. use-doh-server — единственный путь: без фейловера резолв встаёт
Когда задан use-doh-server, RouterOS использует DoH как ЕДИНСТВЕННЫЙ путь и при сбое НЕ откатывается на servers. Поэтому список servers=1.1.1.3,1.0.0.3 сам по себе резерва не даёт — нужен скрипт фейловера (раздел 4) или ручной аварийный сброс:
/ip dns set use-doh-server=""
/ip dns cache flush
# вернуть: /ip dns set use-doh-server="https://family.cloudflare-dns.com/dns-query" verify-doh-cert=yes
6.8. Статические DNS-записи перекрывают семейный фильтр
Записи в /ip dns static имеют приоритет над ответом DoH. Если какой-то домен прописан статикой (например, pin на реальный IP), фильтр для него не сработает — проверка «работает ли фильтр» даст ложный провал. Пример: :resolve pornhub.com возвращает реальный IP, хотя на соседних устройствах — 0.0.0.0.
/ip dns static print # искать записи, перекрывающие фильтр
/ip dns cache print where name~"pornhub"
Это же свойство полезно в обратную сторону: статикой удобно закреплять внутренние имена (split-horizon) и блокировать нежелательные домены через type=NXDOMAIN.
6.9. Динамические серверы провайдера и проверка «не по памяти»
На WAN по DHCP могут прийти dynamic-servers провайдера. При заданном use-doh-server они не используются, но в /ip dns print видны и сбивают с толку при диагностике. Итоговое правило — проверять факт командой, а не предположением:
/ip dns print # servers, use-doh-server, verify-doh-cert, dynamic-servers
/certificate print where trusted=yes
/system ntp client print # status: synchronized
7. Заключение
Представленная архитектура превращает RouterOS v7.20.6 в корпоративный шлюз безопасности DNS, соответствующий стандартам 2025 года. Ее ключевые преимущества:
- Отказоустойчивость enterprise-уровня: Многоуровневое резервирование от основного DoH до публичных DNS.
- Абсолютное применение политик: Благодаря правилам redirect классический DNS-трафик LAN идёт на роутер; при этом клиентский DoH/DoT это не блокирует.
- Полная наблюдаемость: Каждый этап резолвинга логируется и может быть промониторен.
- Совместимость с современными схемами: Готова к работе в гетерогенных сетях с CAPsMAN, VLAN и сложной маршрутизацией.
- Самодостаточность: Все компоненты хранятся в бинарной конфигурации и восстанавливаются из
.backupфайла.
Честно о границах решения: оно шифрует DNS-трафик маршрутизатора через один DoH-апстрим, контролирует его доступность и при сбое временно переключается на обычные семейные DNS-серверы. Оно перенаправляет классический DNS-трафик LAN к роутеру, но не предотвращает использование клиентского DoH/DoT — для этого нужны дополнительные политики блокировки.
Готовые проверяемые скрипты, схемы и changelog — в репозитории github.com/paulmann/mikrotik-doh-failover. Там же принимаются отчёты о несовместимости с другими конфигурациями MikroTik.