22 октября 2026 г.

Файрвол nftables на VPS: правила «всё закрыто», которые можно прочитать и проверить

Один короткий файл вместо кучи команд iptables: закрыть всё, открыть веб и SSH, проверить синтаксис до загрузки, не отрезать себя.

Файрвол - это список правил о том, кто может стучаться в какой порт. Годами этот список жил в iptables: каждое правило - отдельная команда, а прочитать всё вместе трудно. Его сменил nftables: вы пишете один файл, проверяете и загружаете целиком. На Ubuntu 24.04 он уже установлен. Ниже соберём рабочий набор правил и докажем, что он блокирует то, что должен.

Коротко. Поставьте политику drop на цепочку input, затем разрешите уже установленные соединения, loopback, ping, порты 80 и 443 и SSH. Проверьте файл командой nft -c -f, загрузите через nft -f и держите вторую SSH-сессию открытой, пока не убедитесь, что вход работает.

Что такое nftables и чем он отличается от iptables

Оба работают с одним и тем же механизмом ядра Linux - фильтром пакетов netfilter. nftables - его более новая оболочка с единой командой nft. В современной Ubuntu даже старая команда iptables - тонкая обёртка над ним: на нашем сервере iptables -V выводит iptables v1.8.10 (nf_tables). По умолчанию выбор не между двумя движками, а между двумя способами записи правил.

Слов нужно немного:

  • table (таблица) - контейнер для правил. Семейство inet значит «и IPv4, и IPv6 сразу», так что всё не придётся писать дважды.
  • chain (цепочка) - упорядоченный список правил в определённой точке пути пакета. input - трафик, адресованный этому серверу, output - исходящий, forward - транзитный.
  • policy (политика) - что делать с пакетом, которому не подошло ни одно правило цепочки.
  • set (набор) - именованный список адресов или портов, на который ссылаются правила.

Правила «всё закрыто»

Идея: закрыть всё, а потом открыть только то, что можете назвать. Сохраните текст ниже как /etc/nftables.conf на настоящем сервере или в любой файл для пробы. Первая строка, flush ruleset, стирает все загруженные правила - запомните это, дальше пригодится.

Что делает каждая часть:

  • policy drop на input - всё, что не разрешено ниже, молча отбрасывается. Это одно слово и есть файрвол.
  • ct state established,related accept - ответы на соединения, которые начали вы, и ваша текущая SSH-сессия продолжают работать. Без этой строки сервер отрежет собственные ответы.
  • ct state invalid drop - пакеты, которые conntrack (отслеживание соединений в ядре) считает искажёнными или невозможными для своего состояния, отбрасываются сразу. Свежий первый пакет невалидным не считается, он new и идёт дальше по правилам.
  • iif "lo" accept - сервер общается сам с собой через 127.0.0.1, например nginx передаёт запросы приложению на локальном порту. Закройте это, и половина служб встанет.
  • limit rate 5/second - ping работает, а поток пингов нет.
  • tcp dport { 80, 443 } accept - веб, отовсюду.
  • tcp dport 22 ip saddr @admin_ips accept - SSH только с адресов из набора. Замените адрес из примера на свой. В наборе адреса IPv4, поэтому SSH по IPv6 будет отброшен; строка с icmpv6 выше тоже не для красоты: IPv6 нужны эти сообщения, чтобы находить соседей. IPv6 в лаборатории я не проверял. Если домашний адрес часто меняется, это правило может запереть вас снаружи, поэтому для начала оставьте просто tcp dport 22 accept и ужесточите позже.
  • forward с policy drop - обычному VPS чужой трафик маршрутизировать незачем. Подвох в Docker: он цепляет свои правила в ту же точку, а отброс в любой цепочке этой точки убивает пакет. На хосте с Docker такая политика сломает порты контейнеров и их выход в интернет, пока вы не добавите правила forward или не оставите политику accept. Docker здесь я не проверял.

Проверьте файл до загрузки

Ошибка в файле файрвола - не то же, что ошибка на веб-странице. Ключ -c только разбирает файл и ничего не загружает, так что запускайте его каждый раз.

sudo nft -c -f /etc/nftables.conf

Тишина значит, что синтаксис в порядке. Опечатка (здесь polcy вместо policy) даёт ошибку с точным указанием места:

Ошибка показана дважды, потому что опечатка есть в двух цепочках. Ничего не загрузилось, код выхода 1, правила в памяти остались прежними.

Загрузка и проверка

На рабочем сервере я это не гонял. Опыты с файрволом на машине, к которой вы подключены, - хороший способ её потерять, поэтому собрал небольшую лабораторию: два сетевых пространства имён (srv и cli, изолированные копии сетевого стека), соединённые виртуальным кабелем, и тестовый веб-сервер на портах 80 и 8080 внутри srv. Адрес клиента 192.0.2.20 - тот, что в admin_ips. До правил отвечали оба порта. После nft -f:

Порт 80 отвечает HTTP 200. Порт 8080 даёт 000, а curl выходит с кодом 28 - таймаут: пакет отброшен, никто не ответил. Так drop выглядит снаружи, и поэтому сканерам портов на таком сервере тяжелее, чем на том, что отвечает «connection refused». Ping по-прежнему проходит, как и сказано в правиле.

Чтобы увидеть, что загружено, спросите ядро:

sudo nft list ruleset

Вывод - ваш файл в нормализованном виде. Заметите мелочи: priority 0 печатается как priority filter, у limit rate 5/second появляется burst 5 packets, а icmpv6 превращается в ipv6-icmp. Правила те же, написание каноническое.

Правка живых правил

Для небольшой правки не нужно перезагружать весь файл: правила добавляются на лету. Правило только со счётчиком counter и без вердикта ничего не разрешает, оно лишь считает пакеты, прошедшие мимо, - дешёвый способ увидеть, кто стучится в порт:

sudo nft add rule inet filter input tcp dport 8080 counter

После двух запросов на порт 8080 правило показало:

Два пакета, 120 байт, и политика всё равно их отбросила. Правила, добавленные так, живут только в памяти. Чтобы удалить правило, нужен его номер (handle): sudo nft -a list chain inet filter input печатает его в конце строки (вроде # handle 13, у вас будет другой), затем sudo nft delete rule inet filter input handle 13.

Наборы меняются так же, не трогая правила, которые их используют:

sudo nft add element inet filter admin_ips '{ 198.51.100.7 }'

После этого в admin_ips лежали оба адреса, 192.0.2.20 и 198.51.100.7, и правило для SSH охватывало оба, без перезагрузки. (Я проверил содержимое набора, а не вход со второго адреса.)

Сохранение после перезагрузки

Правила, загруженные вручную, исчезают при перезагрузке. На Ubuntu 24.04 пакет nftables приносит службу, которая загружает /etc/nftables.conf при старте; у нас она установлена, но выключена: systemctl is-enabled nftables отвечает disabled, а стандартный /etc/nftables.conf содержит пустые цепочки без правил. Включайте только после успешной проверки:

sudo systemctl enable --now nftables

Собрали правила вживую и хотите их в файл? sudo nft list table inet filter | sudo tee /etc/nftables.conf сохранит нашу таблицу. Весь набор правил на машине с UFW или Docker выгружать не стоит: в дамп попадут и их правила. Строку с shebang и flush ruleset добавьте сверху сами, в выводе нет ни той, ни другой.

Как не запереть себя снаружи

  • Не закрывайте текущую SSH-сессию и проверьте новый вход во втором окне, прежде чем закрыть первое. Правила для established удерживают старую сессию, даже если новое правило для порта 22 неверное.
  • Если всё же заперли себя, вкладка «Консоль» в панели хостинга работает без SSH. Выполните там sudo nft delete table inet filter: ваши правила исчезнут вместе с политикой drop, а правила UFW и Docker останутся.
  • Для страховки, которую я здесь не проверял, запланируйте ту же очистку до загрузки нового файла: sudo systemd-run --on-active=5m nft delete table inet filter. Если через пять минут вы на месте, отмените её командой sudo systemctl stop run-*.timer; если заперли себя, файрвол откроется сам. Простой sleep в фоне умер бы вместе с оборвавшейся SSH-сессией, а она-то и нужна.

UFW, Docker и что ломает flush ruleset

UFW и Docker не пишут файлы nftables, а добавляют правила командой iptables, которая в Ubuntu 24.04 попадает в те же таблицы ядра. На нашем сервере это видно: nft list ruleset начинается с предупреждения table ip filter is managed by iptables-nft, do not touch! и перечисляет цепочки UFW. Два следствия:

  • flush ruleset удаляет всё, включая правила UFW и Docker. С Docker я не проверял, но порты опубликованных контейнеров, скорее всего, перестанут работать, пока Docker не перезапустить, а UFW нужен sudo ufw reload, чтобы вернуть свои правила.
  • Если UFW и ручной файл nftables работают одновременно, трудно понять, кто именно блокирует порт. Выберите что-то одно. Если нужно проще, UFW разобран в нашей статье nginx и UFW перед вашим приложением. nftables берите, когда нужен один читаемый файл либо наборы и более тонкие лимиты, чем у UFW.

Перевод старых правил iptables

Если у вас есть команды iptables из старой инструкции, iptables-translate напечатает эквивалент для nft, ничего не применяя:

iptables-translate -A INPUT -p tcp --dport 443 -j ACCEPT

Ответ: nft 'add rule ip filter INPUT tcp dport 443 counter accept'. Правило с ограничением по источнику, -s 203.0.113.5 --dport 22, превратилось в ip saddr 203.0.113.5 tcp dport 22. Считайте вывод отправной точкой: перевод кладёт правило в таблицу filter семейства ip, и в свою таблицу inet его придётся перенести вручную.

Посмотрите, что на самом деле слушает

Файрвол прячет порты, но не закрывает их. Прежде чем решать, что открывать, посмотрите, какие службы вообще слушают: ss -tlnp; вывод разбирает статья ss вместо netstat. Основы защиты свежего сервера - в инструкции как защитить новый VPS.

Вопросы и ответы

nftables лучше iptables?

Для новых правил - да: единый синтаксис для IPv4 и IPv6, наборы и файл, который можно прочитать и проверить. Старая команда iptables работает и попадает в тот же механизм ядра, так что переписывать существующие настройки срочно не нужно.

Как посмотреть правила nftables?

sudo nft list ruleset печатает всё, sudo nft list table inet filter - одну таблицу. Ключ -a добавляет номера правил (handle).

Как проверить конфиг, не применяя его?

Командой sudo nft -c -f file.nft: она разбирает файл и сообщает об ошибках, но ничего не загружает. Поведение, в отличие от синтаксиса, проверяйте в сетевом пространстве имён или на запасном сервере, как выше.

Нужен ли nftables, если я пользуюсь UFW?

Нет. UFW - оболочка над тем же фильтром ядра, и обычному серверу его хватает. nftables нужен, когда нужен более тонкий контроль или один декларативный файл.

Почему закрытый порт молчит, а не отвечает «отказано»?

drop молча отбрасывает пакет. reject возвращает ошибку, и клиент падает сразу. drop раскрывает меньше, reject удобнее для ваших собственных инструментов. Для публичного сервера обычно выбирают drop.

Итоги

  • nftables - одна команда nft и один читаемый файл; старый iptables на Ubuntu 24.04 уже работает через него.
  • Файрвол - это policy drop на input и короткий список разрешённого: установленные соединения, loopback, ping, веб, SSH.
  • Запускайте nft -c -f перед каждой загрузкой: тогда опечатка ничего не стоит.
  • Не закрывайте одну SSH-сессию, пока не сработает вторая.
  • flush ruleset стирает и правила UFW с Docker, поэтому отдайте файрвол одному инструменту.
PUBLISHED
22 октября 2026 г.
AUTHOR
HIP-HOSTING
LANGUAGES
EN · RU