# DNS Autoresolver Утилита автонастройки и проверки DNS-зон: multi-tenant сервис, который сверяет фактическое состояние зоны у провайдера (Selectel DNS API v2) с шаблоном записей, показывает диф и применяет изменения только вручную — никакого автоматического apply без подтверждения оператора. ## Возможности - **Multi-tenant**: проекты, аккаунты провайдера, домены — с авторизацией (регистрация/логин, сессии); всё изолировано по проекту. - **Провайдер Selectel (Cloud DNS v2)**: авторизация через project IAM-токен сервисного пользователя (не статический API-ключ — см. ниже), чтение зон/RRSet, импорт зон, диф против шаблона, ручной apply. - **Шаблоны записей с плейсхолдером `{{domain_name}}`**: один шаблон переиспользуется на многих доменах — при проверке подставляется имя зоны. Шаблон можно завести вручную или снять снимком с существующей зоны («создать шаблон из зоны», с авто-параметризацией имени домена). - **Просмотр зоны без шаблона**: текущие записи зоны видны даже до привязки шаблона; статус домена без шаблона — «без шаблона», а не `unknown`. - **Диф + выборочный ручной apply**: чекбоксы на каждой записи (updates и prunes), удаления по умолчанию сняты (opt-in). Удаления применяются **перед** обновлениями — иначе провайдер отвергает конфликт (например `CNAME` на имени, где ещё жива `A`-запись). При ошибке показывается реальный ответ провайдера, а не generic-текст. - **Расписание проверок**: планировщик периодически гоняет read-only check+notify (без Apply), пишет историю проверок и статус drift. - **Уведомления**: каналы Telegram и Webhook, per-channel статус доставки. - **Метрики**: Prometheus `/metrics` (публичный, без auth, только агрегаты). - **Health-check**: `/healthz` — liveness-проба, используется как Docker `HEALTHCHECK` через встроенный CLI-режим `app -healthcheck`. ## Учётные данные Selectel Cloud DNS v2 требует **project IAM-токен**, а не статический API-ключ. При добавлении аккаунта Selectel в UI указываются данные **сервисного пользователя**: - имя сервисного пользователя, - пароль, - номер аккаунта (`account_id`, вида `123456`), - имя проекта. Сервисный пользователь создаётся в панели Selectel (раздел [Пользователи и роли](https://my.selectel.ru/iam/users)) и ему выдаётся роль на нужный проект. Приложение само обменивает эти данные на 24-часовой IAM-токен (Identity API `cloud.api.selcloud.ru`) и кэширует его; данные хранятся зашифрованными (AES-256-GCM), пароль не логируется. Учётные данные проверяются пробным логином прямо при добавлении аккаунта. ## Рабочий процесс 1. Зарегистрироваться (self-registration, автоматически создаётся личный проект). 2. Добавить аккаунт Selectel (данные сервисного пользователя, см. выше). 3. Импортировать зоны аккаунта — на каждую зону заводится домен. 4. Привязать шаблон: создать снимком из зоны или собрать вручную с плейсхолдерами `{{domain_name}}`; без шаблона доступен только просмотр записей. 5. Открыть диф домена, отметить нужные изменения/удаления, применить. ## Стек Go 1.26 (statically-linked бинарь, SPA встроена через `embed`), React + Vite (SPA), PostgreSQL 17, Prometheus client, distroless/static-debian12 рантайм-образ. ## Запуск в Docker Требуется Docker Engine + Docker Compose v2. 1. Скопировать пример конфигурации: ```bash cp .env.example .env ``` 2. Сгенерировать ключ шифрования секретов (провайдеров/каналов) — ровно 32 байта в base64 — и вписать его в `.env` как `DNS_AR_ENC_KEY`: ```bash openssl rand -base64 32 ``` Также задать `POSTGRES_PASSWORD` (без дефолта — сервис не поднимется без явного пароля). 3. Поднять стек (postgres + app), сборка образа приложения — на лету: ```bash docker compose up -d --build # или: make docker-up ``` `app` стартует только после того, как `postgres` станет healthy; миграции схемы БД приложение накатывает само при старте. 4. Открыть UI: http://localhost:8080 - Метрики (Prometheus): http://localhost:8080/metrics - Health-check: http://localhost:8080/healthz Остановить стек: `docker compose down` (или `make docker-down`). Логи приложения: `docker compose logs -f app` (или `make docker-logs`). ### Переменные окружения (`.env`) | Переменная | Назначение | По умолчанию | |---------------------|----------------------------------------------------------|--------------| | `POSTGRES_USER` | пользователь PostgreSQL | `dnsar` | | `POSTGRES_PASSWORD` | пароль PostgreSQL — **обязателен**, без дефолта | — | | `POSTGRES_DB` | имя БД | `dnsar` | | `APP_PORT` | порт публикации приложения на хосте | `8080` | | `DNS_AR_ENC_KEY` | ключ шифрования секретов, base64 → ровно 32 байта — **обязателен** | — | Секреты передаются только через переменные окружения, никогда — в образ, логи или git. ## Локальная разработка (без Docker) ```bash make build # go build ./... make test # go test ./... (тесты internal/store требуют Docker — testcontainers) make web # сборка SPA (npm ci && npm run build) в internal/web/dist make build-all # web + build go test ./internal/service/ -run TestName -v # один тест / пакет cd web && npm run test -- --run # фронт-тесты (Vitest) cd web && npx tsc --noEmit # проверка типов SPA ``` Для запуска бинаря напрямую нужны те же переменные окружения: `DNS_AR_DB_DSN`, `DNS_AR_ENC_KEY` (обязательные), `DNS_AR_LISTEN` (по умолчанию `:8080`). > `internal/web/dist/` — цель `go:embed`; в git коммитится только плейсхолдер > `index.html`. `npm run build` перезаписывает его — перед коммитом выполнить > `git checkout internal/web/dist/index.html`, а реальный бандл собирать через > `make web`.