В этой статье я покажу вам, как настраивается фаервол (netfilter) на сервере Debian 11 или Ubuntu 22.04 при помощи инструмента nftables.

Netfilter

В Linux брандмауэр встроен в ядро и называется он Netfilter. А для управления им могут использоваться различные утилиты. В Debian и Ubuntu более старших версий использовалась утилита iptables. А в Debian 11 и Ubuntu 22.04 начали использовать — nftables. Именно с этой утилитой я вас и познакомлю. Но вначале узнаем как вообще работает Netfilter.

Фаервол Netfilter делит весь трафик на цепочки (chain) и обрабатывает каждую цепочку по отдельности.

Прохождение пакетов по цепочкам Netfilter

Входящий трафик сразу же обрабатывается в цепочке prerouting (здесь может сработать входящий NAT).

После чего, если трафик:

  • предназначен серверу, то попадает в цепочку input (здесь пакеты можно принять или отбросить).
  • не предназначен серверу (проходит сквозь него), то попадает в цепочку forward (здесь также пакеты можно принять или отбросить).

Если трафик попал в цепочку forward, то перед самым выходом он попадёт в цепочку postrouting (здесь может сработать исходящий NAT).

А весь исходящий трафик обрабатывается цепочкой output (здесь тоже может отработать исходящий NAT).

Направление трафика и прохождение пакетами цепочек
Направление трафика и прохождение пакетами цепочек

Над пакетами совершаются различные действия, которые группируются в таблицы. И каждая цепочка может входить в ту или иную таблицу, или может находится в нескольких или во всех таблицах сразу. Если цепочка входит в определённую таблицу, это означает что в этой цепочке над пакетами можно выполнять действия из этой таблицы.

Список таблиц:

  • filter — здесь пакеты фильтруются, то есть их можно пропустить или отбросить.
    • Содержит цепочки: input, forward, output.
  • nat — здесь работает NAT (проброс портов, входящий и исходящий NAT).
    • Содержит цепочки: prerouting , output, postrouting.
  • mangle — позволяет вносить изменения в заголовки ip-пакетов.
    • Содержит все цепочки.
  • raw — позволяет выборочно пропускать или отбрасывать пакеты перед тем, как они попадут в механизм отслеживания соединения (connection tracking), что значительно снижает нагрузку на процессор.
    • Содержит цепочки: prerouting и output.

О чем эта статья

Этот курс предполагает что вы используете сервер Linux именно как сервер, а не как роутер. Поэтому проходящий сквозь сервер трафик я рассматривать не буду. И это условие значительно снижает количество таблиц и цепочек, которые мы рассмотрим.

Так как сервер принимает от клиентов какие-то запросы, обрабатывает их и отвечает на них. То здесь рассматриваются всего две цепочки:

  • input — отвечает за фильтрацию входящих пакетов к самому серверу. Например, подключение к серверу по ssh или ping сервера.
  • output — отвечает за пакеты исходящие с самого сервера. Это может быть VPN соединение с самого сервера, или ping каких-то адресов тоже с самого сервера.

Рассматривать мы будем запрещающие или разрешающие правила, они присутствуют в таблице filter.

Также в этой статье мы рассмотрим механизм connection tracking.

Настройка Nftables на Linux

Nftables — это инструмент, с помощью которого идет настройка фаервола Netfilter. Для настройки он использует конфиг — /etc/nftables.conf и команду nft. А также Nftables может быть запущен как служба.

Служба и конфиг nftables

В системе Debian 11 и Ubuntu 22.04 по умолчанию существует служба nftables, что облегчает управление файрволлом. Вы можете выполнять следующие команды:

  • $ sudo systemctl enable nftables — включить автозагрузку службы
  • $ sudo systemctl disable nftables — отключить автозагрузку службы
  • $ sudo systemctl start nftables — запустить службу
  • $ sudo systemctl stop nftables — остановить службу
  • $ sudo systemctl restart nftables — перезагрузить службу

По умолчанию эта служба выключена и перед добавлением правил в файрволл, её нужно включить. При запуске служба читает конфиг /etc/nftables.conf и запускается с указанными там правилами.

