Заметки об IPFW. Зацените, да и вообще нужно ли.
Вместо предисловия:
Вопрос- "В FreeBSD есть 3 разных фаервала, какой использовать?"
Ответ - "Правильно насторенный."
Далее по тексту обсуждается IPFW как наиболее часто используемый во FreeBSD фаервал.
Как задействовать IPFW?
Как известно существует два способа подключения IPFW:
1. Подключение скомпилированного модуля ядра при загрузке системы.
2. Комплияция IPFW в ядро системы.
Начнем с последнего - компиляция IPFW в ядро, в MAN-ах этот пункт достаточно подробно освещен:
Обычно ипользуются следующие опции в конфиге ядра:- подключение IPFWКод: Выделить всё
options IPFIREWALL
- проходящие пакеты записываются в лог-файл, при использовании опции log в правилахКод: Выделить всё
options IPFIREWALL_VERBOSE
ограничение количества записей в лог-файл по одному правилу, в правилах IPFW значение можно изменить через опцию logamountКод: Выделить всё
options IPFIREWALL_VERBOSE_LIMIT=100
- форвардинг пакетов, очень полезная опция при настройке прозрачного прокси на машине, где одновременно работает IPFW и прокси-сервер (например SQUID или FROX)Код: Выделить всё
options IPFIREWALL_FORWARD
- подключение поддержки NATD (трансляция адресов)Код: Выделить всё
options IPDIVERT
- поключение функций управления трафиком (ограничение ширины канала, имитация задержек и потерь пакетов), и наконец для правила по умолчанию, которое присутствует в конфиге IPFW в любом случае под номером 65535, будетКод: Выделить всё
options DUMMYNET
- если включена опцияКод: Выделить всё
allow ip from any to any
- либо,Код: Выделить всё
options IPFIREWALL_DEFAULT_TO_ACCEPT
- если отсутствует.Код: Выделить всё
deny ip from any to any
После сборки и установки нового ядра получаем IPFW встроенный в ядро системы.
Теперь о подключение модулем, тут все проще и сложнее одновременно.
Подключение IPFW в качестве модуля ядра, осуществляется одной командой:- при этом загружается модуль ipfw.ko, который в стандартной поставке имеет поддержку функций управления трафиком (DUMMYNET), к сожалению функции форвардинга (FORWARD) не поддерживаются и без перекомпиляции тут не обойтись.Код: Выделить всё
kldload ipfw
Поддержку демона NATD в IPFW можно получить, загрузив аналогичной командой модуль ipdivert.ko.Код: Выделить всё
kldload ipdivert
IPFW, загруженный таким образом, содержит всего одно правило по умолчанию:.Код: Выделить всё
deny ip from any to any
Рассмотрим теперь как происходит подключение IPFW в процессе загрузки операционой системы. Имеем достаточно типовые переменные в файле /etc/rc.confПервая строка фактически разрешает исполнение стартового скрипта /etc/rc.d/ipfw, который в свою очередь, выполняет уже известную командуКод: Выделить всё
firewall_enable="YES" firewall_script="/usr/local/etc/ipfw.rules" natd_enable="YES" natd_interface="fxp0" natd_flags="-same_ports"
, если IPFW грузится модулем ядра, затем запускает на выполнение скрипт с правилами IPFW, указанный во второй строке. Заметим что строкаКод: Выделить всё
kldload ipfw
требуется как для загрузки IPFW в виде модуля (иначе запускать придется вручную), так и для IPFW компиллированном в ядро (иначе не выполнится скрипт с правилами из второй строки, хотя IPFW все равно запустится с одним правилом по умолчанию). В процессе выполнения скрипт /etc/rc.d/ipfw установит значение системной переменной:Код: Выделить всё
firewall_enable="YES"
которая указывает системе использовать ли IPFW вообще, так как если переменная равна нулю (FALSE), то IPFW использоваться не будет, независимо от того подгружем ли мы модулем IPFW или он вкомпилирован в ядро, попутно отметим следующее: все переменные, которыми можно управлять через sysctl действуют на IPFW одинаково, независимо от способа подключения IPFW.Код: Выделить всё
sysctl net.inet.ip.fw.enable=1
строка указывает расположение нашего скрипта с правилами для IPFW, в принципе её может и не быть, тогда запустится на исполнение стандартный скрипт /etc/rc.firewall, в котором есть стандарные наборы правил, сгруппированные в секции "OPEN","SIMPLE","CLOSED","UNKNOWN", можно также приспособить его под свои нужды (хотя это и не наш метод :-)), например добавив секцию "RULES", тогда строка приобретет вид:Код: Выделить всё
firewall_script="/usr/local/etc/ipfw.rules"
.Код: Выделить всё
firewall_type="RULES"
По поводу строк с NATD сказать особо нечего, все аналогично уже рассмотренному с той разницей что исполняется скрипт /etc/rc.d/natd, и если IPFW используется в виде модуля ядра -грузится ipdivert.ko,
а потом скрипт выполнит команду вида:, и ещё если IP интефейсa на котором крутится NATD получен от DHCP-сервера, скрипт добавит опцию "-dynamic".Код: Выделить всё
natd -interface ${natd_interface} -same_ports
Замечание 1.
Как все-таки подключать IPFW? Пожалуй решение такое: если FreeBSD используется в целях обучения, отладки и т.д. - проще подключать модулем, а если речь идет уже о "боевом" применении - лучше компиляция в ядро.
Как IPFW работает?
Общая схема Замечание 1.
IP- Пакет, попадая в фаервал IPFW, следует согласно порядку расположений его правил до первого удовлетворяющего, где над этим IP-пакетом, согласно данному правилу, совершаются какие-либо действия (пропускается, отбрасывается, возвращается обратно в фаервал и т.д.).
Замечание 2.
Входящие (IN) и исходящие (OUT) пакеты следует рассматривать относительно операционной системы, а не относительно сетевых интерфейсов.
Замечание 3.
Каждый маршрутизируемый IP-пакет попадает в фаервал как на входе в операционную систему, так и на выходе из неё.
Рассмотрим, с учетом этих замечаний, следующие примеры и попытаемся в итоге понять проходят IP-пакеты по правилам IPFW и как составлять эти самые правила.
Предположим:
1. Система имеет 2 сетевых интерфейса – {iif}- внутренний(fxp0) и {oif}-внешний(fxp1), псевдоинтефейc-{lo}, который мы намеренно опустим из виду.
2. {iip} – IP- адрес для {iif}, и соответственно {oip} – для {oif}.
3. {MyLan} - внутренняя сеть
4. В системе работает простейший демон NATD по трансляции внутренних адресов во внешний и наоборот.
Пример №1. Как составлять правила?
Имеем следующее не совсем очевидное, зато очень часто встречающееся правило:Обычно когда пишут такое правило - подразумевают что разрешен доступ от хостов внутренней сети к любому хосту (при этом, часто полагают что речь идет только о внутренней сети :-) ), но есть и еще одна сторона медали, постараюсь её показать.Код: Выделить всё
{fwcmd} add allow ip from {MyLan} to any
С учетом вышеописанных замечаний данное правило распишем в следующий набор правил:Правило №1 - мы разрешаем обращение от любого хоста внутренней сети на любой xoст любой сети через внутренний интерфейс, аналогично и для правила №3, но через внешний интерфейс.Код: Выделить всё
1. {fwcmd} add allow ip from {MyLan} to any in via {iif} 2. {fwcmd} add allow ip from {MyLan} to any out via {iif} 3. {fwcmd} add allow ip from {MyLan} to any out via {oif} 4. {fwcmd} add allow ip from {MyLan} to any in via {oif}
Фактически эти два правила разрешают исходящие IP-пакеты от хостов внутренней сети к любым хостам любой сети.
А теперь о том, что не так очевидно: правило №2, с учетом правила №1 фактически разрешает IP-пакеты между хостами локальной сети через интерфейс {iif}, что нормально, если предположить что в локальной сети хакеров нет, пользователи послушные, сами на роутер не полезут и делать там нечего плохого не будут. Однако сюрприз!
Теперь правило №4 - с одной стороны оно достаточно бессмысленное. Действительно откуда снаружи взяться IP-пакетам от хостов внутренней сети?
Правильно – хакеры, типичный спуфинг.
Критики могут заметить, что со спуфингом умеет бороться чуть ли не каждая железяка, что демоны давно поумнели и т.д. и т.п. Тем не менее, согласитесь, такие правила боевой роутер совсем не красят, поэтому сюрприз №2.
Можно уже делать первые выводы:
Вывод №1.
При составлении правил обязательно указывать о какой сетевом интерфейсе идет речь.
Вывод №2.
При составлении правил желательно указывать направление IP-пакетов.
Вывод №3.
При составлении правил по возможности конкретизировать сети и IP - адреса.
Первые выводы сделаны, давайте попробуем разобраться как IP-пакеты бегают по правилам, которые мы напишем.
Пример №2. Связка IPFW-NATD, как работает, какие нужны правила?
Итак: исходные заданы, напишем правила IPFW, и прокомментируем их.Получили такой вот конфиг IPFW, хотя могли обойтись всего-то одной строчкойКод: Выделить всё
#Разрешим трафик в локальной сети, конкретизировать по направлению не будем и так понятно: 1 {fwcmd} add allow ip from {MyLan} to {MyLan} via {iif} #Вспомним про NATD. 2. {fwcmd} add divert natd ip from {MyLan} to any out via {oif} 3. {fwcmd} add divert natd ip from any to {oip} in via {oif} #Роутер должен же как-то работать - изнутри мы уже все открыли, будем последовательны тоже сделаем и снаружи. 4. {fwcmd} add allow ip from {oip} to any out via {oif} 5. {fwcmd} add allow ip from any to {oip} in via {oif} #Роутер у нас или нет? Давайте разрешим локальным хостам свободно лазить во внешнюю сеть. #Для исходящих пакетов от хостов внутренней сети. 6. {fwcmd} add allow ip from {MyLan} to any in via {iif} 7.{fwcmd} add allow ip from {MyLan} to any out via {oif} #Для входящих IP-пакетов к хостам внутренней сети. 8. {fwcmd} add allow ip from any to {MyLan} in via {oif} 9. {fwcmd} add allow ip from any to {MyLan} out via {iif} #Для порядка завершим 10. {fwcmd} add deny ip from any to any
Примечание - Фактически правило №1 не нужно, т.к. его расширили правилами №6,№9, однако оставим его, поскольку в реальных условиях надо редактировать как раз с №6 по №8, да и само по себе правило №1 не самое удачное решение в плане широковещательных запросов.Код: Выделить всё
{fwcmd} add allow ip from any to any
Пусть хост внутренней сети (192.168.0.75) запрашивает почту с gmail.com (64.233.183.109:995), для нашего роутера внешний IP- 195.34.32.55.
Забудем про DNS, сразу к делу - какими видит IPFW проходящие пакеты:
Правила №-№ 1-5 не подходят, а вот №6 как раз в точку:- так пошел запрос на внутренний интерфейс (fxp0) роутера, с хоста внутренней сети. Операционная система приняла его и по таблице маршрутизации отправила на внешний интерфейс (fxp1), здесь этот пакет стал исходящим и снова уперся в фаервал:Код: Выделить всё
TCP 192.168.0.75:1499 64.233.183.109:995 in via fxp0
– тут вступает в действие NATD (правило №2) он переписывает IP в заголовке пакета, составляет таблицу где запоминает что он сотворил с пакетом и благополучно отпускает путешествовать по правилам IPFW дальше, что собственно будет выглядеть так:Код: Выделить всё
TCP 192.168.0.75:1499 64.233.183.109:995 out via fxp1
- кстати NATD сохранил еще и первоначальный порт 1499, что бывает далеко не всегда. Этот пакет дойдет до правила №4, благополучно покинет фаервал и отправится путешествовать в сеть.Код: Выделить всё
TCP 195.34.32.55:1499 64.233.183.109:995 out via fxp1
Теперь рассмотрим как идет ответ сервера:
Ответный пакет входит в файервал снаружи через внешний интерфейс fxp1:- как видно с какого порта запросили на тот ответ и получили. Правила №1,№2 он благополучно миновал а в правиле №3 его радостно встречает NATD, который проверяет свою таблицу, находит запись и переписывает заголовок уже на:Код: Выделить всё
TCP 64.233.183.109:995 195.34.32.55:1499 in via fxp1
– по правилу №7 пакет выходит из IPFW и попадает в операционную систему, которая маршрутизирует пакет на интерфейс внутренней сети (fxp0), здесь пакет опять упрется в фаервал но уже, как исходящий с интерфейса внутренней сети fxp0.Код: Выделить всё
TCP 64.233.183.109:995 192.168.0.75:1499 in via fxp1
– правило №9 благополучно выпустит пакет из фаервала. Счастливый хост получил ответ на свой запрос.Код: Выделить всё
TCP 64.233.183.109:995 192.168.0.75:1499 out via fxp0
Дайте рассмотрим ещё один случай связки IPFW-NATD - перенаправление входящего запроса на сервер внутренней сети.
Предположим мы хотим открыть для доступа снаружи Web-сервер внутренней сети с IP-адресом 192.168.0.3, для чего изменим в rc.conf строку на.В логах IPFW можно будет увидеть примерно следующее:Код: Выделить всё
natd_flags="-same_ports -redirect_port tcp 192.168.0.3:80 80"
Ситуация аналогична уже рассмотренной:Код: Выделить всё
1. TCP 195.34.32.56:1076 195.34.32.55:80 in via fxp1 2. TCP 195.34.32.56:1076 192.168.0.3:80 in via fxp1 3. TCP 195.34.32.55:1076 192.168.0.3:80 out via fxp0 4. TCP 192.168.0.3:80 195.34.32.56:1076 in via fxp0 5. TCP 192.168.0.3:80 195.34.32.56:1076 out via fxp1 6. TCP 195.34.32.55:80 195.34.32.56:1076 out via fxp1
первая строка и вторая строка - один и тот же пакет - входящий запрос, который попадает в NATD через внешний интрефейс fxp1. NATD переписывает заголовок пакета и далее по правилам IPFW пакет попадает в операционную систему.
третья строка - пакет прошедший по таблице маршрутизации операционной системы, исходящий через внутренний интерфейс fxp0.
четвертая строка - ответ сервера входящий на внутренний интерфейс fxp0.
пятая и шестая строки - один и тот же пакет, прошедший через NATD и ставший исходящим через внешний интерфейс fxp1.
Примечание: Относительно DIVERT в MAN-е по IPFW присутствует присутствует некая двусмысленность, согласно MAN-у для пакета попавшего под правило с DIVERT дальнейший поиск прекращается, а что происходит с пакетом потом - неясно.
Экпериментами установлено, что в случае трансляции адресов, пакет прошедший через DIVERT-сокет, продолжает путь по правилам IPFW сразу после правила с DIVERT, имея переписаный (транслированный) IP в заголовке.
Чтобы закончить со связкой IPFW-NATD, составим примерный список правил для конфига IPFW, обеспечения работоспособности этой связки :Ну и последнее замечание по поводу IPFW-NATD:Код: Выделить всё
#Разрешаем все по интерфейсу обратной петли {fwcmd} add allow ip from any to any via lo #Разрешаем все внутри локальной сети, кроме того эти правила необходимы для связки IPFW-NATD #Для защиты роутера от пользователей локальной сети перед этими правилами нужно втавить что-то запрещающее #Если есть необходимость роутеру в широковещательных запросах, нужно вставить что-то разрешающее {fwcmd} add allow ip from {MyLan} to any in via {iif} {fwcmd} add allow ip from any to {MyLan} out via {iif} #Разрешаем входящие соединения к роутеру, это правило напрямую к связке IPFW-NATD отношения не имеет, оно необходимо для обеспечения работоспособности демонов роутера. #Естественно правило необходимо расширить с учетом потребностей демонов. {fwcmd} add ip from any to {oip} 53 in via {oif} {fwcmd} add ip from any 53 to {oip} in via {oif} #NATD для исходящих соединений {fwcmd} add divert natd ip from {MyLan} to any out via {oif} #Внимание!!! Правило - разрешает исходящие соединения как для самого роутера, так и для хостов внутренней сети, IP которых транслировал NATD {fwcmd} add allow ip from {oip} to any out via {oif} #NATD для входящих соединений {fwcmd} add divert natd ip from any to {oip} in via {oif} #Внимание!!! Правило - разрешает входящие соединения только для хостов внутренней сети, IP которых транслировал NATD {fwcmd} add allow ip from any to {MyLan} in via {oif} {fwcmd} add deny ip from any to any
Что делать если требуется что-то прикрыть в интернете для хостов внутренней сети? Ответ достаточно очевиден:
1. Впереди правил для локальной сети пишем свои запреты
Ну и наоборот если требуется все запретить, разрешив только что-то: к примеру HTTP?
2. Переписываем строки 2-3 к следующему виду:Полагаю тема по связке IPFW-NATD закрыта.Код: Выделить всё
{fwcmd} add allow ip from {MyLan} to {MyLan} via {iif} {fwcmd} add allow tcp from {MyLan} to any http in via {iif} {fwcmd} add allow tcp from any http to {MyLan} out via {iif}
Продолжим исследования, разберем еще один частый случай IPFW-SQUID.
Сначала без прозрачного прокси. Вернемся к первому варианту конфига для IPFW и рассмотрим что происходит. Вот и подходящий кусочек лога:-входящий пакет от внутреннего хоста к роутеру с работающим Squid - правило №1, Squid его обработает и сам отправит в сеть.Код: Выделить всё
TCP 192.168.0.112:1212 192.168.0.9:3128 in via fxp0
-исходящий пакет от SQUID к удаленному хосту - правило №4Код: Выделить всё
TCP 195.34.32.55:52439 64.12.24.102:443 out via fxp1
- входящий ответ на интерфейс внешней сети - правило № 5, Squid опять же его обработал, и ответил хосту локальной сетиКод: Выделить всё
TCP 64.12.24.102:443 195.34.32.55:52439 in via fxp1
- правило №1, пакет вышел из фаервала и побежал к адресату.Код: Выделить всё
TCP 192.168.0.9:3128 192.168.0.112:1212 out via fxp0
Пример несколько утрированный, однако интересен следующим:
Первое - NATD не используется, а вернее так: в NATD входящий пакет от хоста 64.12.24.102:443 все равно попадет, правило №3 никто не отменял. Однако NATD об этом пакете ничего не знает, поэтому делать ничего не будет, он отправит пакет дальше путешествовать по правилам IPFW, пока последний не доберется до правила №4.
Второе - сам адресат: 64.12.24.102:443 - протокол https, а хост из сети 64.12.0.0/16 AOL-MTC, т.е. ICQ.
Продолжим: теперь на очереди прозрачный прокси. Изменим правила IPFW, добавив строку для форвардинга, в итоге получим следующее правила:Предположим хост внутренней сети с IP 192.168.0.100 запрашивает http://www.yandex.ru, на входе в фаервал имеем:Код: Выделить всё
1. {fwcmd} add allow ip from {MyLan} to {MyLan} via {iif} 2. {fwcmd} add fwd {iip},3128 tcp from {MyLan} to not {MyLan} 80 in via {iif} 3. {fwcmd} add divert natd ip from {MyLan} to any out via {oif} 4. {fwcmd} add divert natd ip from to any to {oip} in via {oif} 5. {fwcmd} add allow ip from {oip} to any out via {oif} 6. {fwcmd} add allow ip from any to {oip} in via {oif} 7. {fwcmd} add allow ip from {MyLan} to any in via {iif} 8. {fwcmd} add allow ip from {MyLan} to any out via {oif} 9. {fwcmd} add allow ip from any to {MyLan} in via {oif} 10. {fwcmd} add allow ip from any to {MyLan} out via {iif} 11. {fwcmd} add deny ip from any to any
Как видим для хоста внутренней сети все махинации прокси-сервера остались не заметны.Код: Выделить всё
TCP 192.168.0.100:1083 77.73.24.4:80 in via fxp0 #пакет доходит до правила №2, по которому переправляется на вход локального прокси-сервера, далее уже знакомая картина самостоятельной обработки прокси-сервером запроса: TCP 195.34.32.55:57415 77.73.24.4:80 out via fxp1 TCP 77.73.24.4:80 195.34.32.55:57415 in via fxp1 #И наконец ответ хосту, а здесь уже интересно, поскольку прокси-сервер ответил следующим образом: Count TCP 77.73.24.4:80 192.168.0.100:1083 out via fxp0
Опять оговоримся: в примере рассмотрен только принцип прохождения пакета, реальный лог гораздо сложнее.
Вывод №4. О последствиях того или иного решения.
При использовании прозрачного прокси-сервера, в правилах IPFW должно присутствовать что-то вида:Код: Выделить всё
{fwcmd} add allow tcp from {MyLan} to any 80 in via {iif} {fwcmd} add allow tcp from any 80 to {MyLan} out via {iif}
правда на практике чаще пишут :-):, в то время как с обычным прокси вполне обойдемся более безопасной строкой:Код: Выделить всё
{fwcmd} add allow tcp from any to any via ${iif}
.Код: Выделить всё
{fwcmd} add allow ip from {MyLan} to {MyLan} via {iif}
Очень хочется думать, что помог вам в создании безопасных, а главное осознанных правил IPFW, спасибо всем кто сумел сие прочитать, за сим откланиваюсь.
-cat- ICQ: 328908584 e-mail: mfoleg@gmail.com
P.S. Автор сознательно не использовал конструкции setup-established и check-state keep-state.
setup-established запутали бы процесс понимания правил
keep-state же требует отдельного исследования.
P.P.S. В процессе сочинения сего использовался вывод работающего IPFW, а именно строчка {fwcmd} add count log all from any to any, а также различные виртуальные машины.
Совпадения IP - реальных случайны, случайных - не реальны..