nftables (Русский) - ArchWiki

Jump to content

Ссылки по теме

- iptables (Русский)
- Firewalld

Состояние перевода: На этой странице представлен перевод статьи nftables. Дата последней синхронизации: 11 июля 2021. Вы можете помочь синхронизировать перевод, если в английской версии произошли изменения.

nftables — проект netfilter, разработанный для замены существующего фреймворка {ip,ip6,arp,eb}tables. Предоставляет новую систему фильтрации пакетов, пользовательскую утилиту ntf, а также слой совместимости с {ip,ip6}tables. nftables использует существующие хуки, отслеживание соединений, очереди в пространстве пользователя и подсистему логирования netfilter.

nftables состоит из трёх основных частей: низкоуровневая реализация в ядре, библиотека libnl, которая обеспечивает netlink-взаимодействие, и пользовательский фронтенд nftables. Ядро предоставляет интерфейс настройки netlink, а также занимается runtime-выполнением наборов правил. Библиотека libnl содержит низкоуровневые функции для взаимодействия с ядром. Фронтенд nftables — то, с чем пользователь взаимодействует утилитой nft.

Подробную информацию о nftables можно найти на вики-странице проекта.

## Установка

Установите пакет nftables или git-версию nftables-gitAUR.

Совет: Большинство фронтендов iptables не предлагают прямой или косвенной поддержки nftables, хотя подобная возможность предусмотрена [1]. firewalld — единственный графический фронтенд со встроенной поддержкой как iptables, так и nftables [2].

## Использование

В nftables не различаются временные правила, добавленные из командной строки, и постоянные, загруженные из файла или сохранённые в файл.

Все правила должны создаваться или загружаться утилитой `nft`. Подробнее о работе с ней см. раздел #Настройка.

Текущий набор правил можно узнать командой:

    # nft list ruleset

### Простой межсетевой экран

В пакет nftables входит простая и надёжная конфигурация экрана, сохранённая в файле `/etc/nftables.conf`.

Служба `nftables.service` загрузит эти правила при запуске или включении.

## Настройка

Утилита nft выполняет большую часть обработки правил перед передачей их ядру. Правила объединяются в цепочки, которые, в свою очередь, входят в состав таблиц. В последующих разделах описано создание и модифицирование этих структур.

Прочитать правила из файла можно с помощью опции `-f`/`--file`:

    # nft --file имя-файла

Обратите внимание, что уже загруженные на этот момент правила не будут при этом автоматически стёрты.

Полный список команд можно найти в руководстве nft(8).

### Таблицы

Таблицы содержат #Цепочки. В отличие от iptables, в nftables отсутствуют встроенные таблицы. Количество таблиц и их имена определяется пользователем. Тем не менее, каждая таблица имеет только одно семейство адресации и применяется к пакетам только этого семейства. Таблицы могут относиться к одному из пяти семейств.

семейство nftables | утилита iptables
--- | ---
ip | iptables
ip6 | ip6tables
inet | iptables и ip6tables
arp | arptables
bridge | ebtables

`ip` (т.е. IPv4) — семейство по умолчанию; используется, если семейство не было указано.

Семейство `inet` объединяет протоколы IPv4 и IPv6, что позволяет унифицировать семейства `ip` и `ip6` и упростить создание правил.

Описание остальных семейств адресации вы найдёте в руководстве nft(8) § ADDRESS FAMILIES.

В командах ниже параметр `семейство` будет указываться не всегда; если его нет, то подразумевается семейство `ip`.

#### Создание таблицы

Добавить новую таблицу:

    # nft add table семейство таблица

#### Просмотр таблиц

Вывести список таблиц:

    # nft list tables

#### Просмотр цепочек и правил в таблице

Вывести цепочки и правила выбранной таблицы:

    # nft list table семейство таблица

Например, чтобы вывести на экран правила таблицы `my_table` семейства `inet`, выполните команду:

    # nft list table inet my_table

#### Удаление таблицы

Удалить таблицу со всеми цепочками:

    # nft delete table семейство таблица

#### Очистка таблицы

Стереть все правила в таблице:

    # nft flush table семейство таблица

### Цепочки

Цепочка содержит #Правила. В отличие от iptables, в nftables отсутствуют встроенные цепочки. Соответственно, если опрелённые типы или хуки фреймворка netfilter не задействованы ни в одной цепочке, то проходящие через эти цепочки пакеты обрабатываться не будут (в отличие от iptables).

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

В командах ниже параметр `семейство` будет указываться не всегда; если его нет, то подразумевается семейство `ip`.

#### Создание цепочки

##### Обычная цепочка

Добавить обычную цепочку `цепочка` в таблицу `таблица`:

    # nft add chain семейство таблица цепочка

Например, чтобы добавить обычную цепочку `my_tcp_chain` к таблице `my_table` семейства адресации `inet`, выполните:

    # nft add chain inet my_table my_tcp_chain

##### Базовая цепочка

Чтобы добавить базовую цепочку, укажите хук и значение приоритета:

    # nft add chain семейство таблица цепочка '{ type тип hook хук priority приоритет ; }'

`тип` может принимать значения `filter`, `route` или `nat`.

Для семейств адресации IPv4/IPv6/Inet `хук` может принимать значения `prerouting`, `input`, `forward`, `output` или `postrouting`. Описание других возможных хуков вы найдёте в руководстве nft(8) § ADDRESS FAMILIES.

Параметр `приоритет` задаётся названием приоритета или его численным значением (подробнее см. nft(8) § CHAINS). Цепочки с более низкими значениями обрабатываются раньше. Значение приоритета может быть отрицательным [3].

Например, добавим базовую цепочку для фильтрации входящих пакетов:

    # nft add chain inet my_table my_chain '{ type filter hook input priority 0; }'

Если заменить команду `add` на `create` в любом из правил выше, то цепочка тоже будет создана, но если цепочка с таким названием уже существует, то вы получите сообщение об ошибке.

#### Просмотр правил

Следующая команда выводит на экран все правила в цепочке:

    # nft list chain семейство таблица цепочка

Например, следующая команда выведет правила цепочки `my_output` таблицы `my_table` семейства `inet`:

    # nft list chain inet my_table my_output

#### Изменение цепочки

Для редактирования цепочки укажите её название и правила, которые нужно изменить:

    # nft chain семейство таблица цепочка '{ [ type тип hook хук device устройство priority приоритет ; policy политика ; ] }'

Например, для изменения политики обработки входящих пакетов цепочки исходной таблицы с `accept` на `drop` выполните команду:

    # nft chain inet my_table my_input '{ policy drop ; }'

#### Удаление цепочки

Удалить цепочку:

    # nft delete chain семейство таблица цепочка

Цепочка должна быть пустой (не содержащей правил) и не должна быть целью перехода из другой цепочки.

#### Очистка цепочки

Стереть все правила в цепочке:

    # nft flush chain семейство таблица цепочка

### Правила

Правила конструируются из выражений (expressions) и операторов (statements) и содержатся внутри цепочек.

#### Добавление правила

Совет: Утилита iptables-translate поможет преобразовать правила iptables в формат nftables.

Добавить правило в цепочку:

    # nft add rule семейство таблица цепочка handle маркер оператор

Правило будет прикреплено после `маркер`, который можно не указывать. Если маркер (handle) не задать, то правило добавится в конец цепочки.

Чтобы добавить правило перед определённой позицией, выполните

    # nft insert rule семейство таблица цепочка handle маркер оператор

Если `маркер` не указан, то правило добавится в начало цепочки.

##### Выражения

Обычно `оператор` включает некоторое выражение для сравнения и вынесения решения. Решениями могут быть `accept`, `drop`, `queue`, `continue`, `return`, `jump цепочка` и `goto цепочка`. Возможны и другие операторы помимо решений, подробнее смотрите nft(8).

В nftables доступен ряд выражений, по большей части совпадающий с аналогичными в iptables. Наиболее важное отличие заключается в том, что здесь нет обобщённых или неявных параметров. Обобщённый параметр (generic match) обычно доступен для всех правил, например, параметры `--protocol` или `--source`. Неявные правила (implicit matches) относятся к конкретному протоколу, как параметр `--sport` для пакетов TCP.

Ниже представлен неполный список возможных параметров:

- meta (метасущности, напр. интерфейсы)
- icmp (протокол ICMP)
- icmpv6 (протокол ICMPv6)
- ip (протокол IP)
- ip6 (протокол IPv6)
- tcp (протокол TCP)
- udp (протокол UDP)
- sctp (протокол SCTP)
- ct (отслеживание соединений)

