> For the complete documentation index, see [llms.txt](https://antons-organization-1.gitbook.io/administrirovanie-linux/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://antons-organization-1.gitbook.io/administrirovanie-linux/setevye-nastroiki-linux/iptables-firewalld-ufw-nftables.md).

# Iptables, firewalld, ufw, nftables

В этой статье мы рассмотрим, как настроить базовую защиту с использованием одного из 4х представленных firewall'ов.

## UFW

UFW (Uncomplicated Firewall) - является самым простым и довольно популярным инструментарием командной строки для настройки и управления брандмауэром в дистрибутивах Ubuntu и Debian. Правильно функционирующий брандмауэр является наиболее важной частью полной безопасности системы Linux. UFW позволяет сделать базовые настройки, для более сложных настроек используйте [iptables](https://1cloud.ru/help/security/ispolzovanie-iptables-na-linux).

### Установка и проверка статуса

В случае, если ufw не установлен по умолчанию используйте:

`sudo apt install ufw`

Проверить текущий статус и вывести все текущие правила можно с помощью следующей команды:

`sudo ufw status verbose`

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

```
Status: inactive
```

Включить firewall можно с помощью следующей команды:

`sudo ufw enable`

В диалоговом окне необходимо ответить на вопрос, при использовании нашей инфраструктуры соединение не пропадет, если вы ранее не отключили iptables:

`Command may disrupt existing ssh connections.`\
`Proceed with operation (y|n)? y`\
`Firewall is active and enabled on system startup`*.*

В результате ufw будет активен:

```
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
```

Если Вам потребуется отключить утилиту, то используйте команду:

`sudo ufw disable`

### Политики по умолчанию

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

Политики безопасности по умолчанию находятся в файле **/etc/default/ufw** и могут быть изменены с помощью следующей команды:

`sudo ufw default deny incoming`\
`sudo ufw default allow outgoing`

Первое правило запрещает все входящие подключения, второе разрешает исходящие.

### Профили приложений

При установке программного пакета **ufw** с использованием диспетчера пакетов он будет включать в себя профили приложений, находящиеся в каталоге **/etc/ufw/applications.d**, который определяет приложение или службу и соответствующие им настройки безопасности, например открытые или закрытые порты.

Все профили создаются вручную. Посмотреть созданные профили можно следующим образом:

`sudo ufw app list`

На только, что созданном виртуальном сервере вы скорее всего увидите только профиль для OpenSSH:

```
Available applications:

  OpenSSH
```

Для просмотра детальной информации можно использовать следующую команду:

`sudo ufw app info '<название_ПО>'</название_ПО>`

Например:

`sudo ufw app info 'OpenSSH'`

Результат:

```
Profile: OpenSSH
Title: Secure shell server, an rshd replacement
Description: OpenSSH is a free implementation of the Secure Shell protocol.

Port:
  22/tcp
```

Для создания профиля приложения перейдите в директорию:

`cd /etc/ufw/applications.d`

Создайте текстовый файл с любым названием и вставьте в него строки следующего формата:

`[<название_профиля>]`\
`title=<заголовок>`\
`description=<краткое описание>`\
`ports=<список портов>/<протокол>`

Например:

`[App1]`\
`title=Text editor`\
`description=Most popular open source application for editing text.`\
`ports=53,80,5223,16393:16472/udp`

Для того, чтобы файл был виден брандмауэру, перезагрузите его:

`ufw reload`

Далее разрешите или запретите доступ профилю:

`ufw <allow/deny> <название_профиля>`

Например:

`ufw allow App1`

### Работа с портами

Для открытия портов используется ключевое слово **allow**.

С помощью следующей команды можно открыть порт для входящих подключений:

`sudo ufw allow <порт>/<протокол>`

Например:

`sudo ufw allow 1234/tcp`

Также можно открывать порты по именам конкретных сервисов, например:

`sudo ufw allow http`

*Примечание: если сервер использует порт не по умолчанию, то данное правило использовать нельзя.*

**ufw** позволяет открывать или закрывать промежуток портов:

`sudo ufw allow <портN>:<портM>/<протокол>`

Например:

`sudo ufw allow 5000:5003/udp`

Для закрытия портов используйте ключевое слово **deny**. Синтаксис **ufw** остается прежним, только **allow** заменяется на **deny**. Например, чтобы закрыть порт используется следующая команда:

`sudo ufw deny <порт>/<протокол>`

Например:

`sudo ufw deny 1234/tcp`

#### Работа с IP-адресами

Чтобы разрешить соединение ко всем портам сервера с конкретного IP-адреса, используйте следующую команду:

`sudo ufw allow from <IP-адрес>`

К примеру:

`sudo ufw allow from 111.111.111.111`

Также можно разрешить подключаться к конкретному порту с определенного IP-адреса:

`sudo ufw allow from <IP-адрес> to any port <порт>`

Пример:

`sudo ufw allow from 111.111.111.111 to any port 22`

Для запрета подключения используйте ключевое слово deny:

`sudo ufw deny from <IP-адрес>`

Пример:

`sudo ufw deny from 111.111.111.111`

### Работа с подсетью

С помощью утилиты ufw можно разрешить получение трафика со всей подсети с помощью нотации [CIDR](https://ru.wikipedia.org/wiki/%D0%91%D0%B5%D1%81%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D0%BE%D0%B2%D0%B0%D1%8F_%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0%D1%86%D0%B8%D1%8F):

`sudo ufw allow from <подсеть>`

Например:

`sudo ufw allow from 192.168.1.0/24`

Также можно направить трафик с подсети на конкретный порт:

`sudo ufw allow from <подсеть> to any port <порт>`

Например:

`sudo ufw allow from 192.168.1.0/24 to any port 22`

*Примечание: для запрета данных правил используйте ключевое слово **deny**.*

### Работа с сетевым интерфейсом

С помощью **ufw** можно настроить подключение к конкретному порту определенного интерфейса:

`sudo ufw allow in on <имя интерфейса> to any port <порт>`

Например:

`sudo ufw allow in on eth2 to any port 22`

*Примечание: имена всех интерфейсов сервера можно просмотреть с помощью команды ifconfig -a.*

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

Для удаления правил выведете нумерованный список текущих правил:

`sudo ufw status numbered`

Удалите правила под нужным номером:

`sudo ufw delete <номер_правила>`

Пример:

`sudo ufw delete 1`

Также можно удалить правило с помощью ключевого слова delete, например:

`sudo ufw delete allow 443`

## Iptables

**Iptables** - чрезвычайно гибкая утилита межсетевого экрана, предназначенная для операционных систем Linux. **Iptables** основана на правилах (**rules**), которые контролируют входящие и исходящие пакеты, а также использует цепочки правил для разрешения или блокировки трафика. По умолчанию никакие правила не установлены, пользователь может создавать, добавлять и редактировать правила.

**iptables** почти всегда предустановлена на любом дистрибутиве Linux. Чтобы обновить или установить, просто загрузите пакет iptables с помощью пакетного менеджера, например **apt-get**:

`sudo apt-get install iptables`

Необходимо быть предельно осторожным при настройке правил **iptables**, особенно если вы используете **ssh** на сервере, потому что одна неправильная команда может заблокировать подключение по **ssh**.

Основные конфигурационные файлы:

* **/etc/init.d/iptables** – скрипт инициализации для запуска, остановки, перезапуск и сохранения наборов правил;
* **/etc/sysconfig/iptables** – файл, в который сохраняются правила;
* **/sbin/iptables** – бинарный файл, в котором находится утилита.

В структуре iptables существует 4 таблицы:

* **Filter** - таблица по умолчанию;
* **NAT** - предназначена для преобразования сетевых адресов;
* **Mangle** - предназначена для внесения специализированных изменений в пакеты;
* **Raw** - предназначена для конфигурационных исключений.

### Filter

**Filter** - это таблица по умолчанию для **iptables**. Если вы не определяете собственную таблицу, то будет использоваться эта таблица правил. Таблица **Filter** использует три разные цепочки: INPUT, FORWARD и OUTPUT.

<figure><img src="/files/ZWQuJcnf3MGRvi2QcRww" alt=""><figcaption></figcaption></figure>

### NAT

Таблица NAT имеет следующие встроенные цепочки: PREROUTING, POSTROUTING, OUTPUT.

<figure><img src="/files/Ogd9KvhHofCh4qKxBAKw" alt=""><figcaption></figcaption></figure>

### Правила

Правила содержат критерии и цель.

Если критерии сопоставляются, то действия применяются к целевому объекту.

Если критерии не сопоставляются, то происходит переход к следующему правилу.

Ниже приведены возможные целевые объекты:

<figure><img src="/files/LhcxboaUzeZZfX1X2TcG" alt=""><figcaption></figcaption></figure>

Для того, чтобы посмотреть текущую конфигурацию iptables, выполните следующую команду:

`iptables --list`

Вывод:

```

Chain INPUT (policy DROP)
target     prot      opt   source       destination
f2b-sshd   tcp       --   anywhere      anywhere         multiport dports ssh
f2b-SSH    tcp       --   anywhere      anywhere         tcp dpt:ssh
ACCEPT     all       --   anywhere      anywhere         state RELATED,ESTABLISHED
ACCEPT     icmp      --   anywhere      anywhere
ACCEPT     all       --   anywhere      anywhere
ACCEPT     tcp       --   anywhere      anywhere         tcp dpt:ssh
```

Здесь:

* target - специальная целевая переменная, как видно из вывода, это может быть собственное значение, первые два правила ссылаются на собственные цепочки;
* prot - протоколы;
* opt - специальные опции;
* source - исходный ip-адрес;
* destination - ip-адрес назначения.

*Примечание: по умолчанию правила попадают в таблицу filter.*

Чтобы посмотреть правила конкретной таблицы используйте следующую команду:

`iptables -t <имя_таблицы> --list`

Например:

`iptables -t filter --list`

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

`iptables -L --line-numbers`

Для создания собственной цепочки используйте команду:

`iptables -N <имя_цепочки>`

Например:

`iptables -N MY_CHAIN`

### Блокировка ip-адресов

Часто возникает задача блокирования ip-адресов. Например, вы заметили, что IP 11.22.121.122 постоянно пытается атаковать ваш сервер, и вы хотите его заблокировать. Нужно просто заблокировать все входящие пакеты с этого IP-адреса. Чтобы это сделать, нужно добавить следующее правило в цепочку **INPUT** таблицы **filter**. В примере имя таблицы указано явно, без использования ключа t правило попадает в таблицу по умолчанию:

`iptables -t filter -A INPUT -s 11.22.121.122 -j REJECT`

Здесь:

* t - указывает, в какую таблицу будет входить правило;
* A - указывает на добавление (Append) правила в цепочку INPUT;
* s - указывает источник;
* j - указывает iptables на отклонение трафика с помощью цели REJECT.

Также можете блокировать диапазоны IP-адресов, используя CIDR-нотацию. Если необходимо заблокировать все IP-адреса от 11.22.121.0 до 11.22.121.255, это можно сделать с помощью команды:

`iptables -A INPUT -s 11.22.121.0/24 -j REJECT`

Если необходимо заблокировать исходящий трафик на конкретный ip, используется цепочка OUTPUT и флаг -d для указания ip-адреса назначения:

`iptables -A OUTPUT -d 31.13.78.35 -j DROP`

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

Теперь, скажем, вы заблокировали IP-адрес 11.22.121.0/24 по ошибке. Чтобы удалить существующее правило используйте команду:

`iptables -D INPUT -s 11.22.121.0/24 -j REJECT`

Здесь ключ -D (Drop) означает удаление правила из цепочки.

Также можно удалять правила по номерам строк. Если вы хотите удалить второе правило из цепочки INPUT, команда будет следующей:

`iptables -D INPUT 2`

*Примечание: при удалении правил нумерация изменяется.*

Также возможно удалить все правила цепочки - с помощью опции F:

`iptables -F INPUT`

### Вставка и замена правил

Поскольку **iptables** обрабатывает правила в цепочках одно за другим, возможно вставить правила в определенное место в цепочке. Например, следующая команда вставляет блокировку указанного ip-адреса на первое место:

`iptables -I INPUT 1 -s 11.22.121.10 -j ACCEPT`

Для замены правила стоящем на определенном месте, необходимо использовать ключ -R. Следующая команда заменит правило стоящее на 1 месте:

`iptables -R INPUT 1 -s 11.22.121.11 -j ACCEPT`

### Протоколы, модули, интерфейсы

Например, чтобы заблокировать весь входящий TCP-трафик, нужно с помощью ключа -p указать протокол следующим образом:

`iptables -A INPUT -p tcp -j DROP`

Более полезный пример - блокировка доступа по ssh для диапазона ip-адресов. Для начала необходимо сопоставить трафик TCP, как в примере выше. Затем нужно проверить предназначен ли трафик для SSH порта, используя --dport. Таким образом, вся команда будет:

`iptables -A INPUT -p tcp --dport 22 -s 11.22.121.0/24 -j DROP`

Для того, чтобы заблокировать доступ одновременно и к SSH и VNC для диапазона адресов, нужно использовать модуль multiport и ключ --dports. Команда:

`iptables -A INPUT -p tcp -m multiport --dports 22,5901 -s 59.45.175.0/24 -j DROP`

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

`iptables -A INPUT -p tcp --dport 80 -j ACCEPT`

Для разрешения всех входящих соединений с адаптера, например с eth1, используйте следующее правило:

`iptables -A INPUT -i eth1 -j ACCEPT`

### Пример перенаправления портов

Довольно часто возникает необходимость перенаправления портов и ip-адресов в целях безопасности в локальной сети. Например, чтобы перенаправить трафик с порта 80 сервера, который имеет выход в глобальную сеть, на 85 порт локальной машины.

<img src="https://static.1cloud.ru/img/help/ispolzovanie-iptables-na-linux/1.jpg" alt="схема перенаправления" height="483" width="1021">

Для перенаправления необходимо выполнить следующий набор правил:

`iptables -t nat -A PREROUTING -d <внешний_ip-адрес> -p tcp -m tcp --dport <порт_подключения> -j DNAT --to-destination <целевой_внутренний_ip-адрес>:<целевой_порт>`\
`iptables -t nat -A POSTROUTING -d <целевой_внутренний_ip-адрес> -p tcp -m tcp --dport <целевой_порт> -j SNAT --to-source <внутренний_ip-адрес_шлюза>`\
`iptables -t nat -A OUTPUT -d <внешний_ip-адрес> -p tcp -m tcp --dport <целевой_порт> -j DNAT --to-destination <целевой_внутренний_ip-адрес>`\
`iptables -I FORWARD 1 -i <внешний_ip-адрес> -o <внутренний_ip-адрес_шлюза> -d <целевой_внутренний_ip-адрес> -p tcp -m tcp --dport <целевой_порт> -j ACCEPT`

Например:

`iptables -t nat -A PREROUTING -d 11.22.121.10 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.1.2:85`\
`iptables -t nat -A POSTROUTING -d 10.0.1.2 -p tcp -m tcp --dport 85 -j SNAT --to-source 10.0.1.1`\
`iptables -t nat -A OUTPUT -d 11.22.121.10 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.1.2`\
`iptables -I FORWARD 1 -i 11.22.121.10 -o 10.0.1.1 -d 10.0.1.2 -p tcp -m tcp --dport 85 -j ACCEPT`

### Минимальная конфигурация firewall

Конкретные правила фаервола зависят от сервисов, используемых на вашем сервере. Ниже приведен пример правил, подразумевающих использование вами веб-сервера. Прежде, чем применять правила iptables, спланируйте их исходя из вашей вашей конфигурации.

*Примечание: восклицательный знак \`!\` используется для инверсии правил.*

Для начала разрешите все входящие подключения:

`iptables --policy INPUT ACCEPT`

Затем необходимо очистить все правила Iptables:

`iptables -F`

Чтобы не потерять соединение по SSH, откройте 22 порт:

`iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT`

Следующие правила зависят от ваших потребностей:\
Разрешить весь трафик c loopback (lo0):

`iptables -A INPUT -i lo -j ACCEPT`

Перенаправить трафик на localhost, который исходит не из lo0.

`iptables -A INPUT ! -i lo -s 127.0.0.0/8 -j REJECT`

Разрешить ping:

`iptables -A INPUT -p icmp -m state --state NEW --icmp-type 8 -j ACCEPT`

Разрешить соединения HTTP и HTTPS:

`iptables -A INPUT -p tcp --dport 80 -m state --state NEW -j ACCEPT`\
`iptables -A INPUT -p tcp --dport 443 -m state --state NEW -j ACCEPT`

Разрешить входящий трафик из установленных соединений:

`iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT`

Отклонить все остальные входящие пакеты:

`iptables -A INPUT -j REJECT`

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

После перезагрузки системы правила iptables очищаются, поэтому после их применения их необходимо сохранить.

На операционных системах Ubuntu и Debian можно использовать утилиту netfilter-persistent (или iptables-persistent):

`apt-get install iptables-persistent`

Сохранить правила:

`sudo netfilter-persistent save`

*Примечание: если у Вас включен uwf, необходимо его отключить:*

`sudo ufw disable`

Для сохранения правил на серверах Centos используйте команду:

`service iptables save`

*Примечание: если при использовании этой команды на Centos у вас возникает следующая ошибка:*

```
The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, force-reload, status). For other actions, please try to use systemctl.
```

То необходимо отключить firewalld, который введен для управления iptables, установить пакет iptables-services и запустить его:

`systemctl stop firewalld`\
`systemctl mask firewalld`\
`yum install iptables-services`\
`systemctl enable iptables`\
`systemctl start iptables`\
`service iptables save`

## Firewalld

**Firewalld** запускается как демон, новые правила добавляются без перезапуска и без сброса установленного файрвола. Изменения в конфигурации могут быть сделаны в любое время и применяются мгновенно: сохранять или применять изменения не требуется. Поддерживается IPv4, IPv6, автоматическая загрузка модулей ядра и сетевые зоны, определяющие уровень доверия соединений. Предоставляется простой интерфейс добавления правил для служб и приложений, белый список приложений, имеющих право менять правила. В настоящее время такую возможность поддерживает **libvirt**, **Docker**, **fail2ban**, **Puppet**, **скрипт установки Virtuozzo** и многие другие проекты. В репозитории **YUM** уже есть пакеты **fail2ban-firewalld** и **puppet-firewalld**, поэтому подключить их можно одной командой.

**Firewalld** предоставляет информацию о текущих настройках брандмауэра через **D-Bus API**, а также принимает изменения через **D-Bus** с использованием методов аутентификации **PolicyKit**. В качестве бэкенда используются **iptables**, **ip6tables**, **ebtables**, **ipset** и планируется [**nftables**](https://xakep.ru/2014/01/11/using-nftables/). Но сами правила, созданные непосредственно этими утилитами, **firewalld** не может разобрать, поэтому оба метода использовать нельзя.

Управление производится при помощи утилит командной строки **firewall-cmd** или графической **firewall-config**, позволяющей настроить все правила в удобной среде. Для помощи в миграции текущих правил **iptables** на **firewalld** используется утилита **firewall-offline-cmd**, по умолчанию считывающая `/etc/sysconfig/system-config-firewall`. В последних релизах появилась утилита **firewallctl**, имеющая простой синтаксис и позволяющая получать информацию о состоянии службы, конфигурации брандмауэра и изменять правила.

<figure><img src="/files/OjbRJvQkiJQnVsDBySN9" alt=""><figcaption><p>Графическая firewall-config поддерживает firewalld</p></figcaption></figure>

<figure><img src="/files/yf7xExC2eLlVYNse4u0K" alt=""><figcaption><p>Параметры firewall-cmd</p></figcaption></figure>

Смотрим статус:

```
# systemctl status firewalld 
# firewall-cmd --state
running
```

Разрешить соединение на определенный порт очень просто:

```
# firewall-cmd --permanent --add-port=22/tcp
```

Чтобы любые изменения вступили в силу, всегда после правок должна быть запущена команда

```
# firewall-cmd --reload
```

Для удаления порта из правил используется параметр `--remove-port`:

```
# firewall-cmd --remove-port=22/tcp
```

Вообще, многие команды `--add-*` имеют значения для проверки статуса `--query-*`, `--list-*` — список, изменения `--change-*` или удаления `--remove` соответствующего значения. Для краткости на этом не будем дальше заострять внимание. После релоада правил проверяем:

```
# firewall-cmd --list-ports
```

В firewalld предусмотрен режим, позволяющий одной командой заблокировать все соединения:

```
# firewall-cmd --panic-on
```

Для проверки, в каком режиме находится файрвол, есть специальный ключ:

```
# firewall-cmd --query-panic 
```

Отключается panic mode:

```
# firewall-cmd --panic-off
```

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

После установки firewalld знает настройки более 50 сервисов, получаем их список.

```
# firewall-cmd --get-services
```

Разрешим подключение к HTTP:

```
# firewall-cmd --add-service=http
```

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

```
# firewall-cmd --info-service=http
```

Firewalld хранит все настройки в XML-файлах в каталогах в /usr/lib/firewalld. В частности, сервисы лежат в services. Внутри файла описание: название, протокол и порт.

```xml
<?xml version="1.0" encoding="utf-8"?>
<service>
    <short>MySQL</short>
    <description>MySQL Database Server</description>
    <port protocol="tcp" port="3600"/>
</service>
```

Это каталог системный, и менять там ничего нельзя. Если нужно переопределить настройки или создать свой сервис, то копируем любой файл в качестве шаблона в `/etc/firewalld/services`, правим под свои условия и применяем настройки.

Для настройки ICMP используется отдельный набор правил. Получаем список поддерживаемых типов ICMP:

```
# firewall-cmd --get-icmptypes
```

Проверяем статус:

```
# firewall-cmd --zone=public --query-icmp-block=echo-reply
# firewall-cmd --zone=public --add-icmp-block=echo-reply    
```

<figure><img src="/files/53YG92OJmO1XoID6VVMC" alt=""><figcaption><p>Все настройки firewalld хранит в XML-файлах</p></figcaption></figure>

<figure><img src="/files/p0BEF9VqIJuAH8zqOSxc" alt=""><figcaption><p>Firewalld знает о почти 50 сервисах</p></figcaption></figure>

### Управление зонами

Для определения уровня доверия сетевому соединению в firewalld используются зоны. Зона может содержать несколько сетевых подключений, но сетевое соединение может входить только в одну зону. Список всех зон получаем командой `firewall-cmd --get-zones`.

<figure><img src="/files/tfhng1sNI30hXTeEKF76" alt=""><figcaption><p>Получаем список зон</p></figcaption></figure>

После установки создается девять зон, в зависимости от назначения может быть использована одна или несколько зон:

* trusted — все сетевые соединения разрешены;
* work/home/internal — зоны похожи по настройкам, отличаются назначением. Устанавливается максимальное доверие к компьютерам в сети, разрешается устанавливать только конкретные входящие соединения (по умолчанию SSH и DHCPv6 client, в home и internal плюс MDNS и Samba client);
* dmz — для компьютеров, находящихся в демилитаризованной зоне, доступные из Сети и с ограниченным доступом к внутренней сети. Разрешаются только указанные входящие соединения (по умолчанию SSH);
* external — правило, подходящее для роутеров, для использования во внешних сетях с разрешенным маскарадингом, с максимальным недоверием и четко установленными разрешенными входящими соединениями (по умолчанию SSH);
* public — для использования в общественных местах, с максимальным недоверием к другим компьютерам, разрешены только конкретные входящие соединения (по умолчанию SSH и DHCPv6 client);
* block — входящие сетевые соединения отклоняются с icmp-host-prohibited сообщением, разрешены только соединения, инициированные в этой системе;
* drop — разрешаются только исходящие соединения, все входящие блокируются.

Описания зон также представлены в XML-файлах в `/usr/lib/firewalld/zones`.

После установки системы обычно используется зона public. Если имеющихся зон недостаточно, то можно создавать новые зоны при помощи

```
# firewall-cmd --permanent --new-zone=zone_name
```

<figure><img src="/files/bx16BeqkjJtpkjQS68RM" alt=""><figcaption><p>Настройки зон по умолчанию</p></figcaption></figure>

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

```
# firewall-cmd --get-default-zone
```

Теперь — какие зоны сейчас активны и какие интерфейсы к ним привязаны.

```
# firewall-cmd --get-active-zones
```

Также можем получить обратную информацию — к какой зоне привязан интерфейс.

```
# firewall-cmd --get-zone-of-interface=eno1
```

Смотрим настройки зоны (сервисы, порты, протоколы...).

```
# firewall-cmd --zone=public --list-all
# firewall-cmd --zone=public --list-services
```

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

```
# firewall-cmd --zone=home --add-interface=eno1 --permanent
```

Если сейчас проверить вывод `firewall-cmd --zone=public --list-all`, то увидим, что из списка установок пропал сетевой интерфейс. Разрешим подключение сервиса:

```
# firewall-cmd --zone=home --add-service=openvpn --permanent
```

Удаляется он так же:

```
# firewall-cmd --zone=home --remove-service=openvpn --permanent
```

К зонам можно привязывать и другие источники, определяемые по MAC, отдельному IP или адресу сети. Пакет, пришедший из такого источника, будет обрабатываться по правилам зоны.

```
# firewall-cmd --permanent --zone=trusted --add-source=192.168.1.0/24    
```

Список всех source смотрим при помощи `--zone=trusted --list-sources`. NAT, позволяющий нескольким компьютерам подключаться к сети, в firewalld включается одной командой. Смотрим текущие настройки маскарадинга:

```
# firewall-cmd --zone=external --query-masquerade
```

Если в ответ получим `no`, то включаем:

```
# firewall-cmd --zone=external --add-masquerade
```

Это все. Для доступа извне настроим форвардинг порта в один из компьютеров. Например, нам нужен доступ по SSH к внутреннему серверу:

```
# firewall-cmd --zone=external --add-forward-port=port=22:proto=tcp:toport=22:toaddr=192.168.1.100
```

Проверяем:

```
# firewall-cmd --zone=external --list-all
```

Удаляется правило форвардинга при помощи `--remove-forward-port`.

&#x20;

### Сложные правила

Для отдельного ПК или небольших сетей базовых возможностей вполне хватает, для настройки сложных правил в firewalld изначально предлагался так называемый direct-синтаксис, чуть позже появился собственный язык Rich Language. В первом варианте достаточно знать синтаксис iptables, рекомендуется использовать в крайнем случае, так как правила не сохраняются после перезагрузки.

Синтаксис direct правила такой:

```
# firewall-cmd [--permanent] --direct --add-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>
```

Позиция полностью совпадает с синтаксисом iptables. Получаем текущие установки:

```
# firewall-cmd --direct --get-chains ipv4 filter
# firewall-cmd --direct --get-rules ipv4 filter input
```

Добавляем правило, разрешающее соединение по 25-му порту:

```
# firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -m tcp -p tcp --dport 25 -j ACCEPT
```

Пробросим соединение по 22-му на другой сервер:

```
# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i eno1 -o eno2 -p tcp --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
```

Проверяем:

```
# firewall-cmd --direct --get-all-rules
```

**Rich Language** позволяет записывать сложные правила в более удобном для понимания виде. В правиле можно указывать любые параметры, характеризующие пакет: источник, назначение, сервис, порт, протокол, маскарадинг, журналирование, аудит и действие. Например, разрешим подсети соединяться по HTTP и добавляем аудит:

```
# firewall-cmd --permanent --zone=public --add-rich-rule="rule family="ipv4" source address="192.168.0.0/24" service name="http" audit limit value="1/m" accept
```

Большой плюс **Rich Language** в том, что все параметры можно описать в XML в файле зоны. Формат файла очень простой и повторяет названия параметров:

```xml
<rule>
    <service name="ssh"/>
    <accept/>
</rule> 
```

Настройка файрвола — дело привычки. Часто удобнее вбить команду, которой пользуешься уже не один год, чем осваивать новую утилиту. Поэтому иногда все-таки хочется вернуть классический инструмент. Это не проблема. Iptables в CentOS 7 не ставится, поэтому его нужно вернуть:

```
# yum install -y iptables-services
```

Чтобы не настраивать все повторно, лучше сохранить текущие правила, сгенерированные firewalld.

```
# iptables-save > /etc/sysconfig/iptables
# ip6tables-save > /etc/sysconfig/ip6tables
```

Останавливаем firewalld и запускаем iptables:

```
# systemctl stop firewalld && systemctl disable firewalld
# systemctl start iptables && systemctl enable iptables
# systemctl start ip6tables && systemctl enable ip6tables
```

Проверяем текущие правила:

```
# iptables -L
# iptables -S
```

Запрещаем автозапуск firewalld при загрузке ОС:

```
# systemctl disable firewalld
```

## Nftables

### Чем не устраивает iptables?

Проект **netfilter/iptables** был основан в 1998 году и с версии ядра 2.4 используется по умолчанию. Команда разработчиков сохранила основную идею, заложенную еще в **ipfwadm**, — список правил, состоящих из критериев и действия, которое выполняется, если пакет соответствует критериям. **Netfilter** разрешал подключать дополнительные модули (ранее архитектура ядра такой возможности не предоставляла). Это позволило очень просто развивать подсистему фильтрации, и со временем появилось большое количество новых функций и модулей. Полноценная поддержка IPv6 появилась только в 2011 году, что, правда, потребовало редизайна netfilter. В результате модуль NAT разделили на два независимых компонента, один из которых включает в себя ядро подсистемы NAT, а второй реализует поддержку протокола третьего уровня. Помимо фильтрации, модули обеспечивают классификацию трафика (вплоть до седьмого уровня OSI), балансировку нагрузки, манипуляцию с пакетами, маршрутизацию и прочее.

Со временем накапливались проблемы на функциональном уровне и в дизайне. Код ядра дублировался, становилось все сложнее его поддерживать и добавлять новые возможности. Обработка некоторых параметров жестко вшита в ядро, модуль нередко обслуживает только свой протокол. Например, за извлечение номера порта UDP и TCP отвечают два разных модуля. Для реализации любой функции в userspace требуется поддержка модулем ядра, это затрудняет разработку, и без пересборки часто не обойтись. Правила загружаются как один большой дамп, в случае изменения правила выгружаются, меняются, и весь набор отправляется обратно. Без учета дополнительных расширений количество опций конфигурирования в ядре уже давно перевалило за сотню.

<figure><img src="/files/NKi16e1hxrxyI24G9feG" alt=""><figcaption><p>Iptables содержит большое количество модулей</p></figcaption></figure>

Еще один важный мотив — необходимость сбросить текущий ABI (Application Binary Interface), представляющий собор набор соглашений между программами, библиотеками и ОС, обеспечивающими их взаимодействие на низком уровне. В iptables ABI жестко прописаны специфические для протоколов поля, поэтому расширить его сложно. Как результат, приходится сразу запускать iptables, arptables и ebtables, по существу выполняющие одну работу, но каждый на своем уровне. По общим оценкам дублируется 10 000 строк кода. Учитывая, что все защитные механизмы и цепочки (даже пустые) грузятся изначально и активны, iptables потребляет больше ресурсов, чем реально необходимо.

Пользователям и администраторам управлять большим количеством правил довольно тяжело, трудно с ходу разобраться, что делают все цепочки, правила начинают повторяться, их становится сложно обслуживать и обновлять. Чтобы настроить два разных действия (вроде MARK и ACCEPT), правила приходится дублировать. Каждое расширение имеет свой синтаксис, одни поддерживают диапазоны, отрицание, префиксы, другие — нет.

<figure><img src="/files/feR8Fq3ChW7KByVTCOEO" alt=""><figcaption><p>Дополнительные расширения к iptables со своим синтаксисом создают еще большую путаницу</p></figcaption></figure>

### Журналирование в nftables

Для регистрации событий используется **Netfilter**, при помощи модулей **xt\_LOG** (регистрирует в syslog) и/или **nfnetlink\_log**. Последний использует демон сбора информации **ulogd2**, вышедший примерно полтора года назад и способный накапливать данные на уровне отдельных пакетов или потоков и сохранять их, в том числе и в БД.

Механизм журналирования для каждого протокола настраивается через /proc.

```
# cat /proc/net/netfilter/nf_log
0 NONE (nfnetlink_log)
1 NONE (nfnetlink_log)
2 ipt_LOG (nfnetlink_log,ipt_LOG)
```

Под номером 2 у нас скрывается IPv4. Меняем на **nfnetlink\_log**:

```
# echo "nfnetlink_log" >/proc/sys/net/netfilter/nf_log/2
```

### Назначение nftables

Об [nftables](http://netfilter.org/projects/nftables) впервые заговорили в октябре 2008 года на конференции Netfilter Workshop. Задача проекта — заменить подсистемы iptables, ip6table, arptables и ebtables одним решением. Разработкой новой подсистемы пакетной фильтрации стала заниматься та же команда, только под руководством **Патрика Мак-Харди** (Patrick McHardy). Альфа-версия была представлена в марте 2009 года, хотя до 2012 года проект практически спал.

Дерево патчей состояло из более 100 патчей, которые в конце октября 2013-го были объединены в 17. В стандартную ветку Linux nftables включен с версии 3.13, хотя высокоуровневые инструменты все еще находятся в разработке, а документация, ориентированная на пользователя, отсутствует. Старая и новая подсистемы будут некоторое время сосуществовать рядом, так как nftables еще требует доработки и тестирования. Для обеспечения обратной совместимости предоставляется специальная прослойка, позволяющая использовать iptables/ip6tables поверх инфраструктуры nftables.

В **nftables** реализована идея, схожая с **BPF (Berkeley Packet Filters)**: правила фильтрации в пространстве пользователя компилируются в байт-код, а затем через **Netlink API** передаются в ядро. После этого для принятия решения по дальнейшим действиям с пакетом они выполняются с использованием так называемого конечного автомата (**pseudo-state machine**), который представляет собой простейшую виртуальную машину, выполняющую байт-код.

Виртуальная машина способна манипулировать наборами данных (как правило, IP-адреса), позволяя заменить несколько операций сравнения единым набором поиска. Для принятия решений на основе этих данных могут быть использованы арифметика, битовые операторы и операторы сравнения. Возможен и обратный процесс декомпиляции объектов, позволяющий воссоздать текущую конфигурацию в ядре.

Использование **userspace** значительно упрощает код ядра и позволяет гораздо проще анализировать и принимать решения по отдельным протоколам. Отсутствует дублирование кода, особенности каждого протокола уже не встраиваются.

Все операции по определению условий и связанных с ними действий выполняются в пространстве пользователя, в ядре производится только базовый набор операций, таких как чтение данных из пакета, сравнение данных. Присутствует поддержка словарного маппинга и поиск по наборам правил (sets), работа которых реализована через хеши и rb-деревья. При этом элементы наборов могут быть заданы в виде диапазонов значений (можно определять подсети).

В качестве базовых блоков по-прежнему используются компоненты netfilter, в том числе существующие хуки, система отслеживания состояния соединений, компоненты организации очередей и подсистема ведения лога. Хотя работа в userspace позволяет получать больше отчетов об ошибках.

Отличается и алгоритм работы фильтра, он сделан более универсальным, теперь разборкой пакета занимаются операторы (**expression**). Специальный механизм payload expression загружает данные из пакета в один из регистров общего назначения. Базовое смещение, специфичное для протокола, берется из структуры **nft\_pktinfo** и модулей **netfilter** (IPv4, ARP и так далее). То есть уже нельзя сказать: «сравни IP источника с IP 192.168.0.1», — теперь фильтр «знает», что нужно извлечь определенную часть заголовка, помещает ее в переменную и затем сравнивает с нужным адресом. Обработка пакета несколькими правилами за счет введения так называемого verdict register стала значительно проще. Также легко пропустить ненужные операции, вроде счетчиков, если в них нет необходимости, меньше ресурсов требует поиск и сопоставление с диапазоном.

В итоге простое правило iptables в памяти занимает 112 байта, аналогичное nftables — 24 байта. Проверка пинга "-d 192.168.0.1 -p icmp –icmp-type echo-request" — 152 и 96 байт соответственно.

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

Для взаимодействия **kernel <-> userspace nftables API** использует особый компонент ядра **Netlink**, позволяющий через обычный сокет передавать и принимать сообщения, сформированные особым образом. При этом сам **Netlink** позволяет:

* получать уведомления об изменении сетевых интерфейсов, таблиц маршрутизации и состоянии пакетного фильтра;
* управлять параметрами сетевых интерфейсов, таблицами маршрутизации и параметрами **netfilter**;
* управлять ARP-таблицей;
* взаимодействовать со своим модулем в ядре.

Именно через Netlink работает утилита iproute2, пришедшая на смену ifconfig и route.

Собственно взаимодействие с кодом, работающим на уровне ядра, возложено на интерфейсные библиотеки **libmnl (Netlink)**, **libnftables (userspace Netlink API)** и построенный поверх фронтенд, работающий на уровне пользователя. Для формирования правил фильтрации в nftables подготовлена утилита nft, которая проверяет корректность правил и транслирует их в байт-код. Утилита **iptables-nftable** позволяет использовать правила iptables.

### Правила

Конечно, писать низкоуровневые правила пользователи не будут, доступен понятный язык описания. Новый синтаксис правил непохож на iptables, главное отличие — использование иерархических блочных структур вместо линейной схемы. Группировка позволяет легко составлять, читать и понимать настройки без особых пояснений. Синтаксис при этом чем-то напоминает ipfw из FreeBSD.

Язык классификации правил основан на реальной грамматике, при обработке которой используется парсер bison. К сожалению, документация в этом вопросе мало помогает, она просто еще не готова. О возможностях можно судить только по исходному коду nftables и обрывкам информации в специализированных мейл-листах.

<figure><img src="/files/b4ODk4vTgkcFdh3Qc39R" alt=""><figcaption><p>Для настройки правил используется утилита nft</p></figcaption></figure>

Правила могут содержать:

* таблицы — контейнеры для одного семейства протоколов, поддержка мультипротокола не реализована в netfilter, хотя в будущем, вероятно, что-то изменится;
* цепочки (chains) — контейнеры для правил, могут использоваться в действии перехода (jump), но в отличие от iptables цепи не содержат счетчики;
* базовые цепочки (base chains) — особый тип цепи, регистрируются с хуками netfilter, обеспечивая отправную точку для таблиц. Регистрируются при вставке первого правила;
* правила (rules) — атомарная единица, содержащая expressions.

Сами правила выглядят следующим образом. Разрешаем ping (ICMP-сообщения echo-request):

```
nft add rule filter input icmp type echo-request accept
```

Теперь разрешаем доступ с подсети 192.168.1.0/24 и IP 192.168.0.10 по SSH, блокируем для всех доступ по 80-му порту и разрешаем все пакеты уже установленного соединения (connection tracking).

```
nft add rule ip global filter ip daddr {192.168.1.0/24, 192.168.0.10} tcp dport {22} accept
nft add rule ip filter input tcp dport 80 drop
nft insert rule filter input ct state established accept
```

Счетчики — необязательный элемент в правилах, и, если он нужен, его необходимо активировать. Теперь в одном правиле можно указать сразу несколько действий. Например, подсчитаем количество пакетов, отправленных на 192.168.0.1, и заблокируем соединение:

```
nft add rule ip filter output ip daddr 192.168.0.1 counter drop
```

Вместо IP в правилах возможно использование доменного имени:

```
nft add rule ip6 filter output ip daddr example.org accept
```

Фильтр интерфейса позволяет указывать правило для конкретного сетевого интерфейса (сетевуха должна присутствовать в системе, иначе правило не будет активным):

```
nft insert rule filter input meta iif eth0 accept
```

Еще один нюанс. Утилита nft может работать в трех режимах, чем-то напоминая управление в Cisco. Например, все настройки можно указать в файле и затем просто скормить конфиг nft, это очень удобно для переноса рулесетов на несколько ПК. В каталоге `files/nftables` с исходными текстами уже имеется ряд шаблонов, перед началом использования nftables необходимо выбрать нужные и активировать:

```
# nft -f files/nftables/ipv4-filter
# nft -f files/nftables/ipv6-filter
```

Также есть режим командной строки, когда нужная настройка указывается сразу, и интерактивный режим CLI. Перейти в CLI просто:

```
# nft -i
```

Теперь можем последовательно давать команды или считывать настройки. Единичные правила группируются в таблице, образуя иерархическую блочную структуру, напоминающую pf и npf.

```
nft> list table filter -n -a
table filter {
  chain output {
  table filter hook output priority 0;
  ip protocol tcp counter packets 190 bytes 21908
  ip daddr 192.168.1.100 drop
  }
}
```

Правило можно вставить в нужную позицию (handle), здесь работают две директивы — add и insert. Первая добавляет правило после указанной позиции, а вторая — перед. Например, чтобы вставить правило перед handle 8, пишем:

```
nft insert rule filter output position 8 ip daddr 127.0.0.1 drop
```

Удаляются правила в цепочке при помощи параметра delete:

```
nft delete rule filter output handle 9
```

Если не указывать номер правила, то будет очищена вся цепочка. При помощи flush сбрасывается вся таблица:

```
nft flush table filter
```

Настройка NAT не сложнее, вначале необходимо загрузить модули:

```
modprobe nft_nat
modprobe nft_chain_nat_ipv4
modprobe nft_chain_nat_ipv6
```

Создаем цепочку:

```
nft add table nat
nft add chain nat post \{ type nat hook postrouting priority 0 \; \}
nft add chain nat pre \{ type nat hook prerouting priority 0 \; \}
```

И добавляем в нее правила:

```
nft add rule nat post ip saddr 192.168.1.0/24 meta oif eth0 snat 192.168.1.1
nft add rule nat pre udp dport 53 ip saddr 192.168.1.0/24 dnat 8.8.8.8:53
```

Первое активирует NAT для всего трафика с 192.168.1.0/24 на интерфейсе eth0, второе перенаправляет весь DNS-трафик на 8.8.8.8.

<figure><img src="/files/PmEVID2RnyuQIYPxqIB9" alt=""><figcaption><p>В поставке nftables идут шаблоны правил</p></figcaption></figure>

### WWW

Сайт nftables: [netfilter.org/projects/nftables](http://netfilter.org/projects/nftables)

### Установка nftables в Ubuntu

На данный момент единственный выход изучить новинку — это установить **nftables** самостоятельно. Нам потребуются библиотеки и стандартный набор для сборки ПО:

```
$ sudo apt-get install autoconf2.13 dh-autoreconf libmnl-dev libmnl0
```

Библиотеку можно поставить из [сырцов](https://git.netfilter.org/libmnl), но пока версии, доступной в репозитории, вполне достаточно. Копируем код libnftables и nftables.

```
$ git clone git://git.netfilter.org/libnftables
$ git clone git://git.netfilter.org/nftables
```

<figure><img src="/files/5TC3WdHh5umtUh7F3NBj" alt=""><figcaption><p>В nftables добавлен модуль совместимости с iptables</p></figcaption></figure>

Сборка в том и другом случае стандартна, переходим в каталог и даем команды:

```
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
```

В некоторых системах (в Ubuntu, например) при конфигурировании **nftables** следует активировать ряд функций:

```
$ ac_cv_func_malloc_0_nonnull=yes ac_cv_func_realloc_0_nonnull=yes ./configure
```

Далее вводим `make oldconfig`, переходим в Core Netfilter Configuration, где находим специфичные для nftables настройки (их, кстати, на порядок меньше, чем для iptables), собираем ядро.

<figure><img src="/files/wZmDjLv6s9cC7g4urYdU" alt=""><figcaption><p>Конфигурирование поддержки nftables в ядре Linux</p></figcaption></figure>

### Пакетный фильтр FF

**Джефри Мерки (Jeffrey Merkey)** представил в списке рассылки разработчиков ядра Linux код нового специализированного пакетного фильтра [**FF**](http://lkml.org/lkml/2009/1/8/467), предназначенного для блокирования большого числа IP-адресов в сетях с интенсивным трафиком. FF состоит из модуля Linux-ядра и утилиты для управления пакетным фильтром. Представленный пакетный фильтр не отличается такой гибкостью, как iptables, но опережает последний по производительности и потребляет значительно меньше памяти в расчете на один IP.

От **ipset** новая система отличается тем, что поддерживает блокирование на уровне драйвера e1000. Набор утилит, работающий на уровне пользователя, обеспечивает сохранение БД-адресов на диске с кешированием базы в памяти ядра.

**Главная задача FF** — защита от DoS/DDoS-атак, блокирование различного флуда и паразитного трафика. В качестве примера представлен код для интеграции разработки с Postfix с целью борьбы с роботами спамеров, выступающий в роли более жесткой системы блокирования для серых списков и RBL-систем.

### INFO

До появления iptables для обеспечения возможностей межсетевого экрана в Linux использовались проекты ipchains в Linux 2.2 и ipfwadm в Linux 2.0 (основанный на ipfw из BSD).
