Версия 0.4.1 — это релиз про надёжность, а не про новые возможности: цепочка отказоустойчивости DoH на MikroTik получила жёсткий таймаут на каждой пробе, симметричную проверку при переключении на третий эндпоинт и честный счётчик попыток на всю цепочку. Но отдельной строки в истории изменений мало: с момента, когда вышла основная статья про этот набор (она описывала состояние 0.2.0), прошли ещё 0.3.1 и 0.4.0 — и там появилось то, ради чего всё делалось: падение в обычный DNS как последняя ступень, проба в памяти без временных файлов и автомат состояний с гистерезисом. Ниже — что именно изменилось, что обновлять на роутере и как убедиться, что изменения прижились.

mikrotik-doh-failover v0.4.1 — обложка обзора версии
Цепочка primary → secondary → tertiary → plain и жёсткий таймаут на каждой пробе

TL;DR: что нового в 0.4.1

  1. Жёсткий таймаут. Каждый /tool fetch в мониторе несёт duration=5s — зависший эндпоинт больше не блокирует прогон дольше интервала планировщика.
  2. Третий эндпоинт проверяется так же, как второй. Переключение на dohUrlC теперь идёт с проверкой после переключения и откатом при неудаче.
  3. Счётчик честный. DNSFailoverAttempts растёт на каждом реальном переключении — на второй, третий и обычный DNS, — а не только на первом.
  4. Возврат из plain DNS не ждёт основной. Если основной эндпоинт лежит, а второй отвечает, монитор поднимается на любой рабочий DoH.
  5. Обновление — один импорт. Меняется только 05-dns-health-monitor.rsc, остальные скрипты трогать не нужно.
/import file-name=05-dns-health-monitor.rsc
:global DNSMonitorState; :set DNSMonitorState ""
:global DNSFailCount; :set DNSFailCount 0
:global DNSRecoverCount; :set DNSRecoverCount 0
:global DNSFailoverAttempts; :set DNSFailoverAttempts 0
:global DNSMonitorRunning; :set DNSMonitorRunning false

Проблема, которую решает версия

Монитор DoH — это скрипт, который сам решает, когда сеть переезжает на резервный резолвер. У такого скрипта три места, где ошибка стоит дорого: проба может зависнуть, переключение может оставить сеть на мёртвом эндпоинте, а возврат может застрять на неполной логике.

Первое место до 0.4.1 было открытым. Если эндпоинт принимает соединение и молчит, /tool fetch без таймаута ждёт столько, сколько даст сеть, и прогон выходит за интервал планировщика. На практике это значит, что следующий запуск накладывается на предыдущий, а в 0.3.1 появился защитный флаг от наложения — то есть сеть в такой ситуации не переключалась бы вообще, пока висит первая проба.

Второе место: третий эндпоинт (dohUrlC) добавлялся как «ещё один резерв», но проверка после переключения у него отсутствовала. Если адрес принимает соединение, а имён не разрешает, монитор оставался на сломанной третьей ступени, хотя основной к тому времени мог уже ожить.

Третье место — счётчик попыток. Логика «не больше трёх попыток на инцидент» полезна только тогда, когда считает все переключения; до 0.4.1 он рос лишь на первом, поэтому длинная цепочка могла прокручиваться дальше, чем задумано.

И четвёртое, самое неприятное по последствиям: возврат из обычного DNS. Пока резолвер работал без шифрования, монитор ждал именно основной эндпоинт. Если основной лежал час, а второй был жив, сеть час жила без DoH — при том что любая шифрованная ступень лучше открытого DNS.

Что нового: четыре изменения по существу

Жёсткий таймаут на каждой пробе

В скрипте на main шесть вызовов /tool fetch, и у всех теперь есть duration=5s (посчитано поиском по duration=5s в routeros/05-dns-health-monitor.rsc). Автор указывает, что поведение проверено на RouterOS 7.23.1.

Что былоЧто сталоЗачем
проба без ограничения времениduration=5s на каждом fetchзависший эндпоинт не выносит прогон за интервал планировщика
наложение прогонов защищено флагомфлаг остался, но теперь реже срабатываетменьше «пропущенных» проверок

Симметрия переключения: третий эндпоинт как второй

СтупеньПроба до переключенияПроверка послеОткат при неудаче
secondary (dohUrlB)былабылабыла
tertiary (dohUrlC)былапоявилась в 0.4.1появилась в 0.4.1
plain DNS (plainFallback)—по содержимомувозврат на DoH

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

Счётчик на всю цепочку

DNSFailoverAttempts теперь растёт на каждом реальном переключении: secondary, tertiary и plain. Вместе с maxAttempts = 3 это даёт ровно то поведение, которое обещано в документации: не больше трёх переключений на инцидент, дальше монитор ждёт восстановления основного и не забивает журнал.

Возврат из plain DNS на любой рабочий DoH

БылоСтало
из состояния plain выход только на основной эндпоинтвыход на любой рабочий DoH: если основной лежит, а второй отвечает — сеть возвращается в DoH
основной лежит час — час открытого DNSоткрытый DNS только пока не отвечает ни один DoH

Это изменение важнее, чем кажется: plain — это состояние с пониженной приватностью (в журнал оно пишется строкой PRIVACY DEGRADED), и держать его дольше необходимого нельзя.

Ключевые изменения с 0.2.0: что вы пропустили

Основная статья описывала 0.2.0. Между ней и текущей версией вышли две содержательные версии, и без них картина неполная.

flowchart LR
  V020["0.2.0: один DoH и резерв DoH, проба файлом"] --> V031["0.3.1: автомат состояний, гистерезис, строгий режим, Zabbix"]
  V031 --> V040["0.4.0: цепочка primary → secondary → tertiary → plain"]
  V040 --> V041["0.4.1: duration=5s, проверка tertiary, возврат на любой DoH"]
  V041 --> NOW["текущая цепочка отказоустойчивости DoH"]
ВерсияЧто появилосьПочему это важно
0.3.1автомат состояний HEALTHY → SUSPECT → FALLBACK → RECOVERING → HEALTHY плюс STUB, DEGRADED, CLOCK_INVALID, MANUAL-ONLYповедение стало предсказуемым, а не «набором условий в скрипте»
0.3.1failureThreshold и recoveryThreshold (оба 2) как защита от дребезгасеть не переключается на каждой одиночной неудаче
0.3.1настраиваемый monitorInterval (60 с вместо жёстких 3 минут)проверка чаще, при этом без ручной правки тела скрипта
0.3.1enforcement=strict в 02-security-policies.rsc: блокировка DoT (853) и известных публичных DoHзакрывает обход принудительного DNS, но с честно описанным остаточным риском
0.3.1модуль zabbix/: 8 элементов, 5 триггеров, 2 дерева сервисовсостояние DoH видно по парку, а не по одному роутеру
0.4.0проба через as-value output=user — ответ в памяти, без временных файлов в /fileубраны гонки очистки файлов
0.4.0упорядоченная цепочка primary → secondary → tertiary → plainпоявилась последняя ступень на случай, когда не отвечает ни один DoH
0.4.0новые настройки dohUrlC, probeUrlC, plainFallback, plainServersцепочка описывается конфигом, а не правкой тела
0.4.1таймаут, симметрия tertiary, счётчик, возврат из plainто, о чём эта статья

Отдельная строка про гистерезис: два плохих прогона подряд на переключение и два хороших на возврат — это компромисс между скоростью реакции и дребезгом. При интервале 60 секунд переключение происходит примерно через две минуты после устойчивой деградации, а возврат — через две минуты после устойчивого восстановления.

Замеры и цифры

mikrotik-doh-failover v0.4.1: ключевые числа релиза и проверенной базы
ЧтоЗначениеОткуда
Изменено файлов между 0.4.0 и 0.4.16заметки к релизу v0.4.1
Коммитов между 0.4.0 и 0.4.13там же
Вызовов /tool fetch с duration=5s6поиск по routeros/05-dns-health-monitor.rsc на main
Значений гистерезиса по умолчанию2 и 2config.example.rsc: failureThreshold, recoveryThreshold
Попыток на инцидент по умолчанию3maxAttempts
Интервал монитора по умолчанию60 сmonitorInterval
Тиков таймаута пробы5 сduration=5s
Проверенных устройств6docs/verification.md
Проверенных версий RouterOS7.23.1 и 7.24.5там же
Объектов Zabbix в модуле8 элементов, 5 триггеров, 2 дереваzabbix/
Скриптов RouterOS в наборе7 (около 360 строк)README, раздел «В цифрах»

Все числа выше — из репозитория: либо из заметок к релизу, либо из самого кода на main. Замеров на собственном роутере в этом прогоне не было, поэтому строки про поведение на устройстве помечены как данные автора, а не как мой прогон.

