Когда сервер работает в глобальной сети, к нему подключаются со всего мира. Его задача — произвести вычисления и/или предоставить информацию по запросу. Но обращения могут быть как легитимными (от пользователей), так и от злоумышленников. Поэтому при управлении сервером важно фильтровать трафик.
Файрвол (от англ. firewall), или брандмауэр — это система или программа, предназначенная для контроля сетевого трафика. Его основная задача — защищать компьютеры, серверы или сети от несанкционированного доступа и атак, а также перенаправлять трафик в зависимости от настроек.
Один из современных инструментов для реализации и настройки брандмауэра в Linux — пакет nftables. Он позволяет управлять правилами фильтрации сетевого трафика и является частью пакетного фильтра ядра Linux (netfilter).
Чем отличается от iptables
Файрвол nftables — новый инструмент, который приходит на смену iptables. Мы кратко рассмотрим их основные отличия.
Iptables давно работает на серверах, поэтому имеет привычный, но громоздкий и неочевидный для новичков синтаксис. Nftables — это его современный аналог со структурированным, компактным и логичным синтаксисом.
Расширения для iptables сделали кодовую базу достаточно раздутой, что усложняет его поддержку и приводит к дублированию функций. В nftables попытались устранить эту проблему. Кроме того, у nftables есть другие преимущества:
- новая универсальная инфраструктура наборов (set);
- возможность управлять IPv4 и IPv6 без использования дополнительных инструментов;
- улучшенная поддержка динамических обновлений наборов правил;
- гибкость в создании пользовательских цепочек, в отличие от жёстко заданного набора стандартных цепочек в iptables.
Итак, nftables — это более современное решение, чем iptables. Но сообщество linux в общем и системные администраторы в частности не всегда используют нововведения, поскольку это может затруднить поддержку без того сложных систем, а в новом инструменте могут встречаться недоработки.
Поэтому, если всё работает без проблем, отказываться от старых решений не всегда целесообразно. А iptables ещё долго будет оставаться популярным из-за большого количества наработанных инструментов за время своей долгой жизни. К тому же, из-за недостатка практического опыта работы с nftables, многие проблемы пока не изучены, а в сети меньше ответов на возникающие вопросы. Однако nftables стоит установить, если важно использовать современное ПО или в нем есть необходимые для вас функции.
Установка nftables
Для работы с nftables будем использовать ОС Debian. Сначала необходимо убедиться, что на сервере установлены необходимые пакеты и удалены пакеты, связанные с iptables — они могут мешать друг другу и путать при проверках правил. Например, администратор может вносить изменения только в пакете iptables, не зная, что фактически в системе активен nftables.
Для работы с файрволом на сервере должен быть установлен пакет nftables. Проверить это можно с помощью пакетного менеджера dpkg:
dpkg -l | grep nftables
Если службы nftables на вашем сервере нет, его можно установить через apt:
apt install nftablesУбедиться, что на вашем сервере отсутствует iptables, можно через dpkg:
dpkg -l | grep iptabЧтобы удалить iptables, используйте утилиту apt:
apt purge iptablesПосле установки nftables необходимо запустить службу nftables.service и добавить её в автозагрузку:
systemctl enable nftables.service
systemctl start nftables.serviceТеперь при запуске сервера правила будут загружаться из файла конфигурации: /etc/nftables.conf.
Служба nftables.service также позволяет производить загрузку правил из конфигурации через команду:
systemctl restart nftables.serviceУправление nftables
Общий синтаксис правил
Чтобы проверить действующие правила, используйте команду:
nft [-a] list rulesetДополнительный ключ -a необходим, чтобы видеть номера правил, например, для удаления.
Если необходимо часто проверять правила на этапе настройки, можно воспользоваться alias:
alias nft-ls='nft -a list ruleset'После этого в текущей сессии bash вызовите команду через nft-ls для вывода правил. Чтобы оставить данный псевдоним для каждой сессии, можно добавить эту команду в .bash_rc или .bash_alias нужного пользователя.
Общий синтаксис для работы с nftables и добавления правил можно описать следующим образом:
nft – оператор – объект – семейство – таблица – цепочка – правило – действие
- Оператор — действие с объектом:
add, delete, list, insertи т. д. - Объект — элемент, с которым выполняют действия:
rule, chain, table. - Семейство — семейство адресации. Операции применяются к пакетам только того семейства, которое указано: например, ip только IPv4, ip6 только IPv6, inet одновременно IPv4 и IPv6 и т. д. Может принимать следующие значения: ip, ip6, inet, erp, bridge, netdev.
- Правило – условие, по которому нужно определить подходящий пакет.
- Таблица, цепочка — способ организации структуры: все правила должны быть в цепочках, а цепочки — в таблицах.
- Действие — что необходимо сделать с пакетом, который попал под правило.
Если вы только что установили nftables, поставили службу nftables.service в автозагрузку и запустили её, то на сервере должны работать правила, которые входят в пакет nftables и находятся в /etc/nftables.conf:

В правилах есть таблица filter семейства inet и цепочки input, forward и output. В конфигурации по умолчанию указывается семейство правил inet. Оно объединяет протоколы IPv4 и IPv6, что позволяет унифицировать семейства ip и ip6 и упростить создание правил. Если при создании таблицы не указать семейство, то по умолчанию будет использоваться семейство ip, которое работает только с IPv4.
Но, как мы ранее упоминали, ничто не мешает изменить названия цепочек и таблиц на другие, поскольку поведение цепочек определяется хуками (hook input/forward/output) и типами (type filter). Если определённые типы или хуки фреймворка netfilter не задействованы ни в одной цепочке, то пакеты будут проходить через эти цепочки без обработки. Это отличает nftables от iftables.
Добавление основных таблиц и цепочек
Для примера создадим собственные цепочки и добавим в них минимальный набор правил, необходимый для работы сервера:

Советуем придерживаться оригинальных названий цепочек — это облегчит новому системному администратору работу с файрволом. Далее создадим правила с нуля, чтобы понять, как управлять nftables:
Сбрасываем текущие правила командой:
nft flush rulesetТеперь на сервере не работает ни одного правила.
Добавим базовые таблицы и цепочки:
nft add table inet filter
nft add chain inet filter input '{type filter hook input priority filter; policy accept;}'
nft add chain inet filter forward '{type filter hook forward priority filter; policy drop;}'
nft add chain inet filter output '{type filter hook output priority filter; policy accept;}'Так, мы почти полностью повторили все правила из базовой конфигурации. Но запретили пересылку пакетов, предназначенных не для нашего сервера, дальше по сети (в цепочке forward указали действие для всех пакетов как drop).
Стоит отметить, что в iptables цепочки и таблицы имеют строгую структуру и иерархию. Поэтому, если вы планируете только добавлять правила в цепочки, то лучше использовать именно iptables. Но если вы планируете сложную и многосоставную конфигурацию, то nftables поможет решить задачу эффективнее.

Добавление правил
После добавления базовых цепочек можно приступить к созданию самих правил. Коротко рассмотрим, какие правила и для каких субъектов можно установить:
- meta — метасущности, например, интерфейсы:
oif <интерфейс-отправитель НОМЕР>
iif <интерфейс-получатель НОМЕР>
oifname <интерфейс-отправитель ИМЯ>
iifname <интерфейс-получатель ИМЯ>
(параметры oif и iif принимают строковые значения, которые конвертируются в номер)
(параметры oifname и iifname более гибкие, но они медленнее из-за необходимости выполнять сравнение строк)- icmp:
type <тип icmp>- ip:
daddr <адрес получателя>
saddr <адрес отправителя>- ipv6:
daddr <адрес получателя>
saddr <адрес отправителя>- tcp:
dport <порт получателя>
sport <порт отправителя>- udp:
dport <порт получателя>
sport <порт отправителя>- ct (connection tracking):
state <new | established | related | invalid>- log:
prefix <дополнительная строка в начале сообщения>
level <emerg | alert | crit | err | warn [default] | notice | info | debug | audit>После описания правила укажите действие с пакетом, которое необходимо выполнить:
accept— принять пакет.drop— отбросить пакет.continue— продолжить обработку правил. Если не указать действие в конце правила, то это действие будет работать по умолчанию.return— позволяет вернуть обработку пакета в предыдущую цепочку. Если цепочка базовая, то применяется действие из политики (policy) базовой цепочки.jump CHAIN— перенаправляет обработку пакета в начало другой цепочкиCHAIN. То есть текущая оценка пакета будет продолжаться в новой цепочке до вынесения решения/действия (например,acceptилиdrop) или возврата к предыдущей цепочке черезreturn.
Эти списки не исчерпывающие. Но они включают популярные субъекты и действия, которые можно совершить с пакетом после совпадения с правилом.
Разберём создание правил на примере создания базовой конфигурации. Допустим, на сервере нужно обеспечить:
- беспрепятственную работу служб на сервере с localhost;
- доступ по SSH;
- проверку доступности сервера с помощью ping;
- возможность сервера отвечать на запросы, инициированные локальной системой (например, обновлять пакеты);
- подключение к сервисам на самом сервере, например, ftp или http/https.
Весь порядок действий выполняют следующие команды:
nft add rule inet filter input iifname "lo" accept comment \"Accept any localhost traffic\”
nft add rule inet filter input tcp dport 22 accept comment \"Accept ssh\"
nft add rule inet filter input icmp type echo-request accept comment \"Accept icmp echo-request\"
nft add rule inet filter input ct state established,related accept comment \"Accept traffic originated from us\"
nft add rule inet filter input tcp dport '{80,443}' accept comment \"Accept http/https \"
nft add inet filter input tcp dport '{20,21,35000-35999}' accept comment \"Accept ftp\"Допускается указывать конкретное место для вставки правила с помощью оператора add. Для этого нужно узнать его номер — это можно сделать через оператор list и ключ -a:
nft -a list ruleset # Выводит все текущие правила
nft -a list chain inet filter input # Выводит только правила в цепочке inputНапример, если вы указали правило handle 2, то новое правило появится сразу после него:
nft add rule inet filter input handle 2 tcp dport '{80,443}' accept comment \"Accept http/https \"Приведённый пример создания правил упрощённый: блок comment можно опустить для всех правил, если действия, которые они выполняют, очевидны. Если подписывать каждое, то вывод будет перегружен информацией, и понять логику работы брандмауэра будет сложнее. Но советуем оставить комментарии для сложных правил, чтобы в будущем не забыть их смысл и назначение. Также обратите внимание на обратные слеши \ при записи комментариев — их необходимо добавлять для экранирования символов, иначе возникнет ошибка синтаксиса.
Когда вы добавляете правила с помощью оператора add без указания места, то они по умолчанию записываются в конец цепочки. Если нужно вставить правило в начало или другое конкретное место (например, чтобы оно обрабатывалось раньше), то можно воспользоваться оператором insert. Он позволяет добавлять правило перед тем, что вы укажете в выражении.
Для примера добавим логирование подключений к ftp и поставим его прямо перед правилом для портов ftp (в нашем случае — handle 11):
nft insert rule inet filter input handle 11 tcp dport 21 ct state new log prefix \"New FTP connection: \" continueЕсли в команде insert не указать место для правила, то оно будет поставлено первым.
Суть этого правила: оно вставляется (insert) в цепочку input таблицы filter (семейство inet) вместо 11 правила, но под другим номером. Правило отбирает новые (ct state new) TCP-соединения на 21-й порт (tcp dport 21), логирует их с префиксом “ New FTP connection: " (log prefix) и передаёт пакет дальше по цепочке (continue).
Сообщения правил log передаются в ядро и посмотреть их можно с помощью journald или dmesg:

Порядок обработки правил в этом случае важен. Если добавить правило логирования после правила Accept ftp, то логирования не произойдёт — пакет уже будет принят и передан ftp предыдущим правилом.
Изменение политики для цепочки
После добавления правил можно заблокировать доступ к серверу для остальных портов и служб, которые не попали в правила, указав политику drop. Nftables будет работать по принципу «запрещено всё, что не разрешено явно».
nft chain inet filter input '{type filter hook input priority filter; policy drop;}'Очень важно менять политику цепочки в последнюю очередь. Если вы не успели указать доступ по SSH в разрешённые правила или ошибочно ограничили доступ с определённого хоста (например, через saddr), и ваш текущий хост в него не входит, то сервер сразу разорвёт соединение. В этом случае поможет перезагрузка сервера (при загрузке прочитаются правила из /etc/nftables.conf) или сброс правил через команды по vnc:
nft flush ruleset # Сброс всех правил
nft -f /etc/nftables.conf # Чтение последней рабочей конфигурации
systemctl restart nftables.service # Можно и так перечитать из /etc/nftables.confЕсли вы настраиваете файрвол впервые, и важно не перезагружать сервер для сброса правил, то можно использовать небольшую хитрость во время настройки — добавить в cron задачу на сброс правил каждые несколько минут:
*/15 * * * * /usr/sbin/nft -f /etc/nftables.confТаким образом правила будут загружаться из конфигурации по умолчанию в nftables каждые 15 минут. Если доступ пропадёт, можно просто подождать, пока сработает задание на загрузку правил.
Если правила были значительно изменены, и вы не хотите их перезаписать старыми из конфигурации, то можно изменить сам файл загрузки (/etc/nftables.conf) на другой или создать предварительно файл с рабочей конфигурацией файрвола в любом другом каталоге ОС с указанием в задаче cron.
Сохранение правил
В команде cron видно, как загружаются правила из конфигурации. Сохранение в файл происходит в два шага: сначала добавляется строка команды flush ruleset (через утилиту echo), и только потом правила записываются через утилиту nft.
Поэтому для сохранения правил нужно использовать команды:
echo "flush ruleset" > /путь/до/конфигурации
nft -s list ruleset >> /путь/до/конфигурацииБез flush ruleset правила добавляются к текущей конфигурации, не перезаписывая их полностью, что может привести к проблемам.
Сейчас правила, которые мы добавляли ранее, в nftables выглядят так:

Изменение правила
Если вы ошиблись или нужно изменить добавленное правило, это можно сделать через оператор replace. Допустим, мы хотим расширить возможный диапазон пассивных портов для ftp (порты 35000-35999). В iptables пришлось бы сначала удалять правило и добавлять новое. Желательно — на то же место, если их порядок имеет значение в контексте текущей конфигурации на сервере.
Для примера изменим диапазон, добавив порты до 36999. Это можно сделать с помощью команды:
nft replace rule inet filter input handle 11 tcp dport '{ 20-21, 35000-36999 }' accept comment \"New accept ftp\"
Удаление правил
Допустим, необходимо «отключить» службу ftp или вы просто хотите запретить к ней доступ. Тогда нужно удалить соответствующее правило.
В нашем случае команда для удаления правил ftp будет такой:
nft delete rule inet filter input handle 17
Множества
В nftables можно создавать множества, или наборы элементов. Они позволяют добавлять правила сразу для нескольких сущностей, например, множества адресов, портов и т.д.
Наборы могут быть как именованные, так и анонимные.
Анонимные лучше использовать, когда точно известен набор элементов, с которым вы хотите работать, поскольку для добавления нового элемента придётся полностью менять правило. В примерах мы уже использовали анонимные множества при добавлении правил, но не акцентировали на них внимание. Пример — конструкции вида “{20-21, 35000-36999}” или “{80,443}” в правилах.
Именованные наборы следует использовать, если у вас нет полного списка сущностей и/или в нём будут происходить постоянные изменения. Допустим, необходимо запретить доступ сразу из нескольких подсетей (например, приватных), а также производить логирование, если обнаружатся попытки подключения из этих подсетей. Предполагается, что данный список будет расширяться в будущем и не будет ограничен лишь приватными сетями.
Создадим набор с любым названием, например, blackhole, в который будем добавлять подсети:
nft add set inet filter blackhole '{type ipv4_addr; flags interval; comment "drop all packets from these hosts";}'Обратите внимание на необязательную опцию flags interval — с её помощью можно будет указать подсети. Если этого не сделать, будет возможность только добавления отдельных адресов.
Добавим туда приватные подсети:
nft add element inet filter blackhole {10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16}Вывод правил до и после добавления множества на сервере будет выглядеть следующим образом:

Пока мы определились только с элементами, с которыми хотим работать, но не указали, что нужно с ними делать. Поэтому добавим логирование и блокировку пакетов с этих адресов из blackhole:
nft insert rule inet filter input ip saddr @blackhole drop
nft insert rule inet filter input ip saddr @blackhole ct state new log prefix \"New connection in blackhole: \" continue
Обратите внимание. Мы добавили правила через insert, чтобы поместить их в начало цепочки. Это нужно, чтобы сначала заблокировать все пакеты и подключения из множества. Иначе бы эти правила не сработали — их бы перекрыли правила для SSH, http/https и FTP, которые открывают доступ со всех хостов, и они обрабатывались бы первыми.
Теперь попробуем подключиться из приватной сети по SSH, FTP и выполнить запросы ping. В логах сервера все такие подключения будут детально записаны с указанием типа (ICMP или конкретный порт).

Обратите внимание: множество нельзя удалить, пока оно используется в цепочках правил. Поэтому нужно сначала удалить все правила, которые его содержат. В нашем случае для этого можно использовать команды:
nft delete rule inet filter input handle 23
nft delete rule inet filter input handle 24
nft delete set inet filter blackhole
Шпаргалка
| Команда | Назначение |
|---|---|
Работа со службой | |
systemctl enable nftables.service | добавление в автозагрузку |
systemctl start nftables.service | запуск службы |
systemctl restart nftables.service | перезапуск службы |
| Работа с конфигурацией | |
nft [-a] list ruleset | просмотр текущих правил |
nft [-a] list chain FAMILY TABLE CHAIN_NAME | просмотр текущих правил для цепочки CHAIN_NAME |
nft flush ruleset | сброс всех текущих правил |
nft -f /etc/nftables.conf | загрузка правил из файла |
| создание файла с правилами |
| Работа с таблицами и цепочками | |
nft add table FAMILY TABLE_NAME | создание таблицы TABLE_NAME с семейством FAMILY (ip, ip6, inet, erp, bridge, netdev) |
nft add chain FAMILY TABLE_NAME CHAIN_NAME '{type TYPE hook HOOK priority PRIORITY; policy POLICY;}' | создание базовой цепочки CHAIN_NAME с хуком HOOK (prerouting, input, forward, output или postrouting) с типом TYPE (filter, route или nat) и политикой POLICY |
nft chain FAMILY TABLE_NAME CHAIN_NAME '{ [ type TYPE hook HOOK priority PRIORITY ; policy POLICY ; ] }' | изменение цепочки (обратите внимание: отсутствует оператор add) |
nft delete chain FAMILY TABLE_NAME CHAIN_NAME | удаление цепочки CHAIN_NAME из таблицы TABLE_NAME |
nft flush chain FAMILY TABLE_NAME CHAIN_NAME | очистка всех правил в цепочке CHAIN_NAME и таблице TABLE_NAME |
nft flush table TABLE_NAME | удаление таблицы TABLE_NAME |
Работа с правилами | |
nft add rule FAMILY TABLE_NAME CHAIN_NAME <handle HANDLE> RULE ACTION | добавление правила RULE после номера HANDLE в цепочку CHAIN_NAME таблицы TABLE_NAME с действием ACTION (accept, drop, continue, return, jump CHAIN_NAME) |
nft insert rule FAMILY TABLE_NAME CHAIN_NAME <handle HANDLE> RULE ACTION | вставка правила RULE вместо номера HANDLE в цепочку CHAIN_NAME таблицы TABLE_NAME с действием ACTION |
nft replace rule FAMILY TABLE_NAME CHAIN_NAME handle HANDLE RULE ACTION | изменение правила RULE под номером HANDLE в цепочке CHAIN_NAME таблицы TABLE_NAME с действием ACTION |
nft delete rule FAMILY TABLE_NAME CHAIN_NAME handle HANDLE | удаление конкретного правила RULE под номером HANDLE из цепочки CHAIN_NAME таблицы TABLE_NAME |
Работа с множествами | |
nft add set FAMILY TABLE_NAME SET_NAME '{type TYPE; <flags FLAGS;> <comment "COMMENT";>}' | добавление множества SET_NAME в таблицу TABLE_NAME с типом TYPE (ipv4_addr, ipv6_addr, ether_addr, inet_proto, inet_service, mark), флагами FLAGS (constant, dynamic, interval, timeout) и комментарием COMMENT |
nft add element FAMILY TABLE_NAME SET_NAME {ELEMENT_1, ELEMENT_2,...} | добавление элементов ELEMENT_1, ELEMENT_2 во множество SET_NAME |
nft delete element FAMILY TABLE_NAME SET_NAME {ELEMENT_1, ELEMENT_2,...} | удаление элементов ELEMENT_1, ELEMENT_2 во множество SET_NAME |
nft delete set FAMILY TABLE_NAME SET_NAME | удаление множества SET_NAME из таблицы TABLE_NAME |
Примеры | |
nft add chain inet filter input '{type filter hook input priority filter; policy accept;}' | добавление базовой цепочки input |
nft add rule inet filter input iifname "lo" accept comment | разрешить loopback |
nft add rule inet filter input tcp dport PORT accept | разрешить подключение tcp к порту PORT |
nft add rule inet filter input udp dport PORT accept | разрешить подключение udp к порту PORT |
nft add rule inet filter input ip saddr ADDRESS tcp dport PORT drop | запретить подключение к порту PORT с адреса ADDRESS (допускается указывать подсеть с маской) |
nft add rule inet filter input ip saddr ADDRESS drop | полностью запретить все подключения с адреса ADDRESS (допускается указывать подсеть с маской) |
nft add rule inet filter input icmp type echo-request accept | разрешить эхо-запросы icmp |
nft add rule inet filter input ct state established,related accept | разрешить отвечать на запросы, инициированные локальной системой |
nft add rule inet filter input tcp dport '{80,443}' accept | разрешить http и https |
nft insert rule inet filter input handle HANDLE tcp dport PORT ct state new log prefix \"STRING\" continue | вставить правило логирования новых подключений к порту PORT вместо HANDLE со строкой STRING |
nft add rule inet filter input tcp dport '{ PORT_START-PORT_FINISH}' accept | разрешить подключения tcp к портам из диапазона PORT_START-PORT_FINISH (допускается указывать несколько диапазонов через запятую) |
nft add set inet filter blackhole '{type ipv4_addr; flags interval; comment "drop all packets from these hosts";}' | создание множества blackhole для добавления в него подсетей |
nft add element inet filter blackhole {10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16} | добавление приватных подсетей во множество blackhole |
nft insert rule inet filter input ip saddr @blackhole drop | запретить все подключения из множества blackhole |
nft add set inet filter blackhole_port '{type inet_service; flags interval; comment "drop all packets from these hosts";}' | создание множества blackhole_port для добавления в него портов |
nft add element inet filter blackhole_port {PORT_START-PORT_FINISH} | добавление диапазона портов PORT_START-PORT_FINISH во множество blackhole_port |
nft insert rule inet filter input tcp dport @blackhole_port ip saddr @blackhole drop | запрет на доступ к портам из множества blackhole_port c адресов из множества blackhole |