сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Сети

Фаерволы, NAT и проблемы MTU

Фаерволы, публикация портов и диагностика

Классическая жалоба: локально сервис отвечает, снаружи нет. Причин ровно три, и они проверяются по порядку за пару минут - адрес, на котором сервис слушает, фильтрация и маршрут. Отдельный сюжет рядом: порт, закрытый фаерволом (набором правил ядра о том, что пускать в машину, а что нет), при этом спокойно доступен из интернета, если это контейнер.

Стержень: сначала смотрят, на каком адресе висит сервис, потом кто фильтрует; публикация порта контейнера идёт мимо привычных правил фаервола.

// Формулировки: «локально отвечает, снаружи нет», «почему ufw не защитил порт контейнера?», «маленькие запросы проходят, большие висят»

Порядок локализации

Первое: на каком адресе слушает сервис. Команда ss с ключами -lptn показывает все слушающие сокеты. На этой машине висят 0.0.0.0:80, 0.0.0.0:443 и 0.0.0.0:22 - четыре нуля означают «на всех сетевых картах», такой порт доступен снаружи. А рядом 127.0.0.53:53 - служба имён, и она видна только с самой машины. Если сервис слушает на 127.0.0.1, снаружи он недоступен в принципе, и никакие правила фаервола тут ни при чём.

Второе: фильтрация. Тут выручает разница в поведении, которую я замерял отдельно. Отказ приходит мгновенно, за доли миллисекунды, и означает «до машины дошли, но на этом порту никто не слушает». Молчание до самого предела ожидания, у меня 3003 миллисекунды, означает, что ответа не было вовсе: пакет отбросили молча или потерялся обратный путь. Первое - неверный порт или упавший процесс, второе - фильтр где-то по дороге или неверный маршрут.

// Третье: чужие правила. В облаке поверх машины есть свои списки разрешений, и порт может быть закрыт там, а не на самой машине. Проверять полезно с обеих сторон сразу: запрос с соседней машины и просмотр приходящих пакетов на целевой - это моментально показывает, доходят ли они вообще.

адрес прослушивания
0.0.0.0 означает все сетевые карты, 127.0.0.1 - только сама машина
отказ против молчания
мгновенный отказ - дошли и никого нет; тишина до предела - фильтр или маршрут

Публикация порта контейнера проходит мимо ufw

Правила фаервола разложены по цепочкам, и ядро проходит их в определённом порядке. Привычная надстройка ufw (в Ubuntu она пишет правила ядра за вас, чтобы не возиться с ними руками) кладёт свои запреты в ту цепочку, через которую идут пакеты, адресованные самой машине.

Пакет, адресованный контейнеру, идёт другим путём. Демон docker добавляет собственное правило подмены адреса назначения, и оно разбирается РАНЬШЕ, чем цепочка с запретами ufw: пакет заворачивается в контейнер, минуя её. Я смотрел эти правила на своём сервере - там строки вида «порт 443 перенаправить на 172.18.0.4:443». Результат прямой: порт, «закрытый» в ufw, доступен из интернета. Проверял и это - публикация без указания адреса отвечает и на внешний адрес машины тоже.

// Лечится тремя способами. Публиковать порт на конкретный адрес, например только на 127.0.0.1, и тогда снаружи его нет вовсе. Класть свои запреты в отдельную цепочку DOCKER-USER, которую демон читает раньше своих правил. Либо запретить демону трогать правила ядра и написать всё самому - надёжно, но дальше это твоя забота навсегда.

цепочка правил
список правил фаервола, который ядро проходит по порядку
подмена адреса назначения
правило, заворачивающее пакет с порта машины внутрь контейнера
DOCKER-USER
цепочка, которую демон читает раньше своих правил; туда кладут свои запреты

Маленькие запросы проходят, большие висят

Есть отдельный класс аварий, где рукопожатие проходит, короткие ответы приходят, а стоит отправить что-то крупное - тишина. Причина в предельном размере пакета: у каждой сетевой карты есть потолок, MTU (maximum transmission unit). Замерил на этой машине: пакет с 1472 байтами полезных данных проходит, а с 1473 - уже нет, и приходит ошибка «Frag needed». Всё сходится: 1472 плюс 28 байт служебных заголовков дают ровно 1500, а 1500 и стоит потолком на всех сетевых картах этой машины.

Обычно это никого не беспокоит: если пакет великоват, отправителю приходит служебное сообщение по протоколу ICMP (internet control message protocol) «нужна фрагментация», и он уменьшает размер. Ломается всё там, где такие служебные сообщения вырезаны фаерволом «на всякий случай». Тогда отправитель ничего не узнаёт и продолжает слать пакеты, которые молча пропадают.

// Особенно часто это ловят на туннелях. Туннель - это канал, внутри которого пакеты едут упакованными в другие пакеты; упаковка добавляет свои заголовки, поэтому реальный потолок внутри туннеля меньше обычного. Лечения два: выставить правильный потолок на интерфейсе или включить принудительное уменьшение размера сегмента, MSS (maximum segment size) clamping, - тогда стороны договорятся о меньшем размере ещё на рукопожатии. И перестать вырезать служебные сообщения о размере.

MTU
предельный размер пакета, который пролезает в сетевую карту
MSS clamping
принудительное уменьшение размера сегмента при установке соединения

Как отвечать: «В ufw порт закрыт, а контейнер доступен из интернета. Почему?»