Ниже приведён неполный список аргументов для параметров (подробнее см. nft(8)):

    meta:
      oif <интерфейс-отправитель НОМЕР>
      iif <интерфейс-получатель НОМЕР>
      oifname <интерфейс-отправитель ИМЯ>
      iifname <интерфейс-получатель ИМЯ>

      (параметры oif и iif принимают строковые значения, которые конвертируются в номер)
      (параметры oifname и iifname более гибкие, но они медленнее из-за необходимости выполнять сравнение строк)

    icmp:
      type <тип icmp>

    icmpv6:
      type <тип icmpv6>

    ip:
      protocol <протокол>
      daddr <адрес получателя>
      saddr <адрес отправителя>

    ip6:
      daddr <адрес получателя>
      saddr <адрес отправителя>

    tcp:
      dport <порт получателя>
      sport <порт отправителя>

    udp:
      dport <порт получателя>
      sport <порт отправителя>

    sctp:
      dport <порт получателя>
      sport <порт отправителя>

    ct:
      state <new | established | related | invalid>

#### Удаление

Отдельные правила могут быть удалены только с помощью маркера. Команда `nft --handle list` выведет список маркеров.

Ниже приведён пример правила с маркером и команда для его удаления. Аргумент `--numeric` позволяет выводить IP-адреса в численном виде.

    # nft --handle --numeric list chain inet my_table my_input

    table inet my_table {
         chain my_input {
              type filter hook input priority 0;
              ip saddr 127.0.0.1 accept # handle 10
         }
    }

    # nft delete rule inet my_table my_input handle 10

Стирание всех цепочек таблицы выполняется командой `nft flush table`. Отдельные цепочки могут быть стёрты командами `nft flush chain` или `nft delete rule`.

    # nft flush table таблица
    # nft flush chain семейство таблица цепочка
    # nft delete rule семейство таблица цепочка

Первая команда стирает все цепочки в ip-таблице `таблица`. Вторая очищает цепочку `цепочка` в таблице `таблица` семейства `семейство`. Третья удаляет все правила в цепочке `цепочка` в таблице `таблица` семейства `семейство`.

### Множества

Множества бывают именованные и анонимные. Множество объявляется фигурными скобками, в которых перечислены разделённые запятыми элементы. Анонимные множества "встраиваются" в правило и не могут быть изменены отдельно от него. Если множество необходимо модифицировать, то придётся удалить правило целиком и добавить его заново. Так, не получится удалить "http" из множества dport'ов в следующем правиле:

    # nft add rule ip6 filter input tcp dport {telnet, http, https} accept

