Для начала разберемся, что такое файрвол. Это подсистема, выполняющая обработку трафика, проходящего через интерфейсы машины. Определение не совсем академическое, но, надеюсь, понятное. Под обработкой может подразумеваться фильтрация пакетов на основании определенных правил (что пропустить, а что отбросить), модификация пакетов (например, подмена IP-адресов в ходе NAT-трансляции), перенаправление пакетов (форвардинг), контроль использования доступной полосы пропускания (трафик-шейпинг).
Изначально файрволы проектировались для работы на сетевом и транспортном уровнях модели OSI, то есть фильтрация выполнялась на основе информации, которую можно было получить из заголовков IP- и TCP/UDP-пакетов. Однако зачастую файрволы вторгаются и на другие уровни стека: канальный уровень (фильтрация по MAC-адресам), прикладной уровень (использование специфических для FTP-пакетов параметров) и т.д.
FreeBSD готова предложить тебе целых три файрвола, на любой вкус: родной IPFirewall (ipfw), присутствующий в ней с доисторических времен; IPFilter (ipf), разрабатываемый как независимый продукт и доступный для целой плеяды ОС (разве что Windows осталась обделенной); и PacketFilter (pf), портированный из OpenBSD, слава о безопасности которой уже, наверное, достигла ближайших к нам обитаемых миров. Поддержка ipf появилась во FreeBSD 4.0, pf чуть позже - начиная с 5.3. На границе четвертой и пятой ветвей претерпел некоторые изменения и доморощенный ipfw, предоставив пользователям ряд дополнительных возможностей. В 5.х новая версия стала доступна под именем ipfw2.
Поскольку сейчас проблемы стабильности пятой ветки в версиях 6.x успешно преодолены, то нет особого смысла ставить более древние версии. Так что будем считать, что «из коробки» тебе доступны все три файрвола - ipfw/ipfw2, ipf и pf.
Все включено
Инсталляция - это самый простой вопрос. Все три рассматриваемых фильтра включены в базовую поставку FreeBSD, и для их использования требуется либо загрузить модули, либо вкомпилировать их поддержку в ядро. Лично мне больше по душе второй путь (видимо, привычка), тем более что поддержка некоторых дополнительных опций (например, очередей ALTQ) возможна только из ядра.
Для того чтобы подгружался тот или иной модуль, достаточно в /etc/rc.conf указать соответствующие опции (о них будет сказано ниже).
Подсказки о том, как собрать поддержку этих файрволов в ядре, ты найдешь в /usr/src/sys/conf/NOTES. Основные опции:
### ipfw
options IPFIREWALL # поддержка файрвола
options IPFIREWALL_VERBOSE # поддержка логирования
options IPDIVERT # поддержка divert-сокетов (нужна для natd)
options DUMMYNET # поддержка шейпера dummynet
### ipf
options IPFILTER # поддержка файрвола
options IPFILTER_LOG # поддержка логирования
### pf
device pf # поддержка файрвола
device pflog # поддержка логирования
device pfsync # отслеживание состояния
options ALTQ # поддержка шейпера ALTQ
Инструментарий и конфигурационные файлы
Для инициализации файрволов в системе предусмотрены соответствующие сценарии: /etc/rc.d/{ipfw,ipfilter,pf}. Как и для обычных сервисов, эти скрипты воспринимают команды start и stop. Например, команда /etc/rc.d/ipfw start приводит к следующим действиям: проверяется, доступен ли этот фильтр (в данном случае по переменной sysctl net.inet.ip.fw.enable), и если нет, то предпринимается попытка загрузить модуль ipfw.ko; согласно настройкам в rc.conf, исполняется тот или иной сценарий инициализации (обычно текущий список правил очищается и загружается исходный из указанного в конфиге файла). Аналогично работают и другие сценарии.
Для управления ipfw существует одноименная утилита /sbin/ipfw. Фактически, с ее помощью можно полностью контролировать работу этого файрвола. Подробно с ее синтаксисом можно ознакомиться на странице man ipfw(8). Наиболее часто используемые команды: add (добавить правило), delete (удалить правило), flush (полностью очистить таблицу правил), show (показать текущие правила со счетчиками).
Фильтр ipf управляется семейством более специализированных программ: /sbin/ipf (работа с правилами фильтрации), /sbin/ipfstat (вывод статистики), /sbin/ipmon (сбор логов), /sbin/ipnat (работа с правилами NAT) и т.д. Подробности - на соответствующих man-страницах.
Практически вся работа с фильтром pf выполняется утилитой /sbin/pfctl (смотри man pfctl(8)). Насколько я понял, pfctl (как и ipf) не позволяет работать с отдельными правилами фильтрации прямо из командной строки (как в случае ipfw), но чаще всего возможности загружать правила из файла более чем достаточно (в OpenBSD 4.х это ограничение снято - примечание редактора).
Во FreeBSD основным конфигурационным файлом, отвечающим за работу операционной системы в целом, и за старт соответствующих файрволов в том числе, является /etc/rc.conf. Обычно задаются:
### ipfw
firewall_enable="YES" # поддержка файрвола
firewall_type="/etc/ipfw.rules" # тип (способ инициализации)
natd_enable="YES" # поддержка natd
### ipf
ipfilter_enable="YES" # поддержка файрвола
ipfilter_rules="/etc/ipf.rules" # файл с правилами
ipnat_enable="YES" # поддержка ipnat
ipnat_rules="/etc/ipnat.rules" # файл с правилами NAT
### pf
pf_enable="YES" # поддержка файрвола
pf_rules="/etc/pf.rules" # файл с правилами
Файлы, указанные в примере, содержат правила фильтрации - именно отсюда происходит инициализация фильтра при загрузке (подробности смотри в документации). Поддержка NAT включается лишь в случае необходимости. Правила для сбора логов опущены (на сей счет смотри /etc/defaults/rc.conf).
Простейший пример
Начнем с чего-нибудь простенького и классического. Здесь и далее будем считать, что rl0 - внешний сетевой интерфейс, а ed0 - внутренний. Например, запретим любой входящий трафик на внешний интерфейс, кроме идущего на порты 80 и 8080; через ed0 разрешим все (здесь и далее правила даются в том виде, в котором они будут заноситься в rules-файлы):
add 1000 allow tcp from any to 10.10.10.10 80,8080 via rl0
add 1010 allow tcp from 10.10.10.10 to any via rl0
add 1100 allow all from any to any via ed0
add 2000 deny all from any to any
// ipf (запуск без перезагрузки: ipf -Fa -f /etc/ipf.rules)
block in on rl0 all
block out on rl0 all
pass in on rl0 from any to 10.10.10.10 port = 80
pass in on rl0 from any to 10.10.10.10 port = 8080
pass out on rl0 from 10.10.10.10 to any
pass in on ed0 all
pass out on ed0 all
// pf (запуск без перезагрузки: pfctl -R -f /etc/pf.rules)
block on rl0 all
pass in on rl0 proto tcp from any to 10.10.10.10 port {80,8080}
pass out on rl0 from 10.10.10.10 to any
pass on ed0 all
Сразу отметим две вещи. Во-первых, синтаксисы ipf и pf во многом схожи (хотя есть и детали - в ipf нельзя опускать параметр in/out; pf требует указания протокола, если используется фильтрация по порту и т.д.) и заметно отличаются от такового в ipfw. Во-вторых, важно помнить о порядке срабатывания правил. В ipfw пакет проходит по списку до первого соответствия, после чего к нему сразу применяется указанное действие. В ipf и pf действие, соответствующее правилу, запоминается, а пакет продолжает свое движение по списку, и последующие правила могут переопределить действие предыдущих. То есть в этом случае применяться будет последнее совпавшее правило. Это можно переопределить с помощью ключевого слова quick, которое отменяет дальнейшую проверку.
Особенности посложнее
Как ни странно, но многие умудряются довольствоваться показанными выше простейшими правилами. Однако мощь и гибкость файрволов проявляются в более сложных вещах. Рассмотрим некоторые примеры.
При работе по протоколу TCP сначала устанавливается соединение и затем в рамках этого соединения идет обмен пакетами со схожими характеристиками. Причем если файрвол пропускает первый пакет, то и все последующие в обычной жизни тоже должны быть пропущены. А раз так, то какой смысл прогонять каждый пакет 700-мегабайтного iso-файла по всей цепочке правил, чтобы убедиться в том, что и так уже очевидно? В общем-то, никакого, и потому все три рассматриваемых файрвола поддерживают концепцию «установленного соединения»:
// ipfw
add check-state
add allow tcp from any to any out setup via rl0 keep-state
// ipf
pass out on rl0 proto tcp from any to any flags S keep state
// pf
pass out on rl0 proto tcp from any to any flags S/SA keep state
Так, мы разрешаем соединения, инициированные «изнутри» сети. Обрати внимание, что для ipfw ты можешь поставить правило check-state в любом месте (до него пакеты, принадлежащие установленным соединениям, будут обрабатываться на общих основаниях; если забудешь его указать, динамические правила сработают на первом keep-state), в то время как pf и ipf автоматически вставляют его в начало цепочки. Здесь же демонстрируется способность фильтров работать с флагами заголовков TCP-пакетов.
Натинг, форвардинг и редирект
Еще одной популярной функцией современных файрволов является трансляция сетевых адресов (NAT). В ipfw натинг как таковой не поддерживается и реализуется с помощью внешнего демона natd через divert-сокеты. А ipf и pf реализуют NAT-преобразования непосредственно. Примеры:
// ipfw (должен быть запущен natd на порту 8668)
add divert 8668 ip from 192.168.0.0/24 to any out via rl0
add divert 8668 ip from any to 200.200.200.200 in via rl0
// ipf (правила выносятся в ipnat.rules, запускаются командой ipnat)
map rl0 192.168.0.0/24 -> 200.200.200.200/32
// pf (размещаются в общей конфигурации перед правилами фильтрации)
nat on rl0 from 192.168.0.0/24 to any -> 200.200.200.200/32
Для ipfw, как видишь, используется divert на порт 8668, и нужно самому думать о переброске на этот порт входящих и исходящих пакетов. В ipf и pf все заметно проще - укажи одно правило, а об остальном фильтр позаботится сам. Только не забывай в случае ipf подключать еще и ipnat в /etc/rc.conf (смотри выше). Кстати, nat-правила будут выполняться перед любыми другими - ipnat можно рассматривать как отдельный фильтр, работающий до ipf. Pf и ipf поддерживают также двунаправленную трансляцию между внешним и внутренним адресами - bimap и binat соответственно.
Иногда возникает необходимость выполнить «проброс» внешнего соединения на внутренние адреса. Для этого в ipf и pf используются правила редиректа rdr, в случае ipfw приходится использовать redirect-опции демона natd. Все файрволы поддерживают форвардинг - перенаправление пакетов на произвольный шлюз. В ipfw для этого используется действие fwd, в pf/ipf - опции route-to/fastroute и reply-to.
С NAT-трансляцией связана еще одна вещь - проксирование FTP. Если ты настраивал FTP-сервер, то помнишь, сколько из-за файрвола приходится приложить усилий, чтобы заставить работать пассивный режим (а активный из-за тех же файрволов не сильно любят клиенты). Конечно, открыть верхние порты - самый простой выход, но IPFilter предоставляет для этих целей функцию проксирования соединений на прикладном уровне: он будет анализировать проходящие пакеты и открывать нужные порты динамически, по мере необходимости. Это реализуется добавлением «proxy port ftp ftp/tcp» в конец map-правила (в pf придется использовать штатную программу ftp-proxy). Не путай это с опцией synproxy файрвола pf, которая отвечает за проксирование TCP-соединений (то есть когда удаленная сторона сначала полностью устанавливает соединение с файрволом, и лишь затем оно перебрасывается адресату).
Трафик-шейпинг
Продвинутой фишкой современных файрволов является возможность управлять доступной полосой пропускания. В ipfw это делается с помощью интерфейса dummynet, в pf - посредством ALTQ. У ipf я поддержки шейпинга не обнаружил, хотя упоминания, что он умеет работать с ALTQ-очередями, встречаются. Например, так можно решить задачу ограничения доступной полосы пропускания (по входящему трафику) до 128 кбит/с:
// ipfw
add pipe 1 ip from any to 192.168.0.0/24
pipe 1 config bw 128Kbit/s
// pf
altq on rl0 cbq queue q_limited
queue q_limited bandwidth 128Kb cbq(default)
pass quick from any to 192.168.0.0/24 keep state queue q_limited
Кстати говоря, ipfw тоже умеет работать с очередями ALTQ (правда, ограниченно). Но для управления самими очередями по-прежнему нужно использовать утилиту pfctl. Также нужно отметить в ipfw поддержку опции prob, которая позволяет отбирать из потока правила с некоторой долей вероятности. Например, правило «add prob 0.2 deny ip from any to any» случайным образом отбросит 20% всего трафика. В частности, это можно использовать для имитации канала с потерями. Аналогичные задачи в pf решаются опцией probability.
Прочие особенности
Из дополнительных функций рассматриваемых файрволов можно отметить следующие:
возможность фильтрации по MAC-адресам; стоит отметить, что на сегодняшний день ее поддерживает только ipfw (в OpenBSD при использовании связки bridge + pf фильтрация по MAC-адресам также возможна, смотри мою статью «Файрвол-невидимка» в июльском номере журнала за 2005 год - примечание редактора);
балансировка исходящих соединений между несколькими интерфейсами (в pf и ipf поддерживается алгоритм round-robin, когда использующие его несколько правил динамически меняются местами в процессе работы);
ipfw и pf способны отслеживать принадлежность сокетов конкретным пользователям и таким образом осуществлять фильтрацию на основе UID/GID владельца соединения.
Ipfw поддерживает также признак jail - правило будет применено только в случае, если пакет принадлежит указанной «тюрьме». Фильтр pf предоставляет уникальную возможность выполнить нормализацию пакетов одним правилом – scrub; также есть antispoof - защита от подмены адресов (опция antispoof работает и в ipfw). Все три фильтра (с поправкой на синтаксис и некоторые детали) умеют работать с различными полями IP-, TCP/UDP-заголовков, включая флаги.
Фильтр pf, помимо всего прочего, поддерживает адресные пулы, которые удобны для балансировки нагрузки между несколькими соединениями и для NAT-трансляции в больших сетях. Также может оказаться полезной функция тегирования - маркировки пакетов с помощью специальных тегов и последующая их обработка по этим тегам. Кроме того, pf умеет определять операционную систему источника пакетов! То есть ты можешь пропускать трафик с *nix-машин и блокировать все пакеты Windows-клиентов.
Из синтаксических особенностей отмечу, что и pf, и ipfw позволяют работать с таблицами – это очень удобный способ группировки различных IP-адресов. Плюс к этому, pf дает возможность использовать списки (при обработке они автоматически развернутся в нужное число правил) и макросы.
Итоги
Как видишь, все три файрвола во FreeBSD достаточно функциональны и удобны в работе. Судя по высказываниям, встречающимся в интернете, большинство отдает предпочтение фильтру pf, как более быстрому, мощному и комфортному в работе. С этим трудно не согласиться, хотя ipfw предоставляет некоторые функции, отсутствующие в других файрволах (фильтрацию по MAC, divert-сокеты). С другой стороны, pf выглядит более удобным при работе с очередями (но при этом требуется пересобрать ядро; а вот dummynet можно подгрузить модулем) и особенно для NAT-преобразований. Несомненными плюсами являются нормализация пакетов, антиспуфинг и целый арсенал самых различных «тонких настроек». IPFilter хорош в случае, когда приходится администрировать парк разношерстных систем - есть возможность работать с одним и тем же файрволом (я обычно использую ipf в NetBSD, Solaris и QNX - примечание редактора).
Если говорить с точки зрения удобства синтаксиса, то это во многом вопрос личных предпочтений. Таблицы существенно упрощают администрирование крупных сетей, а списки и макросы pf порой могут очень пригодиться. Зато интерактивность ipfw, позволяющая менять состав правил на лету без полной их перезагрузки, весьма комфортна для разного рода экспериментов.
Кстати, все три файрвола превосходно уживаются вместе. Ты даже можешь часть из них вкомпилировать в ядро, а часть - оставить модулями. Отбрасывается пакет любым фильтром безапелляционно, в то время как для попадания в систему ему нужно получить «одобрям-с» от всех подключенных фильтров. Это позволяет издеваться над трафиком в высшей степени изощренно, заставляя каждый файрвол делать то, что у него получается наилучшим образом.