Симптом
Хостер выдал два IPv4 как две отдельные сетевухи (eth0/eth1) в одной подсети /24. Дальше начинается странное: сервер в панели то connected, то disconnected — «поднимается на 10 секунд и тухнет» при здоровом контейнере. Один IP снаружи не пингуется и не пускает SSH, второй стабилен. После ребута входящий SSH работает, а исходящий трафик мёртв: пинг наружу — 100% потерь, curl возвращает 000.
Это ARP-конфликт, а не взлом, не перегруз и не поломка ноды.
Корень: ARP flux
Два IP на двух интерфейсах в одной подсети при дефолтных arp_ignore=0 / arp_announce=0 дают ARP flux: интерфейсы отвечают и анонсируют за чужой IP, шлюз выучивает IP на неверный MAC. Свитч хостера с anti-spoofing рубит «спуфные» исходящие пакеты — выход в интернет умирает. Вдобавок два default-маршрута дерутся за исходящие. Асимметрия «вход есть, выхода нет» — прямой признак именно этого класса.
Диагностика, только чтение:
ip -br addr # сколько IPv4 на скольких eth в одной подсети
ip route | grep default # ДВА default = беда
sysctl net.ipv4.conf.all.arp_ignore net.ipv4.conf.all.arp_announce # 0/0 → flux
curl -4 -m6 --interface <IP_A> https://<внешний-хост> # код 000 на обоих IP = свитч рубит исходящие
Лечение (runtime)
GW=<gateway>
# 1) ARP-гигиена: каждый интерфейс отвечает и анонсит только за свой IP
sysctl -w net.ipv4.conf.{all,eth0,eth1}.arp_ignore=1
sysctl -w net.ipv4.conf.{all,eth0,eth1}.arp_announce=2
# 2) policy-routing: каждый src-IP выходит через свой NIC
ip rule add from <IP_eth0> lookup 100; ip route replace default via $GW dev eth0 table 100
ip rule add from <IP_eth1> lookup 101; ip route replace default via $GW dev eth1 table 101
# 3) один чистый main default через активный интерфейс
ip route replace default via $GW dev eth1
ip route del default via $GW dev eth0 2>/dev/null
Закрепление, чтобы пережило ребут: sysctl-значения в /etc/sysctl.d/99-arp-fix.conf — важны и all, и default, потому что per-interface значения могут не примениться до поднятия интерфейса. Плюс идемпотентный скрипт восстановления маршрутов и правил в oneshot-сервисе After=network-online.target.
Подводные камни
- Не ребутить живую прод-ноду на панике. Мульти-NIC после ребута поднимается криво: добавляет неправильный IPv6, ломает выход. Ребут редко лечит и часто вредит — только с готовой VNC/rescue-консолью под рукой.
- Входящий SSH работает — не значит, что нода жива. Проверяйте исходящий трафик (
curl,pingнаружу): панель может показывать connected, а нода бесполезна. - Reality-цель может резолвиться только в IPv6 (за CDN) — при кривом IPv6 цель «недоступна». Спасает IPv4-fallback, но IPv6 на ноде стоит привести в порядок.
- Структурный фикс — на стороне хостера: попросить отдать второй IP алиасом на один интерфейс, а не отдельным NIC. Тогда ARP-конфликта нет в принципе. Обычный тикет в техподдержку.
Вывод. Флап ноды плюс асимметрия «вход есть, выхода нет» при двух IP в одной подсети — это ARP flux от дефолтных arp_ignore/arp_announce и двух default-маршрутов. Лечится ARP-гигиеной, policy-routing по src-IP и одним чистым default, закреплёнными boot-сервисом. Не ребутить наугад; правильнее всего попросить хостера выдать второй IP алиасом.