Настраиваем фаервол во FreeBSD при помощи ipfw

15 августа 2016

Когда-то давно мы настраивали фаервол в Linux с помощью iptables. При этом отмечалось, что утилиту iptables я нахожу исключительно неудобной по сравнению с FreeBSD’шным ipfw. Сегодня мы наконец-то познакомимся с этим ipfw и постараемся ответить на вопрос, действительно ли он удобнее. Отмечу, что на момент написания этих строк FreeBSD предлагает аж три фаервола на выбор — ipfw, pf и ipf. Однако по моим представлениям из них ipfw используется чаще всего.

Описанные в этом посте действия были проверены мной на FreeBSD 10.3, но по идее не должны ничем отличаться в других версиях FreeBSD.

Итак, первое, что требуется выполнить при настройке ipfw — это команду:

sudo kenv net.inet.ip.fw.default_to_accept=1

По умолчанию ipfw запрещает передачу вообще всех пакетов. Если вы сидите на сервере по ssh, что весьма вероятно, и просто включите ipfw, то на сервер вы больше не зайдете до следующего ребута. Приведенная команда временно меняет поведение ipfw на «разрешить всем и все», тем самым решая описанную проблему.

Теперь включаем ipfw:

sudo kldload ipfw

При этом в /var/log/messages мы должны увидеть что-то вроде:

ipfw2 (+ipv6) initialized, divert loadable, nat loadable,
  default to accept, logging disabled

Как видите, ipfw представляет собой обычный модуль ядра.

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

sudo ipfw list

Должны увидеть:

65535 allow ip from any to any

Приведенный синтаксис довольно очевидный — номер правила, резрешить / запретить, название протокола, от кого, к кому. Тут мы видим правило с номером 65535, разрешающее передачу входящих и исходящих IP пакетов от кого угодно кому угодно на всех интерфейсах.

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

sudo ipfw -t list

Давайте попробуем добавить парочку правил. Например, запретим пинги:

# флаг -q выполняет команду в "тихом" режиме, без вывода
sudo ipfw -q add 00100 deny icmp from any to any
sudo ipfw list

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

Попытаемся пингануть eax.me и убедимся, что пинги больше не ходят. Теперь, когда мы осознали свою ошибку, удаляем правило:

sudo ipfw -q delete 00100

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

sudo ipfw -q -f flush

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

sudo ipfw -q add 00100 count udp from me to any out via em0
sudo ipfw -q add 00200 count udp from any to me in via em0

Как видите, тут демонстрируется не только новое действие (count), но и расширенный синтаксис с частью in/out и указанием конкретного сетевого интерфейса. Кстати, при срабатывании count-правила следующие за ним правила продолжают применяться.

Теперь посмотрим количество пакетов с их суммарным размером в байтах, соответствующих правилу:

sudo ipfw -a list
# или:
sudo ipfw show

Счетчики можно при желании обнулить так:

# для конкретного правила
sudo ipfw zero 00200

# для всех правил
sudo ipfw zero

В правилах можно использовать не только слова me и any, но и указывать конкретные хосты или подсети:

sudo ipfw -q add 00100 deny tcp from any to 192.168.0.1
sudo ipfw -q add 00200 deny tcp from any to 192.168.0.1/24

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

sudo ipfw -q add 00100 deny tcp from any to 192.168.0.1/24 80
sudo ipfw -q add 00200 deny tcp from any to 192.168.0.1/24 1-1024

Помимо сбора статистики о срабатывании правил, их также можно логировать:

sudo ipfw enable verbose

Пример добавления логируемых правил:

sudo ipfw -q add 00200 deny log \
  tcp from any to 212.193.240.1/24 80

sudo ipfw -q add 00200 deny log logamount 3 \
  tcp from any to 212.193.240.1/24 80

Если теперь несколько раз попытаться прителнетиться к хосту из используемой выше сети и заглянуть в /var/log/security, то увидим что-то вроде:

ipfw: 200 Deny TCP 10.0.2.15:41738 212.193.240.242:80 out via em0
ipfw: 200 Deny TCP 10.0.2.15:43564 212.193.240.242:80 out via em0
ipfw: 200 Deny TCP 10.0.2.15:55804 212.193.240.242:80 out via em0
ipfw: limit 3 reached on entry 200

Логирование можно глобально включать и выключать таким образом:

sudo sysctl net.inet.ip.fw.verbose=1
sudo sysctl net.inet.ip.fw.verbose=0

Если в правиле не указывается logamount, используется следующее значение:

sudo sysctl net.inet.ip.fw.verbose_limit=5

Значение по умолчанию равно нулю, что означает отсутствие ограничений.

Сбросить счетчики логирования можно так:

sudo ipfw resetlog

Для сохранения правил при перезагрузке системы создадим файл /etc/ipfw.rules следующего содержания:

#!/bin/sh

fwcmd="ipfw -q add"

ipfw -q -f flush

$fwcmd 00100 deny log tcp from any to 1.2.3.0/24
$fwcmd 00200 deny log tcp from any to 4.5.6.0/24
$fwcmd 65000 allow ip from any to any

Важно! Если хотите подключиться к машине после перезагрузки, обязательно добавьте последнее правило. Сейчас оно добавляется автоматически, благодаря описанному выше хаку с net.inet.ip.fw.default_to_accept. Однако после ребута вернется поведение фаервола по умолчанию, при котором запрещается вообще все.

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

sudo sh /etc/ipfw.rules
sudo ipfw list

Если все ОК, в /etc/rc.conf дописываем:

firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"

Если решили включить логирование, убедитесь, что вы не забыли дописать в /etc/sysctl.conf что-то вроде:

net.inet.ip.fw.verbose_limit=5

Проверяем:

sudo kldunload ipfw
sudo service ipfw start
sudo ipfw list

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

Это все, о чем я хотел рассказать. Подробности вы найдете в man 8 ipfw. Маны во FreeBSD, как всегда, классные. В частности, из мана вы узнаете про такие элементы синтаксиса правил ipfw, оставшиеся за рамками поста, как skipto, tag / untag и другие.

Неправда ли, синтаксис в ipfw куда более читаемый, чем вот эти все -A, -p, -j, --sport и -m multiport в iptables?

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.