По умолчанию конфиг выглядит так:

$ cat /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
        chain input {
                type filter hook input priority filter;
        }

        chain forward {
                type filter hook forward priority filter;
        }

        chain output {
                type filter hook output priority filter;
        }
}

Давайте разберем его:

  • !/usr/sbin/nft -f — конфиг выполнен в виде скрипта, а в качестве оболочки будет использован /usr/sbin/nft;
  • flush ruleset — первым делом выполняется команда, которая очистит фаервол от всех правил;
  • table inet filter { } — блок в котором мы описываем таблицу filter. Ключевое слово inet означает что таблица создана для ipv4 и ipv6. Здесь содержаться 3 вложенных блока для разделения правил на разные цепочки:
    • chain input { } — блок описывающий правила для input;
    • chain forward { } — этот блок для forward;
    • chain output { } — а этот для output;

В каждой цепочке есть правило, которое её описывает:

type filter hook input (или forward или output) priority filter;

Цепочку вы можете назвать как угодно (chain input, или chain my_input, или chain in), но вот фраза type filter hook input определит эту цепочку как input для таблицы filter. Поменяйте input на forward или output, чтобы поменять тип цепочки.

Редактировать этот конфиг вручную не нужно, для этого есть специальная команда (nft), с помощью которой можно добавлять свои таблицы, цепочки и правила. А также удалять или редактировать их. Но так как в системе уже есть конфиг в котором есть таблица filter и цепочки input, forward и output, то нам остаётся лишь добавлять правила (rule). В этой статье я не буду рассматривать добавление таблиц и цепочек.

Просмотр действующих правил

Чтобы посмотреть правила, которые применяются прямо сейчас, выполните команду:

$ sudo nft list ruleset
table inet filter {
        chain input {
                type filter hook input priority filter; policy accept;
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}

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

Видим почти-что наш конфиг, но без двух строк (!/usr/sbin/nft -f и flush ruleset). То есть команда показывает нам только таблицы, цепочки и правила. А что такое policy accept и почему этого не было записано в конфиге, разберём ниже в этой статье.

Прохождение цепочки

Пакет, попавший в цепочку, начинает проходить правила указанные в ней сверху вниз. Фаервол сравнивает пакет с первым правилом, затем со вторым и так далее. Если фаервол определит правило для этого пакета, то с пакетом будет выполнено действие:

  • drop — пакет будет уничтожен;
  • accept — пакет сразу же покинет эту цепочку и пойдет дальше, то есть нижние правила уже не будут рассмотрены.

А если фаервол не найдет в данной цепочке подходящего правила для пакета, то фаервол выполнит действие с пакетом, которое указано в политике (policy accept или policy drop).

Прохождение пакетом цепочки input
Прохождение пакетом цепочки input

Сейчас во всех цепочках политика accept, и нет других правил. Это означает что все пакеты разрешены во все стороны (нет никаких правил и политика разрешает прохождение пакетов).

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

Обычно для цепочки input добавляют некоторые разрешающие правила, а затем меняют политику на drop. Таким образом всё что мы не разрешили будет запрещено. А цепочку output вообще не трогают, оставляя политику accept. То есть к нашему серверу можно подключиться только определённым образом, а сам сервер может отправлять пакеты куда захочет (например в интернет).

Если в конфиге /etc/nftables.conf мы не обозначили политику для цепочек, то будет использован accept. Именно это и показывает команда nft list ruleset.

Демонстрация настройки файрволла

Предположим наш сервер является samba сервером, web сервером и ntp сервером. В примере разрешим доступ к этим службам:

  • Samba в своей работе использует порты: tcp 445, 139 и udp 137, 138.
  • Web сервер работает на 80 и 443 tcp портах;
  • Сервер времени NTP работает на порту udp 123.

А также разрешим подключаться к серверу по SSH: tcp 22, но только с определённого компьютера.

Разрешим пинговать наш сервер и выходить ему в интернет.

Разрешаем входящие подключения к tcp и udb портам

Разрешим подключаться к серверу по ssh, но только со своего компьютера:

$ sudo nft add rule inet filter input iifname eth0 ip saddr 172.28.80.14 tcp dport 22 counter accept

То есть мы:

  • добавляем правило (addr rule) в таблицу filter и в цепочку input (inet filter input).
  • Описываем правило:
    • для входящего интерфейса eth0 (iifname eth0),
    • для адреса источника 172.28.80.14 (ip saddr 172.28.80.14)
    • и для tcp порта назначения 22 (tcp dport 22).
  • Дальше указываем действия:
    • чтобы команда nft list ruleset показывала счетчик пакетов, которые проходят правило, добавляем counter,
    • разрешаем прохождение таких пакетов (accept).

В своих правилах вы можете использовать следующее:

  • ip saddr <ip-адрес> — исходящий ip адрес;
  • ip daddr <ip-адрес> — ip адрес назначения (в цепочки input является адресом сервера к которому идет подключение);
  • tcp sport <порт> — исходящий tcp порт;
  • tcp dport <порт> — порт tcp назначения (в цепочки input является портом сервера к которому идет подключение);
  • udp sport <порт> — исходящий udp порт;
  • udp dport <порт> — порт udp назначения;
  • iifname <имя интерфейса> — имя входящего интерфейса;
  • oifname <имя интерфейса> — имя исходящего интерфейса.

Теперь покажу как разрешить подключения к samba, web и ntp. Обратите внимание: порты можно сгруппировать уменьшив количество правил. И в примере я разрешаю подключаться к сервисам только из локальной сети:

$ sudo nft add rule inet filter input iifname eth0 ip saddr 172.28.80.0/24 udp dport {137, 138, 123} counter accept
$ sudo nft add rule inet filter input iifname eth0 ip saddr 172.28.80.0/24 tcp dport {445, 139, 80, 443} counter accept

Разрешаем подключаться к loopback интерфейсу

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

$ sudo nft add rule inet filter input iifname lo counter accept

Разрешаем ping сервера

Разрешим пинговать наш сервер, другими словами разрешим входящие icmp запросы:

$ sudo nft add rule inet filter input ip saddr 172.28.80.0/24 icmp type echo-request counter accept

Чтобы разрешить icmp нужно указать протокол и тип запроса:

  • icmp type echo-request — когда кто-то пингует наш сервер ему посылаются запросы (echo-request);
  • icmp type echo-reply — когда наш сервер кого-то пингует то в ответ он получает ответы (echo-reply).

Меняем политику цепочки input

После того как мы добавили все разрешающие правила в цепочке input, поменяем её политику на drop:

$ sudo nft add chain inet filter input '{ policy drop; }'

То есть смена политик выполняется таким образом:

  • nft add chain <семейство> <цепочка> <таблица> ‘{ policy accept; }’ — разрешающая политика;
  • nft add chain <семейство> <цепочка> <таблица> ‘{ policy drop; }’ — запрещающая политика.

Механизм connection tracking

Политика в цепочки output у нас разрешающая. Давайте попробуем попинговать какой-нибудь точно доступный узел с нашего сервера:

$ ping 77.88.8.8

И почему-то этот узел не пингуется, сейчас объясню почему.

Когда у нас работает ping, сервер посылает пакет (echo request) и ждет ответный пакет (echo reply). В момент пинга 77.88.8.8 наш сервер отправляет пакет (echo request) и к нему прилетает ответ от 77.88.8.8 (echo reply), который у нас не разрешен. Но не спешите разрешать echo reply.

Допустим мы захотим обновить наш сервер. Сервер по цепочке output сможет выйти в интернет и подключится к репозиторию (про них будет написано позже в этом курсе). Но в нашу сторону обновления не полетят, потому что в цепочке input мы этого не разрешали.

В общем, в любом случае, когда наш сервер отправляет запрос куда-нибудь, то ответ будет блокироваться в цепочке input. И невозможно разрешить все, да и не нужно!

Тут вы должны понять, что задача у нас не разрешить подключения с тех адресов куда мы будем отправлять запросы. А просто разрешить все ответные пакеты. И механизм connection tracking как раз может определить ответное ли это соединение или нет.

Connection tracking — это модуль, который определяет состояния соединений. Соединения могут быть:

  • new — пакет устанавливает новое соединение;
  • estableshed — пакет является частью существующего соединения (например ответ на ping или ответ на http запрос);
  • related — пакет является частью связанного соединения. Например, FTP использует порт 21 для установления соединения, но данные передаются через другой порт. И вот передача данных будет осуществляться по related соединению;
  • invalid — пакет не является частью каких-либо соединений в таблице connection tracking.

Получается что все ответные соединения это estableshed + related.

И чтобы разрешить такой трафик, выполните команду:

$ sudo nft add rule inet filter input ct state established,related counter accept

Удаления правил

Каждое правило при добавлении получает свой номер (handle). Чтобы посмотреть эти номера нужно использовать ключ -a:

$ sudo nft -a list ruleset
table inet filter { # handle 7
        chain input { # handle 1
                type filter hook input priority filter; policy drop;
                iifname "eth0" ip saddr 172.28.80.14 tcp dport 22 counter packets 885 bytes 86352 accept # handle 4
                iifname "eth0" ip saddr 172.28.80.0/24 udp dport { 123, 137, 138 } counter packets 0 bytes 0 accept # handle 6
                iifname "eth0" ip saddr 172.28.80.0/24 tcp dport { 80, 139, 443, 445 } counter packets 0 bytes 0 accept # handle 8
                iifname "lo" counter packets 0 bytes 0 accept # handle 9
                iifname "eth0" ip saddr 172.28.80.0/24 icmp type echo-request counter packets 2 bytes 120 accept # handle 10
                ct state established,related counter packets 2 bytes 168 accept # handle 11
        }

        chain forward { # handle 2
                type filter hook forward priority filter; policy accept;
        }

        chain output { # handle 3
                type filter hook output priority filter; policy accept;
        }
}

И затем можно удалить правило по его номеру указав путь до цепочки (inet filter input). Например так:

$ sudo nft delete rule inet filter input handle 8

Сохранение правил

Когда мы выполняли команды добавления правил фаервола они не сохранялись в конфиге /etc/nftables.conf. Поэтому перезагрузка службы вернёт фаервол до первоначального состояния. Чтобы этого не произошло, нужно сохранить все правила в конфиг. Это можно сделать двумя способами.

Сохранение правил в файл (вариант с использованием sudo):

$ echo '#!/usr/sbin/nft -f' | sudo tee /etc/nftables.conf
$ echo 'flush ruleset' | sudo tee -a /etc/nftables.conf
$ sudo nft list ruleset | sudo tee -a /etc/nftables.conf

Сохранение правил в файл (под root)

# echo '#!/usr/sbin/nft -f' > /etc/nftables.conf
# echo 'flush ruleset' >> /etc/nftables.conf
# nft list ruleset >> /etc/nftables.conf

После сохранения правил, просто перезагрузите фаервол и можете посмотреть список правил:

$ sudo systemctl restart nftables

$ sudo nft list ruleset
table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                iifname "eth0" ip saddr 172.28.80.14 tcp dport 22 counter packets 1035 bytes 97456 accept
                iifname "eth0" ip saddr 172.28.80.0/24 udp dport { 123, 137, 138 } counter packets 0 bytes 0 accept
                iifname "eth0" ip saddr 172.28.80.0/24 tcp dport { 80, 139, 443, 445 } counter packets 0 bytes 0 accept
                iifname "lo" counter packets 0 bytes 0 accept
                iifname "eth0" ip saddr 172.28.80.0/24 icmp type echo-request counter packets 2 bytes 120 accept
                ct state established,related counter packets 6 bytes 504 accept
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}

