Устройство conntrack и отслеживания соединений¶
Отслеживание соединений (conntrack) — подсистема ядра, делающая возможным stateful-файрвол — именно она позволяет правилу вроде ct state established,related accept (из статьи про файрвол) знать, какие пакеты принадлежат уже разрешённому наружу соединению. Это также часто упускаемый источник реальных продакшн-инцидентов, когда её таблица заполняется.
Как это устроено¶
Что именно отслеживает conntrack¶
Каждое TCP-соединение и каждый UDP-«поток» (у UDP нет настоящих соединений, но conntrack эвристически отслеживает пары запрос/ответ по таймауту) получает запись в таблице внутри ядра: исходный/целевой IP, исходный/целевой порт, протокол и текущее состояние.
Это максимальное число отслеживаемых соединений, которое ядро будет держать одновременно. Каждая запись потребляет небольшой, фиксированный объём памяти ядра (примерно 300-350 байт в зависимости от версии ядра) — 262144 записей это порядка 80-90МБ RAM, немного для современного сервера, поэтому это значение по умолчанию часто оказывается слишком низким для нагрузок с высоким числом соединений (загруженный обратный прокси или NAT-шлюз может исчерпать его за минуты под реальной нагрузкой).
Просмотр таблицы¶
Типичный вывод:
tcp 6 431999 ESTABLISHED src=10.0.0.5 dst=203.0.113.10 sport=51234 dport=443 \
src=203.0.113.10 dst=10.0.0.5 sport=443 dport=51234 [ASSURED] mark=0 use=1
Читаем: протокол (tcp), номер протокола (6), оставшийся таймаут в секундах (431999), состояние (ESTABLISHED), затем оба направления потока — исходное направление и ожидаемое направление ответа, именно та информация, на которую опираются правила ct state established,related. [ASSURED] означает, что хотя бы один пакет был замечен в обоих направлениях, отмечая это как настоящее, двунаправленное соединение (в отличие от одностороннего — возможно, просто скан или соединение, сброшенное до завершения).
sudo conntrack -C # текущее число записей в таблице
sudo conntrack -L -p tcp --dport 443 # фильтр: только HTTPS-записи
Режим отказа: исчерпание таблицы¶
Когда таблица заполняется, ядро молча отбрасывает новые попытки соединения — без ошибки, которую клиент мог бы осмысленно интерпретировать, просто как если бы пакет никогда не пришёл. Это даёт очень специфичный, сбивающий с толку симптом: сервис, нормально работающий при низкой нагрузке, вдруг загадочно начинает отказывать в новых соединениях (пока существующие продолжают работать) как только число одновременных соединений пересекает некий порог — классическое исчерпание conntrack, легко ошибочно принимаемое за баг приложения.
Увеличение лимита¶
# Runtime (теряется при перезагрузке):
sudo sysctl -w net.netfilter.nf_conntrack_max=1048576
# Постоянно:
echo "net.netfilter.nf_conntrack_max = 1048576" | sudo tee -a /etc/sysctl.d/99-conntrack.conf
sudo sysctl --system
Разумный подход к расчёту размера: оцените реалистичное пиковое число одновременных соединений для нагрузки, умножьте на запас прочности (2-3x), и выставите лимит соответственно — вместо выбора произвольно большого числа, которое просто резервирует больше памяти ядра, чем нужно.
Намеренный обход conntrack (когда он не нужен)¶
Для очень высокопроизводительного, по сути безсостояточного трафика (некоторые DNS-резолверы, определённые конфигурации балансировщиков нагрузки) иногда правильно явно исключить конкретный трафик из отслеживания соединений целиком через цель NOTRACK (iptables) или notrack (nftables), снижая накладные расходы на пакет — но только после подтверждения, что ничего нижестоящее (stateful-правило файрвола, NAT) не зависит от того, что этот трафик отслеживается.
Различия Ubuntu/Debian и RHEL-подобных систем¶
Сама подсистема conntrack идентична в коде ядра во всех дистрибутивах. Утилиты пространства пользователя могут быть не установлены по умолчанию: пакет называется conntrack в Debian/Ubuntu (sudo apt install conntrack) и conntrack-tools в RHEL-подобных системах (sudo dnf install conntrack-tools).
Практическое задание¶
- Проверьте текущий
nf_conntrack_maxи текущее использование:cat /proc/sys/net/netfilter/nf_conntrack_maxиsudo conntrack -C. - Откройте несколько исходящих соединений одновременно (например, пачку параллельных запросов
curlк разным хостам) и убедитесь, что счётчик вconntrack -Cсоответственно растёт. - Отфильтруйте таблицу, чтобы показать только записи, относящиеся к одному из этих соединений, через
conntrack -L -p tcp --dport <port>.
Как проверить, что всё сделано правильно¶
- Шаг 2: счётчик
conntrack -Cдолжен вырасти примерно на число открытых новых соединений, и осесть обратно по мере их закрытия и истечения таймаута их записей в таблице. - Шаг 3: отфильтрованный вывод должен показать именно записи, совпадающие с вашими тестовыми соединениями, не пустой результат (подтверждая, что синтаксис фильтра корректен) и не всю таблицу (подтверждая, что фильтр реально ограничивает, а не совпадает со всем).
Типичные ошибки¶
- Диагностика перемежающегося «connection refused под нагрузкой» как бага приложения без проверки
dmesgна сообщения о заполненной таблице conntrack — это симптом уровня ядра, который логи приложения вообще не покажут. - Выставление
nf_conntrack_maxв чрезмерно большое число «на всякий случай» без учёта (скромной, но ненулевой) стоимости памяти на системах с ограниченной памятью. - Забыть про постоянный sysctl-конфиг, исправив симптом только до следующей перезагрузки, которая молча откатит его на старое значение по умолчанию.
⚠ Опасные команды¶
conntrack -F(flush) сбрасывает состояние каждого отслеживаемого соединения сразу. Трансляции NAT теряются, поэтому установленные соединения через NAT рвутся немедленно. Обычные TCP-потоки чаще всего подхватываются заново какESTABLISHEDпо следующему пакету (nf_conntrack_tcp_loose=1, значение по умолчанию), но UDP-потоки и строгие наборы правил могут внезапно начать терять пакеты. На загруженном шлюзе/файрволе это может массово разорвать легитимный трафик.
Связанные статьи¶
- Файрвол Linux с nftables и iptables
- Диагностика высокой нагрузки CPU и памяти
- Сеть TCP/IP и набор команд ip
Контрольные вопросы¶
- Почему заполнение таблицы conntrack даёт «молчаливый сброс», а не явную ошибку клиенту?
- Что означает
[ASSURED]в записиconntrack -L, и почему это важно? - Почему сервис может нормально работать при низком числе соединений и внезапно начать отказывать новым соединениям только после пересечения определённого порога нагрузки?
- В чём практический риск запуска
conntrack -Fна продакшн-хосте файрвола/шлюза с набором правил, зависящим от established-состояния?