Потому что публикация порта контейнера делается правилом подмены адреса назначения, а оно разбирается раньше той цепочки, куда ufw пишет свои запреты. Пакет заворачивается внутрь контейнера и цепочку с запретами просто не проходит, поэтому фаервол машины такой трафик не видит. Я это проверял на своём сервере: публикация без указания адреса отвечает и на внешний адрес тоже, а правила демона видно прямо в таблице подмены. Лечится тремя способами: публиковать порт на конкретный адрес, например только на локальный, и снаружи его не будет вовсе; писать свои запреты в цепочку DOCKER-USER, которую демон читает раньше своих; либо запретить демону управлять правилами и настроить всё самому.

Ответ объясняет путь пакета и даёт три рабочих варианта вместо общего «настройте фаервол правильно». Первый вариант закрывает вопрос в одну строку конфигурации.

На чём валятся

  • Закрывают порт в ufw и считают контейнер защищённым.
  • Не смотрят адрес прослушивания и ищут фильтр там, где сервис висит на локальном адресе.
  • Не различают мгновенный отказ и молчание до предела ожидания.
  • Списывают зависание больших запросов на приложение, не проверив предельный размер пакета.
  • Вырезают служебные сообщения о размере пакета и получают аварии на туннелях.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.

  1. #dvo_net_firewall1 / 5
    Через туннель соединение устанавливается, короткие ответы ходят, а большие запросы висят и отваливаются по таймауту. Диагноз?
    A)Асимметричная маршрутизация: ответы уходят другим путём
    B)Кончились conntrack-записи, и новые сессии рвутся
    C)Чёрная дыра PMTU: крупные пакеты не проходят, а ICMP заблокирован
    D)Переполнена очередь на интерфейсе, пакеты отбрасываются
    показать ответ и разбор
    +C)Чёрная дыра PMTU: крупные пакеты не проходят, а ICMP заблокирован

    // разбор: У туннеля MTU меньше, чем у обычного интерфейса. Отправитель шлёт пакет по-старому с флагом «не фрагментировать», промежуточный узел обязан вернуть ICMP «fragmentation needed», а этот тип часто вырезан фаерволом. В итоге рукопожатие (мелкие пакеты) проходит, а первый крупный сегмент теряется молча и уходит в бесконечные ретрансмиссии. Лечится MSS clamping или корректным MTU на интерфейсе.

  2. #dvo_net_firewall2 / 5
    На хосте default-deny на входящие. Исходящее подключение к внешнему API работает, хотя «обратный» порт не открыт. Почему?
    A)Фаервол stateful: ответы на исходящее идут как established
    B)Исходящие подключения фаервол не фильтрует в принципе
    C)Внешний API сам пробивает обратный путь через NAT хоста
    D)Ответ приходит на тот же порт, что и запрос, потому и проходит
    показать ответ и разбор
    +A)Фаервол stateful: ответы на исходящее идут как established

    // разбор: Stateful-фаервол отслеживает соединения: ответный трафик на инициированное тобой подключение матчится как ESTABLISHED/RELATED и пропускается — эфемерный обратный порт открывать не нужно. Поэтому default-deny на входящие + allow established позволяет исходящим вызовам работать. В stateless-ACL пришлось бы вручную разрешать обратные порты.

  3. #dvo_net_firewall3 / 5
    Клиент к сервису: то сразу connection refused, то висит и отваливается по таймауту. Что каждый случай говорит?
    A)И refused, и таймаут значат, что сервис перегружен
    B)refused — недоступна сеть, таймаут — неправильный DNS
    C)refused (RST) — порт закрыт; таймаут — пакет дропнут
    D)refused бывает только локально, а таймаут только по сети
    показать ответ и разбор
    +C)refused (RST) — порт закрыт; таймаут — пакет дропнут

    // разбор: connection refused = быстро пришёл RST: на порту никто не слушает или фаервол REJECT'ит. Зависание с таймаутом = на SYN нет ответа: фаервол молча DROP'ит, хост лежит или потеря пакетов. Быстрый отказ = «до хоста дошли, порт закрыт»; таймаут = «пакеты не доходят/дропаются». Разные лечения: поднять сервис/открыть порт vs разбираться с маршрутом/DROP/лежащим хостом.

  4. #dvo_net_firewall4 / 5
    Под пиковой нагрузкой шлюз теряет пакеты, в dmesg — nf_conntrack: table full, dropping packet. Что произошло?
    A)Кончились эфемерные порты на исходящих подключениях
    B)Переполнился ARP-кэш из-за множества соседей в сети
    C)MTU слишком мал, из-за фрагментации пакеты и теряются
    D)Переполнилась таблица conntrack соединений
    показать ответ и разбор
    +D)Переполнилась таблица conntrack соединений

    // разбор: Stateful netfilter (фаервол/NAT) держит каждое соединение в таблице conntrack; под флудом новых соединений она переполняется, и ядро дропает новые пакеты («table full»). Симптом — перемежающиеся потери при высоком темпе подключений. Лечится: поднять nf_conntrack_max и hashsize, подтюнить таймауты (короче для TIME_WAIT/established), снизить churn (keep-alive/пул) или NOTRACK для трафика, которому отслеживание не нужно.

  5. #dvo_net_firewall5 / 5
    Что делает межсетевой экран (firewall) на сервере?
    A)Шифрует весь исходящий трафик
    B)Пропускает или отбрасывает пакеты
    C)Ускоряет соединения за счёт кэширования
    D)Проверяет содержимое файлов на вирусы
    показать ответ и разбор
    +B)Пропускает или отбрасывает пакеты

    // разбор: Правила решают, что делать с пакетом по адресу, порту и протоколу — пропустить, отбросить молча или отказать. Отсюда и разница в поведении клиента: явный отказ даёт мгновенное connection refused, а тихий сброс — таймаут.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.