# traefik-selectel-dns Демон, который создаёт A-записи в Selectel DNS (API v2) для хостов из роутеров Traefik. Сертификаты выписывает сам Traefik (встроенный `dnsChallenge.provider: selectelv2`) — кода для этого не нужно. ## Почему не плагин Traefik Плагин (Yaegi) работает внутри цепочки обработки запросов: он не видит создание/удаление роутов и не должен хранить токен с правом записи в DNS. Отдельный демон читает Traefik API (`/api/http/routers`), поэтому одинаково покрывает Docker labels и file-provider. ## Как работает 1. Раз в `interval` (и сразу после событий Docker) читает `GET /api/http/routers`. 2. Из `rule` берёт все `Host(...)` у роутеров со статусом `enabled` (`HostRegexp`, `!Host(...)` игнорируются, `*.x` пропускаются с warning). 3. Хосты, попадающие в `zones` из конфига (равны зоне или её поддомен; при вложенных зонах — самая длинная), получают A-запись `ip`/`ttl`. 4. Записи, созданные демоном, помечаются `comment: managed-by=traefik-selectel-dns`. Меняются/удаляются только они. Существующая A-запись без метки (и имя с CNAME) не трогается — только warning. 5. Удаление выключено по умолчанию. При `delete.enabled: true` запись удаляется после того, как роут отсутствует непрерывно `delete.gracePeriod` (состояние в памяти; после рестарта отсчёт начинается заново). Роутеры со статусом `warning` считаются присутствующими. При ошибке Traefik API или пустом списке (после непустого) цикл пропускается и ничего не удаляется. ## Настройка Пример полного стенда: `deploy/` (`docker-compose.example.yml`, `traefik.example.yml`, `config.example.yaml`, `dynamic/`). 1. Создайте в Selectel сервисного пользователя с минимальной ролью на DNS проекта. 2. Положите креды в файлы секретов (`deploy/secrets/*`, не коммитить) — см. комментарий в compose. 3. Поправьте `zones`, `ip`, `traefik.apiURL` в `config.example.yaml`; wildcard `tls.domains` — в `traefik.example.yml`. 4. Для первого запуска включите `dryRun: true` и проверьте логи. Сборка: `docker build -t traefik-selectel-dns .` (distroless, non-root, HEALTHCHECK через `-healthcheck`). ## Конфигурация YAML (`-config` или `TSD_CONFIG`), неизвестные поля — ошибка. Переопределение через env: | YAML | env | По умолчанию | |---|---|---| | `zones` | `TSD_ZONES` (через запятую) | обязательно | | `ip` | `TSD_IP` | обязательно, IPv4 | | `ttl` | `TSD_TTL` | 300 (60..604800) | | `interval` | `TSD_INTERVAL` | 30s | | `dryRun` | `TSD_DRY_RUN` | false | | `traefik.apiURL` / `.timeout` | `TSD_TRAEFIK_API_URL` / `TSD_TRAEFIK_TIMEOUT` | http://traefik:8080 / 10s | | `docker.enabled` / `.host` | `TSD_DOCKER_ENABLED` / `TSD_DOCKER_HOST` | true / unix:///var/run/docker.sock (`tcp://` тоже) | | `delete.enabled` / `.gracePeriod` | `TSD_DELETE_ENABLED` / `TSD_DELETE_GRACE_PERIOD` | false / 10m | | `selectel.baseURL` / `.authURL` / `.timeout` | `TSD_SELECTEL_BASE_URL` / `_AUTH_URL` / `_TIMEOUT` | api.selectel.ru/domains/v2, cloud.api.selcloud.ru/identity/v3, 30s | | `listen` | `TSD_LISTEN` | :9090 (`/healthz`) | | `logLevel` / `logFormat` | `TSD_LOG_LEVEL` / `TSD_LOG_FORMAT` | info / json | Секреты — только env (каждая поддерживает суффикс `_FILE`; одновременно обе — ошибка): `SELECTEL_USERNAME`, `SELECTEL_PASSWORD`, `SELECTEL_ACCOUNT_ID`, `SELECTEL_PROJECT_ID`. Для Traefik (lego) те же значения передаются как `SELECTELV2_USERNAME_FILE`, `SELECTELV2_PASSWORD_FILE`, `SELECTELV2_ACCOUNT_ID_FILE`, `SELECTELV2_PROJECT_ID_FILE`. ## Безопасность - Секреты не попадают в конфиг, образ и логи; IAM-токен (Keystone) кэшируется в памяти и обновляется заранее. - Traefik API не публикуется наружу и ограничен `ipAllowList` подсетью сети `control`; пользовательские контейнеры в неё не подключать. - Docker socket = root на хосте. Демону нужны только события, поэтому в примере используется `docker-socket-proxy` (`EVENTS=1`), а не прямой сокет. Без Docker events демон работает по опросу. - Чужие записи не перезаписываются; удаление выключено по умолчанию. ## Ограничения - Только A-записи, один фиксированный IPv4 для всех хостов; AAAA нет. - Только `Host(...)` с ASCII-именами (IDN — в punycode); `HostRegexp` и wildcard-хосты не поддерживаются. - Метка владения — exact-match комментария; если её вручную изменить/стереть, запись считается чужой. - Grace-состояние в памяти; несколько реплик демона одновременно не поддерживаются. - Поток Docker events без общего таймаута (переподключается при обрыве; страховка — периодический опрос). - Реальный Selectel API не проверялся (нет кредов) — см. отчёт в `swarm-report/`.