Files
2026-07-05 17:01:33 +07:00

140 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.