Взаимодействие iptables/nftables и маршрутизации в Linux: полный разбор пути пакета | AdminWiki

Взаимодействие iptables/nftables и маршрутизации в Linux: полный разбор пути пакета

17 июля 2026 10 мин. чтения
Содержание статьи

Когда вы настраиваете межсетевой экран или трансляцию адресов в Linux, правила iptables или nftables не работают в вакууме. Их логика неразрывно связана с решением о маршрутизации, которое принимает ядро. Понимание этой связи - ключ к предсказуемой и надёжной настройке сетевого периметра, будь то корпоративный шлюз или домашний роутер.

Эта статья объяснит ментальную модель пути пакета. Вы узнаете, в какой момент ядро смотрит в таблицу маршрутизации и как это влияет на работу правил NAT и фильтрации. Мы разберем типовые ошибки, дадим готовые конфигурации для популярных сценариев и инструменты для самостоятельной диагностики проблем.

Фундамент: как ядро Linux обрабатывает сетевой пакет

Перед тем как добавлять правила, необходимо понять последовательность событий, через которые проходит каждый пакет. В ядре Linux эту функцию выполняет фреймворк netfilter (для iptables) или nftables. Их работа строится вокруг системы "хуков" (hooks) - точек принятия решений, где применяются правила. Критически важно, что между некоторыми из этих хуков ядро обращается к своей таблице маршрутизации, чтобы определить дальнейший путь пакета.

Диаграмма пути пакета: от входящего интерфейса до выходящего

Рассмотрим путь пакета, проходящего через шлюз (транзитный трафик, FORWARD). После попадания на сетевой интерфейс (например, eth0) его судьба определяется следующим образом:

  1. PREROUTING: Пакет попадает в эту цепочку. Здесь, в таблице nat, могут применяться правила Destination NAT (DNAT), изменяющие адрес назначения в заголовке пакета.
  2. Первое решение о маршрутизации (Routing Decision): Сразу после цепочки PREROUTING ядро смотрит в таблицу маршрутизации (ip route). Оно определяет, предназначен ли конечный адрес пакета (уже потенциально изменённый DNAT) локальной системе или её нужно переслать дальше. На этом этапе выбирается выходной интерфейс и адрес следующего прыжка (gateway).
  3. Локальный трафик (INPUT): Если маршрут указывает, что пакет для локального процесса (например, веб-сервера на этом же хосте), он направляется в цепочку INPUT, затем в локальный стек.
  4. Транзитный трафик (FORWARD): Если маршрут указывает на другой интерфейс, пакет направляется в цепочку FORWARD для фильтрации.
  5. Второе решение о маршрутизации: Перед отправкой в цепочку POSTROUTING ядро финализирует маршрут, основываясь на выборе, сделанном на шаге 2.
  6. POSTROUTING: В этой цепочке, также в таблице nat, применяются правила Source NAT (SNAT) или MASQUERADE, изменяющие исходный адрес пакета перед отправкой в сеть.

Эта последовательность - основа. Например, если вы делаете проброс порта, правило DNAT в PREROUTING меняет адрес назначения. Маршрутизационное решение, следующее сразу за этим, использует уже новый адрес и направляет пакет в цепочку FORWARD, а не INPUT.

Ключевой момент №1: Первое решение о маршрутизации (после PREROUTING)

Это центральный элемент взаимодействия. Правило DNAT (например, -j DNAT --to-destination 192.168.1.10) выполняется в цепочке PREROUTING таблицы nat. Оно меняет поле "destination IP" в заголовке пакета.

Сразу после этого ядро выполняет команду, аналогичную ip route get <новый_адрес_назначения>. Результат этого запроса и определяет, пойдёт ли пакет в цепочку INPUT (если адрес локальный) или FORWARD (если адрес на другом интерфейсе). Поэтому для успешного проброса порта в таблице маршрутизации должен существовать маршрут до нового адреса назначения, ведущий через нужный внутренний интерфейс.

Ключевой момент №2: Второе решение о маршрутизации (перед POSTROUTING)

После прохождения цепочки FORWARD (или OUTPUT для локально сгенерированных пакетов) ядро вновь обращается к маршрутной информации, чтобы окончательно определить исходный IP-адрес для исходящего интерфейса и адрес шлюза, если он требуется.

Только после этого, непосредственно перед отправкой пакета в сеть, в цепочке POSTROUTING применяются правила SNAT или MASQUERADE. Они меняют исходный адрес пакета, но уже не влияют на выбор маршрута для этого конкретного пакета - он был сделан ранее. Это объясняет, почему правило MASQUERADE ставится в POSTROUTING: оно "маскирует" исходный адрес пакета, маршрут для которого уже определён.

Практика: настройка NAT и проброса портов без ошибок

Теперь применим теорию к реальным задачам. Перед любыми действиями убедитесь, что включен IP-форвардинг: sysctl -w net.ipv4.ip_forward=1. Для сохранения настройки добавьте net.ipv4.ip_forward=1 в файл /etc/sysctl.conf.

Сценарий 1: Шлюз с маскарадингом (SNAT) для локальной сети

Задача: настроить Linux-сервер с двумя интерфейсами (eth0 - внешний, eth1 - внутренняя сеть 192.168.1.0/24) в качестве шлюза, раздающего интернет.

Решение для iptables:

# Разрешаем форвардинг для установленных и связанных соединений (оптимизация)
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Разрешаем исходящий трафик из внутренней сети
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
# Включаем маскарадинг (динамический SNAT) в POSTROUTING для трафика с eth1
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE

Решение для nftables:

table ip nat {
  chain postrouting {
    type nat hook postrouting priority 100; policy accept;
    oifname "eth0" ip saddr 192.168.1.0/24 masquerade
  }
}

table ip filter {
  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state established,related accept
    iifname "eth1" oifname "eth0" accept
  }
}

Правило MASQUERADE помещается в POSTROUTING, потому что оно меняет исходный адрес после того, как ядро уже определило, что пакет с внутреннего адреса должен быть отправлен через интерфейс eth0 во внешнюю сеть. Если у вас статический внешний IP (например, 203.0.113.10), вместо MASQUERADE используйте -j SNAT --to-source 203.0.113.10.

Сценарий 2: Проброс порта (DNAT) на сервер во внутренней сети

Задача: пробросить TCP-порт 80 с внешнего IP шлюза на порт 80 веб-сервера 192.168.1.100 во внутренней сети.

Решение для iptables:

# DNAT в PREROUTING: меняем адрес назначения для входящих на порт 80
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100:80
# Разрешаем форвардинг для этого трафика (учитываем, что политика FORWARD может быть DROP)
iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 80 -d 192.168.1.100 -j ACCEPT
# Разрешаем обратный трафик (ответы от сервера)
iptables -A FORWARD -i eth1 -o eth0 -p tcp --sport 80 -s 192.168.1.100 -j ACCEPT

Решение для nftables:

table ip nat {
  chain prerouting {
    type nat hook prerouting priority -100; policy accept;
    iifname "eth0" tcp dport 80 dnat to 192.168.1.100:80
  }
}

table ip filter {
  chain forward {
    type filter hook forward priority 0; policy drop;
    ct state established,related accept
    iifname "eth0" oifname "eth1" tcp dport 80 ip daddr 192.168.1.100 accept
    iifname "eth1" oifname "eth0" tcp sport 80 ip saddr 192.168.1.100 accept
  }
}

Здесь критично правило в цепочке prerouting таблицы nat. Оно меняет адрес назначения пакета ДО первого решения о маршрутизации. Затем ядро видит адрес 192.168.1.100, находит для него маршрут через интерфейс eth1 и направляет пакет в цепочку FORWARD, а не INPUT. Более глубокое понимание этого процесса позволяет избежать ошибок, описанных в нашем руководстве по асимметричной маршрутизации.

iptables vs nftables: эквиваленты команд для маршрутизации и NAT

Для удобства миграции или работы в смешанных средах используйте эту таблицу соответствий.

Концепцияiptablesnftables
Добавить правило MASQUERADEiptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEnft add rule ip nat POSTROUTING oifname eth0 masquerade
Добавить правило DNATiptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-dest 10.0.0.2nft add rule ip nat PREROUTING tcp dport 80 dnat to 10.0.0.2
Разрешить форвардинг по интерфейсамiptables -A FORWARD -i eth1 -o eth0 -j ACCEPTnft add rule ip filter FORWARD iifname eth1 oifname eth0 accept
Показать все правилаiptables-save или iptables -L -n -vnft list ruleset

Nftables предлагает единый синтаксис, более высокую производительность при большом количестве правил и встроенные структуры данных (множества, словари). Iptables остаётся широко распространённым, с обилием готовых примеров. Для комплексной настройки маршрутизации, включая работу с политиками, полезно ознакомиться с полным руководством по ip route и таблицам маршрутизации.

Диагностика: почему не работает? Разбор типичных проблем

Если готовые правила не дают результата, следуйте системному подходу к диагностике.

Чек-лист диагностики: от маршрута до правил

  1. Проверьте базовую связность и интерфейсы: ip link show, ping -c 4 <внутренний_адрес> с шлюза.
  2. Проверьте маршрутизацию с учётом NAT: Для диагностики проброса порта используйте ip route get 192.168.1.100 from <внешний_IP> iif eth0. Эта команда симулирует первое решение о маршрутизации для пакета, пришедшего с определённого интерфейса. Убедитесь, что результат указывает на правильный выходной интерфейс (например, eth1).
  3. Убедитесь, что форвардинг включён: sysctl net.ipv4.ip_forward. Должно вернуть 1.
  4. Проверьте логи фаервола: Добавьте правило логирования перед ключевыми правилами.
    Для iptables: iptables -I FORWARD 1 -j LOG --log-prefix "FW-FORWARD: "
    Для nftables: nft add rule ip filter FORWARD log prefix "NFT-FORWARD: "
    Логи можно найти в /var/log/kern.log или dmesg.
  5. Используйте tcpdump: Запустите сниффер последовательно на каждом интерфейсе, чтобы увидеть, где пакет теряется.
    tcpdump -i eth0 -n port 80
    tcpdump -i eth1 -n host 192.168.1.100
  6. Проверьте таблицу соединений (conntrack): conntrack -L -f ipv4 покажет, видит ли система состояние NAT-соединения. Отсутствие записей может указывать на то, что пакеты не доходят до цепочки, где работает NAT.

Частая ошибка №1: «NAT работает только в одну сторону»

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

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

Решение:
1. Убедитесь, что на внутреннем сервере шлюзом по умолчанию назначен IP-адрес интерфейса шлюза во внутренней сети (например, 192.168.1.1).
2. На шлюзе должны быть разрешающие правила в цепочке FORWARD для ответного трафика (как показано в сценарии 2).
3. На шлюзе должно быть активное правило SNAT/MASQUERADE в POSTROUTING для трафика, уходящего во внешнюю сеть. Именно оно заменит исходный адрес ответа от сервера (192.168.1.100) на внешний адрес шлюза, чтобы ответ мог вернуться клиенту.

Частая ошибка №2: «Пакеты теряются после добавления правила»

Возможные причины:

  • Политика цепочки DROP: Политика цепочки FORWARD по умолчанию стоит DROP, а разрешающих правил недостаточно или они расположены неверно. Проверьте: iptables -L FORWARD -n или посмотрите политику в начале цепочки nftables. В начале правил FORWARD всегда должно быть правило для established,related.
  • Reverse Path Filtering (rp_filter): Эта функция ядра может отбрасывать пакеты, у которых исходный адрес не совпадает с обратным маршрутом из таблицы маршрутизации. Проверьте значение: sysctl net.ipv4.conf.all.rp_filter. В сложных сетях с асимметричной маршрутизацией его можно временно отключить (sysctl -w net.ipv4.conf.all.rp_filter=0), но лучше настроить маршруты корректно.
  • Порядок правил: Правило с действием DROP или REJECT расположено выше разрешающего правила и перехватывает трафик. Анализируйте вывод iptables -L -n -v --line-numbers или nft list ruleset.

Для полноценного аудита существующих правил безопасности используйте методики из практического руководства по аудиту сетевой инфраструктуры.

Для продвинутых: управление маршрутизацией через фаервол (Policy Routing)

Стандартная маршрутизация основана на адресе назначения. Policy Based Routing (PBR) позволяет выбирать маршрут на основе других критериев: исходный адрес, протокол, порт или метка, установленная фаерволом. Это открывает возможности для балансировки нагрузки, изоляции трафика или принудительного направления его через VPN-туннель.

Как это работает: fwmark, ip rule и дополнительные таблицы маршрутизации

Рассмотрим сценарий: весь трафик от сервера 192.168.1.50 должен идти через VPN-туннель tun0, а остальной трафик из сети 192.168.1.0/24 - через стандартный шлюз eth0.

Шаг 1: Маркировка пакетов в фаерволе.
В цепочке PREROUTING (или OUTPUT для локально генерируемого трафика) таблицы mangle ставим метку (fwmark).

iptables:
iptables -t mangle -A PREROUTING -s 192.168.1.50 -j MARK --set-mark 100

nftables:

table ip mangle {
  chain prerouting {
    type filter hook prerouting priority -150; policy accept;
    ip saddr 192.168.1.50 meta mark set 100
  }
}

Шаг 2: Создание правила маршрутизации для метки.
Создаём новое правило, которое говорит ядру: для пакетов с меткой 100 использовать таблицу маршрутизации с номером 100.

ip rule add fwmark 100 table 100

Шаг 3: Настройка альтернативной таблицы маршрутизации.
Заполняем таблицу 100 маршрутами. Шлюзом по умолчанию для этой таблицы будет VPN-интерфейс.

ip route add default via 10.8.0.1 dev tun0 table 100
# Важно: добавить маршрут до самого VPN-сервера через основную таблицу (main),
# иначе мы не сможем установить туннель.
ip route add 203.0.113.1 dev eth0

Теперь пакеты от 192.168.1.50 будут маркированы, правило ip rule направит их на просмотр таблицы 100, где для них назначен маршрут через tun0.

Оптимизация производительности: советы для загруженных шлюзов

  • Минимизируйте правила в таблице mangle: Каждое правило здесь проверяется для каждого пакета. Используйте эту таблицу только при необходимости (например, для Policy Routing).
  • Правильный порядок правил фильтрации: Ставьте правила, которые матчатся чаще всего (например, -m conntrack --ctstate ESTABLISHED,RELATED), в самое начало цепочек FORWARD и INPUT. Это позволяет быстро пропускать трафик уже установленных соединений.
  • Используйте множества (sets) в nftables: Для проверки IP-адресов против чёрного списка или больших сетей используйте множества, а не последовательные правила.
    nft add set ip filter bad_ips { type ipv4_addr; }
    nft add rule ip filter INPUT ip saddr @bad_ips drop
  • Контролируйте таблицу conntrack: На загруженных шлюзах таблица соединений может переполниться, что приведёт к сбросу новых сессий. Мониторьте её размер: conntrack -C. Увеличьте лимит при необходимости: sysctl -w net.netfilter.nf_conntrack_max=524288. Не забывайте увеличивать net.netfilter.nf_conntrack_buckets пропорционально.

Для построения комплексной сетевой политики, включающей как классическую маршрутизацию, так и балансировку нагрузки на уровне приложений, изучите основы маршрутизации в 2026 году. Для размещения и масштабирования таких решений можно использовать облачную инфраструктуру, например, Timeweb Cloud, которая предоставляет гибкие серверы и Kubernetes.

Поделиться:
Сохранить гайд? В закладки браузера