68 lines
6.2 KiB
Markdown
68 lines
6.2 KiB
Markdown
# 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/`.
|