«Это ТСПУ РКН, поделать ничего нельзя» — а проблема три месяца ждала в консоли сервера
Время расследования · 2 дня Причин найдено · 3 Команд для победы · 1
Клиент — онлайн-СМИ застройщика федерального уровня, несколько десятков публикаций в день, своя редакция. С июня сайт периодически становился недоступен. Подрядчик, который ведёт техподдержку, прислал такое сообщение:
Из переписки с подрядчиком, 26.08
Мы занимаемся техподдержкой и доработкой функционала сайта. Примерно с июня клиент жаловался на периодическую недоступность. Мы проверили все возможные причины — блокировок ни со стороны хостинга, ни со стороны VPS, ни со стороны сайта не найдено. Обращались в ТП хостера. После нескольких тестов — смены IP и прочего — мы сменили версию TLS-рукопожатия, сменили протокол HTTP, выполнили много других рекомендаций. Доступ со стороны обычных пользователей наладился.
Однако сейчас клиент жалуется на доступ нейросетей — то есть, то нет. Хостер утверждает, что дело в ТСПУ РКН: блокируются пулы айпи-адресов, у этих сервисов они каждый раз разные, отсюда и плавающая проблема. Технических ограничений нет.
Объяснение звучит убедительно: государственная система блокировок, случайные пулы адресов, подрядчик бессилен. Проверить его руками нельзя — а значит и опровергнуть тоже. Идеальный диагноз для того, чтобы закрыть тикет.
Через два дня выяснилось, что причин было три, все на этом же сервере, и ни одна не имела отношения к РКН.
01 · Улика. Таблица, которая не похожа на блокировку
Клиент прислал выгрузку из Google Search Console: столбец «Невыполненные запросы на сканирование» за 89 дней подряд, от 0,2% до 78%, среднее около 40%.
Если бы трафик резали по правилу — по User-Agent, по подсети, по решению регулятора — график выглядел бы как полка: 100%, пока правило действует, 0% вне его. Здесь распределение размазанное, без единого дня на нуле и без единого дня на ста. Отказ происходит вероятностно: конкуренция за ресурс, лимит соединений, таймаут, случайные баны части трафика.
89 дней без единого дня восстановления исключают версию с разовым инцидентом. Это состояние инфраструктуры.
02 · Подозреваемые. Семь гипотез до доступа к серверу
По одной таблице я выписал всё, что могло дать такой процент: PHP-FPM отдаёт robots.txt через самописный роутинг и упирается в лимит пула, лимиты nginx на соединения, временные баны fail2ban для подсетей Google, нестабильный IPv6, перегрузка БД волнами, антибот-периметр хостинга, проблемы TLS-хендшейка.
Список подозреваемых, не диагноз. Первая же команда на сервере расставила всё по местам:
root@server — сравнение протоколов
# IPv4 curl -4 -sS -o /dev/null -w 'code=%{http_code} connect=%{time_connect} total=%{time_total}\n' https://example.ru/robots.txt code=200 connect=0.002458 total=0.066861 # IPv6 curl -6 -sS -o /dev/null -w 'code=%{http_code} connect=%{time_connect} total=%{time_total}\n' https://example.ru/robots.txt curl: (7) Failed to connect: No route to host (3080 ms)
IPv4 отвечает за 66 миллисекунд. IPv6 падает мгновенно с «No route to host» — отказ маршрутизации на уровне ОС, до того как пакет вообще покинул сервер. Google и AI-краулеры при сканировании предпочитают IPv6, а если не получается то уже IPv4. Доля запросов, ушедшая по IPv6, проваливается стопроцентно. Отсюда размазанные 40% — доля, с которой краулер в конкретный день выбирал протокол, не работавший на этом сервере никогда.
MySQL показывал Max_used_connections = 2 из 151. dmesg по OOM и conntrack — пусто. robots.txt отдавался статикой nginx, PHP в цепочке не было. Шесть гипотез из семи отпали за пару минут проверок.
03 · Первая причина. Адрес существует в DNS и нигде больше
root@server
dig +short AAAA example.ru 2001:db8:1:2::68b5 ip -6 addr show eth0 только fe80::58ef:3fff:fecc:5162/64 scope link
DNS обещает адрес, которого на интерфейсе нет. В журнале сервера нашлась запись семинедельной давности:
/var/log/syslog
Jul 07 17:31:04 named[865]: listening on IPv6 interface eth0, 2001:db8:1:2::68b5#53
Локальный BIND при старте перебирает адреса интерфейса и слушает на каждом. Раз он забиндился именно на этот адрес седьмого июля, адрес на сервере тогда был. Кто-то поднял его вручную, через ip -6 addr add, без сохранения в постоянный конфиг. Такая команда живёт до первого рестарта сети. Дальше адрес исчез, а вернуть его было некому: SLAAC на интерфейсе отключён (accept_ra=0), без ручного вмешательства он не появляется сам.
04 · Ловушка. Netplan хранит два файла для одного интерфейса
Первая попытка закрепить адрес постоянно не сработала. Я добавил его в отдельный netplan-файл, netplan try подтвердил конфигурацию — адрес всё равно не появился.
Ответ нашёлся в networkctl status eth0:
networkctl status eth0
Link File: /run/systemd/network/10-netplan-eth0.link Network File: /run/systemd/network/10-netplan-ens3.network
На сервере жили два netplan-определения одного физического интерфейса под разными именами: старое, от установщика Ubuntu, с ключом ens3 (без адреса) и более новое, от cloud-init, с ключом eth0 (с нужным адресом). Netplan честно сгенерировал оба .network-файла. Systemd-networkd применяет для одного линка только первый совпавший файл по алфавиту, а ens3 идёт раньше eth0. Все правки в «eth0»-определениях молча игнорировались с самого начала, включая, вероятно, и оригинальную настройку от cloud-init.
Адрес пришлось прописать не в новый файл, а прямо в победивший ens3. После этого он встал и пережил перезагрузку.
05 · Вторая причина. Nginx слушал IPv6 не на том блоке
В конфиге nginx нашлась ровно одна строка listen [::]:80 — у служебного дефолт-блока server_name localhost, не у боевого vhost'а сайта. Добавил listen [::]:443 ssl http2; в нужный server-блок, перечитал конфиг. Внешняя проверка с другого сервера прошла до конца, TLS-хендшейк завершился успешно.
06 · Контрольная проверка. BGP снимает подозрение с провайдера — и с РКН
Прежде чем радоваться, стоило исключить два варианта: что успех был случайным (второй сервер в той же сети провайдера, трафик не выходил в публичный интернет), и что подрядчик всё-таки прав насчёт блокировки на уровне сети.
Публичный BGP-lookup показал: префикс, в который входит адрес сайта, анонсирован легитимно, с подписанным ROA. Маршрутизация в порядке. Ни хостер, ни какая-либо инспекция трафика на пути не резали этот префикс — иначе анонс был бы невалиден или недостижим, а он достижим отовсюду.
07 · Тупик, который не тупик. Google по-прежнему не может достучаться
Проверка URL в Search Console выдавала «URL недоступен Google», запрос на индексирование отклонялся с формулировкой «обнаружены ошибки». С внешнего сервера при этом соединение проходило безупречно.
Разница была в источнике запроса. Я запустил tcpdump на сетевой карте сервера и одновременно нажал «Test Live URL» в Search Console.
root@server — 40 секунд захвата
sudo timeout 40 tcpdump -i eth0 -n ip6 ... 628 packets captured — всё исходящий трафик сервера, ни одного входящего SYN на :443 ни с одного адреса Google
Пакет не долетал до сетевой карты вообще, при том что с обычного внешнего сервера SYN приходил и обрабатывался без проблем, а в access-логе тем временем спокойно писались обычные посетители и сканеры.
Это первая трещина в версии про ТСПУ РКН: если бы блокировался пул адресов на уровне сети, страдал бы весь входящий трафик из этого пула без разбора, а не только соединения к порту 443 по IPv6 от конкретных сервисов.
08 · Третья причина. Файрвол, о котором подрядчик не спросил
В том же дампе — побочный сюрприз: сервер сам открывал десятки TLS-соединений в секунду к случайным чужим адресам, обменивался парой пакетов и рвал соединение. Выглядело очень подозрительно. lsof и ps aux расставили точки: Imunify360, модуль безопасности хостинг-панели, легитимно синхронизирующийся с облаком репутации. Не инцидент, но повод проверить, раз уж открыли tcpdump.
Дальше — на уровень ниже nginx. Legacy iptables и ip6tables оказались пустыми, ACCEPT по умолчанию. Реальный фильтр жил в nftables:
nft list table ip6 filter
chain imunify360_country_blacklist { match-set i360.ipv6.country-us src counter packets 18 bytes 1516 jump imunify360_log_bl_country } chain imunify360_log_bl_country { log prefix "im360-blacklisted-country" group 36005 counter packets 18 bytes 1516 drop }
Imunify360 блокировал весь входящий IPv6-трафик из США по геолокации — 18 пакетов уже попали под дроп именно за время моих тестов. Инфраструктура Google Search Console, PageSpeed Insights, Rich Results Test и большинства AI-краулеров, которых клиент называл «нейросетями», физически размещена в США. Список доверенных краулеров в Imunify360 существует, с Google в нём же, но проверяется позже: гео-блэклист режет пакет раньше, чем модуль успевает распознать в нём легитимного бота.
Отсюда и «плавающие» ошибки, которые подрядчик приписывал РКН: часть запросов шла с адресов вне США или по IPv4 — проходила; часть шла по IPv6 из США — резалась на входе. Никакой связи с пулами и метками ТСПУ, обычная гео-блокировка одной страны, включённая в модуле безопасности, судя по всему, ещё во время той самой правки TLS и HTTP.
root@server — снято из консоли, без панели
sudo imunify360-agent blacklist country delete US sudo nft list table ip6 filter | grep -A2 country_blacklist chain imunify360_country_blacklist { }
Повторный curl -6 с американского сервера дошёл до TLS handshake, Server hello за секунды. В течение часа доступ подтвердили все три инструмента Google: PageSpeed Insights, Search Console и Rich Results Test.

