Перейти к содержанию

Файрвол Linux с nftables и iptables

iptables считается устаревшим в пользу nftables на современных ядрах, хотя всё ещё широко используется (и ufw/firewalld оба оборачивают один или другой под капотом). Эта статья охватывает и базовый механизм, и инструменты уровня дистрибутива поверх него.

Как это устроено

Модель фильтрации: таблицы, цепочки, правила

И iptables, и nftables разделяют одну концептуальную модель: пакеты проходят через цепочки (INPUT, OUTPUT, FORWARD — стандартные), сгруппированные в таблицы, каждая цепочка содержит упорядоченный список правил. Первое совпавшее правило решает судьбу пакета (ACCEPT, DROP, REJECT), правила проверяются сверху вниз.

nftables (современная замена)

sudo nft list ruleset                    # показать весь текущий набор правил

Минимальный набор правил, записанный в файл и загруженный:

# /etc/nftables.conf
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        ct state established,related accept
        iif lo accept
        tcp dport 22 accept
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept
    }
}
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables

Читаем: policy drop означает всё, что явно не разрешено, отбрасывается — позиция default-deny, единственная безопасная настройка по умолчанию для хоста, смотрящего в интернет. ct state established,related accept критично важен и легко забывается: без него обратный трафик для соединений, которые вы сами инициировали наружу (DNS-запросы, обновления пакетов, исходящие вызовы API), тоже отбрасывался бы, поскольку у цепочки нет памяти о том, что «этот ответ относится к соединению, которое я разрешил наружу». iif lo accept разрешает трафик loopback (многие локальные сервисы общаются друг с другом через 127.0.0.1, например, приложение-сервер с локальным прокси БД) — без этого базовое локальное межпроцессное взаимодействие ломается.

flush ruleset в начале делает загрузку идемпотентной: без него nft -f добавляет правила к уже загруженным, и повторная загрузка файла их дублирует. Проверить синтаксис без применения: sudo nft -c -f /etc/nftables.conf.

iptables (всё ещё распространён, особенно где инструментарий nftables ещё не стал

стандартом)

Эквивалентная политика в синтаксисе iptables:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -P INPUT DROP    # в последнюю очередь: default-deny включаем, когда правила accept уже на месте

Порядок важен, если вы вводите это по SSH: если сначала выполнить -P INPUT DROP, ваша сессия борвётся до появления правила ESTABLISHED. Правила iptables хранятся только в памяти — сохраняйте их через iptables-save и пакет iptables-persistent (Debian/Ubuntu) или iptables-services (RHEL-подобные).

sudo iptables -L -n -v --line-numbers    # список текущих правил со счётчиками пакетов/байт

Счётчики пакетов/байт -v реально полезны для диагностики: нулевой счётчик у правила, которое вы ожидали увидеть совпавшим, говорит, что трафик вообще до него не доходит (неверный порядок, не тот интерфейс, более раннее правило уже его перехватило).

ufw (фронтенд Ubuntu, оборачивает iptables/nftables)

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow from 203.0.113.0/24 to any port 5432
sudo ufw enable
sudo ufw status verbose

ufw allow from <subnet> to any port <port> — паттерн для ограничения сервиса (базы данных, админ-панели) конкретным диапазоном источников вместо всего интернета — на практике гораздо полезнее, чем голый allow <port>.

firewalld (фронтенд RHEL-семейства)

sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

--permanent записывает правило на диск, но не применяет его немедленно — нужен --reload (или --permanent + последующая перезагрузка), чтобы оно вступило в силу, в то время как команда без --permanent применяется немедленно, но теряется при следующей перезагрузке конфига/ребуте. Очень частая ошибка — забыть одну из половинок этой пары.

Различия Ubuntu/Debian и RHEL-подобных систем

Ubuntu/Debian RHEL-подобные
Фронтенд ufw firewalld
Движок под капотом (современные релизы) nftables nftables (слой совместимости iptables-nft на старых)
Концепция зон Нет встроенной концепции зон firewalld организует правила в именованные «зоны» (public, internal и т.д.) с разной политикой по умолчанию

Практическое задание

  1. На тестовой VM (не на хосте, к которому вы активно подключены по SSH без доступа к консоли) проверьте текущий набор правил через sudo nft list ruleset или sudo iptables -L -n -v.
  2. Добавьте правило, разрешающее ещё не используемый вами порт (например, 8080/tcp), через фронтенд-инструмент вашего дистрибутива.
  3. Убедитесь через ufw status verbose / firewall-cmd --list-all / nft list ruleset, что правило присутствует, затем удалите его.

Как проверить, что всё сделано правильно

  • До добавления правила: sudo ss -tulnp | grep 8080 должен ничего не показывать, а попытка подключения к этому порту с другого хоста должна зависать/отклоняться.
  • После добавления правила (с чем-то реально слушающим, например, python3 -m http.server 8080 для быстрого теста): соединение должно пройти с разрешённого источника и провалиться с запрещённого.

Типичные ошибки

  • Забыть правило established,related / «allow outgoing» при переходе на политику default-deny, ломая весь исходящий-инициированный обратный трафик (DNS, обновления, вызовы API) — не только входящие соединения.
  • Тестирование изменений файрвола через то же самое соединение, которое может быть обрезано этими изменениями, без консоли/доступа вне полосы в качестве запасного варианта на удалённом хосте.
  • Разделение runtime/--permanent у firewalld: применение правила с --permanent и забытый --reload, затем недоумение, почему «не сработало» (сработает, после следующей перезагрузки конфига или ребута — просто ещё нет).

⚠ Опасные команды

  • iptables -F (flush) при уже действующей policy DROP мгновенно блокирует весь трафик, ещё не добавленный обратно, включая вашу собственную SSH-сессию — на удалённом хосте без доступа к консоли это мгновенная, жёсткая блокировка. Всегда имейте план отката (запланированная задача at/cron, откатывающая набор правил через 1-2 минуты — распространённая страховка именно для этого сценария), прежде чем тестировать новую политику default-deny удалённо.
  • Включение ufw с default deny incoming без предварительного разрешения собственного SSH-порта. Уже установленная сессия обычно остаётся жива, поэтому всё выглядит нормально — но открыть новое соединение вы уже не сможете, а обрыв сессии или перезагрузка закроют доступ. Выполните sudo ufw allow 22/tcp (или ваш реальный порт SSH) до sudo ufw enable.

Связанные статьи

Контрольные вопросы

  1. Почему ct state established,related accept (или его эквивалент в iptables) необходим в наборе правил default-deny?
  2. В чём практический риск запуска iptables -F на удалённом хосте с уже установленной политикой DROP?
  3. В чём разница между правилом firewalld, применённым с и без --permanent?
  4. Как проверить, что конкретное правило файрвола реально совпадает с реальным трафиком, используя только вывод самого iptables?