Iptables, firewalld, ufw, nftables

Настройки показанные здесь приведены на примере ОС Ubuntu и Debian, работоспособность на других дистрибутивах необходимо уточнить самостоятельно.

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

UFW

UFW (Uncomplicated Firewall) - является самым простым и довольно популярным инструментарием командной строки для настройки и управления брандмауэром в дистрибутивах Ubuntu и Debian. Правильно функционирующий брандмауэр является наиболее важной частью полной безопасности системы Linux. UFW позволяет сделать базовые настройки, для более сложных настроек используйте iptables.

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

В случае, если 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:

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.

NAT

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

Правила

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

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

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

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

Для того, чтобы посмотреть текущую конфигурацию 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 порт локальной машины.

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

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. Но сами правила, созданные непосредственно этими утилитами, firewalld не может разобрать, поэтому оба метода использовать нельзя.

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

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

# 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 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    

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

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

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

  • 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

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

# 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.

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

Для отдельного ПК или небольших сетей базовых возможностей вполне хватает, для настройки сложных правил в 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 в файле зоны. Формат файла очень простой и повторяет названия параметров:

<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 требуется поддержка модулем ядра, это затрудняет разработку, и без пересборки часто не обойтись. Правила загружаются как один большой дамп, в случае изменения правила выгружаются, меняются, и весь набор отправляется обратно. Без учета дополнительных расширений количество опций конфигурирования в ядре уже давно перевалило за сотню.

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

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

Журналирование в 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 впервые заговорили в октябре 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 и обрывкам информации в специализированных мейл-листах.

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

  • таблицы — контейнеры для одного семейства протоколов, поддержка мультипротокола не реализована в 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.

WWW

Сайт nftables: netfilter.org/projects/nftables

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

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

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

Библиотеку можно поставить из сырцов, но пока версии, доступной в репозитории, вполне достаточно. Копируем код libnftables и nftables.

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

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

$ ./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), собираем ядро.

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

Джефри Мерки (Jeffrey Merkey) представил в списке рассылки разработчиков ядра Linux код нового специализированного пакетного фильтра FF, предназначенного для блокирования большого числа 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).

Last updated