Почему это круто

  • Таймаут делает прогон предсказуемым. Монитор с планировщиком обязан укладываться в свой интервал; duration=5s превращает это из пожелания в свойство.
  • Симметрия убирает особый случай. Одна и та же логика для второй и третьей ступени — меньше ветвей, которые кто-то забудет обновить.
  • Счётчик перестал врать. Ограничение «три попытки» наконец означает три попытки на всю цепочку.
  • Возврат быстрее. Открытый DNS остаётся ровно столько, сколько нет ни одного рабочего DoH.
  • Проба не оставляет мусора. С 0.4.0 ответ читается в памяти — временные файлы в /file и гонки их очистки больше не существуют.
  • Цепочка описывается конфигом. dohUrlC пустой — ступени нет; plainFallback=false — открытый DNS запрещён полностью.
  • Строгий режим назван честно. enforcement=strict блокирует DoT и публичные DoH, но остаточный риск описан, а не спрятан.
  • Мониторинг парка в комплекте. Модуль Zabbix видит состояние DoH с шести роутеров по SSH, а не по одному устройству.

Установка и обновление

Если набор уже стоит на 0.3.1 или 0.4.0

Меняется только монитор: якорь доверия, настройка апстрима и политики не тронуты. Порядок из заметок к релизу — импорт скрипта и очистка глобалов состояния, чтобы старые значения не влияли на новый код.

/import file-name=05-dns-health-monitor.rsc
:global DNSMonitorState; :set DNSMonitorState ""
:global DNSFailCount; :set DNSFailCount 0
:global DNSRecoverCount; :set DNSRecoverCount 0
:global DNSFailoverAttempts; :set DNSFailoverAttempts 0
:global DNSMonitorRunning; :set DNSMonitorRunning false

Перед импортом полезно снять состояние до правки — это и есть откат:

/export file=before-041
/system backup save name=before-041

Если набор ставится с нуля

flowchart TD
  BKP["/export file=before-doh и /system backup save"] --> NTP["NTP по IP, без зависимости от DNS"]
  NTP --> CA["00-install-ca.rsc: якорь доверия"]
  CA --> DOH["01-configure-doh.rsc: один апстрим и четыре bootstrap-записи"]
  DOH --> POL["02-security-policies.rsc: принудительный 53 и защита резолвера"]
  POL --> MON{"обновление с 0.4.0?"}
  MON -- да --> ONLY["импорт только 05-dns-health-monitor.rsc плюс очистка глобалов"]
  MON -- "новая установка" --> ALL["05-dns-health-monitor.rsc целиком"]
  ONLY --> VER["проверка по таблице из AGENTS.md §5"]
  ALL --> VER

Порядок важен: якорь доверия до апстрима, апстрим до политик, монитор последним. Каждый шаг проверяется отдельным блоком из AGENTS.md §5, и несовпадение ожидаемого значения — это дефект, а не повод «попробовать ещё раз».

ШагФайлЧто делает
0—/export file=before-doh и /system backup save name=before-doh
1пример конфигурацииNTP по IP-адресам: время не должно зависеть от DNS, который мы защищаем
200-install-ca.rscимпорт корневого CA и отметка trusted=yes
301-configure-doh.rscодин апстрим, verify-doh-cert=yes, четыре bootstrap-записи
402-security-policies.rscпринудительный 53 для локальной сети, отбрасывание DNS из WAN
505-dns-health-monitor.rscскрипт монитора и планировщик

Отдельная деталь про закрепление ссылок: main уходит вперёд тега, поэтому raw-файлы лучше брать по коммиту или тегу. В AGENTS.md закреплён коммит 38ef7e0 — это правильный подход, но помните, что вместе с ним вы получите и состояние документации на тот момент.

Проверка после обновления

Проверять нужно не «работает ли интернет», а четыре конкретные вещи: таймаут в теле скрипта, состояние монитора, счётчик попыток и отсутствие ошибок в журнале.

