← Заметки

Zone-o-Matic: как DynDNS-эндпоинт вырос в сторону записи моего DNS

· dns, acme, cert-manager, go, homelab

Мой авторитативный DNS проще некуда: CoreDNS на хабах отдаёт обычные зонные файлы плагином file, который перечитывает их, когда меняется serial в SOA. Легко читать, легко бэкапить, легко смотреть diff в git.

Zone-o-Matic — небольшой сервис на Go, который пишет в эти файлы. Он не задумывался как большой проект — каждый API в нём появился вместе с новым клиентом, которому нужно было поменять запись.

Исходная задача: роутеры и acme.sh

Август 2025. Часть моих роутеров сидит за динамическими адресами, и мне хотелось держать их имена в своих зонах, а не у стороннего DynDNS-провайдера. Эти же роутеры получают TLS-сертификаты через acme.sh, которому нужно публиковать записи для вызовов dns-01.

Поэтому первая версия говорила на двух уже существующих протоколах:

Оба сводятся к «выставь эту запись в том зонном файле».

Продолжение: lego

Ноябрь 2025. Хосты вне OpenWrt — например, S3-шлюз — продлевают сертификаты через lego. У lego есть универсальный провайдер httpreq: два вызова, /present и /cleanup, которые добавляют и удаляют одно значение вызова. С ними сертификат на имя и его wildcard можно проверять одновременно.

Продолжение: PowerDNS для Proxmox

Апрель 2026. SDN в Proxmox VE умеет регистрировать виртуалки в DNS, но только через сервер PowerDNS. Вместо того чтобы поднимать PowerDNS рядом с CoreDNS, zoneomatic получил то подмножество API PowerDNS, которое Proxmox реально вызывает.

Продолжение: RFC2136 — из-за cert-manager

Сентябрь 2026. Кластеры Kubernetes выпускают сертификаты через cert-manager. Встроенного солвера для lego httpreq в cert-manager нет — пришлось бы ставить отдельный webhook-плагин в каждый кластер. Зато встроен солвер rfc2136: обычный DNS UPDATE с аутентификацией через TSIG.

Так zoneomatic научился говорить на DNS UPDATE. Слушателей два: ACME-шный, который принимает только TXT-записи _acme-challenge, и для полных обновлений, которым могут пользоваться nsupdate и knsupdate. Теперь каждому кластеру нужен только ClusterIssuer и TSIG-ключ — никакого webhook. Примерно тогда же DynDNS-обновления научились синхронизировать соответствующие PTR-записи в обратных зонах.

Бережно с зонными файлами

При таком количестве писателей зонные файлы всё равно должны выглядеть так, будто их аккуратно правил человек:

Форматтер, pkg/dnsfmt, — отдельный модуль Go, а запись зон покрыта фаззингом.

Безопасность и тестирование

HTTP-пользователи берутся из htpasswd-файла с bcrypt-хешами; RFC2136 использует TSIG-ключи с закреплённым алгоритмом. TSIG проверяет подлинность запроса, но не шифрует его, поэтому слушатели RFC2136 должны жить на приватном интерфейсе — у меня это VPN mesh — и вдобавок за списком разрешённых адресов. Писать можно только в заранее заданные зонные файлы.

Кроме юнит- и end-to-end-тестов с настоящими nsupdate/knsupdate, цель make k3d поднимает одноразовый кластер k3d с cert-manager, Pebble в роли ACME-сервера и CoreDNS и выпускает настоящие сертификаты через zoneomatic. CI гоняет то же самое.

Для контейнеров есть Dockerfile, а для OpenWrt — пакет в моём фиде — так он и работает рядом с CoreDNS на роутерах.