Итог

Netfilter — это фаервол, который встроен в ядро Linux.

Nftables — это инструмент с помощью которого настраивается Netfilter.

Netfilter содержит таблицы с возможными действиями, которые можно совершать над пакетами. А также разделяет трафик на цепочки.

Если таблица содержит цепочку, то это означает что в цепочке над пакетами можно совершать действия перечисленные в таблице.

Nftables позволяет создавать (add) или удалять (delete): таблицы (table), цепочки (chain) и правила (rule). Для этого используется команда nft, которую нужно выполнять из под root пользователя или используя sudo.

В Debian 11 и в Ubuntu 22.04 есть служба nftables которая загружает правила из конфига /etc/nftables.conf. Конфиги в обоих системах одинаковые.

Для сохранения созданных правил фаервола нужно перенести их в конфиг /etc/nftables.conf, так как сами они туда не заносятся. И все изменения будут удалены после перезагрузки службы nftables.

Почитать про nftables на сайте debian вы можете здесь.


Сводка
Настройка брандмауэра на Debian
Имя статьи
Настройка брандмауэра на Debian
Описание
В этой статье я покажу вам, как настраивается фаервол (netfilter) на сервере Debian 11 или Ubuntu 22.04 при помощи инструмента nftables

19 Replies to “Настройка фаервола — Nftables”

  1. Добрый день.
    При добавлении правила команда «counter» насколько я понимаю является опциональной?
    Счётчик будет увеличиваться до бесконечности?
    Через несколько лет использования что там может быть, страшно представить.
    Есть ли смысл в её добавлении? (ваше мнение).

    1. Счетчик будет сбрасываться при перезагрузке сервера. Но ключ “counter” действительно необязательный. Я использую чтобы понять работает ли правило. И мне тоже интересно, используют ли это другие.

  2. $ sudo systemctl status nftables – остановить службу ошибка в команде должна быть stop

  3. # echo ‘#!/usr/sbin/nft -f’ > /etc/nftables.conf
    # echo ‘flush ruleset’ >> /etc/nftables.conf

    Обе команды не рабочие, первая просто переносить #!/usr/sbin/nft -f в фаил /etc/nftables.conf, вторая делает тоже самое.
    Необходимо использовать косые кавычки

    1. Команды верные. Нам нужно создать текстовый файл с таким содержимым:
      #!/usr/sbin/nft -f
      flush ruleset
      дальше правила фаервола, которые выводятся с помощью команды nft list ruleset

      Поэтому нужно ввести три команды:
      echo ‘#!/usr/sbin/nft -f’ > /etc/nftables.conf
      echo ‘flush ruleset’ >> /etc/nftables.conf
      nft list ruleset >> /etc/nftables.conf

  4. # nft list ruleset >> /etc/nftables.conf
    > : Перезаписывает существующий файл или создает файл, если файл с указанным именем отсутствует в каталоге.
    >> : Добавляет существующий файл или создает файл, если файл с указанным именем отсутствует в каталоге.

    При вводе команды выше, конфигурационный фаил nftables.conf не перезапишется, а добавит в конце вывод команты nft list ruleset, что создать ошибку.

    1. Первая команда (echo ‘#!/usr/sbin/nft -f’ > /etc/nftables.conf) перезапишет файл. Двумя следующими мы его дополняем.

  5. «related – пакет является частью связанного соединения. Например, FTP использует порт 21 для установления соединения, но данные передаются через другой порт. И вот передача данных будет осуществляться по related соединению;»

    Могли бы вы привести примеры правил для проброса ftp на сервер в локальной сети?
    Я так понимаю, что это немного отличается от реализации в iptables.

    1. Вы имеете ввиду из интернета в локальную сеть на ftp сервер. Или просто от одного компьютера к другому в локальной сети?

  6. если я правильно понимаю, то в postroute прилетает И от forward И от output.
    не доводилось встречать вариант диаграммы с выводом output сразу на интерфейс.

    1. Если честно, то думал что в postrouting должны попадать только проходящие пакеты перед самым выходом. А вот исходящие пакеты вроде в postrouting не попадают. Хотя наверное ошибался (если так, то поправлю в статье). С postrouting на практике почти не работал. Иногда приходилось prerouting настраивать для маркировки пакетов, для балансировки трафика при двух WAN.

  7. Автор еще напиши статью дополни ее такой штукой как dyndns доступ с домашних компов с динамическими адресами IP на подобии dyndns
    Все написаное мною для Debian 11 работает и во многом благодаря вашей статье спасибо большое!
    Вот выкладываю ели что подправь там и замена rc.local
    https://disk.yandex.ru/d/xatLHvH-lbzAnQ

  8. На первой картинке к статье опечатка «prerputing». Очевидно должно быть «prerouting».

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *