softdevtools-dns

Технический проект DNS-заявок

Статус: публичный пилот; проверка PR и DNS-деплой работают. Цель — обработать заявки примерно 96 студентов в одной учебной DNS-зоне softdevtools.ru. Репозиторий создан закрытым для подготовки, затем открыт для пилота. После проверки инструкция будет выдана студентам для fork и PR. Решение принимает владелец репозитория после проверки содержимого и защиты ветки.

Поток данных и границы доверия

Студент: публичный сайт на Pages + PR с YAML
       → проверка PR без секретов
       → ручная сверка автора и одобрение преподавателя
       → merge в main (защищённую перед доступом студентов)
       → повторная проверка и DNS Edit через Cloudflare API
       → отдельная проверка DNS, HTTP и HTTPS

Автор PR и содержимое его ветки недоверенные. ФИО и контактный email поступают через отдельный закрытый канал. Публичная заявка не служит доказательством личности, а поле с самостоятельно указанным github_id не принимается как доказательство владения поддоменом.

Данные заявки: проект схемы

Один файл в records/ описывает один CNAME на одно одноуровневое имя:

subdomain: ivan-ivanov
type: CNAME
target: ivan123.github.io
repository: https://github.com/ivan123/my-lab-site

В MVP разрешены только ASCII-метки из латинских букв, цифр и дефиса, без дефиса по краям; только CNAME на username.github.io, DNS only (proxied=false). Список зарезервированных имён задаётся преподавателем. Не принимаются пользовательские A/AAAA/MX/TXT, wildcard, вложенные поддомены и URL вместо DNS-имени. Для subdomain валидатор принимает только нижний регистр и метку длиной до 47 символов: вместе с .softdevtools.ru полное имя получается короче 64 символов, что требуется GitHub Pages для HTTPS-сертификата. Регистр GitHub-логина в target может быть любым; перед созданием DNS-записи цель приводится к нижнему регистру.

Предпочтительный формат студенческого имени — imya-familiya, например ivan-ivanov.softdevtools.ru: имя и фамилия записываются латиницей в нижнем регистре, через дефис. При совпадении можно предложить свободный вариант с числовым суффиксом, например ivan-ivanov-2. Публиковать реальные имя и фамилию в адресе необязательно: студент может согласовать с преподавателем псевдоним. Валидатор проверяет технический формат, а соответствие выбранного имени договорённости и автору PR сверяет преподаватель.

Сопоставление target, владельца репозитория сайта и автора PR требует отдельной проверки. Для уже принятого имени право изменения нужно закрепить доверенными метаданными, которые студент не может назначить себе сам. Точный формат этих метаданных пока не выбран; до его появления изменения и удаление ранее принятых заявок не разрешаются автоматически.

Проверка PR без секретов

Проверка должна давать конкретную причину отказа. Минимум:

  1. Корректный YAML с известными полями и ровно одной заявкой.
  2. Допустимое, не зарезервированное и ещё не занятое имя; отсутствие дублей внутри предлагаемого набора файлов.
  3. Разрешённый тип и корректный target; ссылка на публичный репозиторий сайта имеет ожидаемую форму.
  4. В студенческом PR изменяются только новые файлы в records/. Workflow, скрипты, конфигурация и уже принятые заявки недоступны для студенческой правки в этом процессе.
  5. Проверки пересчитываются перед merge: два параллельных PR могут выбрать одно имя. Деплой повторно проверяет реальную зону и не перезаписывает неожиданную запись.

Сообщения об ошибке адресованы студенту и написаны по-русски: что не так с заявкой, где именно (файл и строка) и что сделать, без внутренних деталей валидатора. Формулировки живут в scripts/messages.py, правила — в scripts/validate.py и scripts/validate_pr.py; проверка ничего не исправляет автоматически и не ослабляется ради понятности: неверные имена и содержимое по-прежнему отклоняются. В GitHub Actions те же замечания печатаются аннотациями (::error file=…,line=…) для файлов из PR, а полный список попадает в сводку шага (GITHUB_STEP_SUMMARY): аннотаций на шаг не больше десяти, поэтому сводка дублирует их полностью. Для заявок из fork аннотации создаёт доверенный job validate-student-request на событии pull_request_target.

Проверка синтаксиса и структуры должна выполняться без сети и секретов. Сетевые проверки GitHub Pages, DNS и HTTPS выполняются отдельно, когда публикация уже началась. Проверка PR не ставит студенту итоговую оценку.

Применение DNS

Деплой запускается из main после принятия PR. Он повторно валидирует доверенное состояние, сверяет запись с Cloudflare и создаёт либо идемпотентно подтверждает только запись, которой управляет этот проект. Чужие и созданные вручную записи не меняются и не удаляются. Удаление по исчезновению YAML и полная синхронизация зоны в MVP не предусмотрены.

Cloudflare API Token хранится как GitHub Actions Secret или Environment Secret и ограничен правом DNS Edit для одной зоны. Токен не попадает в workflow проверки PR. Безопасность секретного workflow зависит также от защиты ветки и контроля изменений кода: хранение токена в Secrets само по себе не защищает от вредоносного workflow после merge. В публичном пилоте только владелец имеет право записи в основной репозиторий. Перед доступом студентов обязательна защита main и проверка сценария с fork.

Workflow apply-dns.yml получает событие закрытого PR в main только при изменении records/*.yaml и выполняет работу лишь для слитого PR. Он берёт доверенное состояние основной ветки, а не явный SHA из события PR: в pull_request_target actions/checkout@v7 блокирует прямой checkout кода fork. Студенческая ветка может менять только данные заявки, поэтому её код не запускается с секретом. Сам секрет не выдаётся PR. Шаг записи включён переменной репозитория CLOUDFLARE_DNS_DEPLOY_ENABLED=true для закрытого пилота. Скрипт apply_dns.py по умолчанию лишь читает DNS и выводит план; --apply создаёт отсутствующие CNAME. Он помечает свои записи комментарием Managed by denrad/softdevtools-dns, проверяет все имена на конфликты до первой записи и не присваивает чужие записи даже при совпадении имени и цели. При ошибке API после частичной записи повторный запуск пропускает уже созданные им записи.

Проверка результата и сроки

Подробный проект читающего workflow, повторных запусков и его проверок — в плане проверки сайтов. Ниже зафиксирована минимальная модель результата.

Проверяющий процесс хранит отдельные результаты:

Проверка Успех Пока ожидается
dns_live публичный CNAME равен ожидаемому DNS ещё не виден через резолвер
site_live HTTP или HTTPS отдаёт ожидаемый отличительный текст Pages ещё не направляет имя на сайт
https_live обычный HTTPS-запрос проверяет сертификат и текст сертификат ещё выпускается

Команда curl -k может помочь диагностировать страницу при ещё невалидном сертификате, но не даёт статус https_live=passed. HTTP 200 без проверки содержимого также недостаточен. Статусы результата: passed, failed, pending, not_checked. Повторная проверка возможна без нового PR; расписание не должно трактовать временную задержку DNS или сертификата как ошибку студента.

На занятии достаточно подтвердить заявку, DNS и доступность именно нужной страницы по HTTP либо HTTPS. Валидный HTTPS проверяется позже. Итоговое понимание цепочки оценивает преподаватель.

Пилот и незакрытые решения

Справка: GitHub Pages и Custom domain, подтверждение домена, Cloudflare: лимиты DNS.