# 1. в скрипте на роутере есть duration=5s
/system script print where name="DNS_HealthMonitor_Failover"
# 2. состояние и счётчик
:put $DNSMonitorState
:put $DNSFailoverAttempts
# 3. разрешение имён идёт через DoH, а не через заглушку
/ip dns cache flush
:put [:resolve example.com]
# 4. в журнале нет ошибок соединения
/log print where message~"connection error"
flowchart TD
  P["/tool fetch к DNS-JSON API, duration=5s"] --> R{"status пробы"}
  R -- finished --> BODY{"в теле есть Status:0?"}
  R -- "timeout или connection error" --> DOWN["endpoint недоступен: считать отказом"]
  R -- "400 или 405" --> TLS["TLS и сертификат прошли: endpoint жив"]
  BODY -- да --> OK["canary пройдена"]
  BODY -- нет --> BAD["считать неудачей"]
  TLS --> OK
  OK --> COUNT{"failureThreshold плохих прогонов подряд?"}
  BAD --> COUNT
  DOWN --> COUNT
  COUNT -- да --> SW["переключение по цепочке с проверкой и откатом"]
  COUNT -- нет --> WAIT["ждать следующего прогона"]
ПроверкаОжидание
Состояние после чистого прогонаhealthy
Счётчик попыток, пока основной отвечает0
Разрешение example.comреальный адрес, не пара 8.6.112.x / 8.47.69.x
/log print where message~"connection error"пусто на здоровой конфигурации
Форма пробыas-value output=user, без временных файлов

Отдельно про диагностику: нельзя делать вывод «DoH мёртв» по ошибке /tool fetch. Ответ 400 или 405 означает, что TLS-рукопожатие и проверка сертификата прошли, — то есть эндпоинт жив, а запрос просто не того типа. Монитор ищет в ответе "Status":0, а не отсутствие ошибки.

Совместимость и ограничения

ЧтоЗначение
RouterOS7.20+ по заявлению, поведение проверено на 7.23.1 и 7.24.5
device-modeadvanced для планировщика; на home монитор запускается только вручную
dedicated механизм блокировки клиентского DoHenforcement=strict: DoT 853 и список публичных DoH
Провайдеры в цепочкетолько те, что отвечают на DNS-JSON; Quad9 в резерв не годится
Полная реприоритизациянет: подъём «вверх» по цепочке, но не переход «вбок» между резервами

Ограничение про «вбок» стоит прочитать внимательно: монитор поднимается с открытого DNS на любой DoH и с резерва возвращается на основной, но не различает «сижу на втором» и «сижу на третьем», чтобы перескочить между ними. Строгий приоритет сохранён: основной всегда предпочтительнее.

Подводные камни

  • 01 и 02 не идемпотентны. Повторный импорт добавит вторую запись в /ip dns static, второе правило NAT и второе правило фильтра. Перед повторным импортом смотрят, что уже есть, по комментариям DoH bootstrap, force DNS, DNS.
  • Резервному хосту нужен свой корневой CA. Для dns.google это не SSL.com: без корня Google проверка сертификата отклонит резерв, переключение не даст разрешения имён, и монитор откатится — то есть вы останетесь на мёртвом основном.
  • duration не отменяет таймаутов планировщика. Пять секунд на пробу — это потолок на одну операцию; полный прогон из шести проб теоретически может занять больше интервала, если все эндпоинты висят.
  • Quad9 не может быть звеном цепочки. На JSON-пробу он отвечает DoH unable to decode BASE64-URL, и выглядит недоступным.
  • device-mode=home блокирует и планировщик, и fetch. На таком устройстве монитор сохраняет текущую конфигурацию и работает только вручную.
  • Документация отстаёт от тега. AGENTS.md в заголовке называет версию 0.4.0, а README в блоке состояния документации пишет «покрытая версия 0.4.0, последний тег v0.3.1», хотя тег 0.4.1 уже выпущен. Читая инструкцию, сверяйтесь с датой и с самим кодом на main — при расхождении верить коду.
  • Флаг filtering ничего не делает. В мониторе он объявлен и нигде не читается: пометка для читателя, а не переключатель поведения.

Чек-лист обновления

  1. Снять /export file=before-041 и /system backup save name=before-041.
  2. Забрать 05-dns-health-monitor.rsc по закреплённому коммиту или тегу, не с main.
  3. Проверить в скачанном файле наличие duration=5s и as-value output=user — до импорта, а не после.
  4. Импортировать монитор и очистить пять глобалов состояния.
  5. Проверить DNSMonitorState и DNSFailoverAttempts после первого чистого прогона.
  6. Убедиться, что разрешение имён даёт реальный адрес, а журнал не содержит connection error.
  7. Если планируется третья ступень — задать dohUrlC и probeUrlC и проверить, что провайдер отвечает на DNS-JSON.

Критерий «обновление прижилось»: состояние healthy, счётчик попыток 0, разрешение имён работает, в журнале нет ошибок, а в теле скрипта на роутере есть duration=5s.

FAQ

Нужно ли обновлять остальные скрипты, кроме монитора?

Нет. В 0.4.1 меняется только 05-dns-health-monitor.rsc; 00, 01 и 02 остались прежними, и повторный их импорт вреден из-за отсутствия проверки существования записей.

Почему нельзя просто поставить duration=10s и забыть?

Можно, но тогда прогон из шести проб в худшем случае растянется до минуты, а интервал по умолчанию — 60 секунд. Пять секунд выбраны как компромисс между «дождаться медленного эндпоинта» и «уложиться в интервал».

Что будет, если третий эндпоинт настроен, но не разрешает имена?

Монитор проверит его после переключения и откатится на основной. Именно этого не хватало до 0.4.1: раньше сеть могла остаться на достижимом, но бесполезном эндпоинте.

Зачем нужен plainFallback, если он ухудшает приватность?

Как последняя ступень на случай, когда не отвечает ни один DoH. По умолчанию он выключен; если включён — в журнал пишется PRIVACY DEGRADED, и версия 0.4.1 сокращает время пребывания в этом состоянии до минимума.

Монитор может «перескочить» со второго эндпоинта на третий?

Нет. Подъём идёт «вверх» — с открытого DNS на DoH и с резерва на основной; различать «на втором» и «на третьем» для перехода вбок он не умеет. Это записано в известных ограничениях релиза.

Откуда берутся числа про шесть проверенных устройств?

Из docs/verification.md репозитория: три устройства на RouterOS 7.24.5 с беспроводным стеком, два без него и одно на 7.23.1. Собственных замеров на оборудовании в этой статье нет.

Стоит ли сразу включать enforcement=strict?

Сначала посмотрите, чем пользуются клиенты: строгий режим блокирует DoT и известные публичные DoH, и приложения, которые их используют, могут перестать разрешать имена. Остаточный риск при standard описан в документации проекта.

Итог

0.4.1 — маленький релиз, который закрывает три конкретные дыры: проба без таймаута, непроверенное переключение на третью ступень и счётчик, считавший только первое переключение. Вместе с 0.3.1 и 0.4.0 это превращает набор из «скрипта, который переключает DoH» в цепочку отказоустойчивости с предсказуемым поведением: автомат состояний, гистерезис против дребезга, последняя ступень на открытом DNS с честной пометкой в журнале и возврат на любой рабочий DoH. Если у вас стоит версия 0.2.0 — обновление стоит одного импорта и одной проверки.

Цитата: Отказоустойчивость — это не «есть второй адрес», а проверка каждой ступени до и после переключения и честный счётчик того, сколько раз сеть уже переезжала.

Связанные материалы

LLM Summary

  • Тема: mikrotik-doh-failover v0.4.1 — релиз надёжности для цепочки отказоустойчивости DoH на MikroTik RouterOS; до него в блоге была только основная статья по состоянию 0.2.0.
  • Что изменилось в 0.4.1: duration=5s на каждом /tool fetch, проверка третьего эндпоинта после переключения с откатом, DNSFailoverAttempts на всю цепочку, возврат из открытого DNS на любой рабочий DoH.
  • Что было пропущено с 0.2.0: 0.3.1 — автомат состояний, гистерезис failureThreshold/recoveryThreshold по 2, monitorInterval 60 с, enforcement=strict, модуль Zabbix; 0.4.0 — проба as-value output=user без временных файлов, цепочка primary → secondary → tertiary → plain, настройки dohUrlC, probeUrlC, plainFallback, plainServers.
  • Ключевые команды: /import file-name=05-dns-health-monitor.rsc, очистка пяти глобалов состояния, :put $DNSMonitorState, :put $DNSFailoverAttempts, /log print where message~"connection error".
  • Замеры: 6 вызовов /tool fetch с таймаутом (поиск по скрипту на main), 3 коммита и 6 изменённых файлов между 0.4.0 и 0.4.1, 6 проверенных устройств, две версии RouterOS (7.23.1 и 7.24.5), 8 элементов и 5 триггеров Zabbix.
  • Ограничения: нельзя ставить Quad9 в цепочку (не отвечает на DNS-JSON), device-mode=home блокирует планировщик и fetch, перехода «вбок» между резервами нет, 01 и 02 не идемпотентны, документация в репозитории отстаёт от тега.
  • Проверка после обновления: состояние healthy, счётчик попыток 0, реальный адрес при разрешении имён, пустой журнал ошибок соединения, duration=5s в теле скрипта на роутере.
  • Ссылки: https://github.com/paulmann/mikrotik-doh-failover/releases/tag/v0.4.1, https://sysadministrator.ru/mikrotik-doh-failover/

Промт для агента (установка и настройка)

Готовый промт для AI-агента: скопируйте блок целиком кнопкой и отдайте своему ассистенту. Шаги, порядок применения и таблица проверок взяты из AGENTS.md репозитория — это инструкция автора для машинного исполнителя, а не пересказ.

Задача: обновить mikrotik-doh-failover до 0.4.1 на роутере RouterOS и доказать, что обновление прижилось.
Материалы:
- репозиторий: https://github.com/paulmann/mikrotik-doh-failover
- релиз: https://github.com/paulmann/mikrotik-doh-failover/releases/tag/v0.4.1
- контракт исполнителя (прочитать целиком до работы): https://github.com/paulmann/mikrotik-doh-failover/blob/main/AGENTS.md
- история изменений: https://github.com/paulmann/mikrotik-doh-failover/blob/main/CHANGELOG.md
Предусловия из AGENTS.md §2 — каждая строка проверяется, провал означает остановку, а не предупреждение:
- RouterOS 7.20 или новее: /system resource print
- время синхронизировано: /system ntp client print, status: synchronized
- device-mode известен: /system device-mode print (на home планировщик и fetch запрещены)
- административная сессия на роутере: импорт пишет в хранилище сертификатов
- файлы CA и .rsc загружены в ФАЙЛЫ роутера, а не в рабочий каталог: import file-name= читает файловую систему роутера
- имена интерфейсов совпадают с реальностью: /interface bridge print, /interface list print
- есть окно обслуживания: /export file=before-doh до первого шага
Порядок применения из AGENTS.md §4:
0. /export file=before-doh и /system backup save name=before-doh
1. NTP по IP-адресам: /system ntp client set enabled=yes servers=162.159.200.1,216.239.35.0
2. /import file-name=00-install-ca.rsc
3. /import file-name=01-configure-doh.rsc
4. /import file-name=02-security-policies.rsc
5. обновление до 0.4.1: /import file-name=05-dns-health-monitor.rsc, затем очистить глобалы DNSMonitorState, DNSFailCount, DNSRecoverCount, DNSFailoverAttempts, DNSMonitorRunning
Проверка из AGENTS.md §5 — ожидаемые значения, несовпадение является отчётом о дефекте:
- /certificate print where trusted=yes — доверенный SSL.com TLS ECC Root CA 2022
- /ip dns print — servers=1.1.1.1,1.0.0.1, use-doh-server=https://cloudflare-dns.com/dns-query, verify-doh-cert=yes
- /ip dns static print where comment~"DoH bootstrap" — четыре записи: две для cloudflare-dns.com, две для dns.google
- /ip firewall nat print where comment~"force DNS" — два правила redirect, UDP и TCP, порт 53
- /ip firewall filter print where comment~"DNS" — accept из LAN, drop из WAN, UDP и TCP
- /ip dns cache flush, затем :put [:resolve example.com] — реальный адрес, НЕ пара 8.6.112.x / 8.47.69.x
- :put $DNSMonitorState — healthy после чистого прогона
- :put $DNSFailoverAttempts — 0, пока отвечает основной
- проба эндпоинта: /tool fetch с as-value output=user и duration=5s — status finished и неотрицательная позиция :find по "Status":0
- /log print where message~"connection error" — пусто на здоровой конфигурации
Запрещено:
- повторно импортировать 01 и 02 без проверки: они не идемпотентны и создадут дубли (/ip dns static print where comment~"DoH bootstrap", /ip firewall nat print where comment~"force DNS", /ip firewall filter print where comment~"DNS")
- считать ошибку /tool fetch доказательством «DoH мёртв»: 400 или 405 означают, что TLS и сертификат прошли
- ставить Quad9 звеном цепочки: он не отвечает на DNS-JSON
- рассчитывать на автоматический планировщик при device-mode=home: там только ручной запуск /system script run DNS_HealthMonitor_Failover
Что считать успехом: состояние healthy, счётчик попыток 0, разрешение имён даёт реальный адрес, журнал без ошибок соединения, в теле скрипта на роутере присутствует duration=5s.