Именованное множество можно изменить, а также присвоить ему тип и установить флаги. sshguard использует именованные множества для хранения адресов блокированных хостов.

    table ip sshguard {
           set attackers {
                   type ipv4_addr
                   flags interval
                   elements = { 1.2.3.4 }
           }

Команды для добавления и удаления элементов из множества:

    # nft add element ip sshguard attackers { 5.6.7.8/32 }
    # nft delete element ip sshguard attackers { 1.2.3.4/32 }

Если множество имеет тип ipv4_addr, то помимо адреса можно указать маску сети (маска "/32" в примере не обязательна, но указана для большей ясности). Обращение к множеству из примера выше ("TABLE ip sshguard { SET attackers }") производится по имени `ip sshguard attackers`.

### Полная перезагрузка правил

Создать файл для новых правил:

    # echo "flush ruleset" > /tmp/nftables

Сбросить дамп правил в новый файл:

    # nft -s list ruleset >> /tmp/nftables

Теперь можно редактировать файл `/tmp/nftables`, создавая и изменяя правила. Применить изменения можно командой:

    # nft -f /tmp/nftables

## Примеры

### Рабочая станция

    /etc/nftables.conf

    flush ruleset

    table inet my_table {
    	set LANv4 {
    		type ipv4_addr
    		flags interval

    		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
    	}
    	set LANv6 {
    		type ipv6_addr
    		flags interval

    		elements = { fd00::/8, fe80::/10 }
    	}

    	chain my_input_lan {
    		udp sport 1900 udp dport >= 1024 meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"

    		udp sport netbios-ns udp dport >= 1024 meta pkttype unicast accept comment "Accept Samba Workgroup browsing replies"

    	}

    	chain my_input {
    		type filter hook input priority filter; policy drop;

    		iif lo accept comment "Accept any localhost traffic"
    		ct state invalid drop comment "Drop invalid connections"
    		ct state established,related accept comment "Accept traffic originated from us"

    		meta l4proto ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
    		meta l4proto icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
    		ip protocol igmp accept comment "Accept IGMP"

    		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
    		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

    		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
    		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

    		counter comment "Count any other traffic"
    	}

    	chain my_forward {
    		type filter hook forward priority filter; policy drop;
    		# Drop everything forwarded to us. We do not forward. That is routers job.
    	}

    	chain my_output {
    		type filter hook output priority filter; policy accept;
    		# Accept every outbound connection
    	}

    }

### Сервер

    /etc/nftables.conf

    flush ruleset

    table inet my_table {
    	set LANv4 {
    		type ipv4_addr
    		flags interval

    		elements = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 }
    	}
    	set LANv6 {
    		type ipv6_addr
    		flags interval

    		elements = { fd00::/8, fe80::/10 }
    	}

    	chain my_input_lan {
    		meta l4proto { tcp, udp } th dport 2049 accept comment "Accept NFS"

    		udp dport netbios-ns accept comment "Accept NetBIOS Name Service (nmbd)"
    		udp dport netbios-dgm accept comment "Accept NetBIOS Datagram Service (nmbd)"
    		tcp dport netbios-ssn accept comment "Accept NetBIOS Session Service (smbd)"
    		tcp dport microsoft-ds accept comment "Accept Microsoft Directory Service (smbd)"

    		udp sport { bootpc, 4011 } udp dport { bootps, 4011 } accept comment "Accept PXE"
    		udp dport tftp accept comment "Accept TFTP"
    	}

    	chain my_input {
    		type filter hook input priority filter; policy drop;

    		iif lo accept comment "Accept any localhost traffic"
    		ct state invalid drop comment "Drop invalid connections"
    		ct state established,related accept comment "Accept traffic originated from us"

    		meta l4proto ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
    		meta l4proto icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
    		ip protocol igmp accept comment "Accept IGMP"

    		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
    		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

    		ip6 saddr @LANv6 jump my_input_lan comment "Connections from private IP address ranges"
    		ip saddr @LANv4 jump my_input_lan comment "Connections from private IP address ranges"

    		tcp dport ssh accept comment "Accept SSH on port 22"

    		tcp dport ipp accept comment "Accept IPP/IPPS on port 631"

    		tcp dport { http, https, 8008, 8080 } accept comment "Accept HTTP (ports 80, 443, 8008, 8080)"

    		udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
    	}

    	chain my_forward {
    		type filter hook forward priority filter; policy drop;
    		# Drop everything forwarded to us. We do not forward. That is routers job.
    	}

    	chain my_output {
    		type filter hook output priority filter; policy accept;
    		# Accept every outbound connection
    	}

    }

### Ограничение скорости передачи

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

    		iif lo accept comment "Accept any localhost traffic"
    		ct state invalid drop comment "Drop invalid connections"

    		meta l4proto icmp icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
    		meta l4proto ipv6-icmp icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"

    		ct state established,related accept comment "Accept traffic originated from us"

    		meta l4proto ipv6-icmp icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
    		meta l4proto icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
    		ip protocol igmp accept comment "Accept IGMP"

    		tcp dport ssh ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"

    	}

    }

### Переход

Если вы используете переходы (jumps), то целевая цепочка должна быть описана до перехода. Иначе будет получена ошибка `Error: Could not process rule: No such file or directory`.

    table inet my_table {
        chain web {
            tcp dport http accept
            tcp dport 8080 accept
        }
        chain my_input {
            type filter hook input priority filter;
            ip saddr 10.0.2.0/24 jump web
            drop
        }
    }

### Несколько сетевых интерфейсов с разными правилами

Если у вашей системы несколько сетевых интерфейсов и вы хотите использовать для них разные наборы правил, то создайте фильтрующую цепочку-диспетчер, а после неё опишите цепочки для сетевых интерфейсов. Например, пусть ваша машина выступает в качестве домашнего маршрутизатора и вы желаете запустить веб-сервер, доступный по локальной сети (интерфейс `enp3s0`), но не из публичного интернета (интерфейс `enp2s0`). Создайте таблицу вроде этой:

    table inet my_table {
      chain my_input { # this chain serves as a dispatcher
        type filter hook input priority filter;

        iif lo accept # always accept loopback
        iifname enp2s0 jump my_input_public
        iifname enp3s0 jump my_input_private

        reject with icmpx type port-unreachable # refuse traffic from all other interfaces
      }
      chain my_input_public { # rules applicable to public interface interface
        ct state {established,related} accept
        ct state invalid drop
        udp dport bootpc accept
        tcp dport bootpc accept
        reject with icmpx type port-unreachable # all other traffic
      }
      chain my_input_private {
        ct state {established,related} accept
        ct state invalid drop
        udp dport bootpc accept
        tcp dport bootpc accept
        tcp port http accept
        tcp port https accept
        reject with icmpx type port-unreachable # all other traffic
      }
      chain my_output { # we let everything out
        type filter hook output priority filter;
        accept
      }
    }

Можно поступить иначе - задать только одну строку `iifname`, например для интерфейса веб-сервера, а правила для остальных интерфейсов вместо диспетчеризации описать в одном месте.

### Маскарадинг

В nftables есть специальное ключевое слово `masquerade`, "позволяющее автоматически изменять адрес источника на адрес интерфейса-отправителя" [4]. Это особенно удобно в ситуациях, когда IP-адрес интерфейса непредсказуем или нестабилен — например, исходящий интерфейс маршрутизатора, подключённого к нескольким интернет-провайдерам. Маскарадинг позволяет не переписывать правила межсетевого экрана для трансляции сетевых адресов (NAT) при каждом изменении IP-адреса интерфейса.

Правила работы маскарадинга:

- опция `masquerading` в ядре Linux должна быть включена (в стандартном ядре она включена по умолчанию); в противном случае задайте параметр ядра `CONFIG_NFT_MASQ=m`.
- ключевое слово `masquerade` может использоваться только в цепочке типа `nat`.
- masquerading является подвидом SNAT, поэтому работает только для исходящих пакетов.

Пример правил межсетевого экрана для машины с двумя интерфейсами, локальным `enp3s0` и публичным `enp2s0`:

    table inet my_nat {
      chain my_masquerade {
        type nat hook postrouting priority srcnat;
        oifname "enp2s0" masquerade
      }
    }

Поскольку таблица выше относится к типу `inet`, то маскарадингу подвергаются пакеты и IPv4, и IPv6. Чтобы ограничить маскарадинг только IPv4-пакетами (т.к. у IPv6 большое пространство адресов и NAT не требуется) либо добавьте выражение `meta nfproto ipv4` перед `oifname "enp2s0" masquerade`, либо измените тип таблицы на `ip`.

### NAT с пробросом портов

Ниже приведён пример проброса портов 22 и 80 на адрес `ip-адрес_получателя`. Необходимо предварительно с помощью sysctl установить параметры `net.ipv4.ip_forward` и `net.ipv4.conf.wan_интерфейс.forwarding` на `1`.

    table ip my_nat {
      chain my_prerouting {
        type nat hook prerouting priority dstnat;

        tcp dport { ssh, http } dnat to destination_ip
      }

      chain my_postrouting {
        type nat hook postrouting priority srcnat;

        ip daddr destination_ip masquerade
      }
    }

### Счётчик новых соединений

Следующий сниппет позволяет считать открытые HTTPS-соединения:

    /etc/nftables.conf

    table inet filter {
        set https {
            type ipv4_addr;
            flags dynamic;
            size 65536;
            timeout 60m;
        }

        chain input {
            type filter hook input priority filter;
            ct state new tcp dport 443 update @https { ip saddr counter }
        }
    }

Текущее значение счётчика можно вывести командой `nft list set inet filter https`.

### Динамическая блокировка

Следующий сниппет позволяет в течение минуты отклонять все HTTPS-соединения от адресов, которые превысили лимит в 10 запросов на соединение в секунду:

    /etc/nftables.conf

    table inet dev {
        set blackhole {
            type ipv4_addr;
            flags dynamic, timeout;
            size 65536;
        }

        chain input {
            ct state new tcp dport 443 \
                    meter flood size 128000 { ip saddr timeout 10s limit rate over 10/second } \
                    add @blackhole { ip saddr timeout 1m }

            ip saddr @blackhole counter drop
        }
    }

Для вывода заблокированных адресов выполните `nft list set inet dev blackhole`.

## Советы и рекомендации

### Сохранение текущего набора правил

Вывод команды `nft list ruleset` можно использовать в качестве входных данных для других команд. Текущий набор правил можно сохранить в файл, а позже — загрузить их из него.

    # nft -s list ruleset | tee название_файла

Примечание: Команда `nft list` не выводит определения переменных. Если в файле `/etc/nftables.conf` есть какие-либо переменные, то в выводе команды вы их не увидите — все переменные будут заменены на значения.

### Настройка межсетевого экрана

См. также Настройка межсетевого экрана.

#### Одиночная машина

Сотрите текущий набор правил:

    # nft flush ruleset

Добавьте таблицу:

    # nft add table inet my_table

Добавьте базовые цепочки input, forward и output. Политика для входящих и пересылаемых пакетов должна быть drop. Политика для исходящих пакетов — accept.

    # nft add chain inet my_table my_input '{ type filter hook input priority 0 ; policy drop ; }'
    # nft add chain inet my_table my_forward '{ type filter hook forward priority 0 ; policy drop ; }'
    # nft add chain inet my_table my_output '{ type filter hook output priority 0 ; policy accept ; }'

Добавьте две обычные цепочки, которые будут обрабатывать пакеты протоколов TCP и UDP:

    # nft add chain inet my_table my_tcp_chain
    # nft add chain inet my_table my_udp_chain

Разрешите related и established трафик:

    # nft add rule inet my_table my_input ct state related,established accept

Разрешите трафик на петлевой интерфейс:

    # nft add rule inet my_table my_input iif lo accept

Заблокируйте invalid трафик:

    # nft add rule inet my_table my_input ct state invalid drop

Разрешите пакеты ICMP и IGMP:

    # nft add rule inet my_table my_input meta l4proto ipv6-icmp icmpv6 type '{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report }' accept
    # nft add rule inet my_table my_input meta l4proto icmp icmp type '{ destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem }' accept
    # nft add rule inet my_table my_input ip protocol igmp accept

Новый UDP-трафик будет передаваться цепочке `my_udp_chain`:

    # nft add rule inet my_table my_input meta l4proto udp ct state new jump my_udp_chain

Новый TCP-трафик будет передаваться цепочке `my_tcp_chain`:

    # nft add rule inet my_table my_input 'meta l4proto tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump my_tcp_chain'

Заблокировать весь трафик, который не обрабатывается другими правилами:

    # nft add rule inet my_table my_input meta l4proto udp reject
    # nft add rule inet my_table my_input meta l4proto tcp reject with tcp reset
    # nft add rule inet my_table my_input counter reject with icmpx type port-unreachable

В этом месте необходимо выбрать, какие порты будут оставаться открытыми для входящих соединений, обрабатываемых цепочками `my_tcp_chain` и `my_udp_chain`. Например, открыть соединения для веб-сервера:

    # nft add rule inet my_table my_tcp_chain tcp dport 80 accept

Разрешить HTTPS-соединения для веб-сервера на порте 443:

    # nft add rule inet my_table my_tcp_chain tcp dport 443 accept

Разрешить SSH-трафик на порт 22:

    # nft add rule inet my_table my_tcp_chain tcp dport 22 accept

Разрешить входящие DNS-запросы:

    # nft add rule inet my_table my_tcp_chain tcp dport 53 accept
    # nft add rule inet my_table my_udp_chain udp dport 53 accept

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

### Предотвращение атак перебором

Sshguard может обнаруживать атаки перебором и модифицировать сетевые экраны, временно помещая IP-адреса в чёрный список. Описание настройки nftables для работы с sshguard можно найти в статье Sshguard#nftables.

### Журналирование трафика

Действие `log` позволяет вести журнал пакетов. Простейшее правило для сохранения информации обо всём поступающем трафике:

    # nft add rule inet filter input log

Подробнее см. nftables wiki.

## Решение проблем

### Работа с Docker

nftables может создавать помехи сетевой работе контейнеров Docker (возможно, и другим средствам виртуализации тоже). В частности, политика `drop` цепочки `forward` блокирует пакеты, источником которых является docker. Если вы не хотите удалять эту цепочку, то сделайте следующее:

1. Установите пакет iptables-nft; он содержит iptables-совместимый интерфейс nftables, который docker сможет использовать.
2. Модифицируйте цепочку `forward` таблицы `inet`:

         chain forward {
           type filter hook forward priority security; policy drop;
           mark 1 accept
         }

3. Добавьте цепочку DOCKER-USER в таблицу `ip filter`, чтобы помечать (mark) пакеты docker:

         table ip filter {
           chain DOCKER-USER {
             mark set 1
           }
         }

Теперь сгенерированные контейнером docker пакеты будут маркироваться и пересылаться дальше, поскольку docker уже их отфильтровал (цепочка `forward` в docker использует политику `drop`).

## Смотрите также

- netfilter nftables — документация от разработчиков проекта netfilter
- debian:nftables — статья на Debian-wiki
- gentoo:nftables — статья на Gentoo-wiki
- First release of nftables — выход nftables (2009)
- nftables quick howto — советы и подсказки
- The return of nftables — обновление nftables (2013)
- What comes after "iptables"? — nftables со временем заменит iptables

Category:

- Firewalls (Русский)