Ежеквартальная ревизия DNS-записей: что осталось от старых проектов
Откройте DNS-зону своего основного домена и посчитайте, сколько записей в ней указывает на проекты, о которых вы уже год как не вспоминали. У большинства компаний с историей больше пары лет таких записей наберётся десяток-другой: тестовые стенды, лендинги закрытых кампаний, поддомены уволившихся подрядчиков. Часть из них просто занимает место в зоне, а часть — открытая дверь, которой достаточно воспользоваться постороннему. Ниже — рабочий регламент, как раз в квартал приводить DNS-зону в порядок, не ломая при этом то, что всё ещё нужно.
Содержание
- Откуда берутся забытые записи и почему это не безобидный мусор
- Subdomain takeover: когда забытая запись превращается в дыру
- Шаг 1. Собрать полный список записей в зоне
- Шаг 2. Для каждой записи выяснить, куда она указывает и жива ли ещё
- Шаг 3. Период наблюдения перед окончательным удалением
- Как встроить это в регламент, а не делать разово
Откуда берутся забытые записи и почему это не безобидный мусор
DNS-зона растёт только в одну сторону. Когда стартует новый проект — маркетинговая кампания, тестовое окружение для подрядчика, интеграция с внешним сервисом — в зону добавляется запись: A, CNAME, иногда сразу несколько вместе с TXT для верификации домена. Когда проект закрывается, эта запись почти никогда не удаляется вместе с ним: удаление DNS-записи не входит ни в один чек-лист выключения проекта, а сама процедура выглядит рискованной — «а вдруг это на что-то ещё влияет, лучше не трогать».
За несколько лет в зоне обычной компании накапливается несколько типичных категорий мёртвого груза:
- Поддомены закрытых маркетинговых кампаний —
promo2024.example.com,webinar-may.example.com— указывали на лендинги, площадка кампании давно снесена, запись осталась. - Тестовые и staging-окружения, которые подняли для одной задачи и забыли выключить:
test.example.com,dev2.example.com,staging-old.example.com. - Поддомены бывших подрядчиков — CNAME на внешнюю платформу, с которой давно не работаете, но доступ отзывать было некому.
- Записи под интеграции, от которых отказались — TXT-записи верификации для сервисов, которыми больше не пользуетесь, MX-записи для почтовой платформы, с которой мигрировали на другую.
- Записи «на всякий случай» — когда-то держали резервный IP или запасной поддомен для эксперимента, эксперимент не выстрелил, запись осталась «вдруг пригодится».
Механизм тут тот же самый, что и с накоплением любого другого технического долга: удалить что-либо в моменте кажется рискованным, поэтому проще оставить, а стоимость решения накапливается незаметно и оплачивается позже, кем-то другим. И проблема не только в эстетике зоны: забытая запись — это адрес, который продолжает быть частью вашего домена, индексируется поисковиками, попадает в сканеры при разведке цели и в какой-то момент начинает указывать на ресурс, который вам уже не принадлежит.
Subdomain takeover: когда забытая запись превращается в дыру
Самый опасный сценарий — CNAME-запись, которая указывала на внешний облачный сервис (хостинг статики, конструктор лендингов, платформу деплоя приложений), а сам ресурс на стороне этого сервиса был удалён или подписка закончилась. Провайдер освобождает идентификатор ресурса, и в части архитектур он снова становится доступен для регистрации — уже кем угодно. Ваша CNAME-запись при этом никуда не делась и продолжает указывать на тот же идентификатор. Злоумышленнику остаётся зарегистрировать на своей стороне ресурс с тем же именем — и promo2024.example.com начинает открывать не заброшенную страницу, а чужой контент под вашим доменом и вашим TLS-сертификатом (если он выпускается автоматически через Let's Encrypt для этого имени). Это не абстрактная угроза: подробный разбор механики такой атаки — в статье поддомен угнали через забытую запись DNS, а пример конкретного случая с уязвимой CMS на старом поддомене — в статье старый поддомен со сломанной CMS нашли раньше вас.
Практический риск отсюда прямой:
- Фишинг с доверенным доменом. Поддомен вашей компании — это готовая площадка для страницы, имитирующей вход в личный кабинет или форму оплаты, с настоящим сертификатом и настоящим именем в адресной строке.
- Репутационный удар. Пользователь, партнёр или журналист, увидевший чужой контент на
*.example.com, не разбирается в деталях DNS — для него это ваш домен показывает что-то не то. - Обход фильтров и списков доверенных доменов. Некоторые корпоративные фильтры и антивирусы доверяют доменам компаний-партнёров — угнанный поддомен обходит эту проверку бесплатно.
Проверить конкретную запись на признаки уязвимости можно быстро — запросить, куда она указывает, и посмотреть, что оттуда отвечает:
dig +short CNAME promo2024.example.com
# random-id.landing-builder.example
curl -s -o /dev/null -w "%{http_code}\n" https://promo2024.example.com
curl -s https://promo2024.example.com | head -20
Характерный признак «свободного» ресурса — ответ вида NoSuchBucket, There isn't a GitHub Pages site here, No such app, 404 - project not found или похожая заглушка сервиса, а не привычная страница вашего проекта. Именно такая запись — приоритет номер один на удаление, а не «когда-нибудь дойдут руки».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверШаг 1. Собрать полный список записей в зоне
Ревизия начинается не с памяти («вроде помню, что там было»), а с полного экспорта зоны — иначе часть записей неизбежно останется вне поля зрения. Способ экспорта зависит от того, где живёт DNS.
Если зона на собственном PowerDNS:
pdnsutil list-zone example.com
Или через API, если веб-интерфейс не под рукой:
curl -s -H "X-API-Key: $PDNS_API_KEY" \
http://127.0.0.1:8081/api/v1/servers/localhost/zones/example.com. \
| jq -r '.rrsets[] | "\(.type)\t\(.name)\t\(.records[].content)\t\(.ttl)"'
Если зона у стороннего DNS-провайдера (Cloudflare, регистратор, панель хостера) — экспорт делается через его API или через кнопку «экспортировать зону» в веб-интерфейсе, которая обычно выдаёт файл в формате BIND. Пример для Cloudflare API:
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?per_page=500" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-H "Content-Type: application/json" \
| jq -r '.result[] | "\(.type)\t\(.name)\t\(.content)\t\(.ttl)"'
Zone transfer (AXFR) сработает, только если он у вас явно разрешён для своего IP — в большинстве публичных DNS он закрыт по умолчанию, и включать его специально ради разовой выгрузки не стоит.
Результат сохраните в текстовый файл и, что важнее, в git-репозиторий — тогда следующая ревизия через квартал делается через git diff между старым и новым экспортом, и сразу видно, что реально изменилось. Выгрузку стоит разложить по типам записи — с A/AAAA/CNAME работа одна, с MX и TXT (SPF, DKIM, верификация сервисов) другая, осторожнее: одна ошибочно удалённая TXT-запись SPF ломает доставку почты для всего домена. И учитывайте, что изменения расходятся по кэшам резолверов неравномерно и не мгновенно — это пригодится на следующем шаге.
Шаг 2. Для каждой записи выяснить, куда она указывает и жива ли ещё
Для каждой строки списка нужен ответ на два вопроса: на что запись указывает сейчас, и пользуется ли этим кто-то. Проверка проще, чем кажется, если пройти её по порядку для каждой записи.
Куда указывает. Для A/AAAA — сам IP, для CNAME — конечный хост, до которого он резолвится:
dig +short A test.example.com
dig +short CNAME old-crm.example.com
Если запись — A-запись на IP, который принадлежит вашей же инфраструктуре, проверьте, что именно слушает на этом адресе:
curl -sI https://test.example.com
ssh admin@10.0.0.15 "ss -tlnp | grep -E ':80|:443'"
Если это внешний IP или CNAME на чужой сервис — узнайте, чей это ресурс:
whois 203.0.113.42
dig +short CNAME test.example.com
Используется ли ещё. Прямой ответ на этот вопрос почти никогда не даёт одна команда — нужна комбинация источников:
- Логи веб-сервера. Проверьте частоту обращений именно к этому имени хоста за последние 90 дней:
grep "test.example.com" /var/log/nginx/access.log* | wc -l. Ноль или единичные обращения от сканеров — сильный сигнал, что запись не нужна. - Конфиги vhost. Есть ли вообще блок
server_nameпод это имя в текущей конфигурации — если нет, а DNS-запись есть, это явный кандидат на удаление в первую очередь. - Мониторинг и алертинг. Если поддомен не значится в списке отслеживаемых целей и никогда не значился последние полгода, о его работоспособности никто и не заботится.
- Люди. Спросить в общем канале команды прямым текстом: «кто-нибудь ещё пользуется
old-crm.example.com, удаляю через две недели, если возражений нет» — самый недооценённый метод. Формальная тишина за оговорённый срок — достаточное основание двигаться дальше.
Отдельно стоит свериться с реестром доменов и сертификатов, если он у вас ведётся, — записи, которые фигурируют там как «активный проект», трогать нельзя без дополнительного согласования, даже если по логам трафика почти нет: это может быть сервис с редкими, но критичными обращениями (резервный вебхук, интеграция с редко используемым партнёром).
Записи, ответ по которым неочевиден — трафик есть, но что это за трафик, непонятно, — не удаляйте сразу. Переносите в отдельный список «требует выяснения» и разбирайтесь на следующей итерации, если срочности нет.
Шаг 3. Период наблюдения перед окончательным удалением
Прямое удаление записи, даже кажущейся однозначно мёртвой, — плохая практика: DNS-кэши резолверов по всему миру ещё какое-то время хранят старое значение, а сама уверенность «точно никто не пользуется» подтверждена логами за конкретный период, а не гарантированно навсегда. Безопаснее — двухфазный процесс.
Фаза 1. Снизить TTL и подготовиться. За одну-две недели до предполагаемого удаления снизьте TTL записи до минимального значения (300 секунд обычно достаточно) — это не удаляет запись, а лишь ускоряет распространение будущего изменения по кэшам:
# было
old-crm.example.com. 86400 IN CNAME crm-provider.example.net.
# стало
old-crm.example.com. 300 IN CNAME crm-provider.example.net.
Фаза 2. Наблюдение вместо удаления. Вместо того чтобы сразу стереть запись, перенаправьте её на контролируемый вами адрес — отдельный небольшой VPS или существующий сервер с nginx, который отвечает 410 Gone и логирует каждое обращение с исходным IP и User-Agent:
server {
listen 443 ssl;
server_name old-crm.example.com;
ssl_certificate /etc/ssl/observe/fullchain.pem;
ssl_certificate_key /etc/ssl/observe/privkey.pem;
access_log /var/log/nginx/dns-observe.log combined;
location / {
return 410 "Этот адрес больше не используется.";
}
}
Оставьте так на 30 дней — этого хватает, чтобы поймать редкие обращения (месячные cron-задачи на внешних системах, ежеквартальные интеграции, забытые закладки у клиентов). Раз в неделю проверяйте dns-observe.log:
grep old-crm.example.com /var/log/nginx/dns-observe.log | wc -l
awk '{print $1}' /var/log/nginx/dns-observe.log | sort -u
Если за период наблюдения обращений нет или они однозначно опознаны как чужие сканеры (боты, массово перебирающие поддомены наугад — таких много и в норме, отличить их можно по случайным User-Agent и по попыткам обратиться сразу к десяткам несуществующих путей), запись можно удалять окончательно. Если обнаружился неожиданный источник трафика — расследуйте, прежде чем удалять что-либо дальше.
Фаза 3. Удаление и фиксация. После удаления самой записи из зоны запишите факт в реестр доменов (что удалено, когда, кем, на основании чего) — это тот же принцип документирования, что и в регламенте вывода сервиса из эксплуатации: решение и его причина должны остаться в записях команды, а не только в чьей-то памяти, иначе через год кто-то снова потратит время на выяснение, зачем и почему запись пропала.
Как встроить это в регламент, а не делать разово
Разовая уборка закрывает накопленный долг один раз, но зона снова начнёт зарастать записями со следующего же проекта. Смысл в периодичности:
| Действие | Периодичность | Кто отвечает | Инструмент |
|---|---|---|---|
| Полный экспорт зоны | Раз в квартал | Ответственный за инфраструктуру | pdnsutil list-zone / API провайдера |
| Diff с прошлым экспортом | Раз в квартал | Тот же | git diff по сохранённым экспортам |
| Проверка «на что указывает» для новых/подозрительных записей | Раз в квартал | Тот же | dig, curl, whois |
| Запрос в команду по спорным записям | Раз в квартал, по необходимости | Тот же | Общий чат/канал |
| Фаза наблюдения (30 дней) | По факту кандидата на удаление | Тот же | nginx-заглушка с логами |
| Обновление реестра доменов | При каждом удалении | Тот же | Файл/таблица реестра |
Держать список в git даёт побочный эффект — можно частично автоматизировать сравнение. Простой cron-скрипт, который раз в квартал выгружает зону в файл, коммитит его в git-репозиторий и печатает git diff --stat относительно прошлого коммита, снимает вопрос «а вдруг мы забыли посмотреть» и оставляет историю изменений, к которой можно вернуться. Это не замена ручной проверке из шагов 2 и 3 — вручную всё равно нужно смотреть, кто и зачем пользуется конкретной записью, — а лишь способ не потерять из виду, что вообще изменилось.
Хорошая точка привязки регламента — квартальный технический созвон или уже существующая в компании регулярная проверка инфраструктуры: DNS-ревизию можно проводить тем же циклом, тогда она не потребует отдельного напоминания в календаре — просто ещё один пункт к уже существующей регулярной практике.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько времени занимает первая ревизия, если её никогда не делали?
Для домена с полусотней записей закладывайте день-два: основную часть времени съедает не выгрузка, а выяснение по каждой сомнительной записи. Последующие квартальные ревизии — уже пара часов, потому что смотреть нужно только на diff с прошлым разом.
Можно ли автоматически удалять записи, которые не отвечают на пинг?
Нет. Отсутствие ответа по HTTP/ICMP не означает, что запись не нужна — за ней может стоять MX для почты или CNAME для интеграции, которая обращается редко и не отвечает как веб-сервер. Автоматизация должна выявлять кандидатов, а решение об удалении — оставаться за человеком.
Что делать, если запись явно указывает на уже свободный внешний ресурс — удалять сразу, без 30 дней наблюдения?
Такую запись стоит убирать быстрее обычного цикла: каждый день с висящей CNAME на потенциально захватываемый ресурс — это открытое окно риска. Наблюдение имеет смысл для записей, где неясно, используются ли они, а не для явно уязвимых.
Как быть с TXT-записями SPF и DKIM — их тоже нужно ревизовать?
Да, но осторожнее: устаревшая ссылка на сервис рассылок, которым больше не пользуетесь, — тоже мусор и небольшой риск, но удаление SPF-записи целиком по ошибке ломает доставку всей легитимной почты. Каждую строку SPF проверяйте отдельно, не удаляйте всю запись оптом.
Нужно ли включать в ревизию поддомены, делегированные через NS-записи на другого DNS-провайдера?
Обязательно — делегированная зона живёт своей жизнью и точно так же накапливает мусор, а иногда про её существование забывают в первую очередь.
Что если ресурсов на полноценный процесс с фазой наблюдения нет — можно упростить?
Минимальный вариант — раз в квартал выгружать зону, беглым просмотром находить очевидно мёртвые записи и хотя бы снижать им TTL с пометкой на удаление через месяц. Хуже полного процесса, но лучше, чем ничего.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →