В Red Hat Enterprise Linux 8 приоритетным низкоуровневым решением является nftables. В этой статье мы поговорим о том, как начать использовать nftables. Для начала необходимо отметить, что nftables – это userland-утилита, nft и подсистема ядра. Внутри ядра она строится на базе подсистемы netfilter.
Примечание: в этой статье некоторые выходные данные могут быть достаточно длинными, поэтому мы сократим или уберем бесполезные или несущественные части вывода.
Самая первая команда с которой можно начать ознакомление это:
# nft list ruleset
Которая выводит описание брандмауэра. По умолчанию nftables не создает таблицы и цепочки, в отличие от своего предшественника iptables. Пустой набор правил имеет нулевую стоимость ресурсов. Однако если у Вас установлен пакет FireWallD, то демон входящий в этот пакет может работать поверх nftables и тогда если он запущен то Вы увидите некоторый набор правил созданных демоном FireWallD.
Семейства (Families)
Семейства адресов определяют тип обрабатываемых пакетов. Для каждого семейства адресов ядро содержит так называемые перехватчики на определенных этапах обработки пакетов, которые вызывают nftables, если существуют правила для этих перехватчиков.
В nftables есть следующие семейства протоколов:
Семейство |
Перехватчики (Хуки) |
Описание |
ip |
prerouting, input, forward, output, postrouting |
Семейство IPv4-адресов. |
ip6 |
Семейство IPv6-адресов. |
|
inet |
Семейство интернет-адресов (IPv4 / IPv6) – вместе. |
|
arp |
input, output |
Семейство адресов ARP, обрабатывающих пакеты IPv4 ARP. |
bridge |
– |
Семейство адресов моста, обрабатывающих пакеты, которые проходят через устройство моста. |
netdev |
ingress |
Семейство адресов Netdev, обрабатывающее входящие пакеты. |
Таким образом в системе существуют следующие точки перехвата (хуки):
Хук |
Описание |
prerouting |
Все пакеты, поступающие в систему, обрабатываются ловушкой предварительной маршрутизации. Он вызывается перед процессом маршрутизации и используется для ранней фильтрации или изменения атрибутов пакетов, влияющих на маршрутизацию |
input |
Пакеты, доставленные в локальную систему |
forward |
Пакеты, переадресованные на другой хост |
output |
Пакеты, отправленные локальными процессами |
postrouting |
Все пакеты, покидающие систему, обрабатываются обработчиком постмаршрутизации |
ingress |
Этим хуком обрабатываются все пакеты, поступающие в систему. Он вызывается перед обработчиками протокола уровня 3 и может использоваться для ранней фильтрации и контроля. |
Набор правил ruleset можно сбросить или отобразить командами flush ruleset и list ruleset.
nft {list | flush} ruleset [family]
При этом действие команды можно ограничить каким то одним семейством.
По замыслу, выходные данные команды nft list ruleset могут использоваться в качестве входных данных для nft -f.
Таблицы
В nftables вам понадобится создавать таблицы вручную. Таблицы должны определять семейство: ip, ip6, inet, arp, bridge или netdev. Здесь inet означает, что таблица будет обрабатывать пакеты ipv4 и ipv6. Таблицу следует рассматривать как некоторое пространство имен. Таблица всегда идентифицируется семейством и именем (только парой)
Примечание: Для тех, кто переходит с iptables, термин таблица может звучать неоднозначно. В nftables таблица – просто пространство имен, ни больше, ни меньше. По сути, она представляет из себя набор цепочек, правил, наборов и иных объектов.
Команды для управления телицами следующие:
nft {add | create} table [family] table [{ flags flags ; }]
nft {delete | list | flush} table [family] table
nft list tables [family]
nft delete table [family] handle handle
Стоить отметить что команда nft list tables– выведет только список таблиц и их семейств, а nft list table – выведет информацию о конкретной таблице. Если семейство протоколов не задано, то используется значение ip. Разница между add и create, состоит в том, что create выдаст ошибку если таблица уже существует.
Если на пустом наборе, создать первую таблицу и просмотреть набор правил.
# nft add table inet my_table
# nft list ruleset
table inet my_table {
}
Отлично, теперь у нас есть таблица, но сама по себе она мало что дает. Давайте перейдем к цепочкам.
Флаг – «flags dormant» означает, что таблица не будет обрабатываться.# disable the table temporarily — rules are not evaluated anymoreadd table inet mytable { flags dormant; }
Цепочки (Chain)
Цепочки – объекты, которые будут содержать правила брандмауэра. Они существуют в двух видах: основные цепи и регулярные цепи. Базовая цепочка – это точка входа для пакетов из сетевого стека, обычная цепочка может использоваться как цель перехода и используется для лучшей организации правил.
Команды управления цепочками:
- nft {add | create} chain [family] table chain [{ type type hook hook [device device] priority priority ; [policy policy ;] }]
- nft {delete | list | flush} chain [family] table chain
- nft list chains [family]
- nft delete chain [family] table handle handle
- nft rename chain [family] table chain newname
По аналогии с таблицами, цепочки также нужно создавать вручную. При создании цепочки необходимо указать, к какой таблице она относится, а также тип, хук и приоритет.
Команды add и create – добавляют новую цепочку в указанную таблицу. Когда указаны ловушка и значение приоритета, цепочка создается как базовая и подключается к сетевому стеку. Разница между add и create, состоит в том, что create выдаст ошибку если правило уже существует. Delete – удаляет конкретную цепочку, flush – удалит все цепочки в указанной таблице. List – выводит список правил или правила соотносимые с каким то семейством. Rename – переименовывает правило.
Для базовых правил параметры type, hook и priority – обязательны.
Поддерживаемые типы цепочек:
Тип цепочки |
Семейства |
Хуки |
Описание |
filter |
Все |
Все |
Стандартный тип, фильтрация пакетов |
nat |
ip, ip6, inet |
prerouting, input, output, postrouting |
Цепи этого типа выполняют преобразование NAT собственных адресов на основе записей conntrack. Только первый пакет соединения фактически проходит эту цепочку – его правила обычно определяют детали созданной записи conntrack (например, операторы NAT). |
route |
ip, ip6 |
output |
Если пакет прошел через цепочку этого типа и собирается быть принятым, выполняется поиск нового маршрута, если соответствующие части заголовка IP изменились. Это позволяет, например, реализовать селекторы маршрутизации политик в nftables. |
Помимо особых случаев, проиллюстрированных выше (например, тип nat, не поддерживающий перехватчик forward, или тип route, поддерживающий только перехватчик output), есть еще две особенности, на которые стоит обратить внимание:
- Семейство netdev поддерживает только одну комбинацию, а именно тип filter и ловушку ingress. Базовые цепочки в этом семействе также требуют наличия параметра device, поскольку они существуют только для входящего интерфейса.
- Семейство arp поддерживает только ловушки input и output, и только в цепочках типа filter.
Параметр приоритета принимает целочисленное значение со знаком или стандартное имя приоритета, которое определяет порядок обхода цепочек с одинаковым значением ловушки. Порядок возрастающий, т.е. значения с более низким приоритетом имеют приоритет над более высокими.
Стандартные значения приоритета можно заменить легко запоминающимися именами. Не все имена имеют смысл в каждом семействе с каждым крючком (см. Матрицы совместимости ниже), но их числовое значение все же можно использовать для определения приоритетов цепочек.
Эти имена и значения определяются и становятся доступными в зависимости от того, какие приоритеты используются xtables при регистрации своих цепочек по умолчанию.
В большинстве семейств используются одни и те же значения, но bridge использует другие значения. См. Следующие таблицы, в которых описаны значения и совместимость.
Имя |
Значение |
Семейство |
Хук |
raw |
-300 |
ip, ip6, inet |
Все |
mangle |
-150 |
ip, ip6, inet |
Все |
dstnat |
-100 |
ip, ip6, inet |
prerouting |
filter |
0 |
ip, ip6, inet, arp, netdev |
Все |
security |
50 |
ip, ip6, inet |
Все |
srcnat |
100 |
ip, ip6, inet |
postrouting |
Для семейства bridge существует своя матрица совместимости:
Имя |
Значение |
Семейство |
Хук |
dstnat |
-300 |
bridge |
prerouting |
filter |
-200 |
bridge |
Все |
out |
100 |
bridge |
Output |
srcnat |
300 |
bridge |
postrouting |
Базовые арифметические выражения (сложение и вычитание) также могут быть выполнены с этими стандартными именами, чтобы упростить относительную приоритизацию, например mangle – 5 означает -155. Значения также будут напечатаны таким образом, пока значение не станет больше 10 от стандартного значения.
Базовые цепочки также позволяют установить политику цепочки policy, то есть что происходит с пакетами, которые явно не приняты или отклонены в содержащихся правилах. Поддерживаемые значения политики: принять accept (по умолчанию) или отбросить drop.
В этом руководстве мы не будем ничего усложнять и используем filter, input, и priority 0 для фильтрации пакетов, предназначенных для хоста.
# nft add chain inet my_table my_filter_chain { type filter hook input priority 0 \; }
Примечание: Обратный слэш () нужен, чтобы shell не интерпретировал скобку как конец команды.
Цепочки также могут быть созданы без указания хука. Созданные таким образом цепочки эквиваленты цепочкам, созданным пользователями в iptables. Правила могут использовать операторы jump или goto для выполнения правил в цепочке. Эта механика полезна для логического разделения правил или для того, чтобы делиться подмножеством правил, которые в противном случае продублировались бы.
# nft add chain inet my_table my_utility_chain
Правила (Rules)
Правила добавляются в цепочки в данной таблице. Если семейство не указано, используется семейство ip. Правила состоят из двух видов компонентов в соответствии с набором грамматических правил: выражений и утверждений.
Команды добавления add и вставки insert поддерживают необязательный указатель местоположения, который является либо дескриптором handle, либо индексом index (начиная с нуля) существующего правила. Внутренне расположение правил всегда идентифицируется дескриптором, а преобразование из индекса происходит в пользовательском пространстве. Это имеет два возможных последствия в случае одновременного изменения набора правил после того, как преобразование было выполнено: эффективный индекс правила может измениться, если правило было вставлено или удалено перед указанным. Если указанное правило было удалено, команда отклоняется ядром так же, как если бы был задан недопустимый дескриптор.
Комментарий – это одно слово или строка из нескольких слов в двойных кавычках (“), которую можно использовать для заметок, касающихся фактического правила. Примечание. Если вы используете bash для добавления правил, вы должны избегать кавычек, например \ “включить ssh для серверов \”.
Команды управления правилами:
- nft {add | insert} rule [family] table chain [handle handle | index index] statement … [comment comment]
- nft replace rule [family] table chain handle handle statement … [comment comment]
- nft delete rule [family] table chain handle handle
Теперь, когда вы создали таблицу и цепочку, вы можете, наконец, добавить правила для брандмауэра. Давайте добавим правило для разрешения SSH.
# nft add rule inet my_table my_filter_chain tcp dport ssh accept
Здесь следует отметить, что как только мы добавили его в таблицу семейства inet, это единственное правило будет обрабатывать как пакеты IPv4, так и пакеты IPv6.
Глагол add будет добавлять правило в конец цепочки. Также вы можете использовать глагол insert, чтобы добавить правило в начало цепочки.
# nft insert rule inet my_table my_filter_chain tcp dport http accept
Мы добавили два правила, а теперь давайте посмотрим, как будет выглядеть полный набор правил.
# nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport ssh accept
}
}
Обратите внимание, что правило http должно отрабатывать раньше правила ssh, поскольку мы использовали выше глагол insert.
Также вы можете добавить правило в произвольное место в цепочке. Есть два способа сделать это.
- Используйте index, чтобы указать на индекс в списке правил. Использование add добавит новое правило после указанного индекса. Если же вы используете insert, то новое правило будет добавлено перед правилом с заданным индексом. Значения индексов начинаются с 0.
# nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept
# nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
}
}
# nft add rule inet my_table my_filter_chain index 0 tcp dport 1234 accept
# nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport 1234 accept
tcp dport nfs accept
tcp dport ssh accept
}
}
Примечание: Использование index с insert по факту эквивалентно опции iptables -I с индексом. Первое, о чем нужно помнить, так это о том, что индексы в nftables начинаются с 0. Во-вторых, индекс должен указывать на существующее правило. То есть писать “nft insert rule … index 0” в пустой цепочке недопустимо.
- Используйте handle, чтобы указать правило, до или после которого нужно вставлять другое правило. Для вставки после используйте глагол add. Чтобы вставить до правила, используйте insert. Вы можете получить handle правил, добавив флаг –handle при выводе правил.
# nft –handle list ruleset
table inet my_table { # handle 21
chain my_filter_chain { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport ssh accept # handle 2
}
}
# nft add rule inet my_table my_filter_chain handle 3 tcp dport 1234 accept
# nft insert rule inet my_table my_filter_chain handle 2 tcp dport nfs accept
# nft –handle list ruleset
table inet my_table { # handle 21
chain my_filter_chain { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport 1234 accept # handle 8
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}
В nftables handle правил стабилен и не изменится до тех пор, пока правило не будет удалено. Так ссылка на правило получается надежнее, чем просто индекс, который может поменяться при вставке другого правила.
Также вы можете получить handle правила во время создания, используя сразу два флага –echo и –handle. Правило будет выведено в CLI вместе с его handle.
# nft –echo –handle add rule inet my_table my_filter_chain udp dport 3333 accept
add rule inet my_table my_filter_chain udp dport 3333 accept # handle 4
Примечание: старые версии nftables использовали позицию ключевого слова. С тех пор механика ключевых слов устарела и появился handle.
Удаление правил
Удаление правил выполняется с помощью handle правила по аналогии с командами add и insert выше.
Сначала нужно найти handle правила, которое вы хотите удалить.
# nft –handle list ruleset
table inet my_table { # handle 21
chain my_filter_chain { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport 1234 accept # handle 8
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}
Затем используйте этот handle для удаления правила.
# nft delete rule inet my_table my_filter_chain handle 8
# nft –handle list ruleset
table inet my_table { # handle 21
chain my_filter_chain { # handle 1
type filter hook input priority 0; policy accept;
tcp dport http accept # handle 3
tcp dport nfs accept # handle 7
tcp dport ssh accept # handle 2
}
}
Листинг правил
В примерах выше мы выводили весь список правил. Существует много других способов вывести подмножество правил.
Вывести все правила в заданной таблице.
# nft list table inet my_table
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
}
}
Вывести правила в заданной цепочке.
# nft list chain inet my_table my_other_chain
table inet my_table {
chain my_other_chain {
udp dport 12345 log prefix “UDP-12345”
}
}
Наборы
В nftables есть нативная поддержка наборов. Они могут оказаться полезными, если вы хотите, чтобы правило работало с несколькими IP-адресами, портами, интерфейсами или по любым другим критериям.
Анонимные наборы
Любое правило может иметь inline-наборы. Эта механика полезна для наборов, которые вы не собираетесь изменять.
Например, следующая команда будет разрешать весь трафик с 10.10.10.123 и 10.10.10.231.
# nft add rule inet my_table my_filter_chain ip saddr { 10.10.10.123, 10.10.10.231 } accept
# nft list ruleset
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
ip saddr { 10.10.10.123, 10.10.10.231 } accept
}
Недостатком этого метода является то, что если вам нужно изменить набор, то нужно будет заменять и правило. Чтобы получить мутабельные наборы, нужно использовать именованные наборы.
В качестве другого примера, вместо первых трех правил мы могли бы использовать анонимный набор.
# nft add rule inet my_table my_filter_chain tcp dport { http, nfs, ssh } accept
Примечание: пользователи iptables скорее всего привыкли к использованию ipset. Поскольку в nftables есть собственная нативная поддержка наборов, ipset использовать не нужно.
Именованные наборы
Nftables также поддерживает мутабельные именованные наборы. Для их создания необходимо указать тип элементов, которые будут в них содержаться. Например, типы могут быть такими: ipv4_addr, inet_service, ether_addr.
Полная спецификация команд управления набором:
- nft add set [family] table set { type type | typeof expression ; [flags flags ;] [timeout timeout ;] [gc-interval gc-interval ;] [elements = { element[, …] } ;] [size size ;] [policy policy ;] [auto-merge ;] }
- nft {delete | list | flush} set [family] table set
- nft list sets [family]nft delete set [family] table handle handle
- nft {add | delete} element [family] table set { element[, …] }
Наборы представляют собой контейнеры элементов определенного пользователем типа данных, они однозначно идентифицируются определенным пользователем именем и прикрепляются к таблицам. Их поведение можно настроить с помощью флагов, которые можно указать во время создания набора. Команда add – добавит новый набор в указанную таблицу. См. спецификаций параметров ниже для получения дополнительной информации о том, как указать свойства наборов. Команда delete – удалит указанный набор. Команда list – отобразите элементы в указанном наборе. Команда flush – удалит все элементы из указанного набора.
Ключевое слово (параметр) |
Описание |
Тип данных |
type |
Тип данных элементов |
строка: ipv4_addr, ipv6_addr, ether_addr, inet_proto, inet_service, mark |
typeof |
Тип данных элементов |
выражение описывающие тип данных |
flags |
Набор флагов |
Строки: constant, dynamic, interval, timeout |
timeout |
время, когда элемент остается в наборе, является обязательным, если набор добавляется из пути пакета (набора правил). |
строка, десятичная дробь и единица измерения. Единицы: d, h, m, s |
gc-interval |
Интервал сборки мусора, доступен только при активном тайм-ауте или таймауте флага |
строка, десятичная дробь и единица измерения. Единицы: d, h, m, s |
elements |
элементы, содержащиеся в наборе |
Множество элементов указанного типа |
size |
максимальное количество элементов в наборе, обязательно, если набор добавляется из пути пакета (набора правил). |
целое число без знака (64 бит) |
policy |
установить политику |
Строка: performance [по умолчанию], memory |
auto-merge |
автоматическое объединение смежных / перекрывающихся элементов набора (только для наборов интервалов) |
|
Давайте создадим пустой набор.
# nft add set inet my_table my_set { type ipv4_addr \; }
# nft list sets
table inet my_table {
set my_set {
type ipv4_addr
}
}
Чтобы сослаться на набор в правиле используйте символ @ и имя набора после него. Следующее правило будет работать как черный список для IP-адресов в нашем наборе.
# nft insert rule inet my_table my_filter_chain ip saddr @my_set drop
# nft list chain inet my_table my_filter_chain
table inet my_table {
chain my_filter_chain {
type filter hook input priority 0; policy accept;
ip saddr @my_set drop
tcp dport http accept
tcp dport nfs accept
tcp dport ssh accept
ip saddr { 10.10.10.123, 10.10.10.231 } accept
}
}
Конечно, это не особо эффективно, так как в наборе пусто. Давайте добавим в него несколько элементов.
Элементы в наборе (Elements)
Команды, относящиеся к элементам, позволяют изменять содержимое именованных наборов и карт. key_expression – это обычно значение, соответствующее заданному типу. value_expression не допускается в наборах, но обязательно при добавлении на карты, где оно соответствует части данных в определении его типа. При удалении с карт его можно указать, но это необязательно, поскольку выражение key_expression однозначно идентифицирует элемент.
Команда create аналогична команде add, за исключением того, что ни один из перечисленных элементов может уже существовать.
Команда get полезна для проверки, содержится ли элемент в наборе, который может быть нетривиальным в очень больших и / или интервальных наборах. В последнем случае возвращается содержащий интервал, а не только сам элемент.
Спецификация:
1 2 3 4 5 |
nft {<b>add</b> | <b>create</b> | <b>delete</b> | <b>get</b> } <b>element</b> [<i>family</i>] <i>table</i> <i>set</i> <b>{</b> <i>ELEMENT</i>[<b>,</b> ...] <b>}</b> <i>ELEMENT</i> := <i>key_expression</i> <i>OPTIONS</i> [<b>:</b> <i>value_expression</i>] <i>OPTIONS</i> := [<b>timeout</b> <i>TIMESPEC</i>] [<b>expires</b> <i>TIMESPEC</i>] [<b>comment</b> <i>string</i>] <i>TIMESPEC</i> := [<i>num</i><b>d</b>][<i>num</i><b>h</b>][<i>num</i><b>m</b>][<i>num</i>[<b>s</b>]] |
где: timeout – значение таймаута для наборов / карт с таймаутом флага, expires – время до истечения срока действия данного элемента, полезно только для репликации набора правил и comment – комментарий, поле комментария для каждого элемента.
Рассмотрим пример работы с элементами:
# nft add element inet my_table my_set { 10.10.10.22, 10.10.10.33 }
# nft list set inet my_table my_set
table inet my_table {
set my_set {
type ipv4_addr
elements = { 10.10.10.22, 10.10.10.33 }
}
}
Однако попытка добавить диапазон значений приведет к ошибке.
# nft add element inet my_table my_set { 10.20.20.0-10.20.20.255 }
Error: Set member cannot be range, missing interval flag on declaration
add element inet my_table my_set { 10.20.20.0-10.20.20.255 }
Чтобы использовать диапазоны в наборах, нужно создать набор с использованием флагов интервалов. Так нужно, чтобы ядро заранее знало, какой тип данных будет храниться в наборе, чтобы использовать соответствующую структуру данных.
Интервалы в наборах
В наборах также могут использовать диапазоны. Для IP-адресов это может быть крайне полезно. Для использования диапазонов, набор должен быть создан с использованием флагов интервалов.
# nft add set inet my_table my_range_set { type ipv4_addr \; flags interval \; }
# nft add element inet my_table my_range_set { 10.20.20.0/24 }
# nft list set inet my_table my_range_set
table inet my_table {
set my_range_set {
type ipv4_addr
flags interval
elements = { 10.20.20.0/24 }
}
}
Примечание: Нотация маски сети была неявно преобразована в диапазон IP-адресов. Аналогично, мы могли бы написать 10.20.20.0-10.20.20.255 и получить тот же эффект.
Конкатенации наборов
Наборы также поддерживают агрегатные типы и совпадения. То есть элемент набора также может содержать несколько типов, а правило может использовать оператор конкатенации «.» при обращении к набору.
В этом примере мы сможем сопоставлять IPv4-адреса, IP-протоколы и номера портов одновременно.
# nft add set inet my_table my_concat_set { type ipv4_addr . inet_proto . inet_service \; }
# nft list set inet my_table my_concat_set
table inet my_table {
set my_concat_set {
type ipv4_addr . inet_proto . inet_service
}
}
Теперь мы можем добавить элементы к списку.
# nft add element inet my_table my_concat_set { 10.30.30.30 . tcp . telnet }
Как видите, символьные имена (tcp, telnet) также можно использовать при добавлении элементов набора.
Использование набора в правиле аналогично именованному набору выше, но правило должно выполнять конкатенацию.
# nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . tcp dport @my_concat_set accept
# nft list chain inet my_table my_filter_chain
table inet my_table {
chain my_filter_chain {
…
ip saddr { 10.10.10.123, 10.10.10.231 } accept
meta nfproto ipv4 ip saddr . meta l4proto . tcp dport @my_concat_set accept
}
}
Также стоит отметить, что конкатенация может использоваться с inline-наборами. Вот последний пример, отражающий это.
# nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept
Надюсь, теперь вы понимаете всю силу наборов nftables.
Примечание: конкатенации наборов nftables аналогичны агрегатным типам ipset, например, hash:ip,port.
Verdict Map
Verdict map – это интересная функция в nftables, которая позволит вам выполнить действие, основываясь на информации в пакете. Проще говоря, они сопоставляют критерии с действиями.
Например, чтобы логически разделить набор правил, вам нужны отдельные цепочки для обработки TCP и UDP пакетов. Вы можете использовать verdict map, чтобы направлять пакеты в эти цепочки с помощью одного правила.
# nft add chain inet my_table my_tcp_chain
# nft add chain inet my_table my_udp_chain
# nft add rule inet my_table my_filter_chain meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }
# nft list chain inet my_table my_filter_chain
table inet my_table {
chain my_filter_chain {
…
meta nfproto ipv4 ip saddr . meta l4proto . udp dport { 10.30.30.30 . udp . bootps } accept
meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }
}
}
Конечно, по аналогии с наборами вы можете создавать мутабельные verdict map.
# nft add map inet my_table my_vmap { type inet_proto : verdict \; }
Ваши глаза вас не обманывают. Синтаксис с наборами и вправду очень схож. По факту, под капотом наборы и verdict map строятся на одном и том же типе данных.
Теперь вы можете использовать мутабельную verdict map в правиле.
# nft add rule inet my_table my_filter_chain meta l4proto vmap @my_vmap
Таблицы как пространства имен
Еще одна интересная вещь о таблицах в nftables – это то, что они также являются полноценными пространствами имен. То есть две таблицы могут создавать цепочки, наборы и другие объекты с одинаковыми именами.
# nft add table inet table_one
# nft add chain inet table_one my_chain
# nft add table inet table_two
# nft add chain inet table_two my_chain
# nft list ruleset
…
table inet table_one {
chain my_chain {
}
}
table inet table_two {
chain my_chain {
}
}
Это свойство означает, что приложения могут организовывать правила в своих собственных цепочках, не затрагивая другие приложения. В iptables приложениям было тяжело вносить изменения в брандмауэр, не влияя на другие приложения.
Однако и тут есть один нюанс. Каждый хук таблицы или цепочки можно рассматривать как независимый и отдельный брандмауэр. То есть пакет должен пройти через все, чтобы в итоге быть принятым. Если table_one принимает пакет, его все еще может отклонить table_two. Именно здесь в игру вступает приоритезация хуков. Цепочка с более низким приоритетом гарантированно будет выполнена перед цепочкой с более высоким приоритетом. Если приоритеты равны, то поведение не определено.
Сохранение и восстановление набора правил
Правила nftables можно с легкостью сохранить и восстановить. Вывод list в nft можно использовать в инструменте, чтобы провести восстановление. Именно так работает служба nftables systemd.
Сохранить набор правил
# nft list ruleset > /root/nftables.conf
Восстановить набор правил
# nft -f /root/nftables.conf
Конечно же, вы можете включить службу systemd и восстановить правила при перезагрузке. Служба читает правила из /etc/sysconfig/nftables.conf.
# systemctl enable nftables
# nft list ruleset > /etc/sysconfig/nftables.conf
Примечание: некоторые дистрибутивы, включая RHEL-8, отправляют предопределенную конфигурацию nftables в /etc/nftables. Эти сэмплы часто включают в себя конфигурацию таблиц и цепочек в стиле iptables. Они часто указаны в файле /etc/sysconfig/nftables.conf, но могут быть закомментированы.
Пример стандартного брендмауэра для хоста:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
flush ruleset table firewall <span class="o">{</span> chain incoming <span class="o">{</span> <span class="nb">type</span> filter hook input priority <span class="m">0</span><span class="p">;</span> policy drop<span class="p">;</span> <span class="c1"># established/related connections</span> ct state established,related accept <span class="c1"># loopback interface</span> iifname lo accept <span class="c1"># icmp</span> icmp <span class="nb">type</span> echo-request accept <span class="c1"># open tcp ports: sshd (22), httpd (80)</span> tcp dport <span class="o">{</span>ssh, http<span class="o">}</span> accept <span class="o">}</span> <span class="o">}</span> table ip6 firewall <span class="o">{</span> chain incoming <span class="o">{</span> <span class="nb">type</span> filter hook input priority <span class="m">0</span><span class="p">;</span> policy drop<span class="p">;</span> <span class="c1"># established/related connections</span> ct state established,related accept <span class="c1"># invalid connections</span> ct state invalid drop <span class="c1"># loopback interface</span> iifname lo accept <span class="c1"># icmp</span> <span class="c1"># routers may also want: mld-listener-query, nd-router-solicit</span> icmpv6 <span class="nb">type</span> <span class="o">{</span>echo-request,nd-neighbor-solicit<span class="o">}</span> accept <span class="c1"># open tcp ports: sshd (22), httpd (80)</span> tcp dport <span class="o">{</span>ssh, http<span class="o">}</span> accept <span class="o">}</span> <span class="o">}</span> |
НАТИРОВАНИЕ NAT
NAT с отслеживанием состояния включает в себя механизм ядра nf_conntrack для сопоставления / установки информации о состоянии пакета и будет задействован в соответствии с состоянием соединений. Это наиболее распространенный способ выполнения NAT, и мы рекомендуем вам следовать этому подходу.
Имейте в виду, что с версиями ядра до 4.18 вам необходимо зарегистрировать цепочки предварительной / последующей маршрутизации, даже если у вас нет правил, поскольку эта цепочка будет вызывать механизм NAT для пакетов, приходящих в направлении ответа. Оставшаяся документация в этой статье предполагает более новое ядро, которое больше не требует этого неудобства.
Source NAT (SNAT)
Если вы хотите направить через NAT трафик, уходящий из вашей локальной сети в Интернет, вы можете создать новую таблицу nat с цепочкой постмаршрутизации:
1 2 |
% nft add table nat % nft <span class="s1">'add chain nat postrouting { type nat hook postrouting priority 100 ; }'</span> |
Затем добавьте следующее правило:
1 |
% nft add rule nat postrouting ip saddr <span class="m">192</span>.168.1.0/24 oif eth0 snat <span class="m">1</span>.2.3.4 |
что соответствует всему трафику из сети 192.168.1.0/24 к интерфейсу eth0. Адрес IPv4 1.2.3.4 используется в качестве источника для пакетов, соответствующих этому правилу.
Destanation NAT (DNAT)
Вам необходимо добавить следующую таблицу и конфигурацию цепочки:
1 2 |
% nft add table nat % nft <span class="s1">'add chain nat prerouting { type nat hook prerouting priority -100; }'</span> |
Затем вы можете добавить следующее правило:
1 |
% nft <span class="s1">'add rule nat prerouting iif eth0 tcp dport { 80, 443 } dnat 192.168.1.120'</span> |
Это перенаправляет входящий трафик для TCP-портов 80 и 443 на 192.168.1.120.
Маскарадинг
ПРИМЕЧАНИЕ: маскарад доступен начиная с Linux Kernel 3.18.
Маскарад – это особый случай SNAT, где адрес источника автоматически устанавливается равным адресу выходного интерфейса. Например:
1 |
% nft add rule nat postrouting masquerade |
Обратите внимание, что маскарад имеет смысл только в цепочке постмаршрутизации типа NAT.
Redirect
ПРИМЕЧАНИЕ: перенаправление доступно начиная с Linux Kernel 3.19.
При использовании перенаправления пакеты будут перенаправляться на локальный компьютер. Это частный случай DNAT, где адресатом является текущий компьютер.
1 |
% nft add rule nat prerouting redirect |
В этом примере трафик 22 / tcp перенаправляется на 2222 / tcp:
1 |
% nft add rule nat prerouting tcp dport <span class="m">22</span> redirect to <span class="m">2222</span> |
Обратите внимание: перенаправление имеет смысл только в цепочке предварительной маршрутизации типа NAT.
Флаги NAT
Начиная с ядра Linux 3.18, вы можете комбинировать следующие флаги со своими операторами NAT:
random: рандомизировать сопоставление портов источника.
полностью случайный: полная рандомизация портов.
постоянный: дает клиенту один и тот же адрес источника / назначения для каждого соединения.
Например:
1 2 |
% nft add rule nat postrouting masquerade random,persistent % nft add rule nat postrouting ip saddr <span class="m">192</span>.168.1.0/24 oif eth0 snat <span class="m">1</span>.2.3.4 fully-random |