09 · Итог. Три причины, каждая закрывала бы дело сама
1. IPv6-адрес существовал только в DNS: поднимался вручную один раз, автовосстановление было отключено.
2. Даже с адресом на месте nginx не принимал HTTPS по IPv6 на боевом vhost'е — слушал только служебный дефолт-блок.
3. Даже с рабочими первыми двумя пунктами гео-блокировка США резала весь входящий IPv6-трафик из страны, где физически стоит инфраструктура Google и большинства AI-сервисов.
Убери одну из трёх — картина осталась бы прежней: хронические 40% отказов без внятной закономерности, и версия про ТСПУ РКН продолжала бы звучать правдоподобно.
Методологический вывод
У версии подрядчика был изъян, для которого не требовался доступ к серверу: настоящую блокировку на уровне сети нельзя починить одной командой на своём сервере. Если бы дело было в ТСПУ, imunify360-agent blacklist country delete US не изменил бы ничего — трафик резался бы выше, вне зоны досягаемости root-доступа на конкретной VPS.
У нас проблема исчезла мгновенно после одной локальной команды. Это и есть тест: если объяснение звучит как «ничего нельзя сделать», первый вопрос — какая конкретно команда на этом сервере ничего не изменит, если объяснение верно. Нет такой проверенной команды — объяснение не проверено, а просто удобно.
Чек-лист. Как проверять такое у себя
порядок диагностики, сверху вниз:
# 1. Сравнить протоколы curl -4 -v https://example.com/robots.txt curl -6 -v https://example.com/robots.txt # 2. Адрес физически на интерфейсе? ip -6 addr show # 3. Какой netplan-конфиг реально победил networkctl status eth0 cat /run/systemd/network/10-netplan-*.network # 4. nginx слушает IPv6 именно на нужном блоке? sudo ss -6 -ltnp | grep :443 sudo nginx -T | grep 'listen \[::\]' # 5. Маршрут легитимен на стороне провайдера? — публичный BGP-lookup по адресу # 6. Пакет вообще доходит до карты во время живого теста? sudo tcpdump -i eth0 -n ip6 # 7. Если доходит и падает — ищем файрвол, не только в iptables: sudo nft list tables sudo nft list table ip6 filter
Пункт 7 — самый частый провал диагностики. Iptables показал пустые цепочки, и файрвол чуть не списали со счетов. Реальные правила жили в параллельной системе с другим названием таблиц.
Search Console какое-то время кэширует старый статус robots.txt на уровне всего домена и не пересчитывает его при каждой ручной проверке — в этом случае обошлось часом, но закладывать